본문 바로가기
WEB HACKING/웹 해킹[이론]

SQL Injection : Union SQL Injection

by madevth 2021. 10. 28.
반응형

앞선 포스팅에서 SQL Injection을 사용한 인증 우회에 관해 알아보았다.

SQL Injection으로는 인증 우회뿐만 아니라 데이터 탈취도 가능한데, 대표적인 방식으로는

1. Union SQL Injection

2. Error Based SQL Injection

3. Blind SQL Injection 등이 있다.

 

일반적으로 SQL Query문의 결과가 화면에 표시되는지(ex - 게시판 검색, 주소 검색) 표시되지 않는지(ex - 로그인)에 따라 Union SQL Injection을 활용할지 Blind SQL Injection을 활용할지 방향을 잡을 수 있다. Error Based Injection은 DB 에러가 표시되어야 사용할 수 있다.

이번 포스팅에서는 Union SQL Injection의 개념과 활용 방법을 알아보자.

 

① Union SQL Injection이란?

Union SQL Injection은 둘 이상의 테이블에 대한 결과를 한 번에 보여주는 Union 연산자를 이용한 SQL Injection이다.

Union 연산자는 두 테이블에서 같은 개수의 Column을 가져와야 하고, (사용하는 DB에 따라) 데이터 형식도 같아야 에러 없이 결과를 출력해준다.

 

 

② Union SQL Injection의 예시

Union SQL Injection 인증 우회는 앞선 포스팅에서도 많이 다루었기 때문에 자세한 설명은 생략한다.

다만 로그인 인증 우회의 경우 ID: ' union select 'value'의 형태로 삽입했다면, 데이터 탈취의 경우 ' union select * from userInfo와 같이 삽입하여 DB에 저장된 테이블의 정보를 출력할 수 있다.

 

 

③ Union SQL Injection을 위해 필요한 정보

Union SQL Injection을 사용하기 위해서는 몇 가지 정보가 필요한데, 우선 기존의 쿼리에서 출력하고 있는 Column의 개수를 알아야 한다. 위의 예시에서는 기존의 쿼리에서 하나의 column (login_pw)을 출력하고 있기 때문에 'union'이라는 값 하나를 넣어주었지만 login_id, login_pw 두 개를 출력한다면 마찬가지로 'union', 'union'과 같이 값 두 개를 넣어주어야 한다. 또한 DB에 저장된 테이블을 출력하고자 한다면

select column_name from table_name;

위와 같은 형태의 Query문을 출력해야 하므로 column_name과 table_name도 알고 있어야 한다.

 

 

④ Union SQL Injection의 공격 Step

이번 공격 Step에서 활용할 SQL 구문은 게시물 중에서 특정 내용을 포함하는 글을 검색하는 기능의 코드이다.

다음과 같은 [board]라는 Table이 있다고 가정하자.

id content views date
1 How to use MySQL 25 2020-10-31
2 How to use Php 9 2018-03-01
3 Amazing Python 97 2021-10-28
4 I like mathematics 19 2021-05-04
select * from board where content = 'How to use MySQL';

위와 같은 Query문의 결과로는 첫 번째 행이 출력될 것이다.

 

하지만 content의 내용을 정확히 검색하지 않더라도, 사용자가 입력한 구문을 포함하기만 하면 결과를 출력하도록 작성할 수도 있다. like와 %를 이용하면 된다.

select * from board where content like '%ma%';

위와 같은 Query문의 결과로는 content에 ma가 포함된 행, 즉 3번째와 4번째 행이 출력된다.

 

우리가 공격할 사이트의 게시판에서 다음과 같은 SQL Query를 사용한다고 가정하자.

select ? from board where content like '%$content%';

사용하는 column의 개수를 모르기 때문에  ?로 적어주었다.

 

STEP 1. 취약점 확인

첫 번째 스텝에서는 SQL Injection이 가능한지 확인을 해주어야 한다.

여러 가지 방법이 있겠지만 hello를 넣었을 때와 hel' + 'lo로 넣었을 때 차이가 발생하는지(DB에 따라 문자열 분리 방식이 다르다. MySQL의 경우 concat을 사용한다.), and 1 = 1을 추가했을 때 영향을 미치는지 등으로 SQL Injection의 여지가 있는지 체크해준다.

 

STEP 2. Column 개수를 알아내자! (Feat. Order by)

SQL Injection이 발생하는 환경이라는 가정하에 Column의 개수를 알아내 보자.

여기서 말하는 Column의 개수란, 기존의 코드 [select ? from]의 ? 부분에서 몇 개의 Column을 선택하고 있는지를 의미한다. 이는 order by를 사용하면 알아낼 수 있다.

 

order by는 기본적으로 order by [Column명] ASC / DESC의 형태로 Query문의 끝에 붙는다.

'[Column 명]을 기준으로 ASC(오름차순)/DESC(내림차순)으로 정렬해줘'라는 의미이다.

예를 들어 위의 board 테이블에서 다음과 같은 SQL 구문을 입력한다면

