Thursday, June 5, 2008

A GUIDE TO NETWORK PACKET FORGING

Packet forging has long been a desire of all of us network hackers. It gives us control over the network, and control obviously means feeling of power (whether it actually be there or not).

In today's world of connectivity, you'd find thousands of tutorials on the Internet about packet forging, both for winsock and *nix sockets. I shall not cover winsock, as I do not consider it worth the work. Rather, the libraries needed are not free, and require a lot of money, that I don't have, to be spared on things like winsock, that definitely wastes my valuable time.

Although most of the tutorials will have TCP packet forging, I shall start with UDP packet forging. Those of you, that are new to network hacking, but have enough enthusiasm to learn, I suggest reading the rfc 768 (udp) and rfc 791 (ip), in order to be able to understand whatever is written in this article. A working knowledge of the C programming language and linux network system calls is required. If you don't know that, fret not 'cause you can simply google 'em out. I shall use the standard linux library to work on this.

As an introductory note, I'd explain the various fields of IP header and UDP header. Now, what's a header? A header is a sequence of bytes or octets, whatever you're more comfortable with, that defines various options in the packet. This particular packet we will forge here will have two headers. The IP header and the UDP header. The UDP header will follow the IP header. If it were a TCP packet, there would have been no UDP header, and the TCP header would have followed the IP header.

THE IP HEADER

The IP header is the most important header, as IP is the base protocol on the Network layer. Though there are other protocols that are used in the network layer, but on the Internet, IP is used. The IP header has the following format:



0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

I shall not go into the details of all the fields up here, as you can get those in the rfcs, but will explain those that we are going to use in the following program.

The Version is a 4 bit field is used to specify the version of IP we shall use. As of the time of this writing, two versions of IP exist 4 and 6. We shall be using 4, as it is more prevalent, and 6 will take some more time to take over.

Type of Service is a 1 byte field and will specify how the routers have to pass it on, whether it should be treated as a normal packet, or a high priority packet and should be passed with low delay.

Total Length will contain the total length of the packet, including the TCP or in this case UDP header and the payload, which is also called data. Its maximum value can be 65535, as it is 16 bits long.

Flags specify other options, and we shall set it to zero here.

Fragment offset is what is used, when a single packet has to be broken in different parts, when network medium cannot handle it in one go. The offset will specify which octet marks the beginning of that particular packet.

Time to Live spcifies how many hops the packet will survive. After that many hops (gateways or routers), the packet will die, and the corresponding router, will send the sender and ICMP message that the packet expired in the way. It is 1 octet long, and so, has a maximum value of 255.

Protocol of course specifies the protocol being used underneath. 6 is used for TCP, 17 for UDP. In Linux, we have predefined constants for those in
. We shall use them, and you will know which means what.

Header checksum is the checksum generated taking into consideration, the size of the total packet. The algorithm for that is specified in the rfc, and corresponding algorithm in C is given below.

Source address is of course the originating source IP address, that we are going to mess with here. It is 32 bits long, and has the usual four different octets. It's a long story and I won't get into it here. Read rfcs for those too.

Destination address is the destination IP address.


NOTE: Before I go any further, I shall explain the octet arrangement issue. The byte arrangement in the network is always BIG ENDIAN. For those that don't know what this means, BIG ENDIAN byte arrangement is that, where the least significant byte comes last. In LITTLE ENDIAN, exactly the opposite happens. The MSB comes last, and that's exactly the byte representation on our Intel mocroprocessors. All the fields must be changed in the network byte order for further processing etc. There are functions for that, like htons() host to network short, htonl() host to network long, ntohs() network to host short, ntohl() network to host long.

For address conversion, from a string like “65.34.223.4” to the corresponding numeric format, we have function like inet_addr() and inet_aton(). For the opposite, we have the function inet_ntoa(). The inet_aton() will convert into network byte order, so that you don't have to worry about the byte order. But the inet_ntoa() requires the argument to be in network byte order.

THE UDP HEADER

The UDP header is simple, (one of the reasons I chose it here) as it has only four fields.


0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| Source | Destination |
| Port | Port |
+--------+--------+--------+--------+
| | |
| Length | Checksum |
+--------+--------+--------+--------+
|
| data octets ...
+---------------- ...

The fields given here are self explanatory, and I believe they need no further explanation. But you'd need to change the PORT numbers to network byte order with inet_ntoa.

Okay, so here we are, having done with the tedious work of knowing the theory of the network packets, now we'll delve into the program. As you have noticed, I have not included enough comments in the code. I have a special reason behind that. Figuring out what's happening inside, is upto you. If you're unable to understand what the hell is going on, do leave me a comment and I'll explain. Also, I have included a server program, that tells UTC time, so that you can test the packet forging program. The server also runs on UDP (without a doubt).

THE CODE



/*
* rawudp.c
*
* The raw UDP packet forging utility
* Author: Xtreme Great
* Disclaimer:
* This program is for educational purposes only
* Any misuse of this program is not the author's
* intent. I hereby disclaim any responsibility
* for any kind of damage this program may cause
* in any way or any manner. The author shall not
* be responsible for any damage this program may
* cause.
*/


#define __USE_BSD
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/ip.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define __FAVOR_BSD
#include<netinet/udp.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>

