본문 바로가기
Web/Burp Suite

Burp Suite: SQL Injection (Part 1)

by secumark 2025. 6. 23.
728x90

총 51개 페이지로 이루어져 있다. 

 

 

SQL Injection 공격

공격자가 애플리케이션의 db를 구성하는 쿼리에 접근하기 위하는 것을 허용하게하는 웹 취약점 공격이다. 이는 공격자가 일반적으로 찾아내기 어려운 데이터를 볼 수 있게 한다. 다른 user에게 속한 데이터나, 애플리케이션에서 접속할 수 있는 모든 데이터가 이에 해당하는데, 이 데이터를 수정하거나 삭제할 수도 있어 애플리케이션의 콘텐츠나 행위에 지속적으로 영향을 끼친다.

때로는 공격자는 SQL injection을 통해서 권한 상승 공격을 할 수 있는데, 백엔드 인프라나 서버를 탈취할 수도 있고, dos 공격으로 진화할 수도 있을 정도로 매우 무서운 공격이다. 

 

SQL Inejction 탐지는 어떻게 하는가?

애플리케이션의 모든 입력 지점(entry point)을 체계적으로 테스트함으로써 수동으로 탐지할 수 있다. 이때 일반적으로 다음과 같은 방식으로 테스트를 수행한다.

1. 작은따옴표(') 문자 입력으로 에러 메시지나 비정상적인 동작이 나타나는지 확인함.

2. SQL 구문을 구성하는 특정 문법 사용으로 원래 입력값과 같은 결과를 만드는 쿼리와 다른 결과를 만드는 쿼리를 넣어보고, 애플리케이션 응답에 체계적인 차이가 있는지 확인한다.

3. 참, 거짓(boolean) 조건 입력하기. OR 1=1, OR 1=2 같은 조건을 넣고, 각각의 응답에 차이가 있는지 확인한다.
(예: OR 1=1은 참 → 전체 조회, OR 1=2는 거짓 → 조회 실패)

4. 시간 지연(payload)을 이용한 테스트 → SQL 쿼리 내에 SLEEP(5)처럼 고의로 지연을 발생시키는 구문을 삽입하여
응답 속도의 변화를 관찰한다. 시간 차이가 크면 코드가 실행됐다고 볼 수 있음.


5. OAST (Out-of-band Application Security Testing) payload 사용으로 쿼리 실행 시 외부 네트워크 상호작용이 발생하도록 유도하고, 실제로 해당 요청이 발생하는지를 모니터링한다.

그리고 대부분의 SQL 인젝션 취약점은 Burp Scanner와 같은 도구를 사용하면 빠르고 신뢰성 있게 자동 탐지할 수 있다. 

 

참고로 이 개념은 이전 교육? 에서 SQL Injection 할 때 내용이랑 똑같다. 그래도 다시 정리함!

 

SQL 쿼리 내 다양한 위치에서 발생하는 SQL 인젝션

대부분의 SQL 인젝션 취약점은 SELECT 쿼리의 WHERE 절 안에서 발생한다. 물론 이 위치 말고도 다양한 유형의 쿼리에서도 발견될 수 있다.

UPDATE 문: 갱신되는 값(value) 부분 또는 where 조건절
INSERT 문: 삽입되는 값(value) 부분
SELECT 문: 테이블명이나 컬럼명 또는 ORDER BY 절 (정렬 기준 부분)

 

숨겨진 데이터 찾기

쇼핑 애플리케이션이 있고, 사용자가 다양한 카테고리의 상품들을 볼 수 있다고 가정했을 때, 사용자가 Gifts 카테고리를 클릭하면, 브라우저는 다음과 같은 URL을 요청할 것이다.

https://insecure-website.com/products?category=Gifts


그러면 애플리케이션은 다음과 같은 SQL 쿼리를 통해 해당 카테고리의 상품 정보를 데이터베이스에서 가져오게 된다:

 

SELECT * FROM products WHERE category = 'Gifts' AND released = 1

 

이 쿼리는 다음과 같은 의미를 가진다:

SELECT *: 상품의 모든 정보를
FROM products: products 테이블에서
WHERE category = 'Gifts': 카테고리가 'Gifts'인 상품 중에서
AND released = 1: 출시된 상품만 가져온다 (released = 1은 출시됨을 의미)
즉, released = 1 조건은 출시되지 않은 상품(예: released = 0)을 숨기기 위한 필터로 사용되고 있다.

따라서 공격자가 이 released = 1 조건을 우회하면, 출시되지 않은 상품들도 조회할 수 있게 되는 것이고, 이것이 SQL Injection의 핵심 시나리오 중 하나

 

이때 해당 애플리케이션은 SQL Injection 공격에 대해 어떤 필터링도 하고 있지 않기 때문에 공격자는 어떤 공격이든 시행할 수 있다. 

https://insecure-website.com/products?category=Gifts'--

 

예를 들어 다음과 같은 공격을 한다면

SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1

 

-- 이후로 주석 처리가 되고 있다. 그럼 출시되지 않은 제품도 모두 보이게 될 것. 

https://insecure-website.com/products?category=Gifts'+OR+1=1--

 

연결할때는 +로 모두 연결 (띄어쓰기)

애플리케이션도 모르는 카테고리의 모든 상품을 보이게 할 수도 있음. (모든 값이 참이 되게 하는 조건)

SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1

 

 

Lab

user가 카테고리를 선택하면 애플리케이션은 다음과 같은 SQL 쿼리를 불러올 것임.

SELECT * FROM products WHERE category = 'Gifts' AND released = 1

 

1개 이상의 unreleased products를 불러오기 위한 SQL 인젝션 공격을 수행해보자.

 

Category에 있는 gifts를 눌러보면 다음과 같이 세개의 상품이 뜬다.

 

gifts 뒤에 '--를 붙였더니 하나의 상품이 더 보인다. 아마 unreleased된 상품일 것이다.

'+or+1=1--를 붙였더니 다음과 같이 엄청 많은 상품이 보인다.

 

이게 무슨 차이냐면,

SELECT * FROM products WHERE category = 'Gifts' OR 1=1

 

쿼리가 이런식으로 되니까 products 테이블에 있는 모든 상품을 출력해줘~라는 뜻이된다.

 

애플리케이션 로직 파괴하기 (우회하기)

웹사이트 로그인 시 username과 password를 입력하면, 아래와 같은 SQL 쿼리로 인증 확인

SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'
 

이때 공격자가 username에: administrator'-- 을 입력하고 password에: 빈칸이나 아무 문자열을 입력하면 최종 쿼리는 다음과 같음

SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
--가 주석으로 처리되어 AND부터 모두 무시됨
SELECT * FROM users WHERE username = 'administrator'

 

비밀번호 검증 없이 administrator라는 유저가 있으면 바로 로그인 성공

 

Lab

 

'--로 로그인 성공했다.

 

UNION 기반 SQL Injection

UNION은 SQL에서 두 개 이상의 SELECT 결과를 합치는 데 사용되는 키워드다.

SELECT a, b FROM table1
UNION
SELECT c, d FROM table2


첫 번째 SELECT: table1의 a, b 컬럼 값 반환
두 번째 SELECT: table2의 c, d 컬럼 값 반환
→ 두 결과를 합쳐서 하나의 결과로 보여줌

SELECT name, price FROM products WHERE category = 'Gifts'


이 때 SQL Injection이 가능하다면 

' UNION SELECT username, password FROM users--


결과는 다음과 같다. 

SELECT name, price FROM products WHERE category = '' 
UNION SELECT username, password FROM users--'


결과적으로 products 테이블의 상품 정보 + users 테이블의 사용자 정보가 같이 보여짐

대신 두 *SELECT문의 컬럼 개수, 그리고 타입이 같아야만 작동한다는 점을 참고

 

* SELECT문의 컬럼 개수, 그리고 타입이 같아야만 작동한다는게 무슨 뜻이냐면

원래 쿼리가 

SELECT name, price FROM products WHERE category = 'Gifts'
이와 같을때, name은 문자열, price는 숫자 타입으로 2개의 컬럼을 반환한다고 할 수 있다.
 
그런데 공격자가
' UNION SELECT username FROM users--
이렇게 공격을 시도한다면
SELECT name, price FROM products WHERE category = '' UNION SELECT username FROM users--
 

컬럼이 각각 2개, 1개로 컬럼 개수가 맞지 않아 제대로 된 결과를 얻지 못할 것이다

SELECT name, price FROM products WHERE category = '' UNION SELECT username, 1 FROM users--

 

이렇게 수정하면 컬럼 개수도 같고, 타입도 맞으니 공격에 성공할 것이다. username, 1에서 1은 그냥 더미값으로, price가 숫자니까 2번째 select 문의 2번째 컬럼 타입도 숫자로 맞춰준거다.

 

그래서 UNION 쿼리를 갖추기 위해서 2가지 필수 조건은

1. 컬럼 수가 같아야 할 것

2. 각 컬럼의 데이터 타입이 호환되어야 할 것

 

그래서 공격 전에 원래 쿼리가 몇 개의 컬럼을 반환하는지, (Order By 1, 2, 3 식으로 보내볼 수 있음) 그리고 어떤 컬럼이 문자열 컬럼인지 (union select null, 'test', null 이런식으로) 문자열이 보이는 자리가 출력되는 문자 타입 컬럼임.

참고로 null은 어떤 유형에도 넣을 수 있는 유연한 값

 

원래 쿼리가 몇 개의 컬럼을 반환하는지 알아내기

위에서 잠깐 언급한대로, 원래 쿼리가 몇 개의 컬럼을 반환하는지 알아내는 법을 자세하게 공부해본다. 방법은 크게 두 가지로 나뉘는데 먼저 첫 번째는 Order by 절의 카운트 수를 점점 늘려가면서 에러가 어디서 발생하는지 확인하는 것이다. 

 

예를 들어, 인젝션 지점이 WHERE 절 안의 문자열이라고 하면, 아래와 같이 요청을 날릴 것이다.

' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--
… (계속 증가)

이런 요청들은 원래의 쿼리에 정렬 기준이 되는 컬럼을 추가하는 것이다. 참고로 여기서 order by라는게 뭐냐면 ORDER BY는 SELECT 쿼리의 결과를 특정 컬럼을 기준으로 오름차순(ASC) 또는 내림차순(DESC) 으로 정렬하는 것이다. 디폴트는 오름차순이라 ASC를 생략해서 적는다.

SELECT * FROM products
ORDER BY price ASC;

 

이렇게 적혀있으면 가격 기준으로 Products를 정렬할 것이다.


ORDER BY 절에서는 컬럼 이름을 몰라도 컬럼 순서(index) 로 지정이 가능하기 때문에  지정한 인덱스가 존재하지 않는 컬럼 번호를 가리키면, 데이터베이스는 이런 식의 에러를 반환할 것이다.

 

The ORDER BY position number 3 is out of range of the number of items in the select list.

애플리케이션이 DB 에러를 직접 보여주는 경우거나, 일반적인 에러 페이지를 보여주는 경우거나, 아니면 결과가 아예 안 나오는 경우 등 이런 차이를 관찰하면서 쿼리에서 반환되는 컬럼 수가 몇 개인지 추론할 수 있게된다.

 

2번째 방법은 UNION SELECT를 쓸 때 NULL 값을 늘려가며 컬럼 개수를 맞추는 것이다.

' UNION SELECT NULL-- → 1개 컬럼
' UNION SELECT NULL,NULL-- → 2개 컬럼 
' UNION SELECT NULL,NULL,NULL-- → 3개 컬럼
 
오류가 안 날 때까지 반복해서 오류가 사라지면 컬럼 수를 맞추는데 성공할 것이다. 
 
* ORDER BY 기법과 마찬가지로, 애플리케이션이 데이터베이스 오류를 HTTP 응답에 그대로 보여줄 수도 있고, 반대로 일반적인 오류 메시지만 반환하거나, 아무 결과도 보여주지 않을 수도 있다. NULL 값의 개수가 실제 컬럼 개수와 정확히 일치할 경우, 데이터베이스는 모든 컬럼에 NULL이 들어 있는 추가 행을 결과로 반환할 것이다.
이 결과가 HTTP 응답에 어떤 영향을 주는지는, 애플리케이션의 코드에 따라 달라진다. 운이 좋으면 응답에 HTML 테이블의 추가 행 같은 새로운 내용도 나타난다. 반대로 NULL 값이 NullPointerException 같은 다른 오류를 유발할 수도 있다.
최악의 경우, 컬럼 수가 틀렸을 때와 똑같은 응답이 나올 수 있어서 이 방법만으로는 유효한 컬럼 수를 파악할 수 없게 되어 효과가 없어질 수도 있음

 

 

Lab

SQL 인젝션 UNION 공격으로 쿼리가 반환하는 컬럼 수 알아내는 실습

 

Product 카테고리에 SQL Injection 취약점이 있다. 쿼리 결과가 애플리케이션의 응답에 그대로 반환되므로 다른 테이블의 데이터를 조회하기 위해 UNION 공격을 사용할 수 있다. 
해당 공격의 첫 번째 단계는 해당 쿼리가 반환하는 컬럼 수를 파악하는 것으로 이후 실습에서는 이 기법을 활용해 실제 공격을 해볼 예정이다. 

NULL 값이 포함된 추가 행을 반환하는 SQL Union 인젝션 공격을 수행해 쿼리가 반환하는 컬럼의 개수를 알아내보자.

 

메인 화면, 그리고 Accessories를 클릭했을때 패킷을 가로챘다.

 

'+UNION+SELECT+NULL--

 

 

Internal Server Error 가 발생한다.

 

이번에는 '+UNION+SELECT+NULL,NULL--을 해봤다.

 

또 오류가 뜬다.

 

NULL을 세개를 해봤더니

 

다음과 같이 나온다.

 

DBMS별 문법 차이

SQL 주입(SQL Injection) 공격을 할 때, 사용하는 DBMS(데이터베이스 종류)별 문법 차이는 다음과 가탇.

 

Oracle

Oracle DB에서는 SELECT문에 반드시 FROM 절이 있어야 한다. 

' UNION SELECT NULL FROM DUAL--


이런식으로 내장된 테이블인 DUAL을 활용하면 된다. 

 

MySQL

주석(--) 사용시 -- 이후에 반드시 공백이 한 칸 이상 있어야 한다.

SELECT * FROM users WHERE id = 1 # 주석 시작

 

또는 #로 주석을 사용할 수 있음


더 많은 DB별 차이나 페이로드는 SQL Injection Cheat Sheet에서 확인 가능하다.
https://portswigger.net/web-security/sql-injection/cheat-sheet

 

SQL injection cheat sheet | Web Security Academy

This SQL injection cheat sheet contains examples of useful syntax that you can use to perform a variety of tasks that often arise when performing SQL ...

portswigger.net

 

유용한 데이터 타입을 가진 열 찾기

SQL 인젝션 UNION 공격은 삽입된 쿼리 결과를 가져오는 것인데, 이런 데이터는 일반적으로 문자열 형식(string form) 으로 되어 있다. 원래 쿼리 결과에서 데이터 타입이 문자열이거나 문자열과 호환되는 하나 이상의 열(column)을 찾아야 한다는 것을 의미한다.

 

필요한 열의 개수를 알아내고, 각 열이 문자열 데이터를 담을 수 있는지 데스트하고자 각 열을 하나씩 확인할 수 있는데, 이때 각 열에 문자열 값을 하나씩 넣는 방식의 UNION SELECT 페이로드를 사용할 수 있다. 

 

만약 쿼리가 4개의 열을 반환한다면

' UNION SELECT 'a',NULL,NULL,NULL--
' UNION SELECT NULL,'a',NULL,NULL--
' UNION SELECT NULL,NULL,'a',NULL--
' UNION SELECT NULL,NULL,NULL,'a'--

 

이런식으로 첫번째문자열에 a, 뒤에는 모두 NULL을 넣고, 그 값을 찾았으면 두번째 문자열로 넘어감.

 

데이터 타입이 문자열과 호환이 안되면 DB오류가 발생한다. 

 

Conversion failed when converting the varchar value 'a' to data type int.

 

이때 오류가 발생하지 않고, 애플리케이션의 응답에 삽입한 문자열 값이 포함된 추가적인 콘텐츠가 출력된다면 이 Column은 문자열 데이터를 가져오는 데 적합한 열이라는 것을 알 수 있다.

 

Lab

SQL 인젝션 UNION 공격 – 문자열을 포함할 수 있는 열 찾기

상품 category 필터 기능에 sql injection 취약점이 있으니까 union 연산자를 이용해 db의 다른 테이블에서 데이터를 가져올 수 있다. 이 공격을 성공하려면 먼저 기존 쿼리가 반환하는 컬럼(열)의 개수를 알아내야 한다.

ORDER BY, UNION SELECT NULL을 사용하면 된다. 그리고 다음 단계로 문자열 데이터를 담을 수 있는 컬럼을 식별해보자. 


해당 Lab에서는 랜덤 문자열 값 하나를 제공하는데, 이 값을 쿼리 결과 안에 나타나게 해야 한다. (이를 통해서 어떤 컬럼이 문자열 타입과 호환되는지 판별 가능

 

 

db에서 AdFvUk 문자열을 찾아내라고 한다.

 

일단 이전 LAB에서 해당 카테고리의 컬럼이 3개라는 것을 알았으니 다음과 같이 '+UNION+SELECT+NULL,NULL,NULL-- 을 입력해주었다.

 

다음과 같은 결과가 나온다.

컬럼의 개수를 알았으니, AdFvUk 문자열이 속한 컬럼이 어디인지 알아내면 된다.

 

Internal server error 발생

 

 

어쨌든 여기서 목적은 랜덤 문자열이 뭐든간에 문자열 타입을 쓰는 컬럼이 뭔지 찾는게 목적인거다!

 

 

16/51까지 완료

출처: burp suite

 

728x90

댓글