除了基本的栈溢出利用,还有堆溢出、off by one、虚函数、格式化串等漏洞利用技术,下面进行简单介绍。
1.off by one的利用
只溢出一个字节,,配合上特定的溢出场景, off by one 就有可能演化为安全漏洞。
void off_by_one(char * input)
{
char buf[200];
int i=0,len=0;
len=sizeof(buf);
for(i=0; input[i]&&(i<=len); i++)
{
buf[i]=input[i];
}
}
“ i<=len”正确的使用应该是“ i
2.攻击C++虚函数
C++虚函数的入口地址被统一保存在虚表( Vtable)中,虚表指针保存在对象的内存空间中,紧接着虚表指针的是其他成员变量。
#include "windows.h"
#include "iostream.h"
char shellcode[]="xxx";////set fake virtual function pointer
class Hacktest
{
public:
char buf[200];
virtual void test(void)
{
cout<<"Class Vtable::test()"<test();
}
( 1)虚表指针位于成员变量 char buf[200]之前,程序中通过 p_vtable=overflow.buf-4 定位到这个指针。
( 2)修改虚表指针指向缓冲区的 0x004088CC 处。
( 3)程序执行到 p->test()时,将按照伪造的虚函数指针去 0x004088CC 寻找虚表,这里正好是缓冲区里 shellcode 的末尾。在这里填上 shellcode 的起始位置 0x0040881C 作为伪造的虚函数入口地址,程序将最终跳去执行 shellcode,如下图所示。
由于虚表指针位于成员变量之前,溢出只能向后覆盖数据,所以有一定局限性。对象的内存空间位于堆中,如果内存中存在多个对象且能够溢出到下一个对象空间中去,“连续性覆盖”还是有攻击的机会的,如下图所示。
3.Heap Spray:堆喷射
在针对浏览器的攻击中,常常会结合使用堆和栈协同利用漏洞。
(1)当浏览器或其使用的 ActiveX 控件中存在溢出漏洞时,攻击者就可以生成一个特殊的 HTML文件来触发这个漏洞。
( 2)不管是堆溢出还是栈溢出,漏洞触发后最终能够获得 EIP。
( 3)有时我们可能很难在浏览器中复杂的内存环境下布置完整的 shellcode。
( 4)页面中的 JavaScript 可以申请堆内存,因此,把 shellcode 通过 JavaScript 布置在堆中成为可能。
在使用 Heap Spray 的时候,一般会将 EIP 指向堆区的 0x0C0C0C0C 位置,然后用 JavaScript申请大量堆内存,并用包含着 0x90 和 shellcode 的“内存片”覆盖这些内存。
通常, JavaScript 会从内存低址向高址分配内存, 因此申请的内存超过 200MB( 200MB=200 × 1024× 1024 = 0x0C800000 > 0x0C0C0C0C)后, 0x0C0C0C0C 将被含有 shellcode 的内存片覆盖。只要内存片中的 0x90 能够命中 0x0C0C0C0C 的位置, shellcode 就能最终得到执行。
var nop=unescape("%u9090%u9090");
while (nop.length<= 0x100000/2)
{
nop+=nop;
}//生成一个 1MB 大小充满 0x90 的数据块
nop = nop.substring(0, 0x100000/2 - 32/2 - 4/2 - shellcode.length - 2/2 );
var slide = new Arrary();
for (var i=0; i<200; i++)
{
slide[i] = nop + shellcode
}
Java 会为申请到的内存填上一些额外的信息,为了保证内存片恰好是 1MB,我们将这些额外信息所占的空间减去。nop = nop.substring(0, 0x100000/2 - 32/2 - 4/2 - shellcode.length - 2/2 )将一个内存片恰好凑成 1MB 大小。
4.格式化串漏洞
4.1 printf中的缺陷
#include "stdio.h"
main()
{
int a=44,b=77;
printf("a=%d,b=%d\n",a,b);
printf("a=%d,b=%d\n");
}
第二个printf并不会编译报错,运行输出如下:
虽然函数调用时没有给出“输出数据列表”,但系统仍然按照“格式控制符”所指明的方式输出了栈中紧随其后的两个 DWORD。现在应该明白输出“ a=4218928,b=44”的原因了:4218928 的十六进制形式为 0x00406030,是指向格式控制符“ a=%d,b=%d\n”的指针。
4.2 用printf读取内存数据
如果传入的字符串中带有格式控制符时, printf 就会打印出栈中“莫须有”的数据。
例如,输入“ %p,%p,%p……”,实际上可以读出栈中的数据。
#include "stdio.h"
int main(int argc, char ** argv)
{
printf(argv[1]);
}
4.3 用printf向内存写数据
在格式化控制符中,有一种鲜为人知的控制符%n。这个控制符用于把当前输出的所有数据的长度写回一个变量中去。
#include "stdio.h"
int main(int argc, char ** argv)
{
int len_print=0;
printf("before write: length=%d\n",len_print);
printf("hacktest:%d%n\n",len_print,&len_print);
printf("after write: length=%d\n",len_print);
}
第二次 printf 调用中使用了%n 控制符,它会将这次调用最终输出的字符串长度写入变量len_print 中。“hacktest:0”长度为 10,所以这次调用后 len_print 将被修改为 10。
4.4 格式化串漏洞的检测
当输入输出函数的格式化控制符能够被外界影响时,攻击者可以综合利用前面介绍的读内存和写内存的方法修改函数返回地址,劫持进程,从而使 shellcode 得到执行。
比起大量使用命令和脚本的 UNIX 系统, Windows 操作系统中命令解析和文本解析的操作并不是很多,再加上这种类型的漏洞发生的条件比较苛刻,使得格式化串漏洞的实际案例非常罕见。
格式化串漏洞的起因非常简单,只要检测相关函数的参数配置是否恰当就行。通过简单的静态代码扫描,一般可以比较容易地发现这类漏洞。
int printf( const char* format [, argument]... );
int wprintf( const wchar_t* format [, argument]... );
int fprintf( FILE* stream, const char* format [, argument ]...);
int fwprintf( FILE* stream, const wchar_t* format [, argument ]...);
int sprintf( char *buffer, const char *format [, argument] ... );
int swprintf( wchar_t *buffer, const wchar_t *format [, argument] ... );
int vprintf( const char *format, va_list argptr );
int vwprintf( const wchar_t *format, va_list argptr );
int vfprintf( FILE *stream, const char *format, va_list argptr );
int vfwprintf( FILE *stream, const wchar_t *format, va_list argptr );
int vsprintf( char *buffer, const char *format, va_list argptr );
int vswprintf( wchar_t *buffer, const wchar_t *format, va_list argptr );
5.SQL 注入原理
SQL 命令注入的漏洞是 Web 系统特有的一类漏洞,它源于 PHP、 ASP 等脚本语言对用户输入数据和解析时的缺陷。
当攻击者把用户名输入为 admin’#的时候,输入字串中的单引号将和脚本中的变量的单引号形成配对,而输入字串中的“ #”号对于 My SQL 的语言解释器来说是一行注释符。通过这样的输入,攻击者可以轻易绕过身份验证机制,没有正确的密码也能看到管理员的信息。
注入攻击的检测与防范:
- 检测:
SQL注入扫描:NGSSQuirreL - 防范:
一种十分有效的防止 SQL 注入的方法是使用参数化查询( Parameterized Query)的方法。参数化查询就是在访问数据库时,将查询语句中要填入的数据通过参数的方式传递,这样数据库不会将参数的内容视为 SQL 语句的一部分,因此即便参数中含有攻击者构造的查询指令,也不会被执行。
string sql = "select * from users where username=? and password=?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1,username);
pstmt.setString(2,password);//username 和 password 两个变量的值是由用户输入的
6.XSS攻击
XSS 是跨站脚本(Cross Site Script)的意思,由于网站技术中的 Cascading Style Sheets 缩写为 CSS,为了不至于产生概念混淆,故一般用 XSS 来简称跨站脚本。
在很多 Web 应用中,服务器都将客户端输入或请求的数据“经过简单的加工”后,再以页面文本的形式返回给客户端。
当用户进行正常的请求,"http://testapp.com/test.php?input=this is a test",服务器将简单地把“ this is a test”返回给客户端的浏览器。
当请求"http://testapp.com/test.php?input=",服务器也会把“” 当字符串返回给客户端浏览器,浏览器在解析这次反馈的页面时,发现页面中的是脚本命令,而不是数据,因此会把“ ”当做脚本命令进行解析,进而执行,弹出一个警告消息框。
XSS 攻击的目标是客户端的浏览器,因此受影响的范围要远远大于攻击服务器的 SQL 注入攻击;独立的 XSS 漏洞攻击并不是非常严重,但是配合上其他攻击技术往往能产生非常严重的后果。
7.路径回溯漏洞
windows中“ ../”或者“ ..\”,Linux中的“ ../”,在路径中表示“上一级”,当足够多的“ ..”使得路径跳转到根目录时,多余的“ ..”将被忽略掉。
例如有这样一个URL:http://www.testsite.com/download.asp?file=document.pdf
如果没有回溯检查,可构造URL获取敏感文件:http://www.testsite.com/download.asp?file=../../../../etc/passwd