CloudFlare公司经常会收到客户询问为什么他们的一些请求会被 CloudFlare WAF屏蔽。最近,一位客户就提出他不能理解为什么一个访问他主页简单的 GET 请求会被 WAF 屏蔽。

下面是被屏蔽的请求:

GET / HTTP/1.1
Host: www.example.com
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (compatible; MSIE 11.0; Windows NT 6.1; Win64; x64; Trident/5.0)'+(select*from(select(sleep(20)))a)+'
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,fr;q=0.6

正如他说的,一个简单的请求访问 WEB 主页,乍看之下好像没什么问题。除非你仔细查看 User-Agent 部分:

Mozilla/5.0 (compatible; MSIE 11.0; Windows NT 6.1; Win64; x64; Trident/5.0)'+(select*from(select(sleep(20)))a)+

User-Agent 前半部分看起来挺正常的(好像是微软 IE 11),但是却以字符串 '+(select*from(select(sleep(20)))a)+ 结尾。攻击者试图对 User-Agent 值进行 SQL 注入。

普通的 SQL 注入通常是对 URL 及其参数进行的,但这里攻击者却将 SQL 查询语句select * from (select(sleep(20))) 隐藏在了 HTTP 头部的 User-Agent 字段之中 。这种技术通常被各种扫描器所使用,例如,sqlmap 的 -p 参数会尝试对 HTTP 请求头部字段进行注入。

延时注入

许多 SQL 注入都是在尝试从网站中提取信息(例如用户名、密码或其他隐私信息)。但这条语句却不太一样,它请求数据库进程等待 20 秒。这种攻击属于 SQL 盲注,一般的 SQL 注入会将查询的结果返回到 WEB 页面中,而盲注的攻击者则看不到查询的输出,所以他们会另辟蹊径使用其他的方式来判断注入。两种常见的方法就是使 WEB 服务器产生错误或者产生延时。如上使用 sleep 会是 WEB 服务器等待 20 秒才进行响应,攻击者可以根据响应是否产生延时来判断是否存在注入漏洞。

示例

为了更好的说明,我使用 PHP 创建了一个不安全的应用,其中会将 User-Agent保存到 MySQL 数据库中。这类代码可能会存在于真实的应用中用来分析信息,例如统计访问次数。

在这个示例中,我忽略了所有良好安全的编码习惯,因为我想阐述下 SQL 的工作原理。

再次警告:千万不要复制/粘贴以下代码!因为这些代码并不规范。

下面是 PHP 代码:

<?php

$link = new mysqli('localhost', 'insecure', '1ns3cur3p4ssw0rd', 'analytics');

$query = sprintf("INSERT INTO visits (ua, dt) VALUES ('%s', '%s')",
$_SERVER["HTTP_USER_AGENT"],
date("Y-m-d h:i:s")); $link->query($query); ?> <html><head></head><body><b>Thanks for visiting</b></body></html>

这段代码会连接到本地的 analytics 数据库,并将访客 HTTP 头部的 User-Agent 字段不加过滤的插入到数据库中。

这就是一个 SQL 注入的例子,但是因为我们的代码不会产生任何错误,所以攻击者无法通过报错来得知是否存在注入漏洞,除非他们使用类似 sleep() 之类的方法。

为了验证是否存在注入漏洞,只需要执行如下命令(其中 insecure.php 就是上述示例代码):

curl -A "Mozilla/5.0', (select*from(select(sleep(20)))a)) #" http://example.com/insecure.php

这样就会将 HTTP 头部的 User-Agent 字段设置为 Mozilla/5.0', (select*from(select(sleep(20)))a)) # 。而我们不安全的 PHP 代码会不加过滤就直接将这些字符串插入查询语句中,此时的查询语句变成了如下样子:

INSERT INTO visits (ua, dt) VALUES ('Mozilla/5.0', (select*from(select(sleep(20)))a)) #', '2016-05-17 03:16:06')

本来应该插入两个值,但现在只会插入一个值 Mozilla/5.0 并执行(select*from(select(sleep(20)))a) 语句(这会使数据库休眠 20 秒)。而 #是注释符,意味着后续的语句被注释并忽略了(就是忽略了插入日期)。

此时的数据库中会出现这样一个条目:

+---------------------+---------------+
| dt | ua |
+---------------------+---------------+
| 0 | Mozilla/5.0 |
+---------------------+---------------+

