使用mysqli_real_escape_string仍有SQL注入风险?能否绕过登录验证?
Short answer: Yes—you can exploit a timing vulnerability in the password verification logic to guess the target user's password, and you can also simplify the query to match any domain for the user. Let's break this down step by step:
Why Your Initial SQL Injection Attempt Failed
Your try with domain = "' union (SELECT 1, 123456) # a" didn't work because mysqli_real_escape_string() escapes the single quote in your payload. The function turns ' into \', so the resulting SQL query becomes:
SELECT user_id, password FROM users WHERE username = 'Sam' AND domain LIKE '\' union (SELECT 1, 123456) # a'
This is looking for a domain that literally matches that escaped string (which doesn't exist), so your injection doesn't alter the query's logic like you intended. Regular single-quote-based SQL injection is blocked here.
The Critical Flaw: Timing Attack on Password Verification
The password check code is vulnerable to a timing attack, and this is your path to bypass. Here's why:
- The code compares each character of the input password to the stored password one by one, with a
usleep(100000)(0.1 second) delay between every check. - If your input's nth character is wrong, the loop stops right away, returning
FALSEafter ~0.1 * n seconds. - If the nth character is correct, it moves to the next character, adding another 0.1 seconds to the total response time.
How to Guess Sam's Password with This:
- Start with the first character: Try
pwd = "A". If the response is fast (~0.1s), that character is wrong. Trypwd = "S"—if the response takes ~0.2s, that means the first character was correct, and the loop failed on the second character. - Repeat for each position: For every character in the password, test all possible values (letters, numbers, symbols) and measure the response time. The character that triggers a longer delay is the correct one for that spot.
- Full match: Once you've guessed all characters correctly, the loop will run through every character without failing, and the function returns
TRUE.
Bonus: Broaden the Domain Match with a Wildcard
Since mysqli_real_escape_string() doesn't escape LIKE wildcard characters (% and _), you can set domain = "%" to match any domain linked to Sam. This changes the query to:
SELECT user_id, password FROM users WHERE username = 'Sam' AND domain LIKE '%'
This will pull Sam's user record no matter what domain they're associated with (assuming Sam exists in the database). Combine this with the timing attack to authenticate as Sam easily.
Are There Other Bypass Methods?
- Empty password bypass: No, the code checks
empty($password)upfront, so an empty input gets rejected immediately. - Blind SQL injection via query timing: Unlikely—all timing differences come from the password check loop, not the SQL execution itself, so you can't leverage query delays to extract data that way.
内容的提问来源于stack exchange,提问作者keyblade95

