如何利用内存溢出执行其他程序(100分)

  • 主题发起人 主题发起人 star.yu
  • 开始时间 开始时间
S

star.yu

Unregistered / Unconfirmed
GUEST, unregistred user!
有些高级黑客,可以利用一些服务器上的常用软件的BUG产生的内存溢出执行
一些自己想远端执行的程序。
比如,SQL SERVER 的扩展存储过程就有BUG,非超级用户,也可以执行它,
how? why?
我就不知道了,我百思不得其解,只知道跟内存溢出有关,希望有大侠能够解惑。
我将送给你我宝贵的100分。
 
这个比较复杂,我以前研究过
主要就是利用程序里面的堆栈溢出来实现的
一般的情况下,如果你的某个变量在没有对接受值大小检查的情况下
接受了比自己大的值,那么接受值除了给该变量赋值外,多的就把该变量
附近的内存覆盖了,比如你建立一个100大小的数组,但实际上该数组收到了
200个数,这时堆栈就溢出了,通过自己的计算和估计找到溢出点,在该溢出点
后面的一段内存空间放上自己的执行代码,当再次溢出时就会执行我放进去的
代码,从来执行一些平时不可能执行的东西.

在绿盟上(www.nsfocus.com)上高手比较多
 
比如
char cmd1[10];
cmd2[20]="ls -la" //means dir
如果被恶意攻击的话,
在输入cmd1的时候,cmd1="1234567890 rm /*.* -rf ",cmd2的内容就有可能被替换
然后。。。。
 
其实主要是钻系统的漏洞,因为系统内存溢出后产生的异常处理都是一样的,他们都指向一个
个处理单元,这样一来,就可以人为的创造一种错误,而错误代码的调用却用一个跳转到自己的
程序处的指令。其实是一种指令欺骗技术。
 
我不明白为什么内存中多出的那一块会被执行,它多了就多了呗,
为什么会被系统执行。
如果chenlili先生能告诉我更详细一点。。。。。。
 
其实并不是真正的内存溢出
而是堆栈溢出
当程序里面调用一个函数时
程序会形成如下的堆栈,先把原函数的内存地址压入堆栈,
然后进行自己的操作(比如先把我前面说到的100个数组压入,再把200个数组压入)
这时那个200数组多的数据就把先压入的原函数地址覆盖了
这时当该函数返回时,也就是执行汇编指令ret,ret也差不多就是先把原函数的
内存地址弹出到ip寄存器,然后jmp ip,由于前面的那200个数组里面的数据已经
把返回地址覆盖了,所以执行到jmp ip的时候,也就是去执行了我预先准备好的
代码.
 
原作 by Aleph One
aleph1@underground.org
翻译 xuzq@chinasafer.com www.chinasafer.com

'践踏堆栈'[C语言编程] n. 在许多C语言的实现中,有可能通过写入例程
中所声明的数组的结尾部分来破坏可执行的堆栈.所谓'践踏堆栈'使用的
代码可以造成例程的返回异常,从而跳到任意的地址.这导致了一些极为
险恶的数据相关漏洞(已人所共知).其变种包括堆栈垃圾化(trash the
stack),堆栈乱写(scribble the stack),堆栈毁坏(mangle the stack);
术语mung the stack并不使用,因为这从来不是故意造成的.参阅spam?
也请参阅同名的漏洞,胡闹内核(fandango on core),内存泄露(memory
leak),优先权丢失(precedence lossage),螺纹滑扣(overrun screw).

简 介
~~~~~~~

在过去的几个月中,被发现和利用的缓冲区溢出漏洞呈现上升趋势.例如syslog,
splitvt, sendmail 8.7.5, Linux/FreeBSD mount, Xt library, at等等.本文试图
解释什么是缓冲区溢出, 以及如何利用.

汇编的基础知识是必需的. 对虚拟内存的概念, 以及使用gdb的经验是十分有益
的, 但不是必需的. 我们还假定使用Intel x86 CPU, 操作系统是Linux.

