📄 2026.01.30 (Day 67) - Python을 활용한 Blind SQL Injection 자동화 공격


1. 핵심 개념 정리

Blind SQL Injection 공격 기법

# 핵심 개념 설명 실무/보안 관점
1 Blind SQL Injection 쿼리 결과가 직접 반환되지 않고 참/거짓 반응으로만 데이터를 추출하는 기법 일반 SQL Injection보다 탐지가 어렵고 자동화 도구 없이는 공격이 비효율적
2 Boolean-based Blind 조건문의 참/거짓 결과에 따라 페이지 응답이 달라지는 것을 이용 정상 응답과 비정상 응답의 패턴 차이로 탐지 가능
3 Binary Search(이진탐색) 1127 범위를 매번 절반으로 나눠 78회 만에 ASCII 값 특정 공격 시간을 획기적으로 단축 (127회 -> 7회)
4 Oracle vs MySQL 차이 Oracle: user, substr() / MySQL: user(), substring() DBMS별 함수 차이를 이용한 핑거프린팅 가능
5 Brute Force 공격 가능한 모든 조합을 시도하여 비밀번호를 찾는 무차별 대입 공격 계정 잠금 정책, rate limiting으로 방어 필요

Python requests 모듈 활용

# 핵심 개념 설명 실무/보안 관점
6 requests.get() HTTP GET 요청을 보내고 응답을 받는 메서드 URL 파라미터를 통한 SQL Injection 자동화에 활용
7 requests.post() HTTP POST 요청으로 데이터를 전송하는 메서드 로그인 폼, 검색 등 POST 기반 공격 자동화
8 Cookie/Header 설정 세션 유지를 위한 쿠키, 요청 헤더 커스터마이징 인증이 필요한 페이지 공격 시 필수
9 응답 텍스트 분석 응답.text에서 특정 문자열 포함 여부로 참/거짓 판단 “New MacBook Pro” 같은 정상 응답 마커 활용
10 반복문 자동화 for, while을 이용한 대량 요청 자동 전송 수동으로는 불가능한 속도로 데이터 추출

SQL Injection 공격 포인트 탐지

# 핵심 개념 설명 실무/보안 관점
11 검색창 공격 like ‘%검색어%’ 구문에서 %’ or ’e’=‘q% 삽입 가장 흔한 공격 벡터, 입력값 검증 필수
12 상세페이지 ID 파라미터 ?id=62 파라미터에 and (공격쿼리) 추가 숫자형 파라미터도 문자열 검증 우회 가능
13 로그인 폼 우회 _csrf 토큰 재사용, 세션 유지로 무차별 대입 CSRF 토큰도 제대로 검증 안 하면 무용지물
14 에러 메시지 분석 “권한이 없습니다”, “로그인 실패” 등 응답 차이 활용 에러 메시지 통일로 정보 노출 최소화 필요
15 쿼리 구조 유추 공격 성공 패턴으로 백엔드 쿼리문 역추적 개발자가 작성한 쿼리 구조 파악으로 정밀 공격

2. 실습 내용 정리

실습 1: 이진탐색 알고리즘 구현

목표: ASCII 값을 7~8회 만에 찾는 효율적 탐색 알고리즘 이해

이진탐색 로직 (Python):

초기 범위 설정:

  • 아스키 = 1 (실제 탐색할 값)
  • 시작점 = 1, 끝점 = 127

이진탐색 루프:

  • while 시작점 < 끝점:
    • 중간점 = int((시작점+끝점)/2)
    • 아스키 > 중간점이면: 시작점 = 중간점 + 1 (상위 절반 탐색)
    • 아스키 <= 중간점이면: 끝점 = 중간점 (하위 절반 탐색)
  • 종료 시 시작점 = 목표 ASCII 값

알고리즘 특성:

  • 127회 선형 탐색 -> 7회 이진탐색으로 효율 극대화
  • 중간점 계산: (시작+끝)/2를 정수로 변환
  • 범위 좁히기: 참이면 시작점 상승, 거짓이면 끝점 하강
  • 종료 조건: 시작점 == 끝점일 때 값 확정

