Skip to content

Latest commit

 

History

History
115 lines (88 loc) · 4.93 KB

命令执行.md

File metadata and controls

115 lines (88 loc) · 4.93 KB

Command Execute

常见的执行命令模块和函数有

os
subprocess
pty -> 在Linux下使用
codecs
popen
eval
exec

执行某些需要系统命令参与的操作时,或者为了便于程序操作的时候。会直接调用某些系统命令库来执行,比如在CTF上常见的命令执行操作ping,为了达到这个想法,有采用系统模块操作的

os.system('ping -n 4 %s' %ip)

有自己实现ICMP协议来发送的,https://github.com/samuel/python-ping/blob/master/ping.py。原文是python2下的实现,后面提供一份python3下的修改版,或者直接使用`ping3`模块。

Python动态编程语言是能够从字符串执行代码,eval执行一个字符串,还可以用来执行字符串转对象。可以使用的还有exec

关于eval的危险性:https://lucumr.pocoo.org/2011/2/1/exec-in-python/

def command():
    if request.values.get('cmd'):
        sys.stdout = io.StringIO()
        cmd = request.values.get('cmd')
        return Response('<p>输入的值为:%s</p>' %str(eval(cmd)))  
        # return Response('<p>输入的值为:%s</p>' %str(exec(cmd)))   
    else:
        return Response('<p>请输入cmd值</p>')

重定向输出后,可以直接看到执行的命令结果。使用命令模块的场景

def COMMAND(request):
    if request.GET.get('ip'):
        ip = request.GET.get('ip')
        cmd = 'ping -n 4 %s' %shlex.quote(ip)
        flag = subprocess.run(cmd, shell=False, stdout=subprocess.PIPE)
        stdout = flag.stdout
        return HttpResponse('<p>%s</p>' %str(stdout, encoding=chardet.detect(stdout)['encoding']))
    else:
        return HttpResponse('<p>请输入IP地址</p>')

当然python可以命令执行的并不是单一的模块,还有反序列化,格式化字符串,以及web框架模板的模板注入。

subprocess是一个为了代替os其中的命令执行库而出现的,python3.5以后的版本,建议是使用subprocess.run来操作,3.5之前的可以使用库中你认为合适的函数。不过库中的函数都是通过subprocess.Popen的封装而实现,也可以执行使用subprocess.Popen来执行较复杂的操作,在shell=False的时候,第一个字符是列表,或者传入字符串。当使用shell=True的时候,python会调用/bin/sh来执行命令,届时会造成命令执行。

cmd = request.values.get('cmd')
s = subprocess.Popen('ping -n 4 '+cmd, shell=True, stdout=subprocess.PIPE)
stdout = s.communicate()
return Response('<p>输入的值为:%s</p>' %str(stdout[0], encoding=chardet.detect(stdout[0])['encoding']))

修复代码

至于某些操作,可以使用其他模块或者函数来执行的尽量不采用命令模块执行。eval和exec是没必要使用的,虽然某些情况下很好用,但是用来处理输入参数还是太过分了。

比如需要探测系统存活,可以使用ping3。尝试端口的开放使用socket。

ping3.verbose_ping(ip)

如果某些必要的命令操作需要命令模块来执行,建议使用subprocess,并且设置shell=False。可以保护免受shell相关的命令执行。按照官方建议,然后跟 shlex.quote()配合使用。

def COMMAND(request):
    if request.GET.get('ip'):
        ip = request.GET.get('ip')
        cmd = 'ping -n 4 %s' %shlex.quote(ip)
        flag = subprocess.Popen(cmd, shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
        stdout, stderr = flag.communicate()
        return HttpResponse('<p>%s</p>' %str(stdout))   #127.0.0.1&&whoami
    else:
        return HttpResponse('<p>请输入IP地址</p>')

这时候再使用127.0.0.1&&whoami的时候就可以看到,其实是把这个参数当作一个字符串来处理。

ping -n 4 '127.0.0.1&&whoami'
Ping 请求找不到主机 '127.0.0.1&&whoami'。请检查该名称,然后重试。

要是想采用过滤或者上面的方式不合适,还可以使用过滤和白名单的形式。如果采用如下的方式,设置文件的id,通过id来操作,同时id是一个hash字段。

def COMMAND(request):
    if request.GET.get('filte'):
        id = request.GET.get('filte')
        filename = File.objects.get(file_hash=id).filename    # 代表文件的hash字段
        os.system('rm %s' %filename)
        return HttpResponse('<p>删除成功</p>')
    else:
        return HttpResponse('<p>请输入IP地址</p>')

这样看是不是也能达到避免命令执行的效果?实际上,保存的filenam要看是不是后台自动生成的,如果传入一个这样的文件名,还是会存在风险。

aaa;whoami;.jsp

如果是不想依赖第三方模块,又要使用命令执行库,就要考虑怎么处理输入字段。简而言之,进入命令执行的字段一定是处理过的,最好是不可被前端预期的值。

搭建一个命令执行的环境,可以尝试这个项目,还有现成的脚本使用:https://github.com/sethsec/PyCodeInjection