select * from board order by date ASC;
id content views date
2 How to use Php 9 2018-03-01
1 How to use MySQL 25 2020-10-31
4 I like mathematics 19 2021-05-04
3 Amazing Python 97 2021-10-28

위와 같이 date Column을 기준으로 오름차순으로 정렬된다.

 

문제는 Column 명이 아니라 Column의 index 번호로도 정렬이 가능하다는 것이다.

위의 Table에서 views가 3번째 Column이므로 다음과 같은 SQL 구문을 입력한다면

select * from board order by 3;
id content views date
2 How to use Php 9 2018-03-01
4 I like mathematics 19 2021-05-04
1 How to use MySQL 25 2020-10-31
3 Amazing Python 97 2021-10-28

다음과 같이 views를 기준으로 오름차순으로 정렬된다.

 

따라서, 만약 order by 5라고 입력한다면 다섯 번째 Column을 기준으로 정렬해야 하지만 다섯 번째 Column은 존재하지 않으므로 [Unknown column '5' in 'order clause']라는 에러 메시지가 출력된다.

이를 활용하여 column 수를 차례로 늘리거나 줄이면서 Error가 발생하는 지점을 찾는다면 Column의 개수를 찾을 수 있다.

 

STEP 3. Data 출력 위치를 확인하자!

Step 2에서 Column의 수를 찾았고 이 수가 5개라고 가정하자. 그렇다면 선택하는 Column의 수는 5개인데 실제로 출력되는 Column은 (위의 표에서) 4개이므로 어떤 data가 어느 위치에서 출력되는지 확인할 필요가 있다.

(가져오는 Column 수와 실제로 표시되는 Data 수가 다르다고 가정한 것이다.)

(select ~~) union select 1, 2, 3, 4, 5;

위와 같이 Column 수에 맞춰 5개를 넣어준다면

id content views date
1 How to use MySQL 25 2020-10-31
2 How to use Php 9 2018-03-01
3 Amazing Python 97 2021-10-28
4 I like mathematics 19 2021-05-04
1 3 4 5

위와 같이 삽입된 데이터가 표시될 것이다. 그러면 Column들 중 1, 3, 4, 5번째 Column이 사용된다는 것을 파악할 수 있다.

데이터 타입이 같아야 하는 경우 숫자 1, 문자 '1', 숫자도 문자도 아닌 NULL 조합하여 어느 위치에 어느 데이터 타입을 사용하는지도 알아내면 된다.

 

STEP 4. DB 이름을 알아내자!

Step 4 ~ 6은 시스템 테이블을 통해 알아낼 수 있다.

MySQL의 경우 show databases;를 입력해보면, 기존에 만들어져 있는 information_schema를 확인할 수 있을 것이다.

이 information_schema에는 모든 DB, Table, Column에 대한 정보들이 들어있기 때문에 SQL Injection에 자주 활용된다.

 

> 현재 Query문이 동작하고 있는 DB이름 알아내기

select database();

현재 Query문이 동작하고 있는 DB이름은 database() 함수를 통해 알아낼 수 있다.

따라서 위의 $content로 %' union select database(), 2, 3, 4, 5; #를 삽입하면 현재 사용하는 DB 이름을 알아낼 수 있다.

select one, two, three, four, five from board where content like '%%' union select database(), 2, 3, 4, 5; #%';

union을 사용하므로 column 수에 맞게 2, 3, 4, 5로 column 수를 채워주고 주석 처리를 해주어야 하며, Table의 마지막 행 첫 번째 열에 현재 사용하고 있는 데이터베이스의 이름이 출력될 것이다.

 

> 모든 DB 이름 알아내기

select schema_name from information_schema.schemata;

DB의 이름은 information_schema의 schemata 테이블의 schema_name Column에 저장되어 있다.

따라서 위의 예시처럼 union으로 column 수를 맞추어 삽입한다면, 모든 DB의 이름이 출력될 것이다. 

 

STEP 5. Table 이름을 알아내자!

select table_name from information_schema.tables where table_schema = 'DBname';

Step5와 Step6은 information_schema의 어느 Table에서 어느 Column을 사용하는지만 다르고 형태는 Step 4와 같다.

 

STEP 6. Column 이름을 알아내자!

select column_name from information_schema.columns where table_name = 'TBname';

물론 Step 5와 Step 6은 다른 Table에서 알아낼 수도 있으며 외울 필요도 없다. 다만 한 번쯤 information_schema의 구조와 형태를 살펴보면 좋을 것 같다.

 

STEP 7. 데이터 추출

이제 원하는 Table의 원하는 Column을 탈탈 털면 된다.

데이터 탈탈~

반응형

'WEB HACKING > 웹 해킹[이론]' 카테고리의 다른 글

SQL Injection : Blind SQL Injection  (0) 2021.11.05
SQL Injection : Error Based SQL Injection  (0) 2021.11.04
SQL Injection : 로그인 Case 별 인증 우회  (0) 2021.10.22
SQL Injection  (0) 2021.10.21
웹 서버 구조와 동작  (0) 2021.10.18

댓글