RAW_SOCKET to forge UDP packets

Recently, I had to forge UDP packets in order to resolve a challenge, but unfortunately, I faced a lack of documentation on RAW_SOCKET with the usage of UDP. So there is my feedback on how to forge correct UDP packets, with links to documentation I used.

To begin with, let me introduce what is a RAW_SOCKET, and what we can do with it. A RAW_SOCKET is a type of socket that allows you to bypass the operating system encapsulation. You are responsible to fill all headers of the different protocols used during the communication. Thus, you have to fill IP Header, and UDP header on our example. Furthermore, you have to comply with the different RFCs by computing yourself all checksums to keep packet’s integrity while transmitting data in the wild.

A RAW_SOCKET requires root credentials, as forging a packet is really sensitive and allows one to do IP Spoofing on local network (ISP’s protection are pretty efficient :)). You can also use them to do application spoofing in order to send application-specific data, or whatever you want.

Create the socket

For examples purpose, I provide a server created with a SOCK_DGRAM listening  on port 9930 as well as the corresponding client (cf files server.c and client.c). The goal is then to have a client which uses a raw socket to communicate with our server (cf file rawudp.c). In the following, we present the modifications from a « traditional » client to a raw one while creating the socket and sending it.

{codecitation class= »brush: c; »}

if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
perror(« socket: »);

/* … */

if (sendto(s, buf, BUFLEN, 0, &si_other, slen)==-1)
perror(« sendto() : « );

{/codecitation}

is then « translated » to :

{codecitation class= »brush: c; »}

if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
perror(« socket: »);

/* … */

if (sendto(s, (char *)packet, sizepacket, 0,
(struct sockaddr *)&daddr, (socklen_t)sizeof(daddr)) < 0)
perror(« sendto() : « );

{/codecitation}

Fill the protocol headers

And it’s pretty all. The main difference is when we create the socket, and what is sent through it. In a SOCK_DGRAM socket, the buffer contains data that contains the message whereas a buffer passed to a SOCK_RAW socket contains a packet.

The real challenge is then to forge a correct packet, so look in the following snippet what are the options I compiled from different resources.

{codecitation class= »brush: c; »}

int s;
struct sockaddr_in daddr, saddr, answerip;
char packet[LEN];
/* point the iphdr to the beginning of the packet */
struct iphdr *ip = (struct iphdr *)packet; 
struct udphdr *udp = (struct udphdr *)((void *) ip + sizeof(struct iphdr));

daddr.sin_family = AF_INET;
saddr.sin_family = AF_INET;
daddr.sin_port = htons(PDEST);
saddr.sin_port = htons(PSOURCE);
inet_pton(AF_INET, DEST, (struct in_addr *)&daddr.sin_addr.s_addr);
inet_pton(AF_INET, SOURCE, (struct in_addr *)&saddr.sin_addr.s_addr);

ip->ihl = 5; //header length
ip->version = 4;
ip->tos = 0x0;
ip->id = 0;
ip->frag_off = htons(0x4000);        /* DF */
ip->ttl = 64;            /* default value */
ip->protocol = 17;    //IPPROTO_RAW;    /* protocol at L4 */
ip->check = 0;            /* not needed in iphdr */
ip->saddr = saddr.sin_addr.s_addr;
ip->daddr = daddr.sin_addr.s_addr;

udp->source = htons(PSOURCE);
udp->dest = htons (PDEST);

int sizedata = 100;
memset(((void *) udp) + sizeof(struct udphdr), ‘A’, sizedata);

int sizeudpdata = sizeof(struct udphdr) + sizedata;
ip->tot_len = htons(sizeudpdata + sizeof(struct iphdr));    /* 16 byte value */
udp->len = htons(sizeudpdata);

udp->check = udp_checksum(
ip,
udp,
udp);

{/codecitation}

Checksum

The last part I haven’t explained yet, is how to do the checksum for the different protocols. The IP checksum is simple has it’s a computation of all bytes in the header, and header only. But the UDP checksum is computed from UDP Header, UDP Data and also fields from the IP Header. The algorithm itself is simple as it’s – quote from UDP RFC – « 

"Checksum is the 16-bit one's complement of the one's complement sum of a
pseudo header of information from the IP header, the UDP header, and the
data, padded with zero octets at the end (if necessary) to make a
multiple of two octets.
The pseudo header conceptually prefixed to the UDP header contains the
source address, the destination address, the protocol, and the UDP
length"

To reflect it on the code, I’ve recreated the pseudo header to then simply compute it with checksum function defined for IP hdr.

{codecitation class= »brush: c; »}

//based on snippet found
//www.linuxquestions.org/questions/linux-networking-3/udp-checksum-algorithm-845618/
//then modified by Gabriel Serme

struct pseudo_hdr {

u_int32_t source;
u_int32_t dest;
u_int8_t zero; //reserved, check http://www.rhyshaden.com/udp.htm
u_int8_t protocol;
u_int16_t udp_length;
};

uint16_t udp_checksum(const struct iphdr  *ip,
const struct udphdr *udp,
const uint16_t *buf)
{
//take in account padding if necessary
int calculated_length = ntohs(udp->len)%2 == 0 ? ntohs(udp->len) : ntohs(udp->len) + 1;

struct pseudo_hdr ps_hdr = {0};
bzero (&ps_hdr, sizeof(struct pseudo_hdr));
uint8_t data[sizeof(struct pseudo_hdr) + calculated_length];
bzero (data, sizeof(struct pseudo_hdr) + calculated_length );

ps_hdr.source = ip->saddr;
ps_hdr.dest = ip->daddr;
ps_hdr.protocol = IPPROTO_UDP; //17
ps_hdr.udp_length = udp->len;

memcpy(data, &ps_hdr, sizeof(struct pseudo_hdr));

//the remaining bytes are already set to 0
memcpy(data + sizeof(struct pseudo_hdr), buf, ntohs(udp->len) );

return csum((uint16_t *)data, sizeof(data)/2);
}

/* Not my code */
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 ~sum;
}

{/codecitation}

Testing

Test and debug while developping raw socket is not easy, as not every one have remote server to test what is going on. And also, when raw packets are not correctly forged, they can be discarded from routers.

What I used, is a combination of different tools, mostly on command-line.

  • tcpdump
  • the server attached (cf server.c)
  • netcat as client but also has server
  • network grep (ngrep), but really close to tcpdump to just see what is going on
  • wireshark

Some examples :

$ nc -u 127.0.0.1 11111 #allow to generate traffic to a server in udp
# tcpdump -i lo -X -vv 'port 9930' -w gaby.dump #save frames to later open it with wireshark for example
# tcpdump -vv -i lo #listen localhost
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
20:54:31.660300 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 128)
    27.0.0.1.12 > localhost.localdomain.9930: [udp sum ok] UDP, length 100

{jcomments on}

As I used lot of different resources, I’m not able to know which I used for what, so here are useful links I’ve saved :

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *