作者:ZERO 所属团队:Arctic Shell
参考文献:《sql注入攻击与防御》
使用平台:pikachu漏洞练习平台
导语:
在owasp发布的top10排行榜中注入漏洞一直是危害排名极高的漏洞,数据库注入一直是web中一个令人头疼的问题。
一个严重的SQL注入漏洞,可能会直接导致一家公司破产!
这并不是戏言,其实SQL注入漏洞最主要的形成原因是在进行数据交互中,当前端的数据传入后端进行处理时,由于没有做严格的判断,导致其传入的“数据”在拼接到SQL语句中之后,由于其特殊性,被当作SQL语句的一部分被执行,从而导致数据库受损(被脱裤、被删除、甚至整个服务器权限沦陷)。
本文将从web程序的基本原理开始理解SQL注入,然后找到SQL注入利用(本次将演示利用information-schema注入进行一次完整的入侵过程再现)。
0x1: 理解(了解)web应用的工作原理
在日常web开发中,我们会来开发出许多的web应用来服务于我们的日常生活,随着人们生活质量的提高,web应用的形式也越来越多,但大部分的web应用依旧有着相同点,他们都具备交互性,且多半是数据库进行驱动的,在当下的互联网中,用数据库驱动的web应用多如牛毛,他们通常含有一个后台数据库以及很多页面,而在这些页面中包含了利用某种编程语言来写成的服务器端脚本,这些脚本作用于web页面来与用户进行交互时从数据库中提取特定的数据。
一般的web应用通常分三层,分别是表示层、逻辑层、储存层。下面我们介绍一下这三层以及交互方式
表示层是一个web应用的最高层,将该web应用的具体功能以及信息呈现给用户,还可以与其他层进行交互。
逻辑层(中间层)是用来处理细节以达到控制应用的目的。
而存储层包括数据库服务器,用来进行的信息的检索和存贮。
通常的交互方式就是表示层向中间层发出请求,中间层通过查询、检索存储层,获取信息来响应该请求。这就是一次完成的交互过程。
三层架构有一个基本原则:表示层不能直接于数据层通信,在三层架构中,所有通信必须都通过中间层。
0x2:理解SQL注入
前面讲了那么多理论的东西,究竟什么是SQL注入呢?
SQL注入就是构造SQL语句并将他们插入到传入的参数中发动的攻击,然后将这些参数递交给后台SQL服务器加以解析并执行。
这里我将用一个最基本的数字型注入来演示一个基本SQL注入
一个基本的查询页面长这样的:
我们输入一个“1”试一试
这里可以看出查询了两个字段,猜测hello后的应该是用户名,下面的应该是用户所对应的邮箱
因此猜想SQL语句的构造可能是这样的:
select 字段1,字段2 from 表 where id =1
那我们如何获得表的其他数据呢?(俗称爆表)
这里就可以来尝试构造一个利用了:1 or 1=1
(在SQL中or是如果第一个条件和第二个条件中只要有一个成立,则 OR 运算符显示一条记录。1=1永久返回为真,所以where后输入的值将永远为真)
因为是post方法,我们使用hackbar进行post提交,来遍历整个表
一次基础的SQL注入利用就演示完成了。
0x3:如何找到SQL注入
本身我们很难获取到web应用的源代码,这样会导致我们不知道设计者如何设计的,找到SQL注入,这就需要借助黑客本身的自我思维能力来进行测试。借助推理来进行测试的一般步骤:向服务器发送请求,然后查看其中的异常来获取信息。
通过SQL错误来找到SQL注入
在web注入靶场中我们可以通过看源码知道这个web应用的构造,来完成利用,而在实际的测试中,碰到的都是是不开源的web应用,导致对于内部构造完全不清楚,在实际的测试中一般黑客采用让SQL报错的方式来猜测内部构造,以达到找到注入点的目的。
我将通过字符型注入来演示通过SQL报错来查找注入
字符型注入这类利用的一般方式是:先来闭合测试,构造合法的SQL来欺骗后台。
利用的一般样式:xxx (符号闭合) or 1=1(注释)
通常一个输入框我们事先并不知道输入的内容再被传入时被当做字符还是数字,这时就可以通过报错来进行判断,SQL中字符和数字的区别是数字不用’’或者””来进行修饰,而字符则需要,于是我们则需要判断两次,先判断这个输入框是数字型的还是字符型的在判断他是用哪个修饰符来进行修饰
报错内容提示列名不存在,这个报错提示我们SQL服务器认为这个值不是一个数字,那末就是一个列,然后SQL去寻找这个列,然后当然什么都找不到,就返回报错了,通过这里我们可以判断出应该输入的类型
接着来判断修饰符
查询后结果
从报错提示中可以分析出双引号并没有传递进数据库服务器里,也就是存储层,我们再试一个单引号
再次返回报错,在这个报错中我们可以看出修饰符已经进入到了SQL服务器中并执行,说明存在注入点,接着可以来开始进行注入了,知道了该用哪个修饰符进行闭合,这时构造个利用将会变的更加容易。
(每当我们传递一个值到SQL构造语句中的时候,如果传递的一个值是需要符号修饰的,那末SQL语句会自带一个符号去进行闭合,例如:传值为a,到SQL语句中就会使这样:select xxx,xxx from xxx where id=’a’,那两个单引号就是SQL构造语句中用来进行参数闭合的,简单的说,只要你输入一个需要符号修饰的词语,后台就会自动给它加一个修饰符号,这也就是我们为何要把前面进行符号闭合,但是后面就会多了一个‘ 符号,我们直接注释掉就好了。同样的方法,利用order by 可以猜测有几个字段,道理相同,这里就不赘述了。)
0x4: 了解information_schema
information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。
元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
也就是说information_schma可以查到你的所有的搭建的数据库名、表名和列的数据类型,在一切条件未知的情况下,存在注入点的话我们可以直接尝试对information_schema进行访问,从而获得更多的信息。
例如:
SCHEMATA:提供了当前MySQL实例中所有数据库的信息
TABLES:所有数据库表的信息
CONLUMNS:提供了列信息
0x5:了解union联合注入
既然要从一个信息数据库开始注入,也就是另外一个数据库,我们就不能在当前的数据库下进行查询,但是当前数据库查询下还存在一个注入点,所以说如何才能同时查询两次呢?这个时候我们就可以用到union联合注入了,而union的作用是将两个或多个select语句查询语句结果合并起来
几个值得注意的地方:
1.UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔
2. UNION中的每个查询的列数(字段数)必须相同
3. UNION会从查询结果集中自动去除了重复行
比如:
主查询:
select username,password form member where name=“zero“ (union查询)union select id,password form member where id=1
构造 payload:
xx' union select database(),user()#
说明:
Database():这个是查询数据库名称
user():这个是使用者权限
0x6: information_schema注入演示
还是使用字符型注入,在前面的报错中我们知到了两个字段和注入点是单引号,开始注入
首先获取基本信息:当前数据库名称
xxx’union select database(),user()#
获取到当前数据库名称:pikachu
通过information_schema来获取pikachu中有多少个表以及表的名称
xxx’union select table_schema,table_name from information_schema.tables where table_schema=”pikachu”#
获取到表名:users
获取列:
xxx’union select table_name,column_name from information_schema.columns where table_name=’users’#
整理获取到的信息来获取密码:
xxx’union select username,password from users#
0x7: 总结
作为一名Web应用开发人员,一定要对用户输入的数据进行严格的校验处理,否则的话,SQL Injection将会不期而至!