python os.popen基础/os.popen 安全注入(OS命令注入漏洞\subprocess.getstatusoutput OS命令注入漏洞)、python 过滤命令注入危险字符

一、python OS命令注入漏洞

1. 警惕subprocess.getstatusoutput OS命令注入漏洞

python直接执行代码漏洞_警惕OS命令注入漏洞 - python篇
参考URL: https://blog.csdn.net/weixin_39805851/article/details/111079766

问题描述

在后台开发过程中,经常会遇到需要执行一些shell命令,但是命令的参数需要通过Restful API传入,举个简单的例子:

后台实现了一个ping的功能,用户可以指定ping的目标,假设后台代码:

def ping(target):

	return subprocess.getstatusoutput('ping -c 1 %s' % target)

如果用户正确传target值,那么没啥问题,比如用户想ping下百度,最终调用是:ping(“www.baidu.com”)

执行的shell命令是:ping -c 1 www.baidu.com

但是如果有一个用户,恶意的传了这么一个target: baidu.com; rm -rf /,后台直接填充到ping命令中去,那么后台执行的命令就是:ping -c 1 baidu.com; rm -rf /

如果你是以root用户执行的话,那就只能赶紧跑路了。

如何防御

防御的办法有这么几个思路:对传入的值做安全检查,比如必须符合IP或域名的格式,尤其要警惕那些命令连接符号,比如:|,&&,$(),;等

尽量不要用root用户执行,对程序的权限做合理的控制。

使用带有安全机制的函数库,在上面那个例子中,subprocess.getstatusoutput底层是调用的subprocess.check_output并且参数中设置了shell=True

try:

data = check_output(cmd, shell=True, universal_newlines=True, stderr=STDOUT)

exitcode = 0

except CalledProcessError as ex:

data = ex.output

exitcode = ex.returncode

if data[-1:] == '\n':

data = data[:-1]

return exitcode, data

问题就出在shell=True上,设置为True就是直接把命令丢给shell处理的,就会出现上述问题。如果设置成False,再把subprocess接收的Command参数改成一个List,例如:[“ls”, “-l”],在运行时,subprocess会把Command参数List的第一个元素作为执行命令,后面的项都强制作为参数去处理,也就没有注入的风险了。

例如:

# lsblk -l |grep  disk
sda           8:0    0 1000G  0 disk
sdb           8:16   0  500G  0 disk

使用getstatusoutput函数,并将shell参数设置为False,那么命令注入的风险会大大减小。当shell参数为False时,getstatusoutput将不会通过shell来执行命令,而是直接调用系统提供的执行命令的接口。

import subprocess

command = ['lsblk', '-l', '|', 'grep', 'disk']
suc, disk_info = subprocess.getstatusoutput(command, shell=False)

2. os.popen OS命令注入漏洞

os.popen基础

python调用Shell脚本,有两种方法:os.system()和os.popen(),

前者返回值是脚本的退出状态码,后者的返回值是脚本执行过程中的输出内容

os.popen() 方法用于从一个命令打开一个管道。
在Unix,Windows中有效。

os.popen(command):这种调用方式是通过管道的方式来实现,函数返回一个file对象,里面的内容是脚本输出的内容(可简单理解为echo输出的内容)

举例:像调用”ls”这样的shell命令,应该使用popen的方法来获得内容,对比如下:

>>> import os
>>> os.system("ls")   #直接看到运行结果
Desktop    Downloads     Music     Public     Templates  Videos
Documents  examples.desktop  Pictures  systemExit.py  test.sh
0    #返回值为0,表示命令执行成功
>>> n=os.system('ls')
Desktop    Downloads     Music     Public     Templates  Videos
Documents  examples.desktop  Pictures  systemExit.py  test.sh
>>> n
0
>>> n>>8   #将返回值右移8位,得到正确的返回值
0
>>> f=os.popen('ls') #返回一个file对象,可以对这个文件对象进行相关的操作
>>> f
<open file 'ls', mode 'r' at 0x7f5303d124b0>
>>> f.readlines()
['Desktop\n', 'Documents\n', 'Downloads\n', 'examples.desktop\n', 'Music\n', 'Pictures\n', 'Public\n', 'systemExit.py\n', 'Templates\n', 'test.sh\n', 'Videos\n']
>>> 

os.popen()可以实现一个“管道”,从这个命令获取的值可以继续被使用。因为它返回一个文件对象,可以对这个文件对象进行相关的操作。

1. 返回值是文件对象

注意:返回值是文件对象,既然是文件对象,使用完就应该关闭,对吧?!不信网上搜一下,一大把文章提到这个os.popen都是忘记关闭文件对象的。 所以,推荐的写法是:

with os.popen(command, "r") as p:
    r = p.read()

至于with的用法就不多讲了,使用它,不需要显式的写p.close()。

2. 非阻塞

通俗的讲,非阻塞就是os.popen不会等cmd命令执行完毕就继续下面的代码了,

在某些应用场景,可能这并不是你期望的行为,那如何让命令执行完后,再执行下一句呢?

处理方法是使用read()或readlines()对命令的执行结果进行读操作。

本质上os.popen是非阻塞的,为了实现阻塞的效果,我们使用read()或readlines()对命令结果进行读,由此产生了阻塞的效果。但是,如果你的命令执行无法退出或进入交互模式,这种“读”将形成完全阻塞的情况,表现的像程序卡住了。

python os.popen OS命令注入漏洞

demo如下,使用 os.popen,并不能直接阻止注入漏洞,还是取决你传参拼接,它其实还是直接把命令抛给shell处理,如果用户正确传target值,那么没啥问题。

# coding=utf-8
import os


def print_hi():
    target = "www.baidu.com;ls"

    with os.popen('ping -c 2 %s' % target, "r") as p:
        r = p.read()
        print r


if __name__ == '__main__':
    print_hi()

总结:使用os.popen 并不能直接杜绝os命令注入,如果使用时,请根据业务场景过滤。

二、python 过滤命令注入危险字符

python安全开发军规之一:防止shell注入
参考URL: https://baijiahao.baidu.com/s?id=1637242342359715510&wfr=spider&for=pc

在这里插入图片描述
防范命令入注我们一般都会建议过滤\n$&;|‘"()`等shell元字符

命令注入相关的特殊字符

在这里插入图片描述
防范命令入注我们一般都会建议过滤\n$&;|‘"()`等shell元字符

Python3命令注入防范

防范命令入注我们一般都会建议过滤\n$&;|'"()`等shell元字符,但自己实现一个个字符处理还是比较麻烦的,我们尽量寻求一种简单的方法最好是现成的函数或者库进行处理。

最后在这里看到说可以使用shlex.quote()进行处理:https://python-security.readthedocs.io/security.html#shell-command-

injection
import shlex


filename_para = 'somefile'
command = 'ls -l {}'.format(filename_para)
print("except result command:")
print("{}\n".format(command))

filename_para= 'somefile; cat /etc/passwd'
command = 'ls -l {}'.format(filename_para)
print("be injected result command:")
print("{}\n".format(command))

filename_para = 'somefile; cat /etc/passwd'
command = 'ls -l {}'.format(shlex.quote(filename_para))
print("be injected but escape result command:")
print("{}\n".format(command))

运行结果如下:
在这里插入图片描述

从原理上看,shlex.quote()所做的就是两件事,一是在字符串最外层加上单引号使字符串只能做为一个单一体出现,二是将字符串内的单引号用双引号引起来使其失去可能的闭合功能。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西京刀客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值