【读书】《黑客攻防技术宝典 Web实战篇(第2版)》读后总结(第1部分)
注入解释性语言
开发Web应用程序许多的核心语言使用解释器执行, 包括SQL, Perl, PHP, 基于解释性语言的执行方式, 产生了一系列代码注入的漏洞.有的时候攻击者会提交一些专门设计的输入, 通常是有特殊意义的语法, 向应用程序实施攻击, 如果这种攻击获得成功, 它就完全攻破了目标应用程序的组件.
避开登录
不管访问操作是普通用户或者管理员, 应用程序访问数据库方式大致相同. Web应用程序对数据库储存区实施自主访问控制, 基于用户账户和类型构造语句查询来增删改查. 修改查询的成功注入攻击可以避开程序的自主访问控制并获取未授权访问.
如果需要安全保护的应用通过查询结果控制, 攻击就可以通过修改查询来更改应用的逻辑, 以下是一个实例:
|
|
这个查询通过匹配检索用户, 如果返回一名用户的资料, 则登录成功, 建立一个通过验证的会话. 在这种情况下, 如果知道管理员的用户名为admin
,那他就可以通过提交以下用户名和任意密码, 以管理身份登录:
|
|
应用程序将执行以下查询:
|
|
因为其中使用了注释符号(—), 所以查询等同于:
|
|
于是查询完全避开了密码检查…
- 提示
即时攻击者不知道管理员的用户名, 由于大多数应用程序第一个账户为管理用户, 而且程序一般查询有可能返回几名用户的同时, 只会获取处理第一名用户, 于是提交以下用户名, 以整个数据库第一个用户的身份登录:
|
|
应用程序将执行以下查询:
|
|
因为其中使用了注释符号(—), 所以查询等同于:
|
|
返回整个应用所有用户的资料…
SQL注入
首先要意识到一个问题, 随着安全意识增强, SQL注入漏洞愈来愈少, 很多主流应用使用API来避免SQL注入(如果正确使用的话), 有时候查找SQL注入漏洞是一件非常困难的事情, 需要渗透测试员检测不懈的查找和挖掘. 因此这里只能列举一些比较基本的情况, 然后进一步描述最新的技巧
利用基本漏洞
当一个用户搜索Wiley出版的所有书籍时, 应用程序执行以下查询:
|
|
该查询检查了books
表的每一行, 提取所有作者列为Wiley
值的结果, 并以HTML页面的结果返回给用户. 其中Wiley
是一个数据项, 它必须包含在单引号内, 与其它查询内容分开.
现在思考一下, 如果用户搜索所有由O’Reilly出版的书籍会出现什么情况. 此时进行以下查询
|
|
解析过程中遇到的Reilly'
表达式, 这不是有效的SQL语法, 因此应用程序生成一条错误信息, 甚至返回客户端. 如果应用程序以这种方式进行, 那它就很容易受到SQL注入, 攻击者可以提交包含引号的任意查询. 例如输入
|
|
此时进行以下查询
|
|
获取到所有书籍表中的记录
- 提示
有时候不一定需要注释符号来处理字符串末尾的引号, 可以用一个包含字符串数据结束注入的输入, 例如:
|
|
生成以下查询
这是有效查询, 和1=1攻击相同的结果.
前面的几个例子很明显不会造成严重的安全威胁, 因为用户本来就能通过合法的方式访问到所有的书籍信息, 但是利用这种SQL注入漏洞可以从各种表中提取任何数据, 并提升数据库和数据库服务器中的权限. 所以不管在哪个应用程序中, SQL注入漏洞都应该被视为极其严重的威胁.
注入不同语句类型
我们常常出现这样的印象, 即SQL注入漏洞只存在于SELECT语句中. 实际上任何SQL语句都可能存在缺陷.
SELECT语句
SELECT语句用户从数据库中获取信息, SQL注入的攻击点通常是查询中WHERE
子句, 因为WHERE
子句在SELECT语句的最后, 攻击者可以将查询截短到输入的结束位置, 而不会使整个查询失效.
INSERT语句
如果一个应用程序允许用户自主注册, 指定他们自己的用户名和密码, 就可以用下列语法将用户资料插入users
表中:
|
|
如果username
或passowrd
字段存在SQL注入漏洞, 那么攻击者就可以在表中插入任何数据. 然而要想这么做, 必须保证VALUES子句的其他部分正确执行, 特别是数据项的个数和类型. 如注入username
字段时, 尝试提交以下输入:
|
|
它将建立一个ID为9999, privs为0的账户. 如果privs决定账户权限, 那么攻击者就能利用它创建一个管理账户.
- 提示
当设法注入一个INSERT语句时, 如果无法知道需要提交多少个参数或者参数类型, 可以通过在VALUES子句中持续增加一个新的字段, 直到程序创建了期望的账户?
比如注入username
字段时, 可以提交以下输入:
|
|
大多数数据库会隐式地把整数转化为字符串, 所以可以在每个位置都使用一个整数. 这里不管其他字段, 会生成一个用户名为foo
,密码为1
的账户.
如果发现值1仍然遭到拒绝, 尝试使用值2000, 许多数据库也会隐式地将它转化基于数据的数据类型.
UPDATE语句
UPDATE语句运行机制与INSERT语句类似, 例如修改密码时, 应用程序可能执行以下查询:
|
|
如果这项功能存在SQL注入漏洞, 就可以输入以下用户名更新管理员的密码:
|
|
- 提示
因为无法提前知道应用程序会对专门设计的输入执行什么操作, 因此远程探查SQL注入漏洞十分危险. 修改WHERE
子句有可能会使整个数据库表发生彻底的改变!譬如上面的攻击者已经提交了以下用户名:
|
|
那么应用程序可能会执行下列查询:
|
|
它会重设每一名用户的密码!
因此渗透测试员在尝试攻击一些不会更新现有数据的模块(比如登录)时, 也应该要留意这种风险, 因为一些应用程序在用户提交的用户信息后会进行各种UPDATE查询, 这意味着针对WHERE
子句的攻击会迁移到其他语句之中, 给用户资料带来严重破坏. 在尝试探查SQL注入漏洞前, 必须确保应用程序所有者能接受这些无法避免的风险; 同时, 强烈建议他们测试前先对数据库进行完整备份.
DELETE语句
与UPDATE语句相仿, DELETE语句也使用WHERE
子句告诉库表更新那些行的数据. 在UPDATE语句里的警告同样适用这种攻击.
避开过滤
有时候, 易收SQL注入攻击的应用程序可能会执行各种过滤防止攻击者无限制地利用其中存在的缺陷. 例如, 删除或者净化某些字符, 或者阻止SQL关键字. 这种过滤通常非常同意避开, 这时可尝试使用各种技巧.
避免使用被阻止的字符
如果注入数字数据字段或者列名称, 不一定必须使用单引号. 可以通过各种字符串函数, 使用ASCII代码构建一个字符串. 如执行下列查询:
|
|
Oracle
MS-SQL
如果注释符合被阻止, 设计注入数据, 使其不会破坏周围查询语法. 如
|
|
代替
|
|
避免使用简单确认
如果SELECT关键字被阻止, 尝试以下输入:
|
|
使用SQL注释
和一些语言一样, SQL支持行内注释注释内容包含在/*
与*/
符合之间. 可以使用注释替换注入数据中的空白符. 例如
|
|
MySQL中注释还可以插入关键字中,然而我现在的版本试了一下貌似不可以-_-
二阶SQL注入
当数据首次插入数据库时,许多应用程序能够安全处理这些数据. 但是, 一旦数据存储在数据库中, 应用程序本身或其他后台进程可能以危险的的方式处理这些数据.
在一些应用程序中, 用户使用单引号时以转义的方式来确认, 例如前例搜索O'Reilly
时,应用程序执行以下查询:
|
|
用户提交的单引号被转化为两个单引号, 从而传达给数据库的搜索项与用户输入的表达式具有相同的意义.
回到之前用户自我注册INSERT存在SQL漏洞的应用程序, 假如开发者修复了单引号配对导致的错误, 注册用户名foo'
来建立一下查询, 它将不会在数据库中导致错误:
|
|
到目前为止一切正常. 但是假设应用程序还执行密码修改功能, 它要求通过验证的用户能访问这项功能, 而且要求提交原始密码, 然后从数据库中提取密码进行比对. 要完成核对任务, 它首先要从数据库中提取用户的用户名, 然后建立以下查询:
|
|
因为保存在数据库中的用户名是字面量字符串foo'
, 但应用程序访问请求是, 数据库即返回这个值; 只有在字符串传送至数据库时才使用配对的转义序列. 因此当应用程序重复使用这个字符串并将它嵌入另一个查询中时, 就会造成一个SQL注入漏洞. 当用户尝试修改密码时, 应用程序返回以下信息, 暴露了上述缺陷:
|
|
要利用这种漏洞, 攻击者只要注册一个包含专门设计的输入用户名, 然后尝试修改密码, 例如注册以下用户名:
|
|
注册步骤会被应用程序安全处理. 如果有人尝试修改这个用户的密码, 他注入的查询就会执行, 返回一个类型错误的信息, 泄露管理员的密码.
|
|