보안 인사이트:

  • 공격자 입장에서는 시간 효율성이 매우 중요 (빠른 데이터 탈취)
  • 방어자 입장에서는 반복 요청 패턴 탐지가 핵심 (7~10회 규칙적 요청)
  • 이진탐색 특유의 중간값 테스트 패턴을 WAF에서 시그니처로 활용 가능

실습 2: Blind SQL Injection 참/거짓 테스트

목표: 수동으로 Boolean-based Blind SQL Injection 원리 파악

실습 환경:

  • 대상: 쇼핑몰 상품 상세 페이지 (Oracle DB)
  • URL 파라미터: ?id=62
  • 세션 유지: JSESSIONID 쿠키

공격 구조:

URL 구성: …/detail?id=62 and ({공격쿼리})

쿠키 설정: JSESSIONID = 세션ID값

응답 분석:

  • “New MacBook Pro” 포함 시 -> 참!
  • 포함 안 될 시 -> 거짓…

Boolean-based Blind 원리:

  1. 정상 쿼리: SELECT * FROM 상품상세 WHERE 상품번호 = 62 -> “New MacBook Pro” 상품 정보 반환
  2. 참 조건 삽입: WHERE 상품번호 = 62 and (length(user) > 0) -> 조건이 참이면 동일하게 상품 정보 반환
  3. 거짓 조건 삽입: WHERE 상품번호 = 62 and (1=2) -> 조건이 거짓이면 아무것도 반환 안 됨

발견 가능한 정보:

  • DB 사용자명 길이: length(user) > X
  • 사용자명 각 문자: ascii(substr(user,1,1)) > X
  • 테이블 개수: (select count(table_name) from user_tables) > X
  • 권한 정보: (select 1 from dual where user=‘SYSTEM’)

탐지 패턴:

  • 동일 IP에서 동일 URL로 수십~수백 회 반복 요청
  • URL 파라미터에 SQL 함수명 포함 (length, substr, ascii)
  • 응답 크기는 다르지만 HTTP 상태 코드는 200 OK

실습 3: 자동화 유저명 탈취 스크립트

목표: 이진탐색을 활용한 완전 자동화 데이터 추출

스크립트 구조:

이진탐색 함수:

  • def 이진탐색(쿼리): 시작점=1, 끝점=127
  • while 시작점 < 끝점: 중간점 계산 후 요청 전송
  • “New MacBook Pro” 포함 시: 시작점 = 중간점 + 1
  • 미포함 시: 끝점 = 중간점
  • return 시작점 (최종 ASCII 값)

유저명 탈취 흐름:

  1. 글자수 = 이진탐색(“length(user)”) -> 출력: 유저명 글자수
  2. for 글자순번 in range(1, 글자수+1): 이진탐색(“ascii(substr(user,{글자순번},1))”) -> chr(아스키) 출력
  3. 테이블수 = 이진탐색(“select count(table_name) from user_tables”) -> 출력: 테이블개수

자동화 효과:

  • 수동: 5글자 유저명 추출에 5×127 = 635회 요청 필요
  • 자동: 5글자 유저명 추출에 5×7 = 35회 요청 (약 18배 효율)
  • 실행 시간: 수동 1시간 -> 자동 10초 이내

보안 인사이트:

  • 함수화로 재사용성 극대화 (유저명, 테이블명, 컬럼명 모두 동일 로직)
  • 공격자는 이미 완전 자동화 도구 보유 (sqlmap 등)
  • 수동 공격만 막는다고 안전한 게 아님, 근본적 취약점 제거 필요

실습 4: Brute Force 로그인 공격

목표: POST 요청 자동화로 비밀번호 무차별 대입

실습 환경:

  • 대상: 쇼핑몰 로그인 페이지
  • 아이디: admin (고정)
  • 비밀번호 범위: 0700~0999 (4자리 숫자)

스크립트 구조:

요청 설정:

  • 헤더: Content-Type: application/x-www-form-urlencoded
  • 쿠키: JSESSIONID 세션 ID
  • 데이터: _csrf 토큰, memberid=admin, password=비밀번호