在开始之前我们给出几个基本的定义: 缓冲区,简单说来是一块连续的计算机内
存区域, 可以保存相同数据类型的多个实例. C程序员通常和字缓冲区数组打交道.
最常见的是字符数组. 数组, 与C语言中所有的变量一样, 可以被声明为静态或动态
的. 静态变量在程序加载时定位于数据段. 动态变量在程序运行时定位于堆栈之中.
溢出, 说白了就是灌满, 使内容物超过顶端, 边缘, 或边界. 我们这里只关心动态
缓冲区的溢出问题, 即基于堆栈的缓冲区溢出.

进程的内存组织形式
~~~~~~~~~~~~~~~~~~~~
为了理解什么是堆栈缓冲区, 我们必须首先理解一个进程是以什么组织形式在
内存中存在的. 进程被分成三个区域: 文本, 数据和堆栈. 我们把精力集中在堆栈
区域, 但首先按照顺序简单介绍一下其他区域.

文本区域是由程序确定的, 包括代码(指令)和只读数据. 该区域相当于可执行
文件的文本段. 这个区域通常被标记为只读, 任何对其写入的操作都会导致段错误
(segmentation violation).

数据区域包含了已初始化和未初始化的数据. 静态变量储存在这个区域中. 数
据区域对应可执行文件中的data-bss段. 它的大小可以用系统调用brk(2)来改变.
如果bss数据的扩展或用户堆栈把可用内存消耗光了, 进程就会被阻塞住, 等待有了
一块更大的内存空间之后再运行. 新内存加入到数据和堆栈段的中间.

/------------------/ 内存低地址
| |
| 文本 |
| |
|------------------|
| (已初始化) |
| 数据 |
| (未初始化) |
|------------------|
| |
| 堆栈 |
| |
/------------------/ 内存高地址

Fig. 1 进程内存区域


什么是堆栈?
~~~~~~~~~~~~~

堆栈是一个在计算机科学中经常使用的抽象数据类型. 堆栈中的物体具有一个特性:
最后一个放入堆栈中的物体总是被最先拿出来, 这个特性通常称为后进先处(LIFO)队列.

堆栈中定义了一些操作. 两个最重要的是PUSH和POP. PUSH操作在堆栈的顶部加入一
个元素. POP操作相反, 在堆栈顶部移去一个元素, 并将堆栈的大小减一.


为什么使用堆栈?
~~~~~~~~~~~~~~~~
现代计算机被设计成能够理解人们头脑中的高级语言. 在使用高级语言构造程序时
最重要的技术是过程(procedure)和函数(function). 从这一点来看, 一个过程调用可
以象跳转(jump)命令那样改变程序的控制流程, 但是与跳转不同的是, 当工作完成时,
函数把控制权返回给调用之后的语句或指令. 这种高级抽象实现起来要靠堆栈的帮助.

堆栈也用于给函数中使用的局部变量动态分配空间, 同样给函数传递参数和函数返
回值也要用到堆栈.


堆栈区域
~~~~~~~~~~
堆栈是一块保存数据的连续内存. 一个名为堆栈指针(SP)的寄存器指向堆栈的顶部.
堆栈的底部在一个固定的地址. 堆栈的大小在运行时由内核动态地调整. CPU实现指令
PUSH和POP, 向堆栈中添加元素和从中移去元素.

堆栈由逻辑堆栈帧组成. 当调用函数时逻辑堆栈帧被压入栈中, 当函数返回时逻辑
堆栈帧被从栈中弹出. 堆栈帧包括函数的参数, 函数地局部变量, 以及恢复前一个堆栈
帧所需要的数据, 其中包括在函数调用时指令指针(IP)的值.

