wargame/Lord Of SQL Injection

[LOS] zombie

LimeLee 2018. 8. 18. 07:47

문제 의도의 기능을 알고 있었으면 간단하게 풀리고 아니면 엄청 삽질하게 될 수 있다.

 

소스코드 분석

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect("zombie");
  if(preg_match('/rollup|join|ace|@/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = "select pw from prob_zombie where pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['pw']) echo "<h2>Pw : {$result[pw]}</h2>";
  if(($result['pw']) && ($result['pw'] === $_GET['pw'])) solve("zombie");
  highlight_file(__FILE__);
?>

ouroboros와 비슷한 문제이지만 필터링 쪽을 보면 REPLACE 함수를 사용할 수 없도록 막아놓았다.

REPLACE 함수를 사용하지 않고 요청 파라미터와 결과가 동일한 쿼리를 만들어야한다.

 

문제 풀이

 

문제의 의도는 함수 등을 이용해 quine 쿼리를 만드는 것 의외의 방법을 알고 있는가?를 묻고 있지만

ouroboros 문제처럼 replace함수는 사용하지 않되 함수를 이용해서 quine 쿼리를 만드는 방법도 여전히 가능하다.

개인적으로 풀 때에는 대체 함수를 찾아 풀었기 때문에 내가 풀었던 방법 먼저 설명하고 후 출제자가 의도했던 방법 또한 기술하도록 한다.

 

최종 페이로드는 다음과 같다.

 

 ' union select concat(a,char(34),a,char(34),'as a)as a-- -') from (select "' union select concat(a,char(34),a,char(34),'as a)as a-- -') from (select "as a)as a-- -

 

이 복잡한 쿼리가 문제를 보자마자 머리 속에서 출력되기란 너무나 어렵다.

 

몇 수 앞을 보지 말고 그냥 눈 앞에 보이는 과제들을 하나하나 해결해서 천천히 나아가보도록 하자.

먼저 if(($result['pw']) && ($result['pw'] === $_GET['pw'])) 조건을 만족하기 위해서 쿼리를 통해 결과 값을 출력하도록 한다. prob_zombie 테이블 역시 ouroboros 문제와 동일하게 아무런 값을 가지고 있지 않다. union을 통해 1이라는 값을 출력한다.

 

' union select 1-- -

if($result['pw'] === $_GET['pw']) 조건을 만족하기 위해서는 내가 입력한 쿼리와 똑같아야 한다.

그래서 1을 출력하는 게 아니고 select 이후에 내가 입력한 쿼리를 차근차근 따라서 적어보기로 한다.

 

' union select "' union select "-- -

어느 순간 따라 적을 수 없는 구간이 나타나기 시작한다. 

싱글쿼터를 문자열로 출력해주기 위해 select 문에서 출력할 문자열을 더블쿼터(")로 감싸주었다.

 

그랬더니 내가 출력해야할 문자열엔 더블쿼터(")가 추가가 되어 구분자로 사용한 더블 쿼터를 문자열로 출력할 수 없어 더 이상 진행할 수가 없게 되어버렸다. 

또한 더블쿼터를 문자열로 출력할 수 있다고 해도 그만큼 내가 입력한 페이로드는 길어지고 또 그만큼 select 이후에 출력해야할 문자열도 길어지고.. 그러다보면 영원히 select 절에서 벗어날 수 없다.

 

select 절 내 입력한 쿼리를 그대로 따라 적지 않고도 문자열을 출력할 수 있는 방법이 있지 않을까 생각해 삽질한 결과 서브쿼리alias가 떠올랐고 이를 토대로 쿼리를 작성했다.

 

' union select a from (select "' union select a from (select " as a)as a-- -

 

출력할 문자열을 select 절이 아닌 서브 쿼리의 select 절로 미루고 서브쿼리에 입력한 문자열을 alias를 통해 a라는 별칭을 붙이고 select 절에서는 a라는 별칭을 통해 select 절 내 더블쿼터와 select 절 이후의 쿼리 작성 문제를 해결 할 수 있었다. 

 

하지만 더블 쿼터에 대한 문제는 select 절에서 발생하는 문제를 서브쿼리의 select 절로 미룬것이고 단순히 따라적는 것으로 결과로 출력될 문자열이 입력한 쿼리를 따라가는 것은 평행된 두 선이 만나는 것 마냥 따라잡기 힘든 것과 같다.

 

해결할 방법이 도저히 생각이 안나 페이로드를 날로 먹었던 이전 문제 ouroboros 문제로 다시 한 번 돌아가 요청한 쿼리를 제대로 이해하고 분석해 보기로 했다.

 

ouroboros의 요청 쿼리가 quine 쿼리가 되는 핵심은 replace() 함수는 조건에 일치하는 문자열이 여러 개 있을 경우 조건에 일치하는 문자열을 전부 치환한다. 그래서 결과 값에 요청 쿼리를 따라적으면 그만큼 또 차이가 벌어지는 문제를 해결 할 수 있었다.

 

위의 사진에서 요청한 쿼리를 유심히 살펴보자.

 

' union select a from (select "' union select a from (select " as a)as a-- -

 

' union select a from (select 라는 문자열이 2번 반복된다.

 

ouroboros에서는 이런 반복되는 문자열을 $ 에 치환하여 2번 반복 시켰다.

 

위의 요청 쿼리에 대입하면 출력될 데이터에 $$를 넣어 ' union select a from (select ' union select a from (select 를 출력한 셈.

 

하지만 zombie 문제에서는 replace() 함수가 막혀있으므로 대체 함수를 떠올려야한다.

 

replace() 함수와 비슷한 INSERT(), INSTR() 함수를 생각해봤지만 위에서 말했 듯 조건에 일치하는 문자열을 전부 치환하는 것이 핵심인데, INSERT(), INSTR() 함수는 조건에 해당하는 문자열이 여러 개일 경우 1개만 치환한다는 한계점이 존재한다.

 

계속 삽질, 또 삽질을 하다보니 union select로 테이블, 컬럼 명 뽑아 낼 때 컬럼에 대응되는 테이블도 같이 뽑아내고자 concat을 사용했던 기억이 나서 이를 이용했다.

 

' union select concat(a,a) from (select "' union select a from (select " as a)as a-- -

 

동일한 문자열을 2번 출력시키는 데 성공하였다. 서브쿼리의 SELECT 절 이후의 부분까지도 출력할 수 있었다.

 

select 절에 변화가 있을 때 마다(정확히는 서브쿼리의 select 앞 부분) 서브쿼리의 select 절도 동일하게 변경시켜줘야한다.

 

 

거의 다왔다. 더블 쿼터(") 를 문자열로 출력해주는 문제만 해결하면 된다.

 

싱글 쿼터(')를 문자열로 출력해주기 위해 더블 쿼터를 사용했기 때문에 quine 쿼리를 만들려면 더블 쿼터(") 또한 문자열로 출력을 시켜줘야 한다.

 

 

단순히 생각해서 더블 쿼터를 구분자가 아닌 문자열로 사용하려고 하면 싱글 쿼터(')로 감싸줄 필요가 있는데 또 그렇게 되면 구분자의 역할을 한 싱글 쿼터(')만큼 차이가 생기게 된다. 사실 그 이전에 쿼리 에러가 발생한다.

 

더블 쿼터(") 문제도 ouroboros 문제의 쿼리에서 답을 얻었다. 역시 ouroboros에서도 구분자에 대한 문제가 있었는데 이를 char() 함수를 통해 해결하였다. zombie 문제에서도 char() 함수에 대한 필터링은 없으므로 동일하게 해결하도록 한다.

 

' union select concat(a,char(34),a,char(34)) from (select "' union select concat(a,char(34),a,char(34)) from select " as a)as a-- -

 

더블 쿼터(")의 구분자 문제도 해결되었다. 이제는 alias를 위해 삽입한 as a)as a-- - 만 해결하면된다.

단순하게 concat() 함수 사이에 문자열로 끼워넣는 방식으로 해결했다.

 

' union select concat(a,char(34),a,char(34),'as a) as a-- -') from (select "' union select concat(a,char(34),a,char(34),'as a)as a-- -') from select " as a)as a-- -

 

클리어

 


 

아래부터는 문제 출제자는 이를 의도한 방법을 이용한 풀이

 

ouroboros와 조금 다른 점이라면 . , _를 필터링을 하지 않고 있다. 다른 테이블을 들고오라는 의미인가?

 

thanks to kmon

웹해킹이라고 무시하는 것들 보소(New way using SQL injection)

위는 secuinside 2017에서 정도원(rubiya)님이 발표한 자료이다.

발표자료 중 MITM SQL Injection (중간자 공격 SQL Injection)에 대해 보면 된다.

 

information_schema.processlist 라는 테이블은 현재 무슨 쿼리들이 실행되고 있는지 보여준다.

 

요청 쿼리와 동일한 값이 들어가있는 걸 알 수 있다.

 

 

union을 통해 information_schema.processlist.info 컬럼을 출력한 결과 요청한 쿼리 그대로 출력하는 걸 알 수 있다.

원하는 건 싱글쿼터 뒤 부터 시작하는 파라미터 요청값이므로 간단하게 substr 등으로 파라미터 부분만 출력해주면 된다.

 

 

information_schema는 tables와 columns만 사용했는데.. 이렇게 무궁무진한 기능이 있었다니 T.T..

 

반성점


alien과 zombie 문제에 애착이 많은데 추가로 풀린 modsecurity bypass나 다른 환경의 db에서의 sqli에 중점을 둔 것들은 그만큼 생소하기 때문에 난이도 조절이 어느정도 들어가있다.

 

반면 zombie는 mysql 환경에서의 심화 문제 중에서도 마지막 보스급 문제다보니 zombie문제는 한달이 넘는 기간동안 삽질을 했었다.

 

한달이 넘는 기간을 삽질하면서 해결해야 할 문제가 무엇인지 어떻게 해결해야할 것인지 방향성을 잡는 능력을 기르는 데 어느정도 도움이 되었다고 생각한다. 하지만 그 삽질의 기간이 무려 한달이 넘는다. CTF는 길어도 몇 일인데 CTF 대회를 나가서 난관에 봉착했다면 방향성만 잡다가 대회가 끝나있을 것이다. 조금 더 해결해야할 방안의 방향성을 빨리 잡아야하는 것이 이번 문제를 해결하고 남은 과제가 되었다.

 

실제로 이 문제를 풀고나서 다시 곱씹어보면서 느낀건데

zombie문제의 소스코드를 처음 보고 ".", "_" 를 필터링하지 않는 걸 보고 information_schema 이라는 생각이 바로 떠올랐지만 알고 있는 테이블이라고는 tables와 columns 밖에 없었기에 바로 배제를 해버렸다. 그리고 ouroboros와 그 이전문제들이 전부 함수를 사용하는 범위 내에서 풀이가 그쳤기에 mysql 문서 페이지를 펴고 모든 함수를 사용해보면서 대체 함수로 쓸수 있을 법한 것들을 찾아보았다.

 

여기서 방향성을 replace의 대체함수를 찾기위해 모든 함수를 한번씩 사용해보는 삽질 대신 information_schema 에 있는 모든 테이블을 한번씩만 출력해봤어도 processlist 테이블에 대한 지식이 없었지만 이런 테이블이 존재했는지를 확인해볼 수 있었을 것이며 삽질 기간을 더 줄일 수 있었을 거라 생각된다.

'wargame > Lord Of SQL Injection' 카테고리의 다른 글

[LOS] cthulhu  (0) 2019.05.10
[LOS] alien  (0) 2018.08.19
[LOS] ouroboros  (0) 2018.08.09
[LOS] phantom  (0) 2018.08.09
[LOS] frankenstein  (0) 2018.08.09