jyamethyst21 님의 블로그

SQL Injection (Union, Error-Based, Blind SQL Injection) 본문

보안 & IT 지식 🌺

SQL Injection (Union, Error-Based, Blind SQL Injection)

jyamethyst21 2026. 4. 20. 01:01

SQL Injection

SQL Injection은 사용자가 입력한 값이 SQL 문장 안에 그대로 포함될 때 발생한다.
정상적인 검색 기능은 원래 사용자가 입력한 값을 단순한 문자열로 처리해야 하지만, 입력값 안에 ', AND, UNION, SELECT, -- 같은 SQL 문법이 포함되면 데이터베이스는 그것을 단순 문자열이 아니라 SQL 구문의 일부로 인식하게 된다.
이때 공격자는 원래 개발자가 의도한 조회 범위를 벗어나 추가 조건을 넣거나, 다른 테이블을 조회하거나, 에러를 강제로 발생시켜 내부 정보를 얻을 수 있다.

1. UNION SQL Injection

UNION 기반 인젝션은 기존 SELECT 문의 결과와 공격자가 만든 SELECT 결과를 하나로 합치는 방식이다. 

UNION은 양쪽 SELECT의 컬럼 개수가 반드시 같아야 하므로, 원래 쿼리가 몇 개의 컬럼을 반환하는지 먼저 알아야 한다. 

... ORDER BY 1 --
... ORDER BY 2 --
... ORDER BY 3 --
... ORDER BY 4 --

위와 같이 명령어를 입력하면 컬럼 개수를 파악할 수 있다.

1,2,3까지 정상으로 작동하다가 4에서 에러가 발생하면 실제 결과 컬럼 수는 3개임을 의미한다.

여기서 --는 주석 역할이다.

 

또한 추가로 Union에서는 컬럼 수뿐만이 아니라 각 위치의 데이터 타입 호환이 되어야한다.

그래서 아래와 같은 명령어를 통해서 타입을 하나하나 확인하여야 한다.

예시 쿼리:
SELECT zipcode, dong, address FROM ...
-> 숫자, 문자, 문자형이라면

UNION SELECT 'TEXT', 123, 456 FROM ...
-> 위와 같이 위치를 잘못 맞추면 각 컬럼 자리에 들어가는 데이터 타입이 다르기 때문에 에러가 날 수 있다.

UNION SELECT 1, 'A', 'B' FROM ...
-> 이런 식으로 검색해야 에러가 나지 않으므로, 하나하나 타입을 확인하는 과정을 거쳐야 한다.

 

이후에는 오라클 기준 ALL_TABLES를 통해 테이블 목록을 파악하고 의심가는 테이블을 선정한 뒤 ALL_TAB_COLUMNS를 통해 특정 테이블에 있는 컬럼을 검색한다. 

' UNION SELECT col1, col2, col3 FROM ALL_TAB_COLUMNS WHERE TABLE_NAME='ABC' --

컬럼 검색 명령어는 위와 같다. 이 명령어를 통해 검색하고자 하는 컬럼을 파악했다면 SELECT 구문으로 바로 내용을 확인할 수 있다.

만약 계정 정보를 알아내려고 하는데, 컬럼에 ID, PW와 같은 내용이 있다면 명령어 출력은 아래와 같다.

' UNION SELECT num_col, ID, PW FROM ABC --

 

해당 기법은 결과적으로, 기존 SELECT 문의 결과와 공격자가 만든 SELECT 결과를 하나로 합치는 과정에서 내부 내용을 파악하는 공격이라고 볼 수 있다.

 

2. ERROR-BASED SQL Injection

ERROR 기반 인젝션은 결과를 직접 화면에 띄우지 못하는 상황에서, DB가 발생시키는 오류 메시지 안에 원하는 값을 포함시키는 방식이다. 일반적인 경우 웹 애플리케이션은 SQL 실행 결과만 화면에 보여주고, DB 내부 정보는 숨긴다.
그런데 개발 환경이나 취약한 서버 설정에서는 SQL 오류가 발생했을 때, 그 에러 메시지를 그대로 사용자에게 보여주는 경우가 있다. ERROR-Based Injection은 바로 이 점을 이용한다.

 

즉, 목표는 단순히 쿼리를 실행하는 것이 아니라 내부 SELECT로 원하는 값을 하나 구한 다음, 그 값을 오류를 일으키는 함수에 넣으면 DB가 에러를 내면서 그 값을 메시지에 포함하게 만드는 것이다.

