缓冲区溢出如何成为黑客的漏洞?
How can a buffer-overflow be an exploit for hackers?
我看过一个关于 "Buffer overflow exploit" 的视频,视频中的那个人写入的字节数超过了数组的容量,例如:
int foo(const char* cp){
char sz[10];
strcpy(sz, cp); // consider cp is a pointer to 20 characters
return x; // some valid return statement here
}
以上如果 foo
使用 20 个字符的参数调用,那么我确定它是未定义的行为。但他为什么说这可能是一种利用,又是如何利用的?
- 事实上,在 C++ 中,我们总是强烈建议不要这样做,但这怎么可能是一种利用呢?谢谢。
这可能是一个漏洞,有很多条件,列出所有条件会让我们在这个网站上走得太远。基本情况非常简单,但当然有很多保护措施使得很难将其实际用作漏洞利用。要使缓冲区变得可利用,必须满足以下条件。
- 加载到缓冲区中的内容使得代码指针最终遍历缓冲区的内容。这通常意味着它很大,有很多 NOP。
缓冲区的内容包含有效的机器代码。否则你只会得到一个异常和程序崩溃,呃。
启动可利用代码的应用程序具有对利用感兴趣的特权。 OS'es 有很多层,旨在阻止未经验证或未签名的代码。
- 内存使用是可预测/可训练的。现代 OS 重定位内存,因此应用程序不再保证固定指针地址
- 处理器控制流程是可预测/可训练的。有时,漏洞需要 'train' 处理器分支到特定条件状态。
But Why he said it can be an exploit and how?
仅举几个例子:
通过简单地覆盖其他有意义内容的内存内容。也许您的缓冲区结构后面有一个 bool 变量,其中包含访问权限标志。覆盖缓冲区可能会设置访问标志,代码将提供访问权限,这不是我们想要的。
破坏堆栈也是一件坏事。也许您的 return 语句在错误的地址上运行更为关键,执行缓冲区的某些内容。
每个搜索引擎都可以找到很多这样的东西!
操纵标志访问的简单示例:
struct Check
{
char small[4];
bool accessGranted;
};
int main()
{
Check check{ "", false };
strcpy( check.small, "12345" );
if ( check.accessGranted )
{
std::cout << "Ubs...!" << std::endl;
}
}
顺便说一句:我发现这篇论文有点过时但很有用:http://www.cis.syr.edu/~wedu/Teaching/IntrCompSec/LectureNotes_New/Buffer_Overflow.pdf
堆栈不仅包含您的局部变量,还包含您的 return 地址,参见 https://en.wikipedia.org/wiki/Call_stack
通过覆盖堆栈,您可以强制 CPU 跳转到 您选择的 位置,在发生攻击时,该位置是一个内存区域填充了攻击者的恶意代码。
现在计算机以您的权限运行攻击者代码。
我看过一个关于 "Buffer overflow exploit" 的视频,视频中的那个人写入的字节数超过了数组的容量,例如:
int foo(const char* cp){
char sz[10];
strcpy(sz, cp); // consider cp is a pointer to 20 characters
return x; // some valid return statement here
}
以上如果 foo
使用 20 个字符的参数调用,那么我确定它是未定义的行为。但他为什么说这可能是一种利用,又是如何利用的?
- 事实上,在 C++ 中,我们总是强烈建议不要这样做,但这怎么可能是一种利用呢?谢谢。
这可能是一个漏洞,有很多条件,列出所有条件会让我们在这个网站上走得太远。基本情况非常简单,但当然有很多保护措施使得很难将其实际用作漏洞利用。要使缓冲区变得可利用,必须满足以下条件。
- 加载到缓冲区中的内容使得代码指针最终遍历缓冲区的内容。这通常意味着它很大,有很多 NOP。
缓冲区的内容包含有效的机器代码。否则你只会得到一个异常和程序崩溃,呃。
启动可利用代码的应用程序具有对利用感兴趣的特权。 OS'es 有很多层,旨在阻止未经验证或未签名的代码。
- 内存使用是可预测/可训练的。现代 OS 重定位内存,因此应用程序不再保证固定指针地址
- 处理器控制流程是可预测/可训练的。有时,漏洞需要 'train' 处理器分支到特定条件状态。
But Why he said it can be an exploit and how?
仅举几个例子:
通过简单地覆盖其他有意义内容的内存内容。也许您的缓冲区结构后面有一个 bool 变量,其中包含访问权限标志。覆盖缓冲区可能会设置访问标志,代码将提供访问权限,这不是我们想要的。
破坏堆栈也是一件坏事。也许您的 return 语句在错误的地址上运行更为关键,执行缓冲区的某些内容。
每个搜索引擎都可以找到很多这样的东西!
操纵标志访问的简单示例:
struct Check
{
char small[4];
bool accessGranted;
};
int main()
{
Check check{ "", false };
strcpy( check.small, "12345" );
if ( check.accessGranted )
{
std::cout << "Ubs...!" << std::endl;
}
}
顺便说一句:我发现这篇论文有点过时但很有用:http://www.cis.syr.edu/~wedu/Teaching/IntrCompSec/LectureNotes_New/Buffer_Overflow.pdf
堆栈不仅包含您的局部变量,还包含您的 return 地址,参见 https://en.wikipedia.org/wiki/Call_stack
通过覆盖堆栈,您可以强制 CPU 跳转到 您选择的 位置,在发生攻击时,该位置是一个内存区域填充了攻击者的恶意代码。
现在计算机以您的权限运行攻击者代码。