Python3命令注入防范
一、背景说明
说实话自己是做安全的,平时总是给别人代码找茬,但轮到自己写代码有时比开发还不注重安全,其中有安全脚本一般比较小考虑安全那么处理安全问题的代码比重将会大大超过业务代码的问题也有不是专职开发添加一项功能还没开发那么熟练的问题。由于安全脚本一般不是对外开启服务的,所以一般也不会暴出什么问题。
但前段时间被拉群通知说自己写的一个脚本存在命令注入漏洞,开始很讶异,经沟通和分析之后发现是因为脚本有通过system调用一个二进制文件上报执行存在风险命令的操作,当构造一个命中风险判定规则且进一步构造注入语法的命令时,即能实现命令注入。
二、直接使用shlex.quote()进行修复
防范命令入注我们一般都会建议过滤\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()进行修复
shlex.quote()是Python3.3之后才引入的,shlex.quote()代码也很简单,如果要兼容没有该函数的Python版本可以自行实现该函数。
shlex.quote()源代码链接:https://github.com/python/cpython/blob/master/Lib/shlex.py#L325
从原理上看,shlex.quote()所做的就是两件事,一是在字符串最外层加上单引号使字符串只能做为一个单一体出现,二是将字符串内的单引号用双引号引起来使其失去可能的闭合功能。
示例代码如下:
import re def quote(s): """Return a shell-escaped version of the string *s*.""" # return if string in null if not s: return "‘‘" # _find_unsafe = re.compile(r‘[^\%+=:,./-]‘, re.ASCII).search # return if string have no those char. if re.search(r‘[^\%+=:,./-]‘, s, re.ASCII) is None: return s # use single quotes, and put single quotes into double quotes # the string $‘b is then quoted as ‘$‘"‘"‘b‘ return "‘" + s.replace("‘", "‘\"‘\"‘") + "‘" if __name__ == "__main__": 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(quote(filename_para)) print("be injected but escape result command:") print("{}\n".format(command))
运行结果如下:
参考: