SQL Injection을 이용한 공격을 하려면 SQL Injection Point 즉 DB에게 SQL 질의문을 사용하는 곳을 찾아야 한다. 실무에서는 여기서 sql injection이 일어난다고 광고를 하진 않을 것이다. 지금까지는 대게 직접 입력값을 넣는 곳 로그인, 검색 기능 등에서 sql injection을 수행했지만 이번 포스팅에서는 다른 곳에서 발생하는 sql injection에 대해 알아보고자 한다.

아무런 정보도 없는 상태에서 해당 사이트에 SQL Injection Point를 찾아보고자 한다!

회원가입을 해준 후에 로그인을 시도해 주었다. 회원가입 페이지와 로그인 페이지에는 특별한 점을 찾지 못했다.

로그인 성공 시 해당 사이트로 이동하게 된다. 마이페이지와 게시판을 이용할 수 있다. 마이페이지부터 확인해 보도록 하자!

마이페이지로 접근을 하게 되면 사용자 정보가 나오고 update를 할 수 있는 버튼이 있다. 정보를 수정하고 버튼을 누르게 되면 db에게 해당 정보로 update 하라는 요청을 보낼 것이라고 유추할 수 있다. 그럼 사용자 정보는 어떻게 가져오는 것일까? burp suite를 이용해 패킷을 확인해 보자!

쿠키 값을 확인해 보면 user=baul이라고 되어있는 것을 볼 수 있다. 여기서 알 수 있는 건 사용자 정보를 쿠키 값에 있는 user=이름의 정보를 읽어 마이페이지에 보여주고 있는 거라면 여기에 조건문을 사용해 참과 거짓을 구분할 수 있을 것이다.

서버에서 실행되는 쿼리가 select * from user where user='__' 일 거라고 추측되기 때문에 쿠키 값을 baul' and '1'='1입력해 서버에 전달해 보았더니 이전과 같은 화면이 나온 걸 볼 수 있다. 쿠키에 입력한 값이 그대로 출력되는데..? 그거는 쿠키의 user 값을 그대로 placeholder에 넣고 있는 듯하다. 그럼 참이 아닌 거짓인 조건을 넣는다면 어떻게 될까?

baul' and '1'='2 입력해 거짓인 조건을 입력하면 참일 경우랑은 다르게 nothing here 부분이 없어진 걸 알 수 있다. 이로써 참과 거짓을 구분할 수 있으므로 blind injection을 수행할 수 있는 injection point를 찾은 것이다!
계속해서 게시판에서도 찾아보자!

사용자가 작성한 게시글을 볼 수 있다.


일치한 문자가 하나라도 있을 경우 글을 보여준다. 이걸로 알 수 있는 건 like%__%를 사용하는 걸 알 수 있다. 그리고 일치하는 문자가 없을 경우는 에러 메시지가 나오는 걸 볼 수 있다.


패킷의 파라미터를 살펴보면 option_val=username&board_result=b 내가 입력한 b와 username은 작성자 밑에는 title은 제목 카테고리인 것을 알 수 있다. 이 파라미터가 서버에 전달된다면 제목을 기준으로 내가 입력한 문자를 조건으로 찾기 때문에
select * from user where CATEGORY like '%USER_INPUT%'
이런 식으로 쿼리가 작성돼있을 것이라고 예상할 수 있다. 여기서 and 조건문을 사용해 쿼리를 사용할 수 있는지 확인을 해보자. 예상한 select 문을 보면 두 번째 파라미터에 쿼리를 삽입하는 건 의미가 없어 보인다. 그럼 첫 번째에 and 조건문을 넣어보면?
select * from user where title and '1'='1' like '%t%'
아무런 의미가 없다.. 그럼 title 앞에 and 조건문을 넣어 본다면?
select * from user where '1'='1' and title like '%t%'


참인 조건일 경우 기존과 같이 값이 나오고 거짓일 경우에는 값이 안 나오는 걸 볼 수 있다! 참과 거짓을 구분할 수 있으므로 Blind injection을 수행할 수 있다!
이전과는 다르게 동작하는 게시판을 살펴보자!