堆栈既可以向下增长(向内存低地址)也可以向上增长, 这依赖于具体的实现. 在我
们的例子中, 堆栈是向下增长的. 这是很多计算机的实现方式, 包括Intel, Motorola,
SPARC和MIPS处理器. 堆栈指针(SP)也是依赖于具体实现的. 它可以指向堆栈的最后地址,
或者指向堆栈之后的下一个空闲可用地址. 在我们的讨论当中, SP指向堆栈的最后地址.

除了堆栈指针(SP指向堆栈顶部的的低地址)之外, 为了使用方便还有指向帧内固定
地址的指针叫做帧指针(FP). 有些文章把它叫做局部基指针(LB-local base pointer).
从理论上来说, 局部变量可以用SP加偏移量来引用. 然而, 当有字被压栈和出栈后, 这
些偏移量就变了. 尽管在某些情况下编译器能够跟踪栈中的字操作, 由此可以修正偏移
量, 但是在某些情况下不能. 而且在所有情况下, 要引入可观的管理开销. 而且在有些
机器上, 比如Intel处理器, 由SP加偏移量访问一个变量需要多条指令才能实现.

因此, 许多编译器使用第二个寄存器, FP, 对于局部变量和函数参数都可以引用,
因为它们到FP的距离不会受到PUSH和POP操作的影响. 在Intel CPU中, BP(EBP)用于这
个目的. 在Motorola CPU中, 除了A7(堆栈指针SP)之外的任何地址寄存器都可以做FP.
考虑到我们堆栈的增长方向, 从FP的位置开始计算, 函数参数的偏移量是正值, 而局部
变量的偏移量是负值.

当一个例程被调用时所必须做的第一件事是保存前一个FP(这样当例程退出时就可以
恢复). 然后它把SP复制到FP, 创建新的FP, 把SP向前移动为局部变量保留空间. 这称为
例程的序幕(prolog)工作. 当例程退出时, 堆栈必须被清除干净, 这称为例程的收尾
(epilog)工作. Intel的ENTER和LEAVE指令, Motorola的LINK和UNLINK指令, 都可以用于
有效地序幕和收尾工作.

下面我们用一个简单的例子来展示堆栈的模样:

example1.c:
------------------------------------------------------------------------------
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
}

void main() {
function(1,2,3);
}
------------------------------------------------------------------------------

为了理解程序在调用function()时都做了哪些事情, 我们使用gcc的-S选项编译, 以产
生汇编代码输出:

$ gcc -S -o example1.s example1.c

通过查看汇编语言输出, 我们看到对function()的调用被翻译成:

pushl $3
pushl $2
pushl $1
call function

以从后往前的顺序将function的三个参数压入栈中, 然后调用function(). 指令call
会把指令指针(IP)也压入栈中. 我们把这被保存的IP称为返回地址(RET). 在函数中所做
的第一件事情是例程的序幕工作:

pushl %ebp
movl %esp,%ebp
subl $20,%esp

将帧指针EBP压入栈中. 然后把当前的SP复制到EBP, 使其成为新的帧指针. 我们把这
个被保存的FP叫做SFP. 接下来将SP的值减小, 为局部变量保留空间.

我们必须牢记:内存只能以字为单位寻址. 在这里一个字是4个字节, 32位. 因此5字节
的缓冲区会占用8个字节(2个字)的内存空间, 而10个字节的缓冲区会占用12个字节(3个字)
的内存空间. 这就是为什么SP要减掉20的原因. 这样我们就可以想象function()被调用时
堆栈的模样(每个空格代表一个字节):


内存低地址 内存高地址

buffer2 buffer1 sfp ret a b c
<------ [ ][ ][ ][ ][ ][ ][ ]

堆栈顶部 堆栈底部
 
帖了一大篇又没有说到正题
 
请继续或结束
 
请继续或结束
 
有没有用delphi作成的例子啊!
 
这个一般都是靠自己调出来的
 
这个问题得我自己解决了,
chenlili,热血,honestman得分了
 
后退
顶部