#define SRC "3.3.3.3"
#define DEST "127.0.0.1"
#define PORT 7000
#define PAYLOAD "hello me msg"

unsigned short
csum(unsigned short *buf, int nwords){
unsigned long
sum;
for
(sum=0; nwords>0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return
(unsigned short)(~sum);
}


int
main(){
struct
sockaddr_in srvr_addr;
unsigned char
buffer[4096], x, *data = PAYLOAD;
struct
ip *iphdr = (struct ip *)buffer;
struct
udphdr *udphead = (struct udphdr *)(buffer + sizeof(struct ip));
int
sockfd, one =1, i;
const
int *val = &one;

if
((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP)) == -1){
perror("socket() failed");
exit(-1);
}


srvr_addr.sin_family = AF_INET;
srvr_addr.sin_addr.s_addr = inet_addr(DEST);
srvr_addr.sin_port = htons(PORT);
memset(&(srvr_addr.sin_zero), 0, 8);
bzero(buffer, 4096);

iphdr->ip_v = 4;
iphdr->ip_hl = 5;
iphdr->ip_id = htonl(54321);
iphdr->ip_tos = 0;
iphdr->ip_off = 0;
iphdr->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + strlen(data));
iphdr->ip_ttl = 255;
iphdr->ip_p = 17;
iphdr->ip_src.s_addr = inet_addr(SRC);
iphdr->ip_dst.s_addr = srvr_addr.sin_addr.s_addr;


udphead->uh_sport = htons(46544);
udphead->uh_dport = htons(PORT);
udphead->uh_ulen = htons(sizeof(struct udphdr)+strlen(data));
udphead->uh_sum = htons(csum((unsigned short *)buffer, sizeof(struct ip) + sizeof(struct udphdr) + strlen(data)));
iphdr->ip_sum = htons(csum((unsigned short *)buffer, sizeof(struct ip) + sizeof(struct udphdr) + strlen(data)));

if
(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0){
perror("setsockopt() error");
exit(-1);
}


memcpy((char *)(buffer + sizeof(struct ip) + sizeof(struct udphdr)), data, strlen(data));

if
(sendto(sockfd, buffer, ntohs(iphdr->ip_len), 0, (struct sockaddr *)&srvr_addr, sizeof(srvr_addr)) < 0){
perror("sendto() error");
exit(-1);
}


printf("0x\tPrinting hex dump of the packet.\tx0\n");

for
(i=0; i<ntohs(iphdr->ip_len); i++){
x = buffer[i];
printf("%x", x);
}


printf("\nPacket sent.\n");
close(sockfd);
exit(0);
}









/*
* udpdaytimeserver.c
*
* The UDP day time server
* Author: Xtreme Great
* Written as a part of the summer holidays self training.
* This is a day time server based on the UDP protocol,
* which is an iterative and concurrent server.
* when given any arbitrary message in UDP on the specified
* PORT, the server will provide the current Greenwich Mean
* Time.
* Disclaimer:
* This program is for educational purposes only
* Any misuse of this program is not the author's
* intent. I hereby disclaim any responsibility
* for any kind of damage this program may cause
* in any way or any manner. The author shall not
* be responsible for any damage, or any consequences
* thereof, this program may cause.
*/


#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define PORT 7000

int
main(){
int
sockfd, size, f;
struct
sockaddr_in my_addr, clnt_addr;
char
*address, *cnow, buf[20];
time_t now;

printf("All systems up and running.\nInitiating server setup sequence.\n");

if
((sockfd=socket(AF_INET, SOCK_DGRAM, 0))==-1){
printf("socket() error ");
exit(-1);
}


my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(PORT);
memset(&(my_addr.sin_zero), 0, 8);

if
(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1){

perror("bind() failed");
exit(-1);
}


printf("UDP Day time server listening on port number %d\nPress Ctrl+C to quit.\n\n", ntohs(my_addr.sin_port));

wait4conn:

clnt_addr.sin_family = AF_INET;
clnt_addr.sin_addr.s_addr = INADDR_ANY;
clnt_addr.sin_port = htons(0);
memset(&(clnt_addr.sin_zero), 0, 8);
memset(buf, 0, 20);
size = sizeof(struct sockaddr);

if
(recvfrom(sockfd, (char *)buf, 20, 0, (struct sockaddr *)&clnt_addr, &size) == -1){
perror("recvfrom() failed.");
exit(-1);
}


if
((f = fork()) == -1){
perror("fork() failed.");
exit(-1);
}


if
(f == 0){
printf("Received %s from %s\n", buf, inet_ntoa(clnt_addr.sin_addr.s_addr));
time(&now);
now -= 19800;
cnow = (char *)ctime(&now);
printf("Sending time.\n%s", cnow);
sendto(sockfd, cnow, strlen(cnow), 0, (struct sockaddr *)&clnt_addr, sizeof(struct sockaddr));
printf("Time sent.\n\n");
}


goto
wait4conn;

close(sockfd);

exit(0);
}


NEXT UP

Next up would be a udp and a tcp packet sniffer, that is two packet sniffers, followed by a brief theory of packet sniffing. But that's going to take about a few more hours (maybe 24 or so). Keep checking.