본문 바로가기

웹 해킹

웹 해킹 공부일기장 4 - 1 (SQL Injection 포인트 찾기)

모든 기법을 배우고 절차까지 알아보는 과정에 SQL Injection 포인트 찾기가 있었습니다.

챌린지에서도 해당 페이지에 어느 기법을 어떤 곳이 SQL Injection 이 가능할 지 알고있었던 상황입니다.

 

하지만, 실제 실무를 나갈때는 이렇게 친절히 '여기가 SQL이 가능한 곳이고 여기에 어느 기법을 사용하면 됩니다.' 알려주지 않기 때문에 SQL Injection 포인트를 찾는 것이 중요합니다.

 

SQL Injection이 사용가능한 곳이 어디일까요?

사용자의 입력을 받는 곳? 사용자의 입력으로 DB에 질의하는 곳? 물론 이러한 부분도 맞지만, 이렇게 생각하면 단순히 우리가 화면에 어떠한 문자를 입력할 때만 SQLI가 사용 가능하다라고 착각할 수 있습니다.

SQL 구문에 연결된 파라미터라고 생각하면 좋습니다. 파라미터 즉, 우리가 화면에 입력하는 데이터 뿐만아니라 SQL 구문에 연결된 모든 데이터에 대해 SQL Injection이 가능합니다.

 

예를 들어 사용자의 쿠키정보로 데이터를 가져오는 페이지일 경우에도 SQL Injection이 가능합니다.

 

챌린지 1

챌린지 1

 

요청 헤더를 보니 쿠키에 user 값에 저장되어 있습니다. 그리고 아마도 쿠키에 저장된 user 값으로 SQL 질의문을 만들어 마이페이지 부분을 채워주는 것 같습니다.

 

여기서 user의 값으로 SQL 구문에 연결되어 있다고 생각할 수 있고, SELECT * FROM member WHERE user='qwer' 로 해당 유저의 정보를 가져오고 있다고 서버측 쿼리도 예측해볼 수 있습니다.

그럼 똑같이 SQL Injection도 가능하다고 생각할 수 있습니다.

SQL Injection

 

그리고 이때까지 주석을 사용해서 SQL Injection을 사용했는데 주석을 사용할 경우 쿼리가 긴 페이지 같은 경우에 문제가 발생할 수 있어 되도록 사용하지 않는 게 좋다고 합니다.

 

우리가 입력한 부분이 쿼리 중앙에 들어가 뒷부분을 주석처리해버리면 뒤에 실행되어야할 쿼리가 실행하지 못해 당연히 문제가 생길 수 있겠다 생각이 들었습니다.

 

and '1'='1 을 넣어 SQL Injection이 가능한지 보았는데 유저 이름 빼고 바뀐게 없어 성공한 것인지 모르겠습니다.

SQL Injection

 

 

and '1'='2를 넣어 조건을 거짓으로 만드니 Nothing here... 이라는 문장이 없어졌습니다. 아마 조건에 맞는 데이터가 없어 select 한 결과가 없어서 그런것 같습니다. 그럼 참과 거짓일 때 결과가 다른 것을 확인할 수 있습니다.

 

SQL Injection 포인트를 찾았습니다.

이제 이부분에 우리가 배운 SQL Injection 기법을 사용하면 원하는 데이터를 가져올 수 있을 것입니다. (이건 챌린지 풀면서...)

이렇게 우리가 입력한 부분만 SQL Injection이 가능한 것이 아닌 쿠키에서도 가능하다는 것입니다.

그렇기 때문에 SQL Injection 포인트를 찾을 때 화면에 보이는 것이 전부가 아닌 버프 스위트를 사용해 요청, 응답에서 어느 데이터가 오고 가는지도 확인해서 찾아보아야합니다.

 

계속해서 SQL Injection 포인트를 찾는 실습을 해보겠습니다.

 

챌린지 2

이번 챌린지에서는 마이페이지를 들어갈 때 정보를 쿠키에 저장된 세션으로 가져오는 것 같아 우리가 변경할 수 가 없습니다.

요청 헤더 확인

 

다른 SQL Injection 포인트가 있는지 찾아보겠습니다.

 

페이지 사용해보기

 

웹 서비스를 확인해보니 게시글 글쓰기 기능이 있으며, 게시글을 검색하는 기능이 있습니다.

 

게시글을 검색하려면 아마도 SQL 질의문을 사용해 데이터베이스에서 게시글을 가져올 것입니다.

그럼 여기서도 사용자의 파라미터가 SQL 구문에 연결되어 있을 것이라고 추측할 수 있습니다.

SQL Injection 포인트 찾기

 

