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

SQL Injection : Blind SQL Injection

by madevth 2021. 11. 5.
반응형

앞선 포스팅에서는 Union SQLi와 Error Based SQLi에 대해 알아보았다.

일반적으로 SQL Query문의 결과가 화면에 표시된다면(게시판) Union SQL Injection을 사용할 수 있지만, 그 이외의 경우에는 대부분 Blind SQL Injection을 사용할 수밖에 없다. 이번 포스팅에서는 Blind SQL Injection의 개념과 활용 방법에 대해 알아보자.

 

 

① Blind SQL Injection이란?

Blind SQL Injection은 서버가 쿼리에 대한 결과가 참인지 거짓인지에 따라 다른 응답을 주는 점을 이용하여 정보를 얻어내는 공격이다.

 

Blind SQL Injection의 공격 Step

Blind SQLi도 Step 별로 공격의 흐름을 알아볼 것인데, 어떤 쿼리를 보내는지를 제외하면 Union SQLi와 비슷한 흐름을 가진다.

 

STEP 1. 취약점 확인

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

작은따옴표(')를 붙였을 때 이를 문자로 인식하는지 아닌지를 살펴보거나, and 1 = 1을 추가했을 때 영향을 미치는지 등으로 SQL Injection의 여지가 있는지 체크해준다.

 

STEP 2. Blind SQL Injection이 가능한 곳인지 확인

admin' and 1 = 1 #
admin' and 1 = 2 #

admin이라는 아이디가 있다는 가정하에 두 쿼리를 삽입한다면, 위의 쿼리는 참, 아래의 쿼리는 거짓이라는 결과가 나올 것이다. 

로그인 성공

 

로그인 실패

쿼리의 결과가 참이냐 거짓이냐에 따라 다른 결과가 나오기 때문에, Blind SQLi의 여지가 있다는 것을 알 수 있다.

 

 

STEP 3. Blind SQL Injection 임의의 select 문 삽입

이제 본격적으로 Blind SQLi의 사용 방법을 알아볼 것인데, 이를 위해 세 가지 함수(기능)를 짚고 넘어가자.

 

[limit]

limit은 SQL 쿼리문의 끝에 붙여 출력할 데이터의 개수를 조절하는 키워드이다.

select * from tbname limit n, m;

위와 같이 작성하면, 'tbname의 n번째 행부터 m개의 행을 출력해줘.'라는 의미이며 첫 번째 데이터는 0번째 행으로 표현된다.

address 테이블에서 모든 데이터를 출력한 것과, 0번째 데이터부터 1개의 행만, 즉 첫 번째 데이터만 출력한 것을 비교할 수 있다.

 

[substring]

substring은 문자열의 부분을 출력하는 함수이다.

select substring(string, n, m);

위와 같이 작성하면, 'string에 해당하는 문자열의 n번째 인덱스부터 m개의 문자를 출력해줘.'라는 의미이며 첫 번째 문자열은 1번째 인덱스로 표현된다.

hello의 1번째 데이터부터 1개의 문자열을 가져왔기 때문에 h만 출력된 것을 볼 수 있다.

limit과 비슷하지만 인덱스의 시작이 1이라는 점이 다르다.

 

[ascii]

ascii는 문자를 아스키코드 값으로 변환해주는 함수이다.

h는 아스키코드로 104이기 때문에 104가 출력되었다. 숫자로 변경한 후 대소 비교를 통해 알파벳을 알아내는 방식을 사용하기 때문에 사용된다.

 

+) 이진 탐색 알고리즘

Blind SQLi는 하나하나의 문자열을 알아내기 위해 비교하는 과정을 굉장히 여러 번 거쳐야 한다.

몇 번째 데이터를 가져와서, 그 데이터의 몇 번째 문자열에 대해 어떤 숫자와 비교할 것인지를 선택해야 한다.

그래서 일일이 하기보다는 자동으로 비교를 하도록 파이썬으로 코드를 짜는 것이 현명하다.

따라서 알파벳 소문자만 있다고 가정했을 때, 각 데이터의 각 문자열을 97부터 122까지 비교해야 하기 때문에, 순서대로 비교하는 것보다는 이진 탐색 알고리즘을 사용하여 비교하는 것이 훨씬 효율적이다.

 

 

※ Blind SQLi의 쿼리문 형태

(실제로는 Step 4 ~ Step6을 통해 DB/Table/Column 이름들을 알아낸 후에 가능한 공격법이지만, 공격 방식을 알아야 이름을 구할 수 있으므로 이 스텝에서 설명한다.)

 

Blind SQLi은 다음과 같은 형태로 실행할 쿼리문을 넣는다.

admin' and (실행할 쿼리문) and 1 = 1 #

마지막에 and 1 = 1은 굳이 안 넣어줘도 되지만, 괄호가 헷갈릴 수도 있기 때문에 넣어주었다.

 

실행할 쿼리문의 형태는 다음의 단계로 만들 수 있다.

(로그인 테이블에 저장된 패스워드를 모두 알아내는 공격을 한다고 가정하자.)

 

1. 로그인 테이블에서 n번째 행의 패스워드를 가져오는 쿼리문 작성 (0 ≤ n ≤ Table length)

select password from login limit n, 1;

2. 가져온 패스워드의 m번째 문자를 알아내는 쿼리문 작성 (1 ≤ m ≤ password length)

substring((select password from login limit n, 1), m, 1);

3. m번째 문자를 아스키코드로 변환한 후 숫자와 비교

ascii(substring((select password from login limit n, 1), m, 1)) > x;

m번째 문자를 아스키코드로 변환하면 어떤 숫자가 나올 것이고, 그 값을 x와 비교해나가면서 값을 알아낼 수 있다.

예를 들어 m번째 문자가 h였다고 가정하면 좌변은 104가 될 것이고, 우변은 이진 탐색을 통해 (97부터 122까지 넣는다고 가정하면) 110을 넣어보면 거짓이 뜰 테니 103을 넣어보면 참이 뜰 테니 106을 넣고... 를 반복하면서 104 임을 알아낼 수 있다.

 

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

Step 4 ~ 6은 Union SQL Injection 스텝에서의 쿼리문과 같으므로 설명은 생략하였다.

쿼리문을 Step 3의 형태로 작성해서 알아내야 한다.

 

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

select database();

예시) 이 경우는 데이터베이스 하나를 출력하므로 n값을 사용할 필요가 없어진다.

ascii(substring((select database()), m, 1)) > x;

 

> 모든 DB 이름 알아내기

select schema_name from information_schema.schemata;

 

 

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

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

 

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

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

 

STEP 7. 데이터 추출

최종 삽입할 데이터는 다음과 같다.

admin' and ascii(substring(select data from tbname limit n, 1), m, 1) > x and 1 = 1 #

파이썬을 사용하여 n을 0부터 데이터의 개수까지 늘려가며, m을 1부터 문자열의 길이까지 늘려가며, 이진 탐색을 통해 알아내는 코드를 작성하면 편하게 Blind SQli를 시도할 수 있다.

반응형

댓글