渗透测试与漏洞利用系列课程【第二课】

2018-04-0907:00:32 发表评论

渗透测试与漏洞利用系列课程【第二课】

缓冲区溢出漏洞

基本概念

缓冲区 -- 一块连续的内存区域, 存放程序运行时 加载到内存的 代码和数据。

缓冲区溢出 指程序运行时,向固定大小的缓冲区写入超过其容量的数据,多余的数据会越过缓冲区的边界覆盖相邻内存空间,从而造成溢出。

缓冲区的大小是由用户输入的数据决定的,如果程序不对用户输入的超长数据作长度检查,同时用户又对程序进行了非法操作或者错误输入,就会造成缓冲区溢出。

缓冲区溢出攻击 是指发生缓冲区溢出时,溢出的数据会覆盖相邻内存空间的返回地址、函数指针、堆管理结构等合法数据,从而使程序运行失败、或者发生转向去执行其它程序代码、或者执行预先注入到内存缓冲区中的代码。缓冲区溢出后执行的代码,会以原有程序的身份权限运行。如果原有程序是以系统管理员身份运行,那么攻击者利用缓冲区溢出攻击后所执行的恶意程序,就能够获得系统控制权,进而执行其它非法操作。

造成缓冲区溢出的根本原因 是缺乏类型安全功能的程序设计语言(C、C++等)出于效率的考虑,部分函数不对数组边界条件和函数指针引用进行边界检查。例如,C 标准库中和字符串操作有关的函数,像 strcpystrcatsprintfgets等函数中,数组和指针都没有自动边界检查。程序员开发时必须自己进行边界检查,防范数据溢出,否则所开发的程序就存在缓冲区溢出的安全隐患,而实际上这一行为往往被程序员忽略或者检查不充分。


栈溢出漏洞

被调用的子函数中写入数据的长度,大于栈帧的基址到 esp之间预留的保存局部变量的空间时,就会发生栈溢出。要写入数据的填充方向是从低地址向高地址增长,多余的数据就会越过栈帧的基址,覆盖基址以上的地址空间。

  1. 修改返回地址

    如果返回地址被覆盖,当覆盖后的地址是一个无效地址,则程序运行失败。如果覆盖返回地址的是恶意程序的入口地址,则源程序将转向去执行恶意程序。

    栈的存取采用先进后出,程序用它来保存函数调用时的有关信息,如函数参数、返回地址,函数中的非静态局部变量存放在栈中。举例:


void stack_overflow(char* argument)
{
char local[4];
for (int i = 0; argument[i]; i++)
local[i] = argument[i];
}

样例程序中,函数 stack_overflow被调用时堆栈布局

 

  • 图中 local是栈中保存局部变量的缓冲区,根据 char local[4]预先分配的大小为4个字节,当向local中写入超过4个字节的字符时,就会发生溢出。如用 AAAABBBBCCCCDDDD作为参数调用,当函数中的循环执行后,栈顶布局如图右侧。可以看出输入参数中 CCCC覆盖了返回地址,当 stack_overflow执行结束,根据栈中返回地址返回时,程序将转到地址 CCCC并执行此地址指向的程序,如果 CCCC地址为攻击代码的入口地址,就会调用攻击代码。
  • 修改临接变量

    如果返回临近变量的值,可能会更改程序执行流程。

    函数的局部变量在栈中一个挨着一个排列。如果这些局部变量中有数组之类的缓冲区,并且程序中存在数组越界的缺陷,那么越界的数组元素就有可能破坏栈中相邻变量的值,甚至破坏栈帧中所保存的 EBP值、返回地址等重要数据。

    举例:


#include <stdio.h>
#include <iostream>
#define PASSWORD "1234567"
int verify_password(char * password)
{
int authenticated;
char buffer[8];
authenticated = strcmp(password, PASSWORD);
strcpy(buffer, password);
return authenticated;
}
void main()
{
int valid_flag = 0;
char password[1024];
while(1)
{
printf("please input password: ");
scanf("%s", password);
valid_flag = verify_password(password);
if(valid_flag)
printf ("incorrect password!\n\n");
else{
printf("Congratulation! You have passed the verification!\n");
break;
}
}
}

