Raw sockets.

By | November 7, 2021

Approximately year ago I wrote an article “Wiresharking TCP Split Handshake Attack“. Normally client/server application written on socket level does not permit to split handshaking. For example TCP client connect function initiates a connection on a socket and performs all 3 handshaking step. Handshaking process is hidden in Transport layer of ISO model. To manipulate with handshaking signals application must be able to send and receive raw packets and for this purpose raw socket is used.
There is opinion that raw socket is source of malicious software. Yes it is true, but it the same time raw socket opens opportunity to learn protocol internals, experimenting, test firewall and routers.
Here is c++ example of raw socket application (rawsock.cpp) which sends SYN signal to server and does not wait for ACK-SYN response, besides it specifies real source IP with loopback IP address – 127.0 0.1:


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int n, char ** s)
{
  nbsp;if(n<3)
  nbsp;{
  nbsp;  nbsp;printf("Arguments are required: destination poprt number and destination port, spsce separated, order is important\n");
  nbsp;  nbsp;return -1;
  nbsp;}
  nbsp;int destport = atoi(s[2]);
  nbsp;printf("Destination end point: %s:%d\n", s[1], destport);
  nbsp;int sckt;
  nbsp;struct sockaddr_in saddr;
  nbsp;struct send_tcp
  nbsp;{
  nbsp;  nbsp;struct iphdr ip;
  nbsp;  nbsp;struct tcphdr tcp;
  nbsp;} packtcp;

  nbsp;packtcp.ip.version = 4; /* version of IP used */
  nbsp;packtcp.ip.ihl = 5; /* Internet Header Length (IHL) */
  nbsp;packtcp.ip.tos = 0; /* Type Of Service (TOS) */
  nbsp;packtcp.ip.tot_len = htons(40); /* total length of the IP datagram */
  nbsp;packtcp.ip.id = 1; /* identification */
  nbsp;packtcp.ip.frag_off = 0; /* fragmentation flag */
  nbsp;packtcp.ip.ttl = 255; /* Time To Live (TTL) */
  nbsp;packtcp.ip.protocol = IPPROTO_TCP; /* protocol used (TCP in this case) */
  nbsp;packtcp.ip.check = 14536; /* IP checksum */
  nbsp;packtcp.ip.saddr = inet_addr("127.0.0.1"); /* source address */
  nbsp;packtcp.ip.daddr = inet_addr(s[1]); /* destination address */

  nbsp;packtcp.tcp.source = htons(2000); /* source port */
  nbsp;packtcp.tcp.dest = htons(destport); /* destination port */
  nbsp;packtcp.tcp.seq = 1; /* sequence number */
  nbsp;packtcp.tcp.ack_seq = 2; /* acknowledgement number */
  nbsp;packtcp.tcp.doff = 5; /* data offset */
  nbsp;packtcp.tcp.res1 = 0; /* reserved for future use (must be 0) */
  nbsp;packtcp.tcp.fin = 0; /* FIN flag */
  nbsp;packtcp.tcp.syn = 1; /* SYN flag */
  nbsp;packtcp.tcp.rst = 0; /* RST flag */
  nbsp;packtcp.tcp.psh = 0; /* PSH flag */
  nbsp;packtcp.tcp.ack = 0; /* ACK flag */
  nbsp;packtcp.tcp.urg = 0; /* URG flag */
  nbsp;packtcp.tcp.res2 = 0; /* reserved (must be 0) */
  nbsp;packtcp.tcp.window = htons(512); /* window */
  nbsp;packtcp.tcp.check = 8889; /* TCP checksum */
  nbsp;packtcp.tcp.urg_ptr = 0; /* urgent pointer */

  nbsp;saddr.sin_family = AF_INET;
  nbsp;saddr.sin_port = htons(destport);
  nbsp;saddr.sin_addr.s_addr = inet_addr(s[1]);

  nbsp;sckt = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  nbsp;if(sckt == -1)
  nbsp;{
  nbsp;  nbsp;printf("Error code: %d\n", errno);
  nbsp;  nbsp;perror("socket error");
  nbsp;  nbsp;return -1;
  nbsp;}

  nbsp;if(sendto(sckt, &packtcp, sizeof(packtcp), 0, (struct sockaddr *)&saddr, sizeof(saddr)) == -1)
  nbsp;if(sckt == -1)
  nbsp;{
  nbsp;  nbsp;close(sckt);
  nbsp;printf("Error code: %d\n", errno);
  nbsp;perror("sendto error");
  nbsp;return -1;
  nbsp;}

  nbsp;close(sckt);
  nbsp;return 0;
}

Testing using tcpdump:
From one Linux box I ran 3 time rawsock application:


# ./rawsock 192.168.2.59 22
Destination end point: 192.168.2.59:22
# ./rawsock 192.168.2.59 22
Destination end point: 192.168.2.59:22
# ./rawsock 192.168.2.59 22
Destination end point: 192.168.2.59:22

Other Linux box tcpdump captured SYN signals:


# tcpdump -n src 127.0.0.1 && ‘tcp[13] & 12!=0’ && ‘ip[8]==255’
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens160, link-type EN10MB (Ethernet), capture size 262144 bytes
16:22:44.486301 IP 127.0.0.1.2000 > 192.168.2.59.22: Flags [S], seq 16777216, win 512, length 0
16:22:45.588872 IP 127.0.0.1.2000 > 192.168.2.59.22: Flags [S], seq 16777216, win 512, length 0
16:22:46.514978 IP 127.0.0.1.2000 > 192.168.2.59.22: Flags [S], seq 16777216, win 512, length 0

Explanation of tcpdump parameters:

Parameter Line of c++ code Comment
src 127.0.0.1 inet_addr("127.0.0.1");  
tcp[13] & 12!=0 packtcp.tcp.syn = 1; rawsock sends SYN only, tcpdump checks SYN and ACK
ip[8]==255 packtcp.ip.ttl = 255; tcpdump verifies also TTL

Leave a Reply

Your email address will not be published. Required fields are marked *