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

[비박스를 활용한 웹 취약점 진단] 2-1. SQL 인젝션 [GET/SEARCH]

by Robert8478 2023. 12. 11.

SQL 인젝션이란? - 사용자가 입력한 값을 서버에서 검증하지 않아 악성 쿼리를 데이터베이스 쿼리의 일부분으로 인식해서 데이터베이스의 정보가

노출되거나 인증을 우회하는 취약점이다.

사용자가 데이터를 입력할 수 있는 곳 어디에서든 발생할 수 있으며, 이를 통해 공격자는 악성 SQL 쿼리를 변수에 입력해서 데이터베이스 정보 획득 및 시스템 내부를 파악한다.


[난이도 하]

빈칸에 영화 제목을 넣고 search를 하면 데이터베이스를 조회해서 영화들을 보여주는것 같다. 이곳에 ' 를 넣어서 SQL 에러가 일어나는지 보자.

그랬더니 SQL syntax 에러가 나왔다. SQL 오류메시지가 출력되는것은 SQL 인젝션 취약점이 존재하는 것이다.
' 작은 따옴표는 DB에서 작은 따옴표로 문자 데이터를 구분하기 때문에 ' 를 넣으면 DB에 질의하는 쿼리에 문법 오류가 발생하는 것이다.
[ SELECT * FROM movie WHERE title = ''' ]  ' 를 넣어주면 질의하는 쿼리는 이와 같은 방식으로 들어가게 될 것이다. '' 사이에 '가 들어가니 SQL 구문 에러가 발생하였다.

위 오류메시지를 확인해보니 MySQL server version이라고 나오는 것을 보아 MySQL을 쓰고 있다는 것을 알 수 있다.
이제 SQL 인젝션을 시도해보자.  대표적으로 유명한 쿼리는 ' OR 1=1(주석) 이다. 이 구문을 넣게 된다면
[ SELECT * FROM movie WHERE title = '' OR 1=1(주석)' ]  이와 같은 쿼리가 들어가게 되는것이다.
title에 들어갈 내용은 '' 로 막혀버리게 되고 뒤에 OR 구문이 있으니 DB는 OR 에 해당하는 구문을 참조하게 될 것이다.
OR 뒤의 구문은 1=1 즉 결과를 항상 참으로 만드는 쿼리이다. 그리고 주석을 통해 뒤에 남은 ' 를 무효화 시켜버리므로 참 쿼리가 들어가게 된다.

주석은 여러가지가 있는데 여러개를 시도해 보았을때 DB에서 사용하는 주석 문자가 일치하면 웹페이지에서 요청하는 기존 쿼리를 주석처리 하게 되는것이다. 아래 쿼리 예제를 이용하여 쿼리를 넣어보자.

[쿼리 예]
' or 1=1--
' or 1=1#
' or 1=1--+-

다른 구문은 똑같이 SQL 에러가 나오나 ' or 1=1# 을 넣으니 모든 영화 목록이 출력되었다. 즉, 주석을 #으로 써야한다는 것이다.
이제 더 자세한 정보를 알기 위해 union select를 넣어보자. 이는 select 문이 둘 이상일때 이를 결합해서 두 질의의 결과를 하나로 출력한다.
이 페이지에는 DB의 특정 내용을 출력하는 기존 쿼리가 있기 때문에 union select를 이용해야 DB 내용의 파악이 가능할 것이다.

이러한 공격 방식은 Union Based SQL Injection 이라고 한다.

쿼리 예
' UNION SELECT ALL 1#

union 구문을 사용하려면 이전 쿼리에서 사용하는 SELECT 문의 칼럼 수가 일치해야 사용 가능하다. 칼럼 수를 늘려가면서 페이지에 오류메시지가 나오지 않을때까지 시도해 보아야 한다.

우선 ' UNION SELECT ALL 1# 쿼리를 넣어서 칼럼수를 1로 해보았더니 컬럼수가 다르다는 에러가 나왔다. 조금씩 늘려가며 시도해보자.

그랬더니 ' UNION SELECT ALL 1,2,3,4,5,6,7# 칼럼 수 7개에서 오류메시지가 아닌 참의 결과가 나왔다. 기존 칼럼수와 일치하게 된것이다.
이제 2,3,5,4가 보이니 2쪽에 version() 함수 또는 @@version을 이용해 MySQL 버전을 확인해보자.

' UNION SELECT ALL 1,version(),3,4,5,6,7# 와 같은 쿼리를 넣었더니 버전 정보가 노출되었다. 이 외에도 DB 정보를 파악할 수 있는
시스템 변수와 함수는 여러가지 있다.

[시스템 변수 및 함수]
database() - 데이터베이스 명을 알려주는 함수
user() - 현재 사용자의 아이디
system_user() - 최고권한 사용자 아이디
@@version - DB 서버 버전
@@datadir - DB 서버가 존재하는 디렉터리

이제 table_name from information_schema.tables 를 이용해 테이블 명을 확인해 보자.

' UNION SELECT ALL 1,table_name,3,4,5,6,7 from information_schema.tables# 쿼리를 넣으니 DB 서버에 존재하는
모든 테이블 명이 출력되었다. 그 중 users 테이블이 존재하는데 이곳에 계정 정보가 들어있다고 추측할 수 있다.
users 테이블의 칼럼명을 알아내기 위해 where 절을 이용해 users 테이블 정보만 출력하게 조건을 걸어보자.

' UNION SELECT ALL 1,column_name,3,4,5,6,7 from information_schema.columns where table_name="users"#
위와 같은 쿼리를 넣었더니 users 테이블의 칼럼 명들을 출력하였다. 이제 칼럼의 내용을 확인해보자.

칼럼 내용을 확인하려면 페이지에 노출되는 칼럼 순서에 맞추어 확인하고 싶은 칼럼명을 입력하면 된다.
지금처럼 페이지에 노출된 칼럼 수보다 확인하려는 칼럼수가 많을때에는 concat 함수로 칼럼명을 인자로 입력하면 여러 칼럼의 내용을 볼 수 있다.

' UNION SELECT ALL 1,concat(id,login),password,email,secret,6,7 from users#
위 쿼리를 넣어주었더니 Title에 id와 login, Release에 password, Character에 secret, Genre에 email가 출력되었다.
보아하니 id 칼럼에는 회원 순서, login은 아이디, password는 비밀번호 해시값이 저장된듯 싶다.

[난이도 중]

전에 했던 그대로의 쿼리를 넣어보니 적용되지 않는다. ' 만 넣어보았을때에도 오류 메시지가 나오질 않았다.
php 코드를 보았더니 medium은 addslashes를 통해 ' " 를 필터링하고 있었다.
SQL 인젝션 시 ' " 가 필터링 되면 SQL 인젝션으로 공격하기 힘들어지므로 addslashes를 쓰는것은 좋은 대응방안이다.

[난이도 상]
우선 php코드를 먼저 보았더니 return mysql_real_escape_string($data);
PHP 기본 제공 함수인 mysql_real_escape_string 함수를 이용한 필터링을 하고 있었다.
이 함수는 사용자 입력값에 SQL 문법에서 쓰는 특수문자가 있을경우 addslashes 처럼 뒤에 백슬래시를 붙여버려서
입력 데이터를 SQL문법으로 인식하지 않도록 방어한다.
우회하는 문자는 NULL, \n,\r,\,',",^Z 으로 여기서도 ' 가 필터링되므로 SQL 인젝션이 힘들어진다.
그렇기에 mysql_real_escape_string 함수를 이용하는 것은 좋은 대응방안이다.


[우회 방안]

난이도 중과 상에서 addslashes 함수와 mysql_real_escape_string 함수를 우회할 방법이 없을까 찾아보았다.

한가지 있는데 그것은 멀티바이트를 사용하는 언어셋 환경에서 우회 방법이다.

이 환경에서 백슬래시 앞에 %a1 ~ %fe 같은 값을 입력하면 %a1%5C%27 이 될 것이다. (%5C가 백슬래시)

이렇게 되면 앞에 %a1%5C 를 한 문자처럼 취급하여 다른 문자로 변환되게 되고 %27인 ' 는 그대로 남게 되는 것이다.

여기서 앞에 %aa를 붙여주면 %aa%5C가 될텐데 이것은 문자가 무효화 되므로 이 구문을 이용해서 공격을 시도해 볼 수 있다.