반복문:

  • for i in range(700, 1000): pw = str(i).zfill(4) (0700, 0701, … 형식)
  • 데이터[“password”] = pw
  • requests.post(url, headers, cookies, data)
  • ‘로그인에 실패했습니다.’ 포함 시: 비밀번호 틀림 출력
  • 미포함 시: “비밀번호 찾았다! {pw}” 출력 후 break

발견된 취약점:

  • CSRF 토큰 재사용 가능 (토큰 갱신 없음)
  • 계정 잠금 정책 부재 (무제한 시도 가능)
  • Rate Limiting 없음 (초당 수십 회 요청 가능)
  • 실패 응답 명확히 노출 (공격자에게 힌트 제공)

탐지 패턴:

  • 동일 계정으로 짧은 시간 내 다수 로그인 시도
  • 순차적 비밀번호 패턴 (0700, 0701, 0702…)
  • 동일 IP/세션에서 CSRF 토큰 재사용

방어 방법:

  • 5회 실패 시 계정 임시 잠금 (15분)
  • CAPTCHA 추가 (자동화 방지)
  • CSRF 토큰 매 요청마다 재생성
  • IP 기반 Rate Limiting (1분당 10회)

3. 공격 기법 비교 표

SQL Injection vs Brute Force 비교

항목 Blind SQL Injection Brute Force 일반 SQL Injection 사용 시기/적용 방안
공격 대상 DB 쿼리 결과 간접 추출 인증 시스템 직접 공격 DB 직접 결과 조회 SQL Injection은 데이터 탈취, Brute Force는 계정 탈취
탐지 난이도 높음 (에러 없이 정상 요청) 중간 (로그 패턴 명확) 낮음 (에러 메시지 노출) Blind는 WAF 우회 용이, 일반은 시그니처 탐지 쉬움
공격 속도 느림 (수십~수백 회 요청) 빠름 (단순 반복) 빠름 (1~2회 성공) 시간 제약 없으면 Blind도 충분히 효과적
필요 조건 SQL 취약점 존재 약한 비밀번호 정책 SQL 에러 메시지 노출 취약점 종류에 따라 기법 선택

DBMS별 함수 차이

예시 Oracle MySQL SQLite 보안 영향
사용자 조회 user user() N/A DB 핑거프린팅 가능
문자열 추출 substr(str,1,1) substring(str,1,1) substr(str,1,1) 함수명 차이로 DBMS 식별
문자열 길이 length(str) length(str) length(str) 공통 함수는 범용 공격 가능
테이블 목록 user_tables information_schema.tables sqlite_master 메타 정보 위치 차이

4. 심화 분석

Python 자동화 스크립트 구조 분석

구분 이진탐색 함수 반복문 로직 응답 분석 분석/인사이트
핵심 역할 ASCII 값 7회 만에 특정 글자 수만큼 반복 호출 참/거짓 판별 기준 세 요소가 유기적으로 결합되어 완전 자동화
재사용성 모든 수치 추출에 활용 문자열 길이만큼 동적 응답 마커만 변경 함수화로 코드 중복 최소화
성능 O(log n) 복잡도 O(n) 글자 수 O(1) 문자열 검색 전체 성능은 O(n log m), n=글자수, m=ASCII범위

취약점 시나리오 핵심 코드 설명

Boolean-based Blind SQL Injection 취약한 쿼리:

  • query = “SELECT * FROM products WHERE id = " + user_input (직접 삽입)
  • 공격: user_input = “62 and ascii(substr(user,1,1)) > 64”
  • 결과: DB 사용자명 첫 글자 ASCII 값이 64보다 큰지 확인 가능

Brute Force 로그인 취약한 코드:

  • if username == “admin” and password == db_password: login_success() (계정 잠금 없음)
  • 공격: for pw in range(0000, 10000): try_login(“admin”, str(pw).zfill(4))
  • 결과: 계정 잠금 없으면 모든 조합 시도 가능

방어 기법 (Prepared Statement):

  • cursor.execute(“SELECT * FROM products WHERE id = ?”, (user_input,))
  • 파라미터 바인딩으로 SQL 구문 분리, 인젝션 불가능

