【红日Day13-CTF】特殊WAF SQL注入绕过

练习记录

复现代码:

index.php

<?php
require 'config.php';
  function dhtmlspecialchars($string) {
      if (is_array($string)) {
          foreach ($string as $key => $val) {
              $string[$key] = dhtmlspecialchars($val);
          }
      }
      else {
          $string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&amp;', '&quot;', '&lt;', '&gt;', '(', ')'), $string);
          if (strpos($string, '&amp;#') !== false) {
              $string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
          }
      }
      return $string;
  }
  function dowith_sql($str) {
      $check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
      if ($check) {
          echo "非法字符!";
          exit();
      }
      return $str;
  }
  // 经过第一个waf处理
  foreach ($_REQUEST as $key => $value) {
      $_REQUEST[$key] = dowith_sql($value);
  }
  // 经过第二个WAF处理
  $request_uri = explode("?", $_SERVER['REQUEST_URI']);
  if (isset($request_uri[1])) {
      $rewrite_url = explode("&", $request_uri[1]);
      foreach ($rewrite_url as $key => $value) {
          $_value = explode("=", $value);
          if (isset($_value[1])) {
              $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
          }
      }
  }
  // 业务处理
  if (isset($_REQUEST['submit'])) {
      $user_id = $_REQUEST['i_d'];
      $sql = "select * from day13.users where id=$user_id";
      $result=mysql_query($sql);
      while($row = mysql_fetch_array($result))
      {
          echo "<tr>";
          echo "<td>" . $row['name'] . "</td>";
          echo "</tr>";
      }
  }
?>

config.php

<?php
$mysql_server_name="localhost";
$mysql_database="day13";    /** 数据库的名称 */
$mysql_username="root";  /** MySQL数据库用户名 */
$mysql_password="root";  /** MySQL数据库密码 */
$conn = mysql_connect($mysql_server_name, $mysql_username,$mysql_password,'utf-8');
?>
搭建CTF环境使用的sql语句

#
# Structure for table "users"
#
CREATE database day13;
use day13;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `pass` varchar(255) DEFAULT NULL,
  `flag` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

#
# Data for table "users"
#

/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` VALUES (1,'admin','qwer!@#zxca','hrctf{R3qu3st_Is_1nterEst1ng}');
/*!40000 ALTER TABLE `users` ENABLE KEYS */;

漏洞分析:

进入网站:

http://10.211.55.2:100/day13/index.php

做题之前我们先来了解一些需要用到的基础知识。

  1. 对于传入的非法的 $_GET 数组参数名,PHP会将他们替换成 下划线 。经过fuzz,有以下这些字符:
    在这里插入图片描述
  2. 当我们使用HPP(HTTP参数污染)传入多个相同参数给服务器时,PHP只会接收到后者的值。(这一特性和中间件有关系)
    在这里插入图片描述
  3. 通过$_SERVER['REQUEST_URI']方式获得的参数,并不会对参数中的某些特殊字符进行替换。
    在这里插入图片描述

题目中index.php这里的代码中有两个waf。

第一个WAF
在代码第26行-第27行,这里面采用了 dowith_sql()函数,

 // 经过第一个waf处理
  foreach ($_REQUEST as $key => $value) {
      $_REQUEST[$key] = dowith_sql($value);
  }

跟进一下 dowith_sql()函数,该函数主要功能代码在 17-24行

  function dowith_sql($str) {
      $check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
      if ($check) {
          echo "非法字符!";
          exit();
      }
      return $str;
  }

如果 $_REQUEST数组中的数据存在 select|insert|update|delete等敏感关键字或者是字符,则直接exit()。如果不存在,则原字符串返回。

第二个WAF
在代码第29行-第39行

  // 经过第二个WAF处理
  $request_uri = explode("?", $_SERVER['REQUEST_URI']);
  if (isset($request_uri[1])) {
      $rewrite_url = explode("&", $request_uri[1]);
      foreach ($rewrite_url as $key => $value) {
          $_value = explode("=", $value);
          if (isset($_value[1])) {
              $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
          }
      }
  }

这部分代码通过 $_SERVER['REQUEST_URI']的方式获取参数,然后使用 explode函数针对 &进行分割,获取到每个参数的参数名和参数值。然后针对每个参数值调用 dhtmlspecialchars()函数进行过滤。

跟进一下 dhtmlspecialchars()函数,发现其相关功能代码在第3行-第16行

  function dhtmlspecialchars($string) {
      if (is_array($string)) {
          foreach ($string as $key => $val) {
              $string[$key] = dhtmlspecialchars($val);
          }
      }
      else {
          $string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&amp;', '&quot;', '&lt;', '&gt;', '(', ')'), $string);
          if (strpos($string, '&amp;#') !== false) {
              $string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
          }
      }
      return $string;
  }

这个函数主要功能是针对 &, ", <, >, (, ) 等特殊字符进行过滤替换,最后返回替换后的内容。从 第41行和第42行 的代码中,我们可以看到这题的参数都是通过 REQUEST 方式获取。

 if (isset($_REQUEST['submit'])) {
      $user_id = $_REQUEST['i_d'];

我们可以先来看个例子:
在这里插入图片描述
第一次
$_REQUEST仅仅只会输出 i_d=2的原因是因为php自动将i.d替换成了 i_d。而根据我们前面说的第二个特性,PHP取最后一个参数对应的值,因此第一次 $_REQUEST 输出的是2

第二次
$_REQUEST会输出i_d=select&i.d=2是因为$_SERVER['REQUEST_URI']并不会对特殊的符号进行替换,因此结果会原封不动的输出。所以这题的payload可以根据下面这个思维导图进行构造:
在这里插入图片描述

  • 我们通过页面请求 i_d=padyload&i.d=123
  • 当数据流到达第一个WAF时,php会将参数中的某些特殊符号替换为下划线。因此便得到了两个i_d,所以此时的payload变成了i_d=payload&i_d=123
  • 前面我们介绍了,如果参数相同的情况下,默认第二个参数传入的值会覆盖 第一个参数传入的值。因此此时在第一个WAF中 i_d=123,不存在其他特殊的字符,因此绕过了第一个WAF
  • 当数据流到达进入到第二个WAF时,由于代码是通过 $_SERVER['REQUEST_URI']取参数,而我们前面开头的第三个知识点已经介绍过了 $_SERVER['REQUEST_URI']是不会将参数中的特殊符号进行转换,因此这里的i.d参数并不会被替换为i_d,所以此时正常来说i.di_d 都能经过第二个WAF
  • 第二个WAF中有一个 dhtmlspecialchars()函数,这里需要绕过它,其实很好绕过。绕过之后 i_d=payload&i.d=123便会进入到业务层代码中,执行SQL语句,由于这里的SQL语句采用拼接的方式,因此存在SQL注入。

因此最后payload如下:

http://10.211.55.2:100/day13/?submit=&i_d=-1/**/union/**/select/**/1,flag,3,4/**/from/**/day13.users&i.d=123

在这里插入图片描述

发布了35 篇原创文章 · 获赞 19 · 访问量 5185

猜你喜欢

转载自blog.csdn.net/zhangpen130/article/details/104046572
今日推荐