자 이번 문제의 파일은 C 파일이 주어지지 않았다.
단지 64 - bit 짜리 exe 파일만 주어졌기 때문에 우리는 assembly 언어로 작성된 코드들만을 이용해서 문제를 이해하고, 풀어야 한다.
assembly 코드를 살펴보자.
0x0000000000000830 <+0>: push rbp
0x0000000000000831 <+1>: mov rbp,rsp
0x0000000000000834 <+4>: sub rsp,0x10
0x0000000000000838 <+8>: mov r9d,0x0
0x000000000000083e <+14>: mov r8d,0xffffffff
0x0000000000000844 <+20>: mov ecx,0x22
0x0000000000000849 <+25>: mov edx,0x7
0x000000000000084e <+30>: mov esi,0x400
0x0000000000000853 <+35>: mov edi,0x0
0x0000000000000858 <+40>: call 0x680 <mmap@plt>
0x000000000000085d <+45>: mov QWORD PTR [rbp-0x10],rax
0x0000000000000861 <+49>: mov eax,0x0
0x0000000000000866 <+54>: call 0x7da <init>
0x000000000000086b <+59>: mov eax,0x0
0x0000000000000870 <+64>: call 0x81d <sandbox>
0x0000000000000875 <+69>: lea rdi,[rip+0xdc] # 0x958
0x000000000000087c <+76>: mov eax,0x0
0x0000000000000881 <+81>: call 0x690 <printf@plt>
0x0000000000000886 <+86>: mov rax,QWORD PTR [rbp-0x10]
0x000000000000088a <+90>: mov edx,0x50
0x000000000000088f <+95>: mov rsi,rax
0x0000000000000892 <+98>: mov edi,0x0
0x0000000000000897 <+103>: call 0x6a0 <read@plt>
0x000000000000089c <+108>: mov rax,QWORD PTR [rbp-0x10]
0x00000000000008a0 <+112>: mov QWORD PTR [rbp-0x8],rax
0x00000000000008a4 <+116>: mov rdx,QWORD PTR [rbp-0x8]
0x00000000000008a8 <+120>: mov eax,0x0
0x00000000000008ad <+125>: call rdx
0x00000000000008af <+127>: mov eax,0x0
0x00000000000008b4 <+132>: leave
0x00000000000008b5 <+133>: ret
자.. 벌써 사실 막막하다. 먼저 눈에 띄는 함수들이 존재한다.
우리는 read, printf, mmap 함수는 알고 있지만, sandbox 함수는 plt로 지정되어 있지 않은 것으로 보아 아마 함수 내에 정의되어 있는 함수인 것 같다.
일단, sandbox 함수는 나중에 차차 뜯어보기로 하고, main 함수의 실행 과정부터 살펴보자.
다들 아시겠지만, 64 - bit 체제는 함수를 실행할 때, 함수 내의 인자로써 레지스터가 우선순위에 있다.
그 순서는 운영체제마다 약간의 차이가 있겠지만 대부분
이렇게 나열된 순서대로 함수의 인자로 들어가게 된다.
즉, 예시로 scanf 함수를 실행한다고 가정해보면, scanf 함수는 2개의 인자를 필요로 하기에
Ex) scanf(rdi,rsi) 로 나타낼 수 있다.
자 그럼 코드를 크게 5 부분으로 나누어서 분석해보자.
0x0000000000000831 <+1>: mov rbp,rsp 0x0000000000000834 <+4>: sub rsp,0x10 0x0000000000000838 <+8>: mov r9d,0x0 0x000000000000083e <+14>: mov r8d,0xffffffff 0x0000000000000844 <+20>: mov ecx,0x22 0x0000000000000849 <+25>: mov edx,0x7 0x000000000000084e <+30>: mov esi,0x400 0x0000000000000853 <+35>: mov edi,0x0 0x0000000000000858 <+40>: call 0x680 <mmap@plt> 0x000000000000085d <+45>: mov QWORD PTR [rbp-0x10],rax
자. rsp에 0x10을 빼주며 스택의 크기를 0x10으로 정해주고, mmap 함수를 실행하게 된다.
과연 mmap 함수는 무엇일까??
mmap 함수의 형태는 다음과 같다.
따라서 레지스터 값들을 살펴보면, 이 cube라는 파일은
mmap(0, 0x400, 0x7, 0x22, -1, 0x0) 라는 함수를 실행시킨 것이 되겠다.
그 후에, rax ( 함수의 return값) 을 만들어주었던 buffer의 0x10 구간에 넣어줌으로써
C 코드로 구현하게 되면
buf = mmap(0, 0x400, 0x7, 0x22, -1, 0x0); 라는 코드가 완성되게 된다.
0x0000000000000861 <+49>: mov eax,0x0 0x0000000000000866 <+54>: call 0x7da <init> 0x000000000000086b <+59>: mov eax,0x0 0x0000000000000870 <+64>: call 0x81d <sandbox>
먼저, init 함수는 늘 있었던 입출력 버퍼를 초기화해주는 함수라고 여기고 별다른 설명 없이 넘어가도 될 것 같다. 그 후에, 새로운 함수인 sandbox 함수가 실행되게 되는데, 이 함수는 또 어떤 세부 코드를 지니고 있을지 disassemble을 해보도록 하자!
0x000000000000081d <+0>: push rbp 0x000000000000081e <+1>: mov rbp,rsp 0x0000000000000821 <+4>: lea rdi,[rip+0x11c] # 0x944 0x0000000000000828 <+11>: call 0x670 <chroot@plt> 0x000000000000082d <+16>: nop 0x000000000000082e <+17>: pop rbp 0x000000000000082f <+18>: ret
흐음.. 또 새로운 함수가 등장했다. chroot 함수이다.
일단 함수의 첫번째 인자로써 작용하는 rdi의 값은 무엇인지 확인해 보도록 하자.
오.. 일단 chroot 함수는 처음 보는 함수이지만, 결국 이 sandbox 라는 함수는
단순히 chroot("/home/cube/cube_box") 를 실행해준다는 사실을 알게 되었다.
자 그럼 chroot 함수에 대해서 알아보자.
chroot은 무엇일까? <-- 여기를 클릭하면 알 수 있다!
따라서 이 함수를 실행하면, 위치를 옮겨주늗!
0x0000000000000875 <+69>: lea rdi,[rip+0xdc] # 0x958 0x000000000000087c <+76>: mov eax,0x0 0x0000000000000881 <+81>: call 0x690 <printf@plt> 0x0000000000000886 <+86>: mov rax,QWORD PTR [rbp-0x10] 0x000000000000088a <+90>: mov edx,0x50 0x000000000000088f <+95>: mov rsi,rax 0x0000000000000892 <+98>: mov edi,0x0 0x0000000000000897 <+103>: call 0x6a0 <read@plt>
음. 이 부분은 비교적 쉬운 것 같다. printf 에 들어갈 인자와, read 에 들어갈 인자만 확인해주면 될 것 같다.
먼저 printf 의 인자에 들어갈 rdi 의 값을 확인해주자.