System hacking training/Protostar
heap1 풀이
fkillrra
2018. 12. 21. 11:57
[heap1.c]
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
struct internet {
int priority;
char *name;
};
void winner()
{
printf("and we have a winner @ %d\n", time(NULL));
}
int main(int argc, char **argv)
{
struct internet *i1, *i2, *i3;
i1 = malloc(sizeof(struct internet));
i1->priority = 1;
i1->name = malloc(8);
i2 = malloc(sizeof(struct internet));
i2->priority = 2;
i2->name = malloc(8);
strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);
printf("and that's a wrap folks!\n");
}
heap1도 heap0과 마찬가지로 heap overflow 문제다.
i1 = malloc(sizeof(struct internet));
i1->priority = 1;
i1->name = malloc(8);
i2 = malloc(sizeof(struct internet));
i2->priority = 2;
i2->name = malloc(8);
main()에서 malloc으로 struct internet의 크기 만큼 i1에 할당을 한다.
이후 값을 넣어주는데,
i1->name = malloc(8);을 하는 것을 볼 수 있다.
i2도 똑같이 malloc을 2번해서
총 4개의 chunk를 할당받는다.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x080484b9 <main+0>: push ebp
0x080484ba <main+1>: mov ebp,esp
0x080484bc <main+3>: and esp,0xfffffff0
0x080484bf <main+6>: sub esp,0x20
0x080484c2 <main+9>: mov DWORD PTR [esp],0x8
0x080484c9 <main+16>: call 0x80483bc <malloc@plt>
0x080484ce <main+21>: mov DWORD PTR [esp+0x14],eax
0x080484d2 <main+25>: mov eax,DWORD PTR [esp+0x14]
0x080484d6 <main+29>: mov DWORD PTR [eax],0x1
0x080484dc <main+35>: mov DWORD PTR [esp],0x8
0x080484e3 <main+42>: call 0x80483bc <malloc@plt>
0x080484e8 <main+47>: mov edx,eax
0x080484ea <main+49>: mov eax,DWORD PTR [esp+0x14]
0x080484ee <main+53>: mov DWORD PTR [eax+0x4],edx
0x080484f1 <main+56>: mov DWORD PTR [esp],0x8
0x080484f8 <main+63>: call 0x80483bc <malloc@plt>
0x080484fd <main+68>: mov DWORD PTR [esp+0x18],eax
0x08048501 <main+72>: mov eax,DWORD PTR [esp+0x18]
0x08048505 <main+76>: mov DWORD PTR [eax],0x2
0x0804850b <main+82>: mov DWORD PTR [esp],0x8
0x08048512 <main+89>: call 0x80483bc <malloc@plt>
0x08048517 <main+94>: mov edx,eax
0x08048519 <main+96>: mov eax,DWORD PTR [esp+0x18]
0x0804851d <main+100>: mov DWORD PTR [eax+0x4],edx
0x08048520 <main+103>: mov eax,DWORD PTR [ebp+0xc]
0x08048523 <main+106>: add eax,0x4
0x08048526 <main+109>: mov eax,DWORD PTR [eax]
0x08048528 <main+111>: mov edx,eax
0x0804852a <main+113>: mov eax,DWORD PTR [esp+0x14]
0x0804852e <main+117>: mov eax,DWORD PTR [eax+0x4]
0x08048531 <main+120>: mov DWORD PTR [esp+0x4],edx
0x08048535 <main+124>: mov DWORD PTR [esp],eax
0x08048538 <main+127>: call 0x804838c <strcpy@plt>
0x0804853d <main+132>: mov eax,DWORD PTR [ebp+0xc]
0x08048540 <main+135>: add eax,0x8
0x08048543 <main+138>: mov eax,DWORD PTR [eax]
0x08048545 <main+140>: mov edx,eax
0x08048547 <main+142>: mov eax,DWORD PTR [esp+0x18]
0x0804854b <main+146>: mov eax,DWORD PTR [eax+0x4]
0x0804854e <main+149>: mov DWORD PTR [esp+0x4],edx
0x08048552 <main+153>: mov DWORD PTR [esp],eax
0x08048555 <main+156>: call 0x804838c <strcpy@plt>
0x0804855a <main+161>: mov DWORD PTR [esp],0x804864b
0x08048561 <main+168>: call 0x80483cc <puts@plt>
---Type <return> to continue, or q <return> to quit---
0x08048566 <main+173>: leave
0x08048567 <main+174>: ret
End of assembler dump.
각각의 chunk가 어디에 존재하고, 어떤 구조를 갖고 있는지 해당 위치에 bp를 걸고 확인해봤다.
다음과 같은 형태를 띈다.
취약점은 단골 함수인 strcpy()를 호출하여 발생하는데
strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);
딱 봐도 got overwrie를 해야한다.
좀만 생각하면 답이 나오는데 strcpy()를 사용한다고 해서 stack 처럼 ret를 건드릴 수 있는 것도 아니고,
heap 은 좀 다르게 chunk라는 애들이 존재하니까...
그럼 이걸로 어떻게 winner()를 띄우냐...
소스코드를 보면 답이 나온다.
strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);
printf("and that's a wrap folks!\n");
strcpy() 호출 이후 printf()가 호출되니까 printf()의 got를 winner()의 주소로 바꾸면 된다.
(strcpy()도 보기 좋게 2개 있겠다!)
argv[1]에 AAAA argv[2]에 BBBB를 넣고 확인해본 결과
chunk i1에서 마지막 data 부분이 i1->name의 값이 들어가는 주소를 가리킨다.
chunk 2도 마찬가지다.
그럼 printf@got를 overwrite하기 위해서는 먼저 첫번째 strcpy()에서 overflow하여
chunk i2에서 마지막 data 즉 0x0804a038 부분을 printf의 got로 overwrite 하면 된다.
그리면 두 번째 strcpy()가 호출될 때 인자 정보가 다음과 같게 된다.
strcpy(i2->name (= printf@got), argv[2]);
따라서 argv[2]에는 winner()의 주소를 넣어서 다음 printf() 호출 때 winner()를 띄우면 된다.
끗.
반응형