C#创建缓冲区溢出漏洞

7

我正在尝试为学校项目使用C#创建缓冲区溢出:

unsafe
{
    fixed (char* ptr_str = new char[6] {'H', 'a', 'l', 'l', 'o', ','})
    {
        fixed (char* ptr_str2 = new char[6] {'W', 'e', 'r', 'e', 'l', 'd'})
        {
            fixed (char* ptr_str3 = new char[6] {'!', '!', '!', '!', '!', '!'})
            {
                for (int i = 0; i < 8; i++)
                {
                    ptr_str2[i] = 'a';
                }

                for (int i = 0; i < 6; i++)
                {
                    this.Label2.Text += ptr_str[i];
                    this.Label3.Text += ptr_str2[i];
                    this.Label4.Text += ptr_str3[i];
                }
            }
        }
    }
}

我曾以为会把ptr_str2淹没,从而覆盖ptr_str中的字符。 但是实际上并没有发生这种情况。 它确实被执行了,但是ptr_str中的值并没有被覆盖。

有人能帮忙解决这个问题吗? 我不明白自己做错了什么。


1
我猜你可以从 ASP .NET MVC 2 项目开始... - Dan Abramov
2
我们试图避免它们,而你却在尝试创建一个...我们生活在一个奇怪的世界。 - alex
6
这将是一种缓冲区溢出,而不是堆栈溢出。 - Konrad Rudolph
是的,我指的是缓冲区溢出问题... - BigChief
4个回答

6

堆栈溢出是调用堆栈的溢出。

这个问题很容易解决:

int Test ()
{
    return Test ();
}

Console.WriteLine (Test ());

如果您指的是缓冲区溢出,这里有一个类似的问题similar question

是的,我的意思是缓冲区溢出,其中你在堆栈上覆盖变量而不是堆上。 - BigChief
1
是的,这是堆变量(结构体)溢出了。我想知道如何使用栈变量来完成。其中ptr_str2覆盖ptr_str而不是ptr_str2覆盖ptr_str3。 - BigChief

6

传统的攻击利用缓冲区溢出来溢出一个堆栈缓冲区; 你正在溢出一个堆缓冲区。 当两个缓冲区都在堆栈上时,很容易看到对一个缓冲区的写操作破坏了另一个缓冲区。 尝试使用stackalloc而不是new char来强制分配到堆栈中。


谢谢您的建议。我已经在考虑自己是否理解错了什么。我会去查看stackalloc。 - BigChief
除了 stackalloc,它实际上不会让你溢出缓冲区 :) - Mike Marynowski

5

您忽略了一个事实,即数组本身也是对象。它们具有像任何托管引用类型一样的对象头和存储数组大小的私有字段。在开始覆盖数组元素之前,必须首先覆盖它们。在32位机器上,您将从以下内容开始覆盖ptr_str2的第一个元素:

                        for (int i = 0; i < 13; i++) {
                            ptr_str[i] = 'a';
                        }

当然,它必须是13。

通过在for循环上设置断点来观察。调试 + 窗口 + 内存 + 内存1,在地址框中输入“ptr_str”。逐步执行代码以查看内存的变化。接着你会看到ptr_str2,4个字节用于同步块,4个字节用于方法表指针,4个字节用于数组长度。总计12个字节,6个字符。


嘿,这是一个非常好的提示。我还没有找到内存窗口.. :P 但现在我看到我需要覆盖ptr_str3序列...小错误。现在我得检查一下如何在不导致崩溃的情况下覆盖标题值。 - BigChief
嗯,不要去那里。下一次垃圾回收将重新排列数组。它们仅仅因为偶然而在内存中相邻。 - Hans Passant
嗯...好吧,但是垃圾只有在数组不再需要时才会被收集吗?下面的代码似乎可以解决这个问题。for (int i = 0; i < 36; i++) { if (i == 6) i += 24; ptr_str2[i] = 'a'; } - BigChief
垃圾收集器还会压缩堆,这会移动对象。 - Hans Passant
啊,好的,但我认为当你使用fixed时,它会留在内存的同一位置,对吗? - BigChief
你的代码片段中它不会保持固定状态太长时间,你需要使用GCHandle.Alloc()来使其保持更长时间的固定状态。如果仍然不清楚,请提出另一个问题。 - Hans Passant

0

您似乎并没有在这里创建一个 StackOverflow - 您实际上并没有使用堆栈。相反,您似乎在尝试创建缓冲区溢出,我猜想您认为不安全的 C# 与 C 类似,但它们存在一些重要的区别。

可以简单地创建Stackoverflow:

public void Stackoverflow()
{
    Stackoverflow();
}

然后在某个地方调用Stackoverflow()


小修复:将 return Stackoverflow() 改为 void - Dan Abramov
但是,如果您执行(安全的)int i = 99;,您是在使用堆栈,对吗? - BigChief
可能应该说的是你正在使用堆栈,但没有大量增加其大小(实际上,在 .NET VM 中的每个操作中都使用堆栈)。因此,它不会导致堆栈溢出。但是你仍然在寻找缓冲区溢出。 - Callum Rogers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接