cube

원상윤·2022년 6월 25일
1

dreamhack_pwnable

목록 보기
2/2

자 이번 문제의 파일은 C 파일이 주어지지 않았다.
단지 64 - bit 짜리 exe 파일만 주어졌기 때문에 우리는 assembly 언어로 작성된 코드들만을 이용해서 문제를 이해하고, 풀어야 한다.

assembly 코드를 살펴보자.


code 확인

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 함수의 실행 과정부터 살펴보자.


- code 분석 -

다들 아시겠지만, 64 - bit 체제는 함수를 실행할 때, 함수 내의 인자로써 레지스터가 우선순위에 있다.
그 순서는 운영체제마다 약간의 차이가 있겠지만 대부분

  • rdi → rsi → rdx → rcx → r8 → r9 → ~~

이렇게 나열된 순서대로 함수의 인자로 들어가게 된다.
즉, 예시로 scanf 함수를 실행한다고 가정해보면, scanf 함수는 2개의 인자를 필요로 하기에

Ex) scanf(rdi,rsi) 로 나타낼 수 있다.

자 그럼 코드를 크게 5 부분으로 나누어서 분석해보자.

# 1

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 함수의 형태는 다음과 같다.

  • void* mmap(void * start, size_t length, int prot, int flags, int fd, off_t offset);

따라서 레지스터 값들을 살펴보면, 이 cube라는 파일은

mmap(0, 0x400, 0x7, 0x22, -1, 0x0) 라는 함수를 실행시킨 것이 되겠다.

그 후에, rax ( 함수의 return값) 을 만들어주었던 buffer의 0x10 구간에 넣어줌으로써
C 코드로 구현하게 되면

buf = mmap(0, 0x400, 0x7, 0x22, -1, 0x0); 라는 코드가 완성되게 된다.

#2

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은 무엇일까? <-- 여기를 클릭하면 알 수 있다!

따라서 이 함수를 실행하면, 위치를 옮겨주늗!

#3

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 의 값을 확인해주자.

0개의 댓글