우리가 선택한 것들이 요청 바디부분에 포함되어 요청을 보내고 있는 것이 보입니다.

16 줄: option_val=username&board_result=q&board_search=%F0%9F%94%8D&date_from=&date_to=

 

option_val=username 이 아마 작성자를 기준으로 검색

board_result=q 아마 검색한 단어가 포함되는지 확인

board_search=%F0%9F%94%8D 무엇인지는 모르겠지만 해당 데이터도 포함

date_from=&date_to 아마 지정한 요일 사이에 있는 데이터를 검색

 

이렇게 예측해볼 수 있습니다.

그럼 이 파라미터들을 통해 SQL 서버측 쿼리를 예측해보면

SELECT * FROM board WHERE option_val LIKE '%board_result%' and (날짜)
// 날짜는 없어도 검색이 되는 것 같으니 패스

 

일단 board_result 부분에 SQL Injection이 가능한지 확인해보면 될 것같습니다.

board_result부분에 q%' and '1%'='1 을 넣어주면 '%q%' and '1%'='1%' 이 되어 SQL 문법을 해치지 않고 '1%'='1%' 부분이 참이되어 똑같이 q를 포함하는 데이터를 가져올 것입니다.

SQL Injection 가능 확인

 

결과가 나오지 않는 것을 보니 이 우리가 입력한 SQL 문법이 실행되지 않고 실제로 username에 q%' and '1%'='1 을 포함하는 게시글을 불러와서 결과가 나오지 않는 것 같습니다. (저런 username은 없을 것이니까요...)

그럼 이 부분은 SQL Injection이 불가능합니다.

 

그럼 혹시 option_val 부분에도 SQL Injection이 가능할까요?

SELECT * FROM board WHERE option_val LIKE '%q%' and (날짜)

 

우리가 예상한 서버측 쿼리입니다. 어떻게 option_val 부분에 입력을 넣으면 SQL Injection이 가능하다는 것을 확인할 수 있을까요?

 

바로 1=1 and username 을 넣는 것 입니다. 그럼 1=1 부분은 참이되고 뒷부분인 username에 대한 like 절을 실시하게 됩니다.

SELECT * FROM board WHERE 1=1 and username LIKE '%q%' and (날짜)

SQL Injection 포인트 찾기

 

1=1 and username을 넣었더니 결과가 똑같이 나오는 것을 확인할 수 있습니다.

SQL Injection 포인트 찾기

 

1=2 로 거짓으로 만들어주니 결과가 나오지 않습니다. 

참과 거짓에 따른 결과가 다르다는 것을 확인할 수 있고 그럼 이부분으로 SQL Injection이 가능할 것 입니다.

 

SQL Injection 포인트 찾기 성공!!

 

이번 챌린지는 테이블 이름! 컬럼 이름!에도 SQL Injection 이 가능하다는 것을 알려주고 싶었던 챌린지입니다.

flag 찾기는 나중에 챌린지 풀이하면서 해보겠습니다.

 

챌린지 3

역시나 앞의 두가지 SQL Injection 포인트는 막혀있습니다.

요청 헤더 확인

 

하지만, 이번 검색 기능에 추가로 sort 라는 데이터가 함께 보내지고 있습니다.

sort는 보통 정렬할 때 쓰는 단어이기때문에 아마 title을 기준으로 정렬하겠다고 요청을 보내는 것 같습니다.

SQL 에서 정렬은 order by 절을 사용하면 정렬할 수 있죠?

 

그럼 서버측 쿼리를 예상해보면,

SELECT * FROM board WHERE option_val LIKE '%board_result%' and (날짜) order by sort

 

아마도 board 테이블에 option_val 컬럼에 board_result 의 단어가 포함된 게시글을 sort컬럼으로 정렬하는 쿼리라고 예상해볼 수 있습니다.

 

테이블, 컬럼, like 부분은 챌린지 2 에서 썼던걸 써본 결과 SQL Injection 이 안됩니다.

 

order by 부분도 SQL Injection 이 가능할까요? 

이 부분도 가능합니다.

 

이때 알아야할 문법이 한가지 있습니다. 바로 case when 인데 case when 은 조건에 따라 참인 경우와 거짓인 경우에 서로 다른 결과를 반환합니다.

 

case where (조건) then (참일 경우) else (거짓일 경우) end 로 사용할 수 있습니다.

예를 들어, case where (1=1) then (1) else (2) end 를 사용하면 조건이 참이되어 1을 반환하고, 조건을 거짓으로 만들면(1=2) 2가 반환 됩니다.

 

이것을 order by 에서 어떻게 사용할 수 있을까요?