verify_password 函数的栈帧中,局部变量 int authenticated恰好位于缓冲区 char buffer[8]的"下方"。

authenticated为int类型,在内存中是一个 DWORD,占4个字节。所以,如果能够让 buffer数组越界,buffer[8]buffer[9]buffer[10]buffer[11]将写入相邻的变量 authenticated中。

authenticated 变量的值来源于 strcmp函数的返回值,之后会返回给 main函数作为密码验证成功与否的标志变量:当 authenticated为 0 时,表示验证成功;反之,验证不成功。

如果我们输入的密码超过了7个字符(注意:字符串截断符NULL将占用一个字节),则越界字符的 ASCII码会修改掉 authenticated的值。如果这段溢出数据恰好把 authenticated改为0,则程序流程将被改变。

部分内容被隐藏
需登陆后可查看

格式化字符串漏洞

什么是格式化字符串

print()fprint()*print()系列的函数可以按照一定的格式将数据进行输出,举例:

printf("My Name is: %s" , "xxxx")

 

 

部分内容被隐藏
需登陆后可查看

特性三:自定义打印字符串宽度

在格式符中间加上一个十进制整数来表示输出的最少位数,若实际位数多于定义的宽度,则按实际位数输出,若实际位数少于定义的宽度则补以空格或0。


#include <stdio.h>
main()
{
int num=66666666;
printf("Before: num = %d\n", num);
printf("%.100d%n\n", num, &num);
printf("After: num = %d\n", num);
}

 

 

  • 运行后可以看到,我们的 num值被改为了 100

整数溢出漏洞

  • 存储溢出
  • 运算溢出
  • 符号问题

char* integer_overflow(int* data,unsigned int len)
{
unsigned int size = len + 1;
char *buffer = (char*)malloc(size);
if(!buffer)
return NULL;
memcpy(buffer,data,len);
buffer[len]='\0';
return buffer;
}

该函数将用户输入的数据拷贝到新的缓冲区,并在最后写入结尾符0。如果攻击者将 0xFFFFFFFF 作为参数传入 len,当计算 size时会发生整数溢出,malloc会分配大小为 0 的内存块,后面执行 memcpy时会发生堆溢出。

 

SQL注入漏洞


strKeyword = Request["keyword"];
sqlQuery = "SELECT * FROM Aritcles WHERE Keywords LIKE '%' + strKeyword + '%' ";

按照用户提交的关键字 keyword,对软件连接数据库中的文件进行搜索,找出所有包含用户关键字的文章。

假设此时,我们提交 hack,这时,hack会传递给 keyword关键变量。keyword 获得数据 hack后被赋值给 strKeyword变量,然后 strKeyword变量被放入查询语句。此时的查询语句表现为:

SELECT * FROM Aritcles WHERE Keywords LIKE '% hack %'
部分内容被隐藏
需登陆后可查看

查询 -- 删除Aritcles表

常常出现在论坛程序中进行用户认证的程序:


admin1 = trim(request("name"))
password1 = trim(request("password"))
Set rs = Server.CreatObject("ADODB.Recordset")
sql = "select * from userlogin where name = '"&admin1&"' and password = '"&password1&"'"
rs.Open sql, conn, 1, 1
if rs.eof and rs.bof then
response.write"<SCRIPT language = JAVAScript>alert('用户名或密码不正确!')"
response.write"javascript:history.go(-1)</SCRIPT>"
response.end
else # 设置Session对象,重定向 default.asp
session("name") = rs("name")
session("password") = rs("password")
response.Redirect("default.asp")
end if

假设 guest -- 密码123456

sql = "select * from userlogin where name = 'guest' and password = '123456'"

用户名 ' or 1 = ' 1 密码' or 1 = ' 1 and then you are ok! why?

sql = "select * from userlogin where name = " or 1 = '1' and password = " or 1 = '1'"

查询语句永远为真---绕过用户认证

 

其他漏洞

数组越界漏洞

  1. 读取恶意构造的输入数据
  2. 用输入数据计算数组访问索引
  3. 对数组进行读/写操作

Bypass漏洞

部分内容被隐藏
需登陆后可查看
CE安全网
CE安全网广告位招租

发表评论

您必须登录才能发表评论!