5. 실무/보안 적용

보안 전문가 관점 - 탐지/대응 포인트

단계/유형 탐지 포인트 로그 예시 대응 방안
Blind SQL Injection 동일 세션 반복 요청 (50+), URL에 SQL 함수 포함, 응답 크기 일정하나 빈도 높음 GET /detail?id=62 and (length(user))>5 WAF 룰셋에 SQL 함수 시그니처 추가, 세션별 동일 URL 요청 제한, 개발팀에 파라미터 바인딩 적용 요청
Brute Force 동일 계정 다수 로그인 실패, 순차적 비밀번호 패턴, 단시간 대량 POST 요청 POST /login password=0700, 0701, 0702 5회 실패 시 15분 계정 잠금, CAPTCHA 또는 2FA 적용, 비밀번호 복잡도 정책 강화
자동화 도구 사용 User-Agent: Python-requests, 일정한 요청 간격 (자동화), CSRF 토큰 재사용 패턴 User-Agent: python-requests/2.28.0 User-Agent 검증 (비정상 차단), Rate Limiting 적용, CSRF 토큰 매 요청 재생성

WAF 룰셋 예시

ModSecurity 룰셋 주요 항목:

  1. SQL 함수 탐지: length, substr, ascii, substring, concat, user
  2. 반복 요청 제한: 동일 IP에서 60초 내 30회 이상 요청 차단 (429 응답)
  3. Python requests User-Agent 차단

웹 애플리케이션 보안 체크리스트

SQL Injection 방어:

  • 모든 DB 쿼리를 파라미터 바인딩으로 변경
  • ORM 사용 시에도 raw query 금지
  • 화이트리스트 기반 검증 (숫자는 isdigit() 확인)
  • 프로덕션 환경에서 상세 SQL 에러 비노출, 일반적 에러 메시지로 통일
  • DB 계정은 필요한 테이블만 접근 가능, 웹 애플리케이션 계정으로 system 테이블 조회 불가

Brute Force 방어:

  • 5회 실패 시 계정 임시 잠금
  • CAPTCHA 추가
  • CSRF 토큰 매 요청 재생성
  • IP 기반 Rate Limiting

6. 배운 점 및 인사이트

새로 알게 된 점

  • 이진탐색의 실전 활용: 단순 알고리즘 문제가 아니라 실제 공격 효율성을 127배 향상시키는 핵심 기술임을 체감했습니다.
  • Python requests 모듈의 강력함: HTTP 통신을 완전 자동화할 수 있어 수동으로 불가능한 공격도 스크립트로 실현 가능함을 확인했습니다.
  • Blind SQL Injection의 정교함: 에러 메시지 없이도 참/거짓 응답 차이만으로 모든 데이터를 추출할 수 있다는 점이 놀라웠습니다.
  • DBMS별 함수 차이 활용: Oracle의 user와 MySQL의 user() 차이로 대상 시스템을 핑거프린팅하는 기법을 이해했습니다.
  • 자동화 스크립트의 필수성: 현대 공격은 이미 완전 자동화되어 있으며, 보안 전문가도 자동화 도구를 다룰 줄 알아야 함을 깨달았습니다.

이전 학습과의 연결고리

  • SQL Injection 기초와 연계: Union-based, Error-based에서 학습한 SQL 함수들을 Blind 공격에서도 그대로 활용했습니다.
  • SIEM 로그 분석 확장: 이전에 배운 로그 분석 기법으로 오늘 실습한 공격 패턴들을 탐지하는 룰셋 작성이 가능해졌습니다.
  • 웹 취약점 스캐너와 연계: 이진탐색 기반 자동화 스크립트 원리가 SQLMap 등의 자동화 도구와 동일한 메커니즘임을 이해했습니다.

실무 적용 아이디어

보안 전문가 관점:

  • 공격 시뮬레이션 자동화: 오늘 배운 스크립트 기법으로 모의해킹 시 반복 작업 자동화 가능
  • SIEM 탐지 룰 검증: 직접 공격 스크립트를 돌려보고 Wazuh/Splunk에서 제대로 탐지되는지 테스트
  • 취약점 재현 PoC 작성: 발견한 취약점을 개발팀에 전달할 때 Python 스크립트로 명확히 재현 가능

