앞선 포스팅에서는 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를 시도할 수 있다.
'WEB HACKING > 웹 해킹[이론]' 카테고리의 다른 글
XSS : Cross-Site Scripting (0) | 2021.11.15 |
---|---|
SQL Injection 대응 방법 (0) | 2021.11.12 |
SQL Injection : Error Based SQL Injection (0) | 2021.11.04 |
SQL Injection : Union SQL Injection (0) | 2021.10.28 |
SQL Injection : 로그인 Case 별 인증 우회 (0) | 2021.10.22 |
댓글