DVWA를 이용한 SQL INJECTION 실습
개발 환경 : MySQL, DVWA
먼저 SQL injection의 예시(작은따옴표)를 들어 이해를 높이고,
SQL injection의 종류별로 쿼리를 날려보고 DVWA에 날려보면서 실습을 하도록 한다.
SQL 쿼리문을 보면 보통 String을 이용한 statement나 preparedstatement를 이용해 jsp와 연동한다. DVWA의 SQL injection page를 보면, userID를 입력받고 데이터를 불러준다.
F12를 눌러 혹시나 쿼리문이나 코딩된 부분을 볼 수있나 했지만 역시나 볼 수 없다.
직접 구현했던 홈페이지를 떠올려보며 userID를 어떻게 입력받고 어떻게 처리하는지 생각해보자.
우선 좌측 메뉴에서 DVWA Security에서 Security level을 low로 꼭!! 변경해준다.
그래야 실습이 가능하다.
그 다음 SQL 쿼리가 어떤 방식으로 처리될지 한번 생각해보자
직접 만든 홈페이지의 로그인 처리함수 내에 SQL 쿼리문이다. 다음과 같이 statement로 받게 되면 String으로 쿼리문을 받아 중간에 입력값을 삽입한 후 sql과 연동하는 함수를 사용한다.
주석처리로 표시해둔 PreparedStatement도 마찬가지로 쿼리문에 ?로 변수를 취해주고 아래에서 변수를 받아 pstmt.setString(1,userID) 이런식으로 넣어준다.
그렇다면 저 input 칸에 입력받은 값을 String에 이어 붙이는 방식일건데
다음과 같이 작은 따옴표로 쿼리 String을 탈줄해 SQL에 입력 구문을 넣어보자
Select * from users where userid = ‘ + 입력값 + ‘;
입력값에 1’ or ‘1’=’1 앞 뒤에 작은따옴표가 포함되도록 그리고 쿼리문에 1 or 1을 넣어 항상 참이되도록 만들어주면 결과값이 다음과 같이 나온다.
원래 user id에 1을 넣으면 다음과 같이 나오는데,
1이 아닌 모든 아이디의 정보가 흘러나온다.
select user_id, first_name, last_name from users where user_id=1;
이라는 쿼리로 예상된다.
실습 전에 Mysql에 직접 쿼리를 넣어보자
1' union select user_id, password, avatar from users; --
뒤에 ; 세미콜론 붙인 뒤 –로 주석처리하여 뒤에 따라올 작은따옴표’ 와 세미콜론을 무시해주고 실행한다.
안됨 column의 개수가 틀리다고 한다.
Output이 ID, first_name, sur_name이나, id 부분에는 입력값을 그대로 출력시키고 남은 2개의 컬럼만 불러온다고 가정하고 2개의 컬럼만 요청해보자
1’ union select user_id, password from users; --
여기서 Union의 특징을 보자면, 맨위 테이블의 주제격인 변수명은 첫 쿼리의 변수명으로 설정되고 아래에 추가로 union으로 불러온 데이터는 그냥 그 아래에 뿌려질 뿐, 컬럼명은 바꾸어주지 않는다.
First name에는 user_id가, Surname에는 password가 불러져 온 것을 볼 수있다.
Mysql로 해보면,
Select user_id, first_name, last_name from users union select user_id, password, avarar from users;
컬럼의 개수를 3개씩 맞춰주고 불러왔을 때, 컬럼명은 바뀌지 않으나 데이터는 아래에 쭉 이어져 불러와지는 것을 볼 수 있다.
SQL Error 메시지가 보여지는 경우, 논리적인 에러를 발생시켜 데이터를 추출하 는 방식
해당 블로그를 따라 여러가지 해보았다. 우선 다른 쿼리들은 잘 안먹히고 이 쿼리는 먹혔다.
select * from users where true AND(select 1 from(select count(*),concat(version(), FLOOR(rand(0)*2))x from information_schema.TABLES group by x)a);
구글링을 해보다 찾은 sql error based injection에 사용되는 쿼리 예시중 하나이다.
SQL의 버전이 5.7.311이라고 에러메시지와 함께 노출되는 것을 볼 수 있다.
https://byounghee.tistory.com/148
다른 예시들은 이 블로그를 따라 하나하나 적용해보았다. 내 mysql에선 잘 적용이 되지 않는다.
멘토님이 알려준 https://sqlwiki.netspi.com/injectionTypes/errorBased/#mysql
사이트에 있는 예시 쿼리를 날려 보았다.
잘 적용된다.
Dvwa에 각 쿼리들을 적용시켜 보자
Dvwa에는 1’ union 을 붙여 쿼리를 탈출하고 이하 쿼리를 붙여본다.
SELECT extractvalue(rand(),concat(0x3a,(select version())))
SELECT a()
SELECT 1 AND(SELECT 1 FROM(SELECT COUNT(*),concat(0x3a,(SELECT username FROM USERS LIMIT 0,1),FLOOR(rand(0)*2))x FROM information_schema.TABLES GROUP BY x)a)
얻고자 하는 정보의 한글자 한글자 추적해 보도록 한다.
위와 같이 버전이 5.7.31인 숫자를 추적하기 위해 substring(version(),1,1) 버전의 맨 앞글자가 6인지, 4보다 큰지 확인 후 5를 찾아내었다.
다음은 db의 이름을 추적해보자
100보다 큰지 작은지 크다면 200보다는 큰지 작은지 작다면 100과 200의 중간 수인 150을 넣고 150보다는 큰지 작은지
다음과 같이 db의 이름중 맨 앞글자의 ascii코드가 100이라는 것을 알 수 있다.
10진수로 100이 ascii코드를 이용한 문자로 d 라는 것을 알 수 있다. db이름이 dbwa이니 한 글자를 잘 유추해 내었다.
DVWA 에서 실습을 해 보자
해당 쿼리가 참인지 거짓인지에 따라 시간차를 두고 진행되게 만들어 유추하는 방법이다.
이 쿼리는 결과만으로는 알기 힘들다. 쿼리를 날렸을 때 참이면 sleep(5)로 인해 5초 있다가 결과가 나왔고, 아래는 거짓이기 때문에 sleep(3)을 거치지 않고 바로 실행되었다.
우선 중요한 쿼리구문 하나 알고가자
Select case when 1=1 ‘1’ else ‘2’ end
예시로 쓰기 좋은 case 구문이다. 이번 Time-based 쿼리에서는 case구문이 필요로 하다.
해당 구문에서 ‘1’이 참일 경우 나타나는 것이고 물론 3초 후에 나타나게 되어 참인 것을 알수 있게 된다.
거짓일 경우 바로 2가 나온다.
DVWA를 통해 injection을 해보자
1' and '1'=(select case when 1=1 then sleep(5) else '2' end) and '1'='1
다음과 같이 쿼리를 날렸고 5초동안 쉬었다가 잘 작동한다.
성공!