침해사고 대응 관점:

  • 로그 패턴 분석 자동화: 침해사고 발생 시 로그에서 이진탐색 패턴, 순차 비밀번호 시도 등을 자동 탐지하는 스크립트 작성
  • 공격자 IP 추적: requests 라이브러리 특유의 User-Agent 패턴으로 자동화 공격 IP를 빠르게 식별

7. Quick Reference

Python requests 자동화 명령어 모음

GET 요청 기본:

POST 요청 (로그인):

  • 응답 = requests.post(url=주소, headers=헤더, data=데이터)

응답 분석:

  • if “특정문자열” in 응답.text: print(“조건 만족”)

이진탐색 구현:

  • while 시작점 < 끝점:
    • 중간점 = int((시작점+끝점)/2)
    • if 조건 만족: 시작점 = 중간점 + 1
    • else: 끝점 = 중간점

반복문 + zfill (숫자 패딩):

  • for i in range(700, 1000): pw = str(i).zfill(4) -> 0700, 0701, …

Blind SQL Injection 페이로드 요약표

구분 Oracle MySQL 핵심 키워드 적용 방법
사용자명 length(user) length(user()) DB 계정 확인 and length(user)>5
문자 추출 ascii(substr(user,1,1)) ascii(substring(user(),1,1)) 한 글자씩 ASCII and ascii(substr(…))>64
테이블 수 count(table_name) from user_tables count(*) from information_schema.tables 메타 정보 and (select count(*))>10
컬럼 존재 count(*) from 테이블 where 컬럼 is not null 동일 특정 컬럼 유무 and (select count(*) from…)>0

공격 탐지 체크리스트

WAF 룰셋:

  • SQL 함수 시그니처 (length, substr, ascii)
  • 반복 요청 패턴 (30회/분 이상)
  • Python requests User-Agent 차단
  • 파라미터에 괄호/논리 연산자 포함

애플리케이션 방어:

  • Prepared Statement 사용
  • 입력값 화이트리스트 검증
  • 에러 메시지 일반화
  • 계정 잠금 정책 (5회 실패)

로그 모니터링:

  • 동일 세션 50+ 요청
  • SQL 함수명 포함 URL
  • 로그인 연속 실패 패턴
  • CSRF 토큰 재사용 시도

8. 트러블슈팅

문제 원인 해결 방법
“권한이 없습니다” 응답 쿼리 구문 오류로 DB 에러 발생 괄호 닫기 확인, 쿼리 문법 재검증, 테스트 환경에서 먼저 검증
이진탐색이 무한루프 시작점 < 끝점 조건 미충족 초기 범위 확인 (1~127), 중간점 계산 로직 검증, 디버깅 print로 값 추적
세션 만료 에러 JSESSIONID 쿠키 유효기간 초과 브라우저에서 새 세션 복사, 스크립트에 세션 갱신 로직 추가, 타임아웃 시간 단축
zfill() 적용 안 됨 str() 변환 누락 str(i).zfill(4) 순서 확인, int를 먼저 str로 변환 후 zfill

Today’s Insight:

오늘 실습을 통해 공격자가 얼마나 효율적으로 데이터를 탈취할 수 있는지 몸소 체감했다. 단순해 보이는 이진탐색 알고리즘이 실제 공격에서는 시간을 127배 단축시키는 게임 체인저였고, Python 몇 줄로 수백 번의 요청을 자동화하니 수동 공격과는 차원이 달랐다. 특히 Blind SQL Injection은 에러 메시지조차 없는데도 참/거짓 응답 차이만으로 모든 걸 추출할 수 있다는 점에서, 방어자는 단순히 에러 메시지만 숨긴다고 안전한 게 아니라는 걸 깨달았다. 앞으로 보안 전문가로서 이런 자동화 공격 패턴을 SIEM에서 탐지하고, 개발팀에게는 Prepared Statement의 중요성을 설득할 수 있는 근거를 확보했다는 점에서 매우 값진 하루였다.