您的位置 » 首页 » 代码审计,实用工具 » 代码审计:phpmywind4.5.8代码审计缺陷打包和修复方案

代码审计:phpmywind4.5.8代码审计缺陷打包和修复方案

发表于5年前 | 作者: seay | 分类: 代码审计, 实用工具 | 孵化于:2012年11月15日 | 文章热度:11,670 次 全屏阅读

显示不全请点击全屏阅读

  年底就离校了,大专,软件测试专业。求安全公司收留。渗透测试、软件测试、代码审计都可以,最好是做安全。联系方式:root@cnseay.com

PHPMyWind介绍:

PHPMyWind 是一款基于PHP+MySQL开发,符合W3C标准的建站引擎。适用于企业级建站的首选利器。开发之初,团队就从适用于企业级建站为突破口,增强程序的易用性,灵活性,可拆分性,致力于为国内企业网站提供核动力[PHPMyWind 是一款基于PHP+MySQL开发,符合W3C标准的建站引擎。适用于企业级建站的首选利器。开发之初,团队就从适用于企业级建站为突破口,增强程序的易用性,灵活性,可拆分性,致力于为国内企业网站提供核动力

 

作者:Seay

博客:http://www.cnseay.com/

说明:看代码审计的文章,是要学思路,而不是用别人的成果去入侵。不断学习,不断钻研,才能不断进步。

 

审计环境:

用到的工具: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 `#@__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语句里的#@__替换为$this->db_tablepre(在配置文件中为$db_tablepre)

    function SetQuery($sql)

    {

$prefix = ‘#@__’;

        $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 `#@__member` WHERE `username`=’$c_uname'”);   //可能存在SQL查询语句,请注意是否过滤

428  if($r[‘password’] != $oldpassword)

429  {

430  ShowMsg(‘抱歉,旧密码错误!’,’-1′);

431  exit();

432  }

433  }

434 

435  $sql = “UPDATE `#@__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:

phpmywind漏洞,

如果您喜欢我的博客,欢迎点击图片定订阅到邮箱填写您的邮件地址,订阅我们的精彩内容: 也可以点击链接【订阅到鲜果】

如果我的想法或工具帮助到了你,也可微信扫下方二维码打赏本人一杯咖啡


来自 Seay互联网安全博客
本文地址:http://www.cnseay.com/1649/
文章版权说明请看置顶文章,尊重作者,转载请以链接形式标明原文地址

马上分享给你的朋友吧~

发表评论

你的大名(必填)

你的邮箱(必填)

评论内容(必填)