총 3개의 게시글을 작성한 후 검색 기능을 사용해 검색을 해보았다.

검색 카테고리를 작성자로 하고 ba라는 단어를 검색 시 다음과 같은 파라미터가 나왔다. findSQLi_3와 비교해 보면 마지막에 sort=title이라는 파라미터를 볼 수 있다. 이걸로 알 수 있는 것은 sort에 있는 값을 가져가 order by에서 사용한다는 것을 알 수 있다. title 부분을 1부터 계속 입력하다 보면 11에서 값이 나오지 않는다. sql injection을 수행할 수 있는 것이다. 이제 참과 거짓을 구분할 수 있는 쿼리가 필요한데 그때 사용할 수 있는 것이 case 문이다.
case when (조건) then (true) else (false) end
case 문은 sql에서 사용하는 if 문이라고 생각하면 된다. 조건 부분에는 확인하고자 하는 조건 true 부분에는 조건이 참일 시 실행할 내용 false는 조건이 거짓일 시 실행할 내용을 넣으면 된다. 예를 들어
sort=case when (1=1) then username else title end
이라고 입력 시 1=1은 참이니까 user name으로 정렬을 하는 것이다.


참과 거짓의 따라 정렬 기준이 바뀌기 때문에 참 거짓을 구분할 수 있게 되었다. 하지만 column 이름을 모르는 상태에서는 어떻게 참과 거짓을 구분할 수 있을까? 그럴 때 사용하는 쿼리가 있다.
select 1 union select 2 where (1=1)
select 1 union select 2 where (1=2)
해당 쿼리를 살펴보면 조건이 참일 경우는 두 개의 row가 전달이 되면서 에러가 나기 때문에 화면에 아무런 글이 출력되지 않고 조건이 거짓일 경우에는 select 1 즉 하나의 row만 전달이 되어서 화면에 글이 정상적으로 출력되기 때문에 화면에 나오는 결과에 따라 참과 거짓을 구분할 수 있어서 Blind injection을 수행할 수 있는 것이다.


case when은 어떤 column을 기준으로 정렬을 하는지에 따라 참 거짓을 구분했다면 UNION은 게시물이 화면에 출력이 되는가에 따라 참 거짓을 구분할 수 있다.
(select 1 union select 2 where substr((select 'bawool'),1,1)='u')
(select 1 union select 2 where substr((select 'bawool'),1,1)='b')


이제 where 문 뒤에 우리가 원하는 쿼리를 넣음으로써 원하는 정보를 추출할 수 있을 것이다!
마지막으로 살펴볼 취약점은 마이페이지에서 찾을 수 있다.


처음과 같이 쿠키 값에 쿼리를 삽입해 보았지만 바뀌는 게 없었다.. 만약 잘못된 문법을 사용하면?

select * from user where user='bawool''
따옴표를 사용하면 문법에 맞지 않기 때문에 에러가 나게 된다. 이렇게 에러를 이용해 참과 거짓을 구분할 수 있는 경우도 있다! 위에서 사용한 쿼리를 활용해 보면
bawool' and (select 1 union select 2 where (1=2)) and '1'='1
bawool' and (select 1 union select 2 where (1=1)) and '1'='1


결과적으로 matrix 값이 중간에 들어가기 때문에 에러가 발생하게 된다. where 부분에 우리가 원하는 sql 조건을 넣어서 에러 발생 유무로 참 거짓을 구분할 수 있다! 실제로는 prepared statement를 사용하면 sql injection이 아직까지는 전혀 불가능하다고 보면 된다. 그럼에도 우리가 sql injection을 사용하는 이유는 이번에 작성한 글처럼 prepared statement를 사용할 수 없는 곳이 있다. 그게 바로 order by, table 이름, column 이름이다. 눈에 보이는 곳 말고 다른 곳에서도 sql injection을 수행할 수 있다!