Python安全 - 从SSRF到命令执行惨案
前两天遇到的一个问题,起源是在某个数据包里看到url=
这个关键字,当时第一想到会不会有SSRF漏洞。
以前乌云上有很多从SSRF打到内网并执行命令的案例,比如有通过SSRF+S2-016漏洞漫游内网的案例,十分经典。不过当时拿到这个目标,我只是想确认一下他是不是SSRF漏洞,没想到后面找到了很多有趣的东西。截图不多(有的是后面补得),大家凑合看吧。
0x01 判断SSRF漏洞
目标example.com
,根据其中csrf_token的样式,我猜测其为flask开发(当然也可能是一个我不太熟悉的框架使用了和flaskwtf相似的代码):
开着代理浏览了一遍整个网站的功能,功能点不多,比较小众的一个分享型站点。偶然间在数据包里看到url=
,看了一下发现是一个本地化外部图片这么一个功能。这种功能很容易出现两种漏洞:
- SSRF漏洞
- XSS漏洞
SSRF漏洞就不用多说了,在拉取外部资源的时候没有检查URL,导致可以向内网发送请求;XSS漏洞容易被忽略,拉取到目标后储存的时候没有过滤特殊字符,就可能导致XSS漏洞。
简单fuzz一下,依次访问http://127.0.0.1:80/
、http://127.0.0.1:80/404404404not_found
、http://127.0.0.1:12321/
依次返回了error和两个500,这三个结果分别代表什么?
因为平时做Python开发比较多,这种500的情况也见的比较多,通常是因为代码没有捕捉异常导致返回500。感觉第二个可能是HTTP请求404导致抛出异常,而第三个可能是TCP连接被拒绝(12321端口未开放)导致抛出异常。
虽然我还没理清目标的代码逻辑,但我能肯定这其中存在SSRF漏洞。
0x02 鸡肋redis服务?
经过简单的测试,我发现目标站点下载外部资源后,会检查资源类型是否是图片,不是图片则返回error。这样就很尴尬了,这是一个没有回显的SSRF漏洞。
这时候我突然想到,既然是判断图片,会不会是用imagemagick组件来判断的?然后我将imagetragick的POC保存到外网的某个poc.gif里,然后让其访问:
直接把内容返回了,没出现error,也没500,但命令也没执行成功。
当时没想清楚目标究竟是怎么判断图片的,后来拿到shell以后看了源码才知道:目标是判断返回包的content-type,如果不是图片就直接返回error,我想的太复杂了。
imagemagick这条路死了,我就没再研究这块逻辑。因为我不知道目标内网IP段,所以准备先探测一下127.0.0.1的端口,我列了一些常用端口,用Burp跑了一下:
看到6379是200的时候,我着实激动了一下,众所周知,在渗透中遇到redis是一件很愉快的事情。
不过我很快发现,GET请求我没法控制Redis的命令。
科普一下,Redis的协议是简单的文本流,比如我可以向6379端口发送如下TCP流:
SET x 1
SET y 2
每行代表一个命令,上述数据包执行了两条set
命令。但现在尴尬的是,普通GET请求的数据包如下:
GET / HTTP/1.1
Host: example.com
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
我控制不了任意一行的起始部分,也就没法自定义redis命令了,非常鸡肋……
0x03 CVE-2016-5699 化腐朽为神奇
真的鸡肋么?
去年,Python的urllib库曾出过一个头注入的漏洞,CVE-2016-5699( http://blog.neargle.com/SecNewsBak/drops/Python%20urllib%20HTTP%E5%A4%B4%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E.html )
因为在CTF里有过类似的思路,所以我基本第一时间就想到了。我在外网服务器用nc开了个端口,在目标web页面传入http://[vps-ip]%0d%0aX-injected:%20header:12345/foo
,发现果然注入成功了:
有点小激动,因为之前只是听说过这个漏洞,没有真实案例的依托,这次真遇到了。如果我们能注入HTTP头,也就能控制发送往Redis的数据包的某一行,这样就能执行任意Redis命令了。
有点怕影响目标站,我先在本地搭了个类似的环境。
攻击Redis有几个思路,核心就在于写文件。在本地测试,发现了几个巨坑:
CONFIG SET dir /tmp
,传递斜线/
的时候必须进行二次编码(%252f
),否则urllib2会抛出URLError: <urlopen error no host given>
的异常。- url过长会导致抛出
UnicodeError: label empty or too long
的异常,所以我需要依次传入CONFIG SET
、SAVE
等几个命令。
最后,我依次发送http://127.0.0.1%0d%0aCONFIG%20SET%20dir%20%252ftmp%0d%0a:6379/foo
、http://127.0.0.1%0d%0aCONFIG%20SET%20dbfilename%20evil%0d%0a:6379/foo
、http://127.0.0.1%0d%0aSET%20foo%20bar%0d%0aSAVE%0d%0a:6379/foo
,最后成功在本地写入/tmp/evil
文件。
不过目标环境就有点蛋疼了,一是完全没有回显,我无法知道我是否写入成功;二是失败原因我无法预测,有可能是redis有密码,或redis是普通权限,或config set命令被禁用等等。
感觉又是一个比较蛋疼和鸡肋的情境。
0x04 Python反序列化逆袭
果然在线上环境尝试写入cron文件,都没成功反弹回shell。
在这个地方卡了很久,思路一直在考虑“是否真的成功写入文件”这个问题,如果“成功写入了文件”,为什么没有反弹到shell;如果没有成功写入文件,是不是没有权限,是否可以写入python的webshell?总结起来有几个思路:
- 写入ssh key进行getshell。但扫端口发现似乎并没有开放22,推测是更换了ssh端口并进行的IP限制,或者直接没有运行sshd。
- 写入cron尝试反弹shell,但没成功。也许是redis没权限,也许是因为目标是ubuntu或debian,这两个系统对于cron文件格式限制会比较严,很难用redis反弹shell。
- 写入python的webshell,但可能也会遇到文件格式要求过严导致python运行失败,而且通常写入python脚本需要重启服务器才能奏效
- 写入jinja2模板文件,并通过模板引擎支持的语法执行命令。
总结起来,第4个方法最靠谱,因为模板文件对格式要求不严,只要我需要执行的语句放在类似{{ }}
的标签中即可。但经过测试,还是有几个问题:一是web路径(关键是存放模板文件的路径)和模板名称都需要猜,这个太难;二是redis如果是从源进行安装,一般是redis用户运行,一般无法写入web目录。
吃个夜宵再回来想想,我觉得首先得解决“是否真的成功写入文件”这个问题。后面fuzz了一下,测试了一堆目录,发现成功在/var/www/html/static
下写入了文件,并通过http://example.com/static/xxxfile
直接可以访问!
下载刚写入的文件,其实这个文件即为redis的导出文件。我将之导入到自己本地的redis环境中,又是一个惊喜:
看到这个样式的数据,我就知道这一定是反序列化数据,而且是Python2.7的反序列化数据。
这里科普一下,Python2.7和3.5默认使用的序列化格式有所区别,一般带有括号和换行的序列化数据是2.7使用的,而包含
\x00
的一般是3.5使用的。
后续利用就和 https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html 这篇文章一个套路。目标站使用redis存储session数据,且session数据使用序列化保存,我们可以通过反序列化来执行任意命令。
使用python2.7构造一个执行反弹shell脚本的序列化数据,并通过SSRF漏洞设置成session:hacker
的值,然后访问目标站点的时候设置Cookie session=hacker
。
不过有一点需要注意,就是SSRF时URL太长的话会抛出错误(之前本地测试的时候说过),所以需要曲线救国,使用redis的append命令,将数据一段一段写入,类似于这样:
另外还有个坑,写入的时候,特殊字符(如换行)需要转义:http://127.0.0.1%0d%0aAPPEND%20session:hacker%20"(S'id'\np1\ntp2\nRp3\n."%0d%0aSAVE%0d%0a:6379/
,而且只有值被引号包裹时转义符才能转义,否则转义符又会被转义……这个把我坑了好久,差点就以为功亏一篑了。
最后感觉,挖漏洞思路还是得跳,之前一直在考虑怎么通过redis写文件来进行getshell,却没想到通过读取redis的备份文件,找到了突破口。
成功反弹shell:
总结
这一次案例,出现漏洞的根本原因有几个:
- Web层面出现SSRF漏洞
- Python版本过低,存在CVE-2016-5699头注入漏洞
- Redis版本过低,新版Redis写入的文件权限一般是660,可以极大程度上避免写文件造成的漏洞
拿到shell以后我看了下源码,其逻辑是这样:获取用户传入的url参数,直接发送HTTP请求并拿到返回对象,判断返回对象的Content-Type是否包含image,如果包含则离线数据并显示出来,否则返回error。
这就导致HTTP请求一旦出现错误,服务器就会抛出500,而返回error是手工判断的结果,所以状态码还是200。
另外还有个感想,我怕把目标环境搞坏了,整个过程中多次用到了本地环境进行测试,而所有本地环境都是用docker启动的 ,非常方便。
之后我应该会模拟一下这个目标,做一个vulhub环境给大家,有时间再说吧……
Python安全 - 从SSRF到命令执行惨案的更多相关文章
- 转:Python安全 - 从SSRF到命令执行惨案
转:https://www.leavesongs.com/PENETRATION/getshell-via-ssrf-and-redis.html Python安全 - 从SSRF到命令执行惨案 PH ...
- Python开发的3种命令执行方法
在python开发中,我们常常需要执行命令,修改相关信息.那对于初学者来说,python中如何执行命令呢?今天,小编就为大家分享3种python命令执行的方法. 1. 使用os.system(&quo ...
- Python开发程序:RPC异步执行命令(RabbitMQ双向通信)
RPC异步执行命令 需求: 利用RibbitMQ进行数据交互 可以对多台服务器进行操作 执行命令后不等待命令的执行结果,而是直接让输入下一条命令,结果出来后自动打印 实现异步操作 不懂rpc的请移步h ...
- Python实现ssh批量登录并执行命令
局域网内有一百多台电脑,全部都是linux操作系统,所有电脑配置相同,系统完全相同(包括用户名和密码),ip地址是自动分配的.现在有个任务是在这些电脑上执行某些命令,者说进行某些操作,比如安装某些软件 ...
- Python学习总结 06 paramiko 远程执行命令
有时会需要在远程的机器上执行一个命令,并获得其返回结果.对于这种情况,python 可以很容易的实现. 1 工具 Python paramiko 1) Paramiko模块安装 在Linux的Term ...
- python套接字编程实现ntp服务和远程命令执行
python套接字编程实现ntp服务和远程命令执行 目录 基于udp实现ntp服务 基于tcp实现远程命令执行 基于udp实现远程命令执行 tcp与udp的比较 前面关于套接字基础请查阅 https: ...
- Python之路 - Socket实现远程执行命令
Python之路 - Socket实现远程执行命令 os模块实现
- python中模拟进行ssh命令的执行
在进行socket编程的时候,可以实现远程执行命令,然后返回相关的结果,但是这种...很容易就把服务器搞挂了. 在这里需要用到commands模块,commands模块中有一个方法为getstatus ...
- shell基础概念, if+命令, shell中引用python, shell脚本的几种执行方式
说明: 虚拟机中shell_test目录用来练习shell, 其中有个test.log文件用来存放日志 #!/usr/bin/bash # shell文件开头, 用来指定该文件使用哪个解释器 ...
随机推荐
- Testlink解决大用例导入问题
最近公司同事需要将别的testlink的用例迁移过来,由于现在新的服务器也在使用,不能使用数据库导入的办法,只能用xml文件进行导入,不过在导入的时候出现了个没遇到的问题,报错文件太大,无法上传. 解 ...
- 20165223 week4测试补交与总结
JDB调试程序 调试代码 public class SumofRecur1{ public static void main(String[] args) { int i = 0; for(Strin ...
- python面向对象编程 -- 封装、继承
面向对象编程 -- 封装.继承 面向对象编程三要素:封装.继承和多态.本文主要看和封装.继承相关的概念:在python中多态的概念比较模糊,本文不做讨论. 1 封装 封装:将数据和操作组装到一起,对外 ...
- 最短路 次短路 k短路(k很小)
最短路 luogu 3371 https://www.luogu.org/problemnew/show/P3371 #include <cstdio> #include <cstd ...
- haploview出现“results file must contain a snp column”的解决方法
将plink文件用“--recode HV ”的参数生成即可 /software/plink --file yourfile --recode HV --snps-only just-acgt --o ...
- R语言实现两文件对应行列字符替换(解决正负链统一的问题)
假设存在文件file1.xlsx,其内容如下: 存在文件file2.xlsx,其内容如下: 现在我想从第七列开始,将file2所有的字符替换成file1一样的,即第七.八.九.十列不需要改变,因为fi ...
- 论文总结(Frequent Itemsets Mining With Differential Privacy Over Large-Scale Data)
一.论文目标:将差分隐私和频繁项集挖掘结合,主要针对大规模数据. 二.论文的整体思路: 1)预处理阶段: 对于大的数据集,进行采样得到采样数据集并计算频繁项集,估计样本数据集最大长度限制,然后再缩小源 ...
- c#大文件的拷贝
using System.IO; namespace 数据流 { class Demo2 { private string _strSourcePath = @"D:\httpd-2.4.3 ...
- 阿里面试:MYSQL的引擎区别
MyISAM是MySQL的默认数据库引擎(5.5版之前),由早期的ISAM(Indexed Sequential Access Method:有索引的顺序访问方法)所改良.虽然性能极佳,但却有一个缺点 ...
- 信用评分卡 (part 2of 7)
python信用评分卡(附代码,博主录制) https://study.163.com/course/introduction.htm?courseId=1005214003&utm_camp ...