0x00 前言

最近参加了两次AWD型CTF比赛,所谓AWD型比赛,就是选手既是维护者,需要维护分配给自己的防守机,也是攻击者,需要攻击对手的防守机,拿到flag。发现一些小套路,总结一下。

0x01 套路

套路太深,逐个写太麻烦了,我画了一个脑图如下,其中关键的部分我会在下面提到。

naotu.png

先从防开始说吧

  • 备份:拿到服务器权限之后第一件事应该是备份了。记得把web目录和数据库都备份一下。
  • 进程监控:非root用户是无法隐藏进程的,进程监控能快速找到对手留下的后门/运行的命令。
  • 日志监控:这个重要性不多说,可以自己事先写个脚本过滤一下,至少得先过滤出返回码是200的请求吧。
  • WAF:这个慎重上,上的话过滤的内容一定要少。可以考虑把flag关键字,或者flag服务器地址过滤一下。如果准备时间充足的话也可以伪造返回。

AWD比赛还是以攻为主,以攻为守。在整个比赛中,拿到flag是最核心的目的。整个攻击过程都以这个目的为中心。在比赛的过程中,绝大多数情况都是利用多线程批量化攻击脚本进行攻击。这个脚本可以在赛前基本写好,在比赛时,只需要完善一些基本的函数就可以了。可以说,这个脚本的质量决定了你的攻击效率。注意好以下几点,能够事半功倍。

  • 合理抽象:方法不要太抽象,也不要写得像意大利面,很自然的把攻击过程抽象成flag获取,flag去重,flag提交三个方法即可。我在脚本中,关于flag操作的方法都以flag开头,这样,在比赛时写代码时,即使忘记函数名,只要是对flag做操作,IDE都会提示有哪些函数。
  • 单元测试+集成测试:一些方法必定在比赛现场完善,例如flag提交和flag获取,但是对这些方法的单元测试和集成测试函数可以在赛前写好,方便在赛时迅速定位问题,调试bug。
  • 异常处理:CTF比赛环境不稳定非常常见,比如flag提交服务器挂掉,这样,赛前要考虑到这些情况,如果flag服务器挂掉,在异常处理中,可以把flag写入flag文件中,稍后再提交。
  • 合理输出:不要动不动就输出信息到控制台,像http请求中超时异常很常见的,直接忽略就好,输出信息太多,在多线程情况下根本看不过来。只输出关键步骤的关键信息,最好能根据输出判断批量化攻击的运行状态。
    另外,在利用漏洞找到flag和写好批量攻击脚本之间肯定有时间差,这个时候,其他队友先手动攻击,提交flag,直到负责脚本编写的同学完成代码编写和调试。

在比赛中,为了快速写出flag获取的代码,我用了burp的requests插件,这个插件可以把burp suite拦截下来的包转换成python requests代码,非常方便。生成的代码大概如下:

1
2
3
4
5
6
7
8
9
10
11
def flag_submit(flag):
import requests

burp0_url = ""
burp0_cookies = {"PHPSESSID": "30bnlp2apsc9acu5g1qf650es4"}
burp0_headers = {"Accept": "*/*", "Origin": "http://100.100.100.200", "X-Requested-With": "XMLHttpRequest",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}
burp0_data = {"answerVal": flag}
r=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data)

一般来说没什么问题,但是如果需要在每台机器上都登录,再运行poc,才能获取flag,就要注意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def flag_get(ip):
with requests.Session() as s:

burp0_url = "http://"+ip+"/"
burp0_cookies = {"JSESSIONID": "84CBC07052E955A5D2381955B312187C", "ZVh5_2132_saltkey": "o5O3yFez",
"ZVh5_2132_lastvisit": "1530254636", "ZVh5_2132_sid": "LAdoUM",
"ZVh5_2132_lastact": "1530258236%09connect.php%09"}
burp0_headers = {"Cache-Control": "max-age=0", "Origin": "http://102.102.102.82",
"Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}
burp0_data = {"password": "password", "doing": "login"}
# 这里没有指定cookies
s.post(burp0_url, headers=burp0_headers, data=burp0_data)

这里使用requests.Session()来保持登录,注意,在s.post中,不能指定cookies,否则会用指定的cookies去操作,那每个ip都是这个cookie,相当于没有登录。

如果主办方没有给防守机的权限的话,需要自己攻破防守机,获取权限,再寻找flag。找到漏洞攻击之后,不要急于找flag或者加固,先构造一个交互式的shell,例如上传一个菜刀马,用菜刀的虚拟终端,或者反弹shell。一个交互式的shell比一个命令执行的马不知好用到哪里去了。磨刀不误砍柴工。

有的比赛flag会定时刷新,这种比较考验选手的攻防对抗,如果没有root的话,批量种马和后门无非就是那几种,不赘述,提一点小技巧,你可能批量给别人的机器种了几个马,但是一定要给自己的防守机多留几个后门/木马,如果在攻防对抗的后期,所有队伍的web服务都被打挂了,你在别人机器上留的马都没有用了,你还有自己机器的控制权,发起DOS攻击的队伍肯定已经种好了后门,利用好这个后门,可以反客为主打全场。

攻防相对,你也可以在后期种下自己的木马后,把web服务都打挂,一来可以使其他队伍扣分,二来可以使web木马全部失去作用,大部分队伍都失去了权限。

0x02 结语

CTF比赛最核心的还是选手的安全水平,攻防能力,是为道。以上只是一些术,一些能快速得分的方法,希望能给读者一些帮助。