符生 發表於 2020-3-8 19:47:00

乌云1000个PHP代码审计案例(1)

<p>前两天发现的宝藏网站:https://php.mengsec.com/</p>
<p>在github上面找到了源代码:https://github.com/Xyntax/1000php,可以在自己的服务器上面搭建</p>
<p>跟数据结构学习一样,每天跟着上面前辈们的案例进行审计,打基础+学习思路</p>
<p>&nbsp;</p>
<p>1,BlueCMS v1.6 sp1 ad_js.php SQL注入漏洞</p>
<p>使用seay自动审计工具</p>
<p><img src="https://img2020.cnblogs.com/i-beta/1828215/202003/1828215-20200304161042785-284071093.png" alt=""></p>
<p>&nbsp;</p>
<p>&nbsp;可以看到第一条可能出现的漏洞点就是ad_js.php</p>
<p>我们跟进去看,</p>
<p><img src="https://img2020.cnblogs.com/i-beta/1828215/202003/1828215-20200304161123782-1229821125.png" alt=""></p>
<p>&nbsp;</p>
<p>&nbsp;经过getone函数在数据库里面查询出结果并赋值给$ad变量,同时可以看到$ad_id没有包裹引号</p>
<p><img src="https://img2020.cnblogs.com/i-beta/1828215/202003/1828215-20200304161315833-264762874.png" alt=""></p>
<p>&nbsp;</p>
<p>&nbsp;getone函数是在数据库里面进行查询的函数</p>
<p><span class="color_h1">mysqli_fetch_array() 函数从结果集中取得一行作为关联数组,或数字数组,或二者兼有。也就是从数据库中查询。</span></p>
<p><span class="color_h1">我们往上看$ad_id变量的输入位置是否有过滤。</span></p>
<p><span class="color_h1"><img src="https://img2020.cnblogs.com/i-beta/1828215/202003/1828215-20200304161621692-1390276958.png" alt=""></span></p>
<p>&nbsp;</p>
<p>&nbsp;可以看到对于GET方法输入的ad_id变量仅仅是使用trim函数去除了输入值两边的空格,此外就没有另外的过滤了,所以该点确实存在SQL注入漏洞。</p>
<p>&nbsp;</p>
<p>真实环境下利用很简单,使用union联合注入即可出数据</p>
<p>修复方法:$ad_id = !empty($_GET['ad_id']) ? intval($_GET['ad_id']) : '';</p>
<p>将输入的数据强行转换成整型</p>
<p>&nbsp;</p>
<p>2,Discuz!7.2/X1 第三方插件SQL注入及持久型XSS漏洞</p>
<p>这里分析SQL注入漏洞,插件是由Discuz!认证的(http://addons.discuz.com/workroom.php)第三方开发团队“潮流少年工作室 Teen Studio”出品的心情墙插件(http://www.discuz.net/forum.php?mod=viewthread&amp;tid=1632898)</p>
<p>因为去找漏洞插件太麻烦了,我这里直接贴上漏洞源代码吧</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">elseif($action == 'edit_mood' &amp;&amp; moodid) {


          //moodid未初始化,直接代入sql查询


          $check = $db-&gt;result_first("SELECT * FROM {$tablepre}moodwall WHERE id='$moodid' AND uid='$discuz_uid'");





          if(!$check || !$moodid) {


                  showmessage('moodwall:moodwall_inc_php_2', 'plugin.php?id=moodwall&amp;action=user_mood');


          }


          $sql = "SELECT * FROM {$tablepre}moodwall WHERE id='$moodid'";


          $query = $db-&gt;query($sql);


          $moodlist_edit = array();


          while($mood_edit = $db-&gt;fetch_array($query)) {


                  $moodlist_edit[] = $mood_edit;


          }
</pre>
</div>
<p>  可以看到$moodid变量在单引号包裹下代入$sql语句进行查询</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">$sql = "SELECT * FROM {$tablepre}moodwall WHERE id='$moodid'";
</pre>
</div>
<p>  但是在这里漏洞的发现者也说了,需要网站magic_quotes_gpc=off,在magic_quotes_gpc=On的情况下,如果输入的数据有单引号(’)、双引号(”)、反斜线()与 NULL(NULL 字符)等字符都会被加上反斜线。</p>
<p>不过实际上还可以有宽字节注入等特殊情况对网站进行注入</p>
<p>&nbsp;</p>
<p>3,ecshop SQL注射漏洞</p>
<p>因为没有详细的ecshop版本信息,所以同样不进行源码的下载,在wooyun大大的基础上直接进行分析</p>
<p>在include_libcommon.php中存在如下函数,<code>get_package_info函数</code></p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">function get_package_info($id)
{
    global $ecs, $db,$_CFG;
    $now = gmtime();
    $sql = "SELECT act_id AS id,act_name AS package_name, goods_id , goods_name, start_time, end_time, act_desc, ext_info".
         " FROM " . $GLOBALS['ecs']-&gt;table('goods_activity') .
         " WHERE act_id='$id' AND act_type = " . GAT_PACKAGE;
  $package = $db-&gt;GetRow($sql);
  /* 将时间转成可阅读格式 */<br>  if ($package['start_time'] &lt;= $now &amp;&amp; $package['end_time'] &gt;= $now)
  {
    $package['is_on_sale'] = "1";
   }
  else
  {
    $package['is_on_sale'] = "0";
  }
  $package['start_time'] = local_date('Y-m-d H:i', $package['start_time']);
  $package['end_time']   = local_date('Y-m-d H:i', $package['end_time']);
  $row = unserialize($package['ext_info']);
  unset($package['ext_info']);
  if ($row)
  {
    foreach ($row as $key=&gt;$val)
    {
      $package[$key] = $val;
    }
  }
    $sql = "SELECT pg.package_id, pg.goods_id, pg.goods_number, pg.admin_id, ".
    " g.goods_sn, g.goods_name, g.market_price, g.goods_thumb, g.is_real, ".
    " IFNULL(mp.user_price, g.shop_price * '$_SESSION') AS rank_price " .
    " FROM " . $GLOBALS['ecs']-&gt;table('package_goods') . " AS pg ".
    "   LEFT JOIN ". $GLOBALS['ecs']-&gt;table('goods') . " AS g ".
    "   ON g.goods_id = pg.goods_id ".
    " LEFT JOIN " . $GLOBALS['ecs']-&gt;table('member_price') . " AS mp ".
    "ON mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION' ".
    " WHERE pg.package_id = " . $id. " ".
    " ORDER BY pg.package_id, pg.goods_id";
  $goods_res = $GLOBALS['db']-&gt;getAll($sql);
  $market_price      = 0;
</pre>
</div>
<p>  可以看到$sql语句里面代入了输入的$id,我们再定位谁调用了get_package_info函数,在系统的lib_order.php中。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">function add_package_to_cart($package_id, $num = 1)
{
    $GLOBALS['err']-&gt;clean();
  /* 取得礼包信息 */
  $package = get_package_info($package_id);
  if (empty($package))
  {
    $GLOBALS['err']-&gt;add($GLOBALS['_LANG']['goods_not_exists'], ERR_NOT_EXISTS);
    return false;
  }
</pre>
</div>
<p>  可以看到调用get_package_info函数赋值给了$package变量,而$package_id变量是传递给add_package_to_cart函数的,在该函数中也没有看到有对变量的过滤,寻找调用add_package_to_cart函数的文件</p>
<p>在flow.php中存在可控的输入源</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">$package = $json-&gt;decode($_POST['package_info']);
    /* 如果是一步购物,先清空购物车 */
   if ($_CFG['one_step_buy'] == '1')
    {
     clear_cart();
  }
  /* 商品数量是否合法 */
  if (!is_numeric($package-&gt;number) || intval($package-&gt;number) &lt;= 0)
  {
    $result['error']   = 1;
    $result['message'] = $_LANG['invalid_number'];
  }
  else
  {
  /* 添加到购物车 */
    if (add_package_to_cart($package-&gt;package_id, $package-&gt;number))
    {
      if ($_CFG['cart_confirm'] &gt; 2)
</pre>
</div>
<p>  而在这里$package-&gt;package_id是我们可以控制的输入源,根据刚才函数调用的过程,对于我们的这个输入并没有其余的过滤,可知存在SQL注入漏洞</p>
<p>&nbsp;</p>
<p>4,Discuz!漫游插件API本地包含漏洞</p>
<p>先上代码&nbsp;manyou/api/class/MyBase.php</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">128: function parseRequest() {

131: $request = $_POST;

132: $module = $request['module'];

133: $method = $request['method'];

...

174: return $this-&gt;callback($module, $method, $params);

175: }



177: function callback($module, $method, $params) {

...

191: @include_once DISCUZ_ROOT.'./manyou/api/class/'.$module.'.php';
</pre>
</div>
<p>  在parseRequest函数里面,可以看到最后return调用了callback函数,并且传入的三个参数都是我们可以控制的输入,而callback函数里面使用include_once函数包含了当前路径下的传递的$module.php文件</p>
<p>PHP中文件包含函数有以下四种:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">    require()

    require_once()

    include()

    include_once()
</pre>
</div>
<p> include和require的区别在于include在包含的时候出现错误的时候,会抛出一个警告,然后程序继续运行,而require函数在出现错误的时候,会直接报错并且退出程序的执行</p>
<p>require_once和include_once这两个函数与前两个不同的地方在于,这两个函数只包含一次,适合于脚本执行期间同一个文件可能被包括一次以上的情况时,你想确保它只包含一次以避免函数重定义,变量重新赋值等问题</p>
<p>参考自:https://www.freebuf.com/articles/web/182280.html</p>
<p>在这里我们可以控制$module变量,但是这里是有限制的文件包含,因为在后面加上了.php的后缀,当我们想包含当前文件夹任意文件的时候,如果magic_quotes_gpc = Off php版本&lt;5.3.4,我们可以使用%00截断的方式,当然还有其他很多的方式可以绕过这个限制,如果在这里还存在目录穿越漏洞的话,我们就可以包含服务器任意文件了,而不是限制在这个文件夹下。</p>
<p>&nbsp;</p>
<p>5,PHPCMS通杀XSS</p>
<p>这里是一个反射型XSS漏洞,在我要报错功能页下,过滤不严格</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">http://**.**.**.**/error_report/error_report.php?title=1&amp;contentid=1"&gt;&lt;script&gt;alert(/xss/)&lt;/script&gt;
</pre>
</div>
<p>  反射型XSS漏洞比较容易通过扫描器黑盒直接发现,只需要将尖括号,单双引号等提交到web服务器,检查返回的HTML页面里面有没有保留原来的特殊字符即可判断。</p>
<p>在白盒审计中,我们只需要寻找带有参数输出函数,然后根据输出函数对输出内容回溯输入参数,观察有没有进行经过过滤</p>
<p>&nbsp;</p>
<p>6,phpcms 2008 sp4 爆路径及任意文件删除漏洞</p>
<p>漏洞文件是corpandresize/config.inc.php</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">$tmp = $_COOKIE['tmp'];


define("TMP_PATH", $tmp);
</pre>
</div>
<p>  在cookie参数中获取tmp,同时定义TMP_PATH等于变量tmp的值</p>
<p>使用到TMP_PATH的位置是corpandresize/process.php</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">@unlink(TMP_PATH.'/'.$thumbfile);
</pre>
</div>
<p>  这里使用unlink函数删除文件,我们可以使用%00截断删除我们想要删除的任意文件</p>
<p>如在cookie中添加tmp=../index.php%00,按照上面的分析过程,就可以删除首页文件</p>
<p>&nbsp;</p>
<p>7,phpcms2008本地文件包括及利用(执行任意SQL脚本)</p>
<p>漏洞文件存在于wap/index.php</p>
<p>代码如下:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">&lt;?php

include '../include/common.inc.php';

include './include/global.func.php';

$lang = include './include/lang.inc.php';

if(preg_match('/(mozilla|m3gate|winwap|openwave)/i', $_SERVER['HTTP_USER_AGENT']))

{

header('location:../');

}

wmlHeader($PHPCMS['sitename']);



$action = isset($action) &amp;&amp; !empty($action) ? $action : 'index';

if($action)

{

include './include/'.$action.'.inc.php';

}



$html = CHARSET != 'utf-8' ? iconv(CHARSET, 'utf-8', $html) : $html;

echo str_replace('&lt;br/&gt;', "&lt;br/&gt;\n", $html);

wmlFooter();

?&gt;
</pre>
</div>
<p>  可以看到在这一段代码里有:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">$action = isset($action) &amp;&amp; !empty($action) ? $action : 'index';
if($action)
{
    include './include/'.$action.'.inc.php';
}
</pre>
</div>
<p>  <strong>isset()</strong> 函数用于检测变量是否已设置并且非 NULL。在这里即检测$action是否设置,同时检测$action是否为空,空的话就赋值为index,非空即传入值,接着就是包含传入的$action.inc.php文件,对于输入的$action没有进行过多的检测</p>
<p>可以包含存在sql语句的php文件,执行任意sql语句来对漏洞进行利用</p>
<p>例如包含目录include\fields\areaid 下任一文件,执行任意SQL脚本。</p>
<p>比如field_add.inc.php文件,我们只需要传入的$action=fields/areaid/field_add,field_add.inc.php文件代码为:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">&lt;?php

if(!$maxlength) $maxlength = 255;

$maxlength = min($maxlength, 255);

$sql = "ALTER TABLE `$tablename` ADD `$field` VARCHAR( $maxlength ) NOT NULL DEFAULT '$defaultvalue'";

$db-&gt;query($sql);

?&gt;
</pre>
</div>
<p>  在这里我们只需要传入$tablename变量就可以执行$sql语句进行查询</p>
<p>最后的payload为:http://www.phpcms.cn/wap/index.php?action=../../include/fields/areaid/field_add&amp;tablename=xx</p>
<p>&nbsp;</p>
<p>8,Ecshop2.7.2持久型XSS(可获得管理员帐号)</p>
<p><img src="https://img2020.cnblogs.com/i-beta/1828215/202003/1828215-20200308194025258-102490229.png" alt=""></p>
<p>&nbsp;</p>
<p>&nbsp;可以看到当POST传输的数据extend_field_index没有任何的过滤就进入数据库进行更新了。</p>
<p>这里的输入是密码保护问题这一项,我们可以在密码保护问题里输入XSS,但是后台查看会员资料是不显示密码保护问题的,所以这里必须要网站后台添加了新的 “会员注册项”时,后台查看资料就会显示了,显示了之后就可以进行XSS攻击了。</p>
<p>这里不懂的地方是原漏洞作者导入外部js的时候,js文件里面的内容是Ajax:</p>
<p>Ajax.call('privilege.php?act=update','id=1&amp;user_name=heihei&amp;email=10001@**.**.**.**','',"POST","JSON");</p>
<p><img src="https://img2020.cnblogs.com/i-beta/1828215/202003/1828215-20200308194500644-1631484665.png" alt=""></p>
<p>&nbsp;</p>
<p>&nbsp;<img src="https://img2020.cnblogs.com/i-beta/1828215/202003/1828215-20200308194504242-496507017.png" alt=""></p>
<p>&nbsp;</p>
<p>&nbsp;而不是写入外部js盗取管理员cookie,以后明白了再补充这里。</p><br><br>
来源:https://www.cnblogs.com/Cl0ud/p/12410285.html
頁: [1]
查看完整版本: 乌云1000个PHP代码审计案例(1)