请注意,其中日期值为 0 ,这正是 (select*from(select(sleep(20)))a) 语句执行的结果,另外 ua 的值为 Mozilla/5.0 ,而这可能就是攻击者成功执行 SQL 注入后留下的唯一痕迹了。

下面是接收到上述请求后服务器的运行结果,我们使用 time 命令来看看这个过程到底需要多长时间:

$ time curl -v -A "Mozilla/5.0', (select*from(select(sleep(20)))a) #" http://example.com/insecure.php
* Connected to example.com port 80 (#0)
> GET /insecure.php HTTP/1.1
> Host: example.com
> User-Agent: Mozilla/5.0', (select*from(select(sleep(20)))a) #
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 16 May 2016 10:45:05 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< Server: nginx <html><head></head><body><b>Thanks for visiting</b></body></html>
* Connection #0 to host example.com left intact real 0m20.614s
user 0m0.007s
sys 0m0.012s

看到 20 秒了吗,成功的执行了 SQL 注入。

漏洞利用

看到这,你也许会想“这的确很简单,但是黑客好像并没有攻击我的网站啊”。

但不幸的是,丰富的 SQL 语句使得哪怕是只有 3 行 PHP 代码的 insecure.php,也可以使得攻击者获得远不只使数据库睡眠 20 秒的效果。虽然攻击者执行的INSERT INTO 的查询只会向数据库中写入数据,但这样仍然可以让他们提取出敏感信息和获取访问权限。

为了一个演示的例子,我们在数据库中创建了一个名为 user 的表,其中包含两个用户分别名为 root 和 john 。下面来展示攻击者是如何发现存在 john 用户的,他们可以手工构造用户名并根据相应时间来判断是否存在这个用户。

例如:

curl -A "Mozilla/5.0', (select sleep(20) from users where substring(name,1,1)='a')) #" http://example.com/insecure.php

这个访问会被立即响应,因为数据库中并没有以 a 打头的用户名,但是

curl -A "Mozilla/5.0', (select sleep(20) from users where substring(name,1,1)='j')) #" http://example.com/insecure.php

这个请求却会花费 20 秒时间。这样在猜出用户名的首字母后,攻击者就可以继续猜测用户名第二个、第三个字母等等,相同的技术还可以用来从数据库中提取其他数据。

如果我们的应用比较复杂,例如是一个博客的评论系统,那么我们可以利用这个漏洞将数据库的一些信息转存到一条评论中,这样我们就可以通过访问网页直接查看到数据库信息了,这种方法通常会在需要提取大量数据时使用。

代码加固

加固代码最好的方式就是像如下这样:

<?php

$link = new mysqli('localhost', 'analytics_user', 'aSecurePassword', 'analytics_db');

$stmt = $link->prepare("INSERT INTO visits (ua, dt) VALUES (?, ?)");
$stmt->bind_param("ss", $_SERVER["HTTP_USER_AGENT"], date("Y-m-d h:i:s"));
$stmt->execute(); ?> <html>
<head></head>
<body><b>Thanks for visiting</b></body>

首先将 SQL 查询语句使用 prepare 进行准备,随后使用 bind_param 绑定两个参数( User-Agent 和日期),最后才是使用 execute 执行查询。

bind_param 可以确保一些 SQL 特殊字符会先被进行转义,随后才被执行。现在让我们来看一看在收到跟之前一样的 SQL 注入后数据库中的条目是什么样子的:

+---------------------+----------------------------------------------------+
| dt | ua |
+---------------------+----------------------------------------------------+
| 2016-05-17 04:46:02 | Mozilla/5.0',(select*from(select(sleep(20)))a)) # |
+---------------------+----------------------------------------------------+

此时,攻击者的 SQL 语句没有被执行而是被简单的存入了数据库当中。

总结

SQL 注入是攻击者最喜欢使用的攻击方式之一,它可能出任何由攻击者掌控输入的 WEB 应用中。最容易想象的就是出现在各种表单或 URL 之中,但即使是 HTTP 请求头部也同样可能出现。所以从安全角度来说,任何由 WEB 浏览器发送给 WEB 应用的数据都应当被假设为是恶意的。

User Agent注入攻击及防御的更多相关文章

  1. 《sql注入攻击与防御 第2版》的总结 之 如何确定有sql注入漏洞

    看完<sql注入攻击与防御 第2版>后,发现原来自己也能黑网站了,就一个字:太爽了. 简单总结一下入侵步骤: 1.确定是否有sql注入漏洞 2.确定数据库类型 3.组合sql语句,实施渗透 ...

  2. XXE注入攻击与防御

    在研究XXE注入攻击之前先了解一下什么是XXE 定义 XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言.XML文档结构包括 ...

  3. [转载] MySQL 注入攻击与防御

    MySQL 注入攻击与防御 2017-04-21 16:19:3454921次阅读0     作者:rootclay 预估稿费:500RMB 投稿方式:发送邮件至linwei#360.cn,或登陆网页 ...

  4. 安全性测试入门:DVWA系列研究(二):Command Injection命令行注入攻击和防御

    本篇继续对于安全性测试话题,结合DVWA进行研习. Command Injection:命令注入攻击. 1. Command Injection命令注入 命令注入是通过在应用中执行宿主操作系统的命令, ...

  5. SQL注入攻击和防御

    部分整理...   什么是SQL注入? 简单的例子, 对于一个购物网站,可以允许搜索,price小于某值的商品 这个值用户是可以输入的,比如,100 但是对于用户,如果输入,100' OR '1'=' ...

  6. SQL参数化查询--最有效可预防SQL注入攻击的防御方式

    参数化查询(Parameterized Query 或 Parameterized Statement)是访问数据库时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值. 在使用参 ...

  7. Mysql注入攻击与防御(思维导图笔记)

  8. 注入攻击-SQL注入和代码注入

    注入攻击 OWASP将注入攻击和跨站脚本攻击(XSS)列入网络应用程序十大常见安全风险.实际上,它们会一起出现,因为 XSS 攻击依赖于注入攻击的成功.虽然这是最明显的组合关系,但是注入攻击带来的不仅 ...

  9. web服务端安全之SQL注入攻击

    一.SQL注入攻击的原理攻击者在HTTP请求中,注入恶意的SQL代码,并在服务端执行.比如用户登录,输入用户名camille,密码 ' or '1'='1 ,如果此时使用参数构造的方式,就会出现 ' ...

随机推荐

  1. spring+springMVC+hibernate整合

    首先我们要知道hibernate五大对象:,本实例通过深入的使用这五大对象和spring+springMVC相互结合,体会到框架的好处,提高我们的开发效率 Hibernate有五大核心接口,分别是:S ...

  2. PHP中常见的几种运行代码的方式

    常见的运行程序的方法有 shell_exec ``(反引号) eval system exec passthru 下面分别介绍他们的用法: 名称 解释 返回值 注意 shell_exec 通过 she ...

  3. erlang 求N以内的质数

    素数,又称质数,在一个大于1的自然数中,除了1和此整数自身之外,不能被其他自然数整除的数. 比1大但不是素数的数称为合数. 1和0既不是素数,也不是合数. 算术基本定理证明每个大于1的正整数都可以写成 ...

  4. Laravel核心之IOC和Facade 架构分析1

    控制反转(Inversion of Control) 缩写为IoC 最常见的方式叫做依赖注入 简单说来,就是一个类把自己的的控制权交给另外一个对象,类间的依赖由这个对象去解决. Laravel 中的使 ...

  5. Python鸡汤

    标准库 很正确 外部库 有一些风险,可能有bug,可能文档不全,可能长时间未更新. ipython 1 pip 这应该是安装Python后第一个需要的命令 pip install -i -i, --i ...

  6. 值类型,Nullable类型

    1. 值类型 比如说int吧,是值类型,是个struct,是这样声明的 public struct Int32 : IComparable, IFormattable, IConvertible, I ...

  7. openresty 定时器

    [1]nginx定时器应用 (1)文件目录结构 (2)nginx.conf配置 lua_package_path "/usr/local/lib/ubcsrvd/lualib/?.lua;; ...

  8. C语言中的指针运算

    int a[5]={1,2,3 ,4,5} *p=a; *p++ 等价于*(p++) 等价于a[i++](i++ i首先会被使用任何进行自+) *++p等价于*(++p) 等价于 a[++i] (++ ...

  9. centos7 运行postgres 数据库脚本db.sql

    [root@localhost ~]# su postgresbash-4.2$ psqlcould not change directory to "/root": Permis ...

  10. strstr使用

    extern char strstr(char str1, const char *str2); 语法: strstr(str1,str2) str1: 被查找目标 string expression ...