diff --git a/src/libxt_ts3init.c b/src/libxt_ts3init.c index 92da248..16dbbf2 100644 --- a/src/libxt_ts3init.c +++ b/src/libxt_ts3init.c @@ -1,146 +1,146 @@ -/* - * "ts3init" match extension for iptables - * Niels Werensteijn , 2016-10-03 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License; either version 2 - * or 3 of the License, as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ts3init_random_seed.h" -#include "ts3init_match.h" - -#define param_act(t, s, f) xtables_param_act((t), "ts3init", (s), (f)) -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) - -static void ts3init_help(void) -{ - printf( - "ts3init match options:\n" - " --client Match ts3init client packets.\n" - " --server Match ts3init server packets.\n" - " --command Match packets with the specified command.\n" - ); -} - -static const struct option ts3init_opts[] = { - {.name = "client", .has_arg = false, .val = '1'}, - {.name = "server", .has_arg = false, .val = '2'}, - {.name = "command", .has_arg = true, .val = '3'}, - {NULL}, -}; - -static int ts3init_parse(int c, char **argv, int invert, unsigned int *flags, - const void *entry, struct xt_entry_match **match) -{ - struct xt_ts3init_mtinfo *info = (void *)(*match)->data; - int command; - - switch (c) { - case '1': - param_act(XTF_ONLY_ONCE, "--client", info->specific_options & CHK_TS3INIT_CLIENT); - param_act(XTF_NO_INVERT, "--client", invert); - info->specific_options |= CHK_TS3INIT_CLIENT; - *flags |= CHK_TS3INIT_CLIENT; - return true; - - case '2': - param_act(XTF_ONLY_ONCE, "--server", info->specific_options & CHK_TS3INIT_SERVER); - param_act(XTF_NO_INVERT, "--server", invert); - info->specific_options |= CHK_TS3INIT_SERVER; - *flags |= CHK_TS3INIT_SERVER; - return true; - - case '3': - param_act(XTF_ONLY_ONCE, "--command", info->specific_options & CHK_TS3INIT_COMMAND); - param_act(XTF_NO_INVERT, "--command", invert); - command = atoi(optarg); - if (command < 0 || command > 255) - xtables_error(PARAMETER_PROBLEM, - "ts3init: invalid command number"); - info->specific_options |= CHK_TS3INIT_COMMAND; - info->command = (__u8)command; - *flags |= CHK_TS3INIT_COMMAND; - return true; - - default: - return false; - } -} - -static void ts3init_save(const void *ip, const struct xt_entry_match *match) -{ - const struct xt_ts3init_mtinfo *info = (const void *)match->data; - if (info->specific_options & CHK_TS3INIT_CLIENT) - { - printf(" --client"); - } - if (info->specific_options & CHK_TS3INIT_SERVER) - { - printf(" --server"); - } - if (info->specific_options & CHK_TS3INIT_COMMAND) - { - printf(" --command %i", (int)info->command); - } -} - -static void ts3init_print(const void *ip, const struct xt_entry_match *match, - int numeric) -{ - printf(" -m ts3init"); - ts3init_save(ip, match); -} - -static void ts3init_check(unsigned int flags) -{ - bool client = flags & CHK_TS3INIT_CLIENT; - bool server = flags & CHK_TS3INIT_SERVER; - if (client && server) - { - xtables_error(PARAMETER_PROBLEM, - "ts3init_: --client and --server can not be specified at the same time"); - } - if (flags & CHK_TS3INIT_COMMAND) - { - if (!client && !server) - { - xtables_error(PARAMETER_PROBLEM, - "ts3init: --command requires either --client or --server"); - } - } -} - -/* register and init */ -static struct xtables_match ts3init_mt_reg[] = -{ - { - .name = "ts3init", - .revision = 0, - .family = NFPROTO_UNSPEC, - .version = XTABLES_VERSION, - .size = XT_ALIGN(sizeof(struct xt_ts3init_mtinfo)), - .userspacesize = XT_ALIGN(sizeof(struct xt_ts3init_mtinfo)), - .help = ts3init_help, - .parse = ts3init_parse, - .print = ts3init_print, - .save = ts3init_save, - .extra_opts = ts3init_opts, - .final_check = ts3init_check, - }, -}; - -static __attribute__((constructor)) void ts3init_mt_ldr(void) -{ - xtables_register_matches(ts3init_mt_reg, ARRAY_SIZE(ts3init_mt_reg)); -} +/* + * "ts3init" match extension for iptables + * Niels Werensteijn , 2016-10-03 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License; either version 2 + * or 3 of the License, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ts3init_random_seed.h" +#include "ts3init_match.h" + +#define param_act(t, s, f) xtables_param_act((t), "ts3init", (s), (f)) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +static void ts3init_help(void) +{ + printf( + "ts3init match options:\n" + " --client Match ts3init client packets.\n" + " --server Match ts3init server packets.\n" + " --command Match packets with the specified command.\n" + ); +} + +static const struct option ts3init_opts[] = { + {.name = "client", .has_arg = false, .val = '1'}, + {.name = "server", .has_arg = false, .val = '2'}, + {.name = "command", .has_arg = true, .val = '3'}, + {NULL}, +}; + +static int ts3init_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_ts3init_mtinfo *info = (void *)(*match)->data; + int command; + + switch (c) { + case '1': + param_act(XTF_ONLY_ONCE, "--client", info->specific_options & CHK_TS3INIT_CLIENT); + param_act(XTF_NO_INVERT, "--client", invert); + info->specific_options |= CHK_TS3INIT_CLIENT; + *flags |= CHK_TS3INIT_CLIENT; + return true; + + case '2': + param_act(XTF_ONLY_ONCE, "--server", info->specific_options & CHK_TS3INIT_SERVER); + param_act(XTF_NO_INVERT, "--server", invert); + info->specific_options |= CHK_TS3INIT_SERVER; + *flags |= CHK_TS3INIT_SERVER; + return true; + + case '3': + param_act(XTF_ONLY_ONCE, "--command", info->specific_options & CHK_TS3INIT_COMMAND); + param_act(XTF_NO_INVERT, "--command", invert); + command = atoi(optarg); + if (command < 0 || command > 255) + xtables_error(PARAMETER_PROBLEM, + "ts3init: invalid command number"); + info->specific_options |= CHK_TS3INIT_COMMAND; + info->command = (__u8)command; + *flags |= CHK_TS3INIT_COMMAND; + return true; + + default: + return false; + } +} + +static void ts3init_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_ts3init_mtinfo *info = (const void *)match->data; + if (info->specific_options & CHK_TS3INIT_CLIENT) + { + printf(" --client"); + } + if (info->specific_options & CHK_TS3INIT_SERVER) + { + printf(" --server"); + } + if (info->specific_options & CHK_TS3INIT_COMMAND) + { + printf(" --command %i", (int)info->command); + } +} + +static void ts3init_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + printf(" -m ts3init"); + ts3init_save(ip, match); +} + +static void ts3init_check(unsigned int flags) +{ + bool client = flags & CHK_TS3INIT_CLIENT; + bool server = flags & CHK_TS3INIT_SERVER; + if (client && server) + { + xtables_error(PARAMETER_PROBLEM, + "ts3init_: --client and --server can not be specified at the same time"); + } + if (flags & CHK_TS3INIT_COMMAND) + { + if (!client && !server) + { + xtables_error(PARAMETER_PROBLEM, + "ts3init: --command requires either --client or --server"); + } + } +} + +/* register and init */ +static struct xtables_match ts3init_mt_reg[] = +{ + { + .name = "ts3init", + .revision = 0, + .family = NFPROTO_UNSPEC, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_ts3init_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_ts3init_mtinfo)), + .help = ts3init_help, + .parse = ts3init_parse, + .print = ts3init_print, + .save = ts3init_save, + .extra_opts = ts3init_opts, + .final_check = ts3init_check, + }, +}; + +static __attribute__((constructor)) void ts3init_mt_ldr(void) +{ + xtables_register_matches(ts3init_mt_reg, ARRAY_SIZE(ts3init_mt_reg)); +} diff --git a/test/torture.py b/test/torture.py index 81a71a7..dc3c8f6 100644 --- a/test/torture.py +++ b/test/torture.py @@ -1,141 +1,141 @@ -""" -Generates as fast as possible COMMAND_GET_COOKIE packets to see how well -the TeamSpeak 3 Server can withstand a simple DOS attack. - -It has two modes: -* a testing mode that checks that the server continues to reply with correct - messages, even when under heavy load. -* a spoofing mode, where answers are not expected or waited for, but the source - ip and port are spoofed, in order to trick simple filtering. -""" -from socket import socket, getaddrinfo, IPPROTO_UDP, AF_INET, SOCK_RAW, IPPROTO_RAW, inet_aton -from argparse import ArgumentParser -from select import select -from random import randint -from itertools import repeat -from struct import pack, unpack_from -from time import time -from sys import version_info, exit - -if version_info < (3,0): - print('python3 required.') - exit(1) - -parser = ArgumentParser(description='Tests if the ts3init module, can withstand heavy loads.') -parser.add_argument('host', help='target host') -parser.add_argument('--port', type=int, default=9987, help='target port') -parser.add_argument('--count', type=int, default=100000, help='number of packets to send') -parser.add_argument('--response', type=int, default=1, help='what command number is expected to be returned from the server') -parser.add_argument('--spoof', action='store_const', const=True, default=False, help='should the source address be spoofed') -parser.add_argument('--version', type=int, default=1459504131, help='version number send to the server') -args = parser.parse_args() - -def generateSpoofedHeader(dest_address, dest_port, payload): - # checksum functions needed for calculation checksum - def checksum(msg): - s = 0 - for i in range(0, len(msg), 2): - w = msg[i+1] + (msg[i] << 8) - s = s + w - s = (s>>16) + (s & 0xffff); - s = s + (s >> 16); - s = ~s & 0xffff - return s - - source_address = randint(0, (1 << 32) - 1) - source_port = randint(0, (1 << 16) - 1) - udp_length = 8 + len(payload) - udp_checksum = checksum(pack('!I4sxBHHHH2x', - source_address , dest_address, IPPROTO_UDP, udp_length, - source_port, dest_port, udp_length) + payload) - if udp_checksum == 0: - udp_checksum = (1 << 16) - 1 - - return pack('!BBHHHBBHI4sHHHH', - (4 << 4) + 5, # Version, IHL - 0, # TOS - 0, # Total Length, kernel will fill the correct total length - 0, # Identification - 0, # Fragment Offset - 255, # TTL - IPPROTO_UDP, # Protocol - 0, # Header checksum, kernel will fill the correct checksum - source_address, # Source Address - dest_address, # Destination Address - source_port, # Source Port - dest_port, # Destination Port - udp_length, # UDP Length - udp_checksum); # UDP Checksum - -def generatePayload(version): - number = randint(0, (1 << 32) - 1) - return pack('!8sHHBIBII8x', - b'TS3INIT1', # Literal - 101, # Packet ID - 0, # Client ID - 0x88, # Flags, - version, # Version - 0, # Command - int(time()), # Timestamp - number); # Random-Sequence - -def validateResponse(answer, expectedCommand): - (literal, packet_id, flags, command) = unpack_from('!8sHBB', answer) - return (literal == 'TS3INIT1' - and packet_id == 101 - and flags == 0x88 - and command == expectedCommand) - -target = getaddrinfo(args.host, args.port, 0, 0, IPPROTO_UDP)[0] -print("Sending %i packets to %s:%i..." % (args.count, *target[4])) - -if not args.spoof: - send = 0 - sendErrors = 0 - recieved = 0 - invalid = 0 - sock = socket(*target[0:3]) - try: - sock.connect(target[4]) - sock.setblocking(False) - finished_writing = False - while True: - (canRead, canWrite, _) = select([sock.fileno()], [sock.fileno()] if not args.spoof and send < args.count else [] , [], 1) - if canRead: - data = sock.recv(128) - if not validateResponse(data, args.response): - invalid += 1 - recieved += 1 - if canWrite: - try: - packet = generatePayload(args.version) - sock.send(packet) - send += 1 - except: - sendErrors += 1 - if not canRead and not canWrite: - break - finally: - sock.close() - print("send: %i(errors: %i); recieved: %i; invalid: %i" % (send, sendErrors, recieved, invalid)) -else: - send = 0 - sendErrors = 0 - sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW) - try: - dest_address = inet_aton(target[4][0]) - dest_port = target[4][1] - - for _ in repeat(None, args.count): - try: - payload = generatePayload(args.version) - packet = generateSpoofedHeader(dest_address, dest_port, payload) + payload - select([sock.fileno()], [] , [], 0) - sock.sendto(packet, target[4]) - send += 1 - except BaseException as e: - print(e) - sendErrors += 1 - finally: - sock.close() - print("send: %i(errors: %i);" % (send, sendErrors)) +""" +Generates as fast as possible COMMAND_GET_COOKIE packets to see how well +the TeamSpeak 3 Server can withstand a simple DOS attack. + +It has two modes: +* a testing mode that checks that the server continues to reply with correct + messages, even when under heavy load. +* a spoofing mode, where answers are not expected or waited for, but the source + ip and port are spoofed, in order to trick simple filtering. +""" +from socket import socket, getaddrinfo, IPPROTO_UDP, AF_INET, SOCK_RAW, IPPROTO_RAW, inet_aton +from argparse import ArgumentParser +from select import select +from random import randint +from itertools import repeat +from struct import pack, unpack_from +from time import time +from sys import version_info, exit + +if version_info < (3,0): + print('python3 required.') + exit(1) + +parser = ArgumentParser(description='Tests if the ts3init module, can withstand heavy loads.') +parser.add_argument('host', help='target host') +parser.add_argument('--port', type=int, default=9987, help='target port') +parser.add_argument('--count', type=int, default=100000, help='number of packets to send') +parser.add_argument('--response', type=int, default=1, help='what command number is expected to be returned from the server') +parser.add_argument('--spoof', action='store_const', const=True, default=False, help='should the source address be spoofed') +parser.add_argument('--version', type=int, default=1459504131, help='version number send to the server') +args = parser.parse_args() + +def generateSpoofedHeader(dest_address, dest_port, payload): + # checksum functions needed for calculation checksum + def checksum(msg): + s = 0 + for i in range(0, len(msg), 2): + w = msg[i+1] + (msg[i] << 8) + s = s + w + s = (s>>16) + (s & 0xffff); + s = s + (s >> 16); + s = ~s & 0xffff + return s + + source_address = randint(0, (1 << 32) - 1) + source_port = randint(0, (1 << 16) - 1) + udp_length = 8 + len(payload) + udp_checksum = checksum(pack('!I4sxBHHHH2x', + source_address , dest_address, IPPROTO_UDP, udp_length, + source_port, dest_port, udp_length) + payload) + if udp_checksum == 0: + udp_checksum = (1 << 16) - 1 + + return pack('!BBHHHBBHI4sHHHH', + (4 << 4) + 5, # Version, IHL + 0, # TOS + 0, # Total Length, kernel will fill the correct total length + 0, # Identification + 0, # Fragment Offset + 255, # TTL + IPPROTO_UDP, # Protocol + 0, # Header checksum, kernel will fill the correct checksum + source_address, # Source Address + dest_address, # Destination Address + source_port, # Source Port + dest_port, # Destination Port + udp_length, # UDP Length + udp_checksum); # UDP Checksum + +def generatePayload(version): + number = randint(0, (1 << 32) - 1) + return pack('!8sHHBIBII8x', + b'TS3INIT1', # Literal + 101, # Packet ID + 0, # Client ID + 0x88, # Flags, + version, # Version + 0, # Command + int(time()), # Timestamp + number); # Random-Sequence + +def validateResponse(answer, expectedCommand): + (literal, packet_id, flags, command) = unpack_from('!8sHBB', answer) + return (literal == 'TS3INIT1' + and packet_id == 101 + and flags == 0x88 + and command == expectedCommand) + +target = getaddrinfo(args.host, args.port, 0, 0, IPPROTO_UDP)[0] +print("Sending %i packets to %s:%i..." % (args.count, *target[4])) + +if not args.spoof: + send = 0 + sendErrors = 0 + recieved = 0 + invalid = 0 + sock = socket(*target[0:3]) + try: + sock.connect(target[4]) + sock.setblocking(False) + finished_writing = False + while True: + (canRead, canWrite, _) = select([sock.fileno()], [sock.fileno()] if not args.spoof and send < args.count else [] , [], 1) + if canRead: + data = sock.recv(128) + if not validateResponse(data, args.response): + invalid += 1 + recieved += 1 + if canWrite: + try: + packet = generatePayload(args.version) + sock.send(packet) + send += 1 + except: + sendErrors += 1 + if not canRead and not canWrite: + break + finally: + sock.close() + print("send: %i(errors: %i); recieved: %i; invalid: %i" % (send, sendErrors, recieved, invalid)) +else: + send = 0 + sendErrors = 0 + sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW) + try: + dest_address = inet_aton(target[4][0]) + dest_port = target[4][1] + + for _ in repeat(None, args.count): + try: + payload = generatePayload(args.version) + packet = generateSpoofedHeader(dest_address, dest_port, payload) + payload + select([sock.fileno()], [] , [], 0) + sock.sendto(packet, target[4]) + send += 1 + except BaseException as e: + print(e) + sendErrors += 1 + finally: + sock.close() + print("send: %i(errors: %i);" % (send, sendErrors))