order by 는 해당 컬럼이나 인덱스로 정렬을 해줍니다. 

SELECT * FROM board WHERE option_val LIKE '%board_result%' and (날짜)
ORDER BY case when (1=1) then username else title end
// 조건이 참이므로 order by username 

SELECT * FROM board WHERE option_val LIKE '%board_result%' and (날짜)
ORDER BY case when (1=2) then username else title end
// 조건이 거짓이므로 order by title

 

조건이 참: username 정렬

 

조건이 거짓: title 정렬

 

이렇게 조건의 참/거짓 에 따라 order by 정렬 기준이 달라져 결과가 바뀌게 됩니다. 그럼 이 조건을 사용하여 SQL Injection을 사용할 수 있습니다.

 

SQL Injection 포인트 찾기 성공!!

이번 챌린지도 SQL Injection 이 쿠키, 테이블, 컬럼 뿐만 아니라 order by 부분에서도 가능하다는 것을 확인시켜줍니다.

 

또 다른 경우도 있습니다.

이렇게 참과 거짓의 결과가 분명하게 화면에 나타날 경우에는 위와 같이하면 되지만, 참과 거짓에 따른 결과가 같은 경우도 있습니다.

SQL Injection은 가능한 것 같은데 참과 거짓의 결과가 같을 때 우리는 일부러 에러를 발생시켜 참과 거짓에 결과를 다르게 만들어야합니다.

즉, 참일때는 정상적인 결과가 나오게 하고 거짓일 때는 일부러 에러를 발생시키는 것입니다.

 

만약 챌린지 3의 경우

SELECT * FROM board WHERE option_val LIKE '%board_result%' and (날짜)
ORDER BY case when (1=1) then username else title end

 

username로 정렬한 결과와 title로 정렬한 결과가 같아 참과 거짓에 따른 결과가 구별이 안될 수 있듯이 어느 경우에서든 결과가 같은 경우가 나올 수 있습니다.

 

SELECT * FROM board WHERE option_val LIKE '%board_result%' and (날짜)
ORDER BY case when (1=1) then username else (select 1 union select 2) end

 

이럴 경우 위와 같이 거짓일 경우 에러를 발생시킬 수 있는 코드를 넣습니다.

select 1 union select 2가 왜?? 에러가 발생하나요?

만약 참일 경우에는 order by username 으로 username으로 정렬한다는 의미가 되어 문제가 없지만

거짓일 경우에는 order by (select 1 union select 2) 가 되어 

1
2

두 행에 대해 정렬을 해라 ?? 말이 안됩니다. 어떤 컬럼이나 어떤 인덱스로 정렬하라가 아니라 테이블을 보여주며 정렬하라가 되는 거죠

어떤걸로 정렬해라는 것이 명확하지 않습니다. 

에러 발생

즉, 이렇게 에러를 발생시키게 되면 쿼리 오류로 결과가 나오지 않아 게시글이 아무것도 나오지 않습니다.

물론 참일때는 username으로 정렬된 게시글들이 나옵니다.!!!

 

이렇게 참과 거짓의 결과가 다르게 만들 수 있습니다. 그럼 이것을 활용하여 SQL Injection 도 가능하겠죠!

 

case when 을 활용하여 에러발생시켜 SQL Injection 포인트를 찾는 것을 이해하셨다면

똑같이 order by (select 1 union (select 2 where 1=1) ) 도 이해할 수 있을 것입니다. 

 

where 조건이 참이되면 select 2가 실행되며 order by (select 1 union select 2)가 되어 에러가 발생하고,

조건이 거짓이되면 order by 1 이 되기 때문에 1번째 컬럼으로 정렬하게되어 정상적인 결과가 반환될 것입니다.

 

실제 실무에서는 어떤 상황일지 모르기 때문에 SQL Injection 포인트를 찾는 것이 쉽지 않을 것입니다.

 

마지막으로 정리를 하면, SQL Injection 포인트는

SQL 구문에 연결된 파라미터에서 가능하며 해당 파라미터는 사용자의 입력 뿐만아니라 쿠키, HTTP 헤더도 포함되며,

연결된 부분이 단순 조건 검색하는 부분뿐만 아니라 테이블 이름, 컬럼이름, order by 절 등 SQL 어디에든 연결될 수 있습니다.

 

그렇기 때문에 DB와 연결되어 있을 것 같은 부분이 있으면 어떻게 서버측 쿼리가 설정되어있을지 추측하는 것이 중요합니다.

 

각 챌린지별 찾은 SQLI 포인트로 flag 찾는 것은 과제풀이 할때 하겠습니다.