정상적인 입력' AND CTXSYS.DRITHSX.SN(1,(SELECT COUNT(TABLE_NAME) FROM USER_TABLES))=1--

 

이 구문을 부분별로 나누면 다음과 같다.

 

(1) 정상적인 입력'

이 부분은 원래 검색창에 넣던 정상 입력의 연장선이다. 앞부분은 애플리케이션이 기대하는 문자열 형식을 유지하기 위한 역할을 한다.

 

(2) '

작은따옴표는 원래 SQL 문장에서 문자열 리터럴을 닫는 역할을 한다. 즉, 애플리케이션이 내부적으로 WHERE account LIKE '%사용자입력%' 과 같은 구조를 만들고 있다면, 공격자는 '를 넣어서 원래 문자열을 강제로 끝내버린다.

 

(3) AND

이제부터는 문자열이 아니라 새로운 SQL 조건식이 된다. 즉, 원래 WHERE절 뒤에 공격자가 원하는 조건을 덧붙이는 것이다.

 

(4) CTXSYS.DRITHSX.SN(1, (SELECT ...))

이 함수는 특정 상황에서 오류를 발생시키며, 내부 인자로 전달된 값이 에러 내용에 노출될 수 있다. 따라서 공격자는 SELECT 결과를 이 함수의 인자로 넣어, 원하는 값을 오류 메시지로 빼내려는 것이다.

 

(5) =1

전체 식을 비교 연산 형태로 마무리해서 WHERE 절 안에 자연스럽게 들어가게 한다. 

 

(6) --

뒤의 원래 SQL 구문을 주석 처리한다. 이 부분이 없으면 뒤쪽에 남아 있는 '나 다른 조건 때문에 문법 오류가 날 수 있다.

결과적으로 위와 같은 명령어를 통해 출력되는 에러 내용을 기반으로, 원하는 정보를 확인할 수 있다.
 

3. Blind SQL Injection

Blind SQL 인젝션은 데이터베이스 질의 결과가 화면에 직접 표시되지 않는 상황에서, 쿼리의 실행 결과에 따른 서버의 반응(참/거짓)이나 응답 시간 차이를 이용해 데이터를 추적해 나가는 공격 기법이다.

 

먼저 해당 기법을 사용하기 위해서는 찾고자 하는 정보의 길이를 알아야한다.

만약 테이블명을 찾고자 하고, 해당 길이를 알아내기 위해서는 아래와 같은 명령어를 사용한다.

정상적인 내용' and (select length(table_name) from (select table_name, rownum as rnum from user_tables) where rnum = 3) = 8--

 

(1) (select table_name, rownum as rnum from user_tables)

USER_TABLES에서 테이블명 목록을 가져오고, 각 행에 번호를 붙인다. 

각 행의 번호란 일련번호라고 생각하면 된다.

 

(2) where rnum = 3

그중 3번째 테이블 하나만 선택한다.

 

(3) length(table_name)

선택된 테이블명의 문자열 길이를 계산한다.

 

(4) = 8

그 길이가 8과 같은지 검사한다. 그리고 그 전체 조건이 참이면 화면에 결과가 출력되고, 거짓이면 출력되지 않는다.

 

위 명령어에서 '= 8'에 대한 부분을 1씩 증가하면서 값이 출력되는지 확인하고, 값이 출력된다면 해당 길이를 가지고 있다는 것을 확인할 수 있게 된다.

ASCII(SUBSTR(table_name, 1, 1)) = 76

8글자임을 알아냈다면, 그 다음은 위와 같은 명령어를 통해 한글자 한글자를 직접 조회해가면서 해당 글자가 무엇인지 알아내야 한다. 위 내용은 ASCII 값을 기준으로 찾고자 하는 테이블명의 첫번째 자리가 76인가를 묻는 질문이다. 76은 'L'을 의미한다. 이렇게 실행했을 때 값이 출력된다면 첫 번째 글자가 'L'임을 의미한다. 이런식으로 8글자 모두 값을 증가해가면서 대입해서 값을 찾아낸다.

.

.

 

오늘은 세 가지의 SQL Injection 종류를 간단하게 살펴보았다. 해당 공격은 중요도가 높은 공격인만큼 잘 정리해두면 좋을 것 같다.