ghkdtlwns987

[pwnable.tw] seethefile 본문

pwnable.tw

[pwnable.tw] seethefile

2020/03/31 2021. 1. 16. 14:39

glibc 가 2.23 이다. 

 

main

더보기
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char nptr; // [esp+Ch] [ebp-2Ch]
  unsigned int v4; // [esp+2Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  init();
  welcome();
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%s", &nptr);
    switch ( atoi(&nptr) )
    {
      case 0:
        puts("Invaild choice");
        exit(0);
        return;
      case 1:
        openfile();
        break;
      case 2:
        readfile();
        break;
      case 3:
        writefile();
        break;
      case 4:
        closefile();
        break;
      case 5:
        printf("Leave your name :");
        __isoc99_scanf("%s", &name);
        printf("Thank you %s ,see you next time\n", &name);
        if ( fp )
          fclose(fp);
        exit(0);
        return;
    }
  }
}

main() 함수에서 5번을 입력하게 되면 scanf() 함수에서 overflow 가 발생한다. 

name 변수에서 입력받는데, name 변수 바로 위에 FILE * 가 존재하기 때문에, 

fp 에 값을 덮을 수 있다. 이를 이용해 exploit 할 것이다. 

 

미리 말해보자면 fclose(fp) 에서 fclose() 를 system() 으로 덮고, fp에 '/bin/sh' 로 덮어줄 것이다.

 

openfile

더보기
int openfile()
{
  int result; // eax

  if ( fp )
  {
    puts("You need to close the file first");
    result = 0;
  }
  else
  {
    memset(magicbuf, 0, 0x190u);
    printf("What do you want to see :");
    __isoc99_scanf("%63s", filename);
    if ( strstr(filename, "flag") )
    {
      puts("Danger !");
      exit(0);
    }
    fp = fopen(filename, "r");
    if ( fp )
      result = puts("Open Successful");
    else
      result = puts("Open failed");
  }
  return result;
}

filename 에 입력을 받는데, filename이 flag라면 종료된다. 

flag가 아니라면 fopen을 통해 filename 을 읽는다. 

 

readfile

더보기
int readfile()
{
  int result; // eax

  memset(magicbuf, 0, 0x190u);
  if ( !fp )
    return puts("You need to open a file first");
  result = fread(magicbuf, 0x18Fu, 1u, fp);
  if ( result )
    result = puts("Read Successful");
  return result;
}

fread()로 magicbuf 에 0x18만큼 값을 쓴다. 

 

writefile

더보기
int writefile()
{
  if ( strstr(filename, "flag") || strstr(magicbuf, "FLAG") || strchr(magicbuf, 125) )
  {
    puts("you can't see it");
    exit(1);
  }
  return puts(magicbuf);
}

flag, FLAG, 가 있으면 종료되는데, 그게 아니면 maigcbuf 를 출력한다. 

 

closefile

더보기
int closefile()
{
  int result; // eax

  if ( fp )
    result = fclose(fp);
  else
    result = puts("Nothing need to close");
  fp = 0;
  return result;
}

fp가 존재하면 fclose() 한다.

 

문제를 풀기전 libc leak 을 해야한다.

leak 하는 방법은 /proc/self/maps 를 출력하면 된다. 

/proc/self/maps 파일은 gdb에서 vmmap 을 입력하면 나오는 주소가 저장되어 있다. 

여기서 보면 /lib/i386-linux-gnu/libc-2.27.so 파일의 주소가 보이는데,

맨 위의 주소가 libc_base 이다. (0xf7db7000) 이를 이용해 libc leak 해 주면 된다. 

 

이로서 libc_leak 을 마쳤다.

다음엔 이를 이용해 fp 를 덮어 exploit 해야 한다. 

 

위에서 언급했듯이 fclose(fp) 부분에서 fp 를 '/bin/sh' 로, fclose() 를 system() 함수로 덮어주어야 한다. 

scanf() 에서 overflow() 가 발생하는데, 이를 이용해 name(0x20) 만큼 값을 채우고 '/bin/sh'를 넣어주면 

fp -> '/bin/sh' 가 들어가게 되고, 

IO_file_jumps 에서 flose() 를 system() 으로 덮으면 

fclose() -> system() 이 된다.

_IO_FILE_jumps

 

exploit 코드를 보고 이해를 돕기 위해 사진 한장을 준비했다.

이 부분이 jump_table 주소이다. 

빨간색으로 네모친 부분에 fake_vtable 을 넣어 줄 것이다. 

(또한, glibc.2.23 이므로 vtable을 직접 변경해도 상관 없음)

 

32bit 와 64bit 의 fp 구조는 서로 다르다.

lock 에 값을 넣어주고 어쩌고 하다보면 exploit 이 된다. 

 

 

get_flag 를 실행시키고 Give me the flag 를 입력하면 flag 가 출력된다. 

'pwnable.tw' 카테고리의 다른 글

[pwnable.tw] unexploitable  (0) 2021.01.23
[pwnable.tw] applestore  (0) 2020.12.29
[pwnable.tw] silver_bullet  (0) 2020.12.24
[pwnable.tw] hacknote  (0) 2020.12.07
[pwnable.tw] dubblesort  (0) 2020.11.22
Comments