From 56a896d90bf874ebe33a417e05ed98f486f1b54f Mon Sep 17 00:00:00 2001 From: Alex M <20775507+packetzero@users.noreply.github.com> Date: Sun, 30 Oct 2022 19:01:59 -0500 Subject: [PATCH 1/4] Add some Linux T1040 packet capture tests using raw sockets --- atomics/T1040/T1040.yaml | 120 +++++++++++++++++++- atomics/T1040/src/linux_pcapdemo.c | 171 +++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 atomics/T1040/src/linux_pcapdemo.c diff --git a/atomics/T1040/T1040.yaml b/atomics/T1040/T1040.yaml index 2fb0a94c..2296d265 100644 --- a/atomics/T1040/T1040.yaml +++ b/atomics/T1040/T1040.yaml @@ -153,4 +153,122 @@ atomic_tests: cleanup_command: |- pktmon filter remove name: command_prompt - elevation_required: true \ No newline at end of file + elevation_required: true +- name: Packet Capture Linux socket AF_PACKET,SOCK_RAW with sudo + description: | + Captures packets with domain=AF_PACKET, type=SOCK_RAW for a few seconds. + supported_platforms: + - linux + input_arguments: + csource_path: + description: Path to C program source + type: String + default: PathToAtomicsFolder/T1040/src/linux_pcapdemo.c + program_path: + description: Path to compiled C program + type: String + default: /tmp/t1040_linux_pcapdemo + dependency_executor_name: bash + dependencies: + - description: | + compile C program + prereq_command: | + exit 1 + get_prereq_command: | + cc #{csource_path} -o #{program_path} + executor: + command: | + sudo #{program_path} -a -t 3 + cleanup_command: | + rm -f #{program_path} + name: bash + elevation_required: true +- name: Packet Capture Linux socket AF_INET,SOCK_RAW,TCP with sudo + description: | + Captures packets with domain=AF_INET,type=SOCK_RAW,protocol=TCP for a few seconds. + supported_platforms: + - linux + input_arguments: + csource_path: + description: Path to C program source + type: String + default: PathToAtomicsFolder/T1040/src/linux_pcapdemo.c + program_path: + description: Path to compiled C program + type: String + default: /tmp/t1040_linux_pcapdemo + dependency_executor_name: bash + dependencies: + - description: | + compile C program + prereq_command: | + exit 1 + get_prereq_command: | + cc #{csource_path} -o #{program_path} + executor: + command: | + sudo #{program_path} -4 -p 6 -t 3 + cleanup_command: | + rm -f #{program_path} + name: bash + elevation_required: true +- name: Packet Capture Linux socket AF_INET,SOCK_PACKET,UDP with sudo + description: | + Captures packets with domain=AF_INET,type=SOCK_PACKET,protocol=UDP for a few seconds. + SOCK_PACKET is "obsolete" according to the man page, but still works on Ubuntu 20.04 + supported_platforms: + - linux + input_arguments: + csource_path: + description: Path to C program source + type: String + default: PathToAtomicsFolder/T1040/src/linux_pcapdemo.c + program_path: + description: Path to compiled C program + type: String + default: /tmp/t1040_linux_pcapdemo + dependency_executor_name: bash + dependencies: + - description: | + compile C program + prereq_command: | + exit 1 + get_prereq_command: | + cc #{csource_path} -o #{program_path} + executor: + command: | + sudo #{program_path} -4 -P -p 17 -t 3 + cleanup_command: | + rm -f #{program_path} + name: bash + elevation_required: true +- name: Packet Capture Linux socket AF_PACKET,SOCK_RAW with BPF filter for UDP with sudo + description: | + Captures packets with domain=AF_PACKET,type=SOCK_RAW for a few seconds. + Sets a BPF filter on the socket to filter for UDP traffic. + supported_platforms: + - linux + input_arguments: + csource_path: + description: Path to C program source + type: String + default: PathToAtomicsFolder/T1040/src/linux_pcapdemo.c + program_path: + description: Path to compiled C program + type: String + default: /tmp/t1040_linux_pcapdemo + dependency_executor_name: bash + dependencies: + - description: | + compile C program + prereq_command: | + exit 1 + get_prereq_command: | + cc #{csource_path} -o #{program_path} + executor: + command: | + sudo #{program_path} -a -f -t 3 + cleanup_command: | + rm -f #{program_path} + name: bash + elevation_required: true diff --git a/atomics/T1040/src/linux_pcapdemo.c b/atomics/T1040/src/linux_pcapdemo.c new file mode 100644 index 00000000..0e608612 --- /dev/null +++ b/atomics/T1040/src/linux_pcapdemo.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include + +#include //Provides declarations for icmp header +#include //Provides declarations for udp header +#include //Provides declarations for tcp header +#include //Provides declarations for ip header +#include +#include + +static const struct option longopts[] = { + { "afpacket", no_argument, NULL, 'a'}, + { "afinet4", no_argument, NULL, '4'}, + { "afinet6", no_argument, NULL, '6'}, + { "protocol", required_argument, NULL, 'p'}, + { "sockpacket", no_argument, NULL, 'P'}, + { "sockraw", no_argument, NULL, 'R'}, + { "time", required_argument, NULL, 't'}, + { 0, 0, 0, 0 } +}; + +static void usage(const char *progname) +{ + printf("usage: %s \n", progname); + printf(" -a --afpacket Set domain to AF_PACKET.\n"); + printf(" -4 --afinet Set domain to AF_INET (default).\n"); + printf(" -6 --afinet6 Set domain to AF_PACKET.\n"); + printf(" -p --protocol Integer value to set as protocol argument. For AF_INET default is IPPROTO_TCP=6. Others:IPPROTO_UDP=17 IPPROTO_ICMP=1\n"); + printf(" -P --sockpacket Set sock_type to SOCK_PACKET.\n"); + printf(" -R --sockraw Set sock_type to SOCK_RAW (default)\n"); + printf(" -t --time Exit after number of seconds. Default is to run until killed.\n"); +} + +void ProcessPacket(unsigned char* buf, int size, int af); + +int sock_raw; +int tcp=0,udp=0,icmp=0,others=0,igmp=0,total=0,i,j; +struct sockaddr_in source,dest; + +int main(int argc, char *argv[]) +{ + int af = AF_INET; + int sock_type = SOCK_RAW; + int sock_protocol_inet = IPPROTO_TCP; + int sock_protocol = sock_protocol_inet; + int timeout = 0; + + int sock_protocol_packet = htons(3); // AF_PACKET + + int saddr_size , data_size; + struct sockaddr saddr; + struct in_addr in; + + unsigned char *buffer = (unsigned char *)malloc(65536); + + int c; + + while(1) + { + int option_index = 0; + + c = getopt_long(argc, argv, "a46p:PRt:", longopts, &option_index); + if (c == -1) + break; + + switch (c) { + case 'a': + af = AF_PACKET; + sock_protocol = sock_protocol_packet; + break; + case '4': + af = AF_INET; + break; + case '6': + af = AF_INET6; + break; + case 'P': + sock_type = SOCK_PACKET; + break; + case 'R': + sock_type = SOCK_RAW; + break; + case 'p': + sock_protocol = atoi(optarg); + printf("using protocol=%d (0x%x)\n", sock_protocol, sock_protocol); + break; + case 't': + timeout = atoi(optarg); + printf("will exit after %d seconds\n", timeout); + break; + default: + printf("invalid argument: '%c'\n", c); + usage(argv[0]); + return -1; + } + } + + printf("Starting...\n"); + + // create RAW socket to capture packets + + sock_raw = socket(af , sock_type , sock_protocol); + if(sock_raw < 0) + { + printf("Socket Error\n"); + return 1; + } + time_t tstop = time(NULL) + timeout; + while(1) + { + int flags = MSG_DONTWAIT; + saddr_size = sizeof saddr; + //Receive a packet + data_size = recvfrom(sock_raw , buffer , 65536 , flags , &saddr , &saddr_size); + if(data_size <0 ) + { + if (EAGAIN == errno || EWOULDBLOCK == errno) { + usleep(15000); + if (timeout > 0 && time(NULL) >= tstop) { + break; + } + continue; + } + + printf("Recvfrom error , failed to get packets\n"); + return 1; + } + + ProcessPacket(buffer , data_size, af); + } + close(sock_raw); + printf("Finished\n"); + return 0; +} + +void ProcessPacket(unsigned char* buffer, int size, int af) +{ + ++total; + + if (AF_PACKET == af) + { + return; // TODO: parse ether + } + + //Get the IP Header part of this packet + struct iphdr *iph = (struct iphdr*)buffer; + switch (iph->protocol) //Check the Protocol and do accordingly... + { + case IPPROTO_ICMP: + ++icmp; + break; + + case IPPROTO_TCP: + ++tcp; + break; + + case IPPROTO_UDP: + ++udp; + break; + + default: + ++others; + break; + } + printf("TCP : %d UDP : %d ICMP : %d Others : %d Total : %d\n",tcp,udp,icmp,others,total); +} From d9f46753de90f6125855c838372e4ce437a4cb47 Mon Sep 17 00:00:00 2001 From: Alex M <20775507+packetzero@users.noreply.github.com> Date: Sun, 30 Oct 2022 20:27:36 -0500 Subject: [PATCH 2/4] linux pcap : Add BPF filter and clang-format --- atomics/T1040/src/linux_pcapdemo.c | 342 ++++++++++++++++------------- 1 file changed, 195 insertions(+), 147 deletions(-) diff --git a/atomics/T1040/src/linux_pcapdemo.c b/atomics/T1040/src/linux_pcapdemo.c index 0e608612..a4da5917 100644 --- a/atomics/T1040/src/linux_pcapdemo.c +++ b/atomics/T1040/src/linux_pcapdemo.c @@ -6,166 +6,214 @@ #include #include -#include //Provides declarations for icmp header -#include //Provides declarations for udp header -#include //Provides declarations for tcp header -#include //Provides declarations for ip header -#include #include +#include +#include +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) static const struct option longopts[] = { - { "afpacket", no_argument, NULL, 'a'}, - { "afinet4", no_argument, NULL, '4'}, - { "afinet6", no_argument, NULL, '6'}, - { "protocol", required_argument, NULL, 'p'}, - { "sockpacket", no_argument, NULL, 'P'}, - { "sockraw", no_argument, NULL, 'R'}, - { "time", required_argument, NULL, 't'}, - { 0, 0, 0, 0 } -}; + {"afpacket", no_argument, NULL, 'a'}, + {"afinet4", no_argument, NULL, '4'}, + {"afinet6", no_argument, NULL, '6'}, + {"protocol", required_argument, NULL, 'p'}, + {"sockpacket", no_argument, NULL, 'P'}, + {"sockraw", no_argument, NULL, 'R'}, + {"filter", no_argument, NULL, 'f'}, + {"time", required_argument, NULL, 't'}, + {0, 0, 0, 0}}; -static void usage(const char *progname) -{ - printf("usage: %s \n", progname); - printf(" -a --afpacket Set domain to AF_PACKET.\n"); - printf(" -4 --afinet Set domain to AF_INET (default).\n"); - printf(" -6 --afinet6 Set domain to AF_PACKET.\n"); - printf(" -p --protocol Integer value to set as protocol argument. For AF_INET default is IPPROTO_TCP=6. Others:IPPROTO_UDP=17 IPPROTO_ICMP=1\n"); - printf(" -P --sockpacket Set sock_type to SOCK_PACKET.\n"); - printf(" -R --sockraw Set sock_type to SOCK_RAW (default)\n"); - printf(" -t --time Exit after number of seconds. Default is to run until killed.\n"); +static void usage(const char *progname) { + printf("usage: %s \n", progname); + printf(" -a --afpacket Set domain to AF_PACKET.\n"); + printf(" -4 --afinet Set domain to AF_INET (default).\n"); + printf(" -6 --afinet6 Set domain to AF_PACKET.\n"); + printf(" -p --protocol Integer value to set as protocol " + "argument. For AF_INET default is IPPROTO_TCP=6. " + "Others:IPPROTO_UDP=17 IPPROTO_ICMP=1\n"); + printf(" -P --sockpacket Set sock_type to SOCK_PACKET.\n"); + printf( + " -R --sockraw Set sock_type to SOCK_RAW (default)\n"); + printf(" -f --filter Adds BPF filter for UDP. Use with " + "AF_PACKET.\n"); + printf(" -t --time Exit after number of seconds. " + "Default is to run until killed.\n"); } -void ProcessPacket(unsigned char* buf, int size, int af); +void ProcessPacket(unsigned char *buf, int size, int af); int sock_raw; -int tcp=0,udp=0,icmp=0,others=0,igmp=0,total=0,i,j; -struct sockaddr_in source,dest; +int tcp = 0, udp = 0, icmp = 0, others = 0, igmp = 0, total = 0, i, j; +struct sockaddr_in source, dest; -int main(int argc, char *argv[]) -{ - int af = AF_INET; - int sock_type = SOCK_RAW; - int sock_protocol_inet = IPPROTO_TCP; - int sock_protocol = sock_protocol_inet; - int timeout = 0; +/* + * Instructions generated using 'sudo tcpdump udp -dd' + * https://andreaskaris.github.io/blog/networking/bpf-and-tcpdump/ + */ +void SetBpfFilter(int sock_raw, int af) { + if (AF_PACKET == af) { + struct sock_filter instructions[] = { + {0x28, 0, 0, 0x0000000c}, {0x15, 0, 5, 0x000086dd}, + {0x30, 0, 0, 0x00000014}, {0x15, 6, 0, 0x00000011}, + {0x15, 0, 6, 0x0000002c}, {0x30, 0, 0, 0x00000036}, + {0x15, 3, 4, 0x00000011}, {0x15, 0, 3, 0x00000800}, + {0x30, 0, 0, 0x00000017}, {0x15, 0, 1, 0x00000011}, + {0x6, 0, 0, 0x00040000}, {0x6, 0, 0, 0x00000000}, + }; + struct sock_fprog filter = {ARRAY_SIZE(instructions), instructions}; + int rv = setsockopt(sock_raw, SOL_SOCKET, SO_ATTACH_FILTER, &filter, + sizeof(filter)); + if (0 > rv) { + printf("Failed to set BPF filter. errcode:%d\n", errno); + } + } else { - int sock_protocol_packet = htons(3); // AF_PACKET + // instructions have to be modified to offset from IP layer - int saddr_size , data_size; - struct sockaddr saddr; - struct in_addr in; - - unsigned char *buffer = (unsigned char *)malloc(65536); - - int c; - - while(1) - { - int option_index = 0; - - c = getopt_long(argc, argv, "a46p:PRt:", longopts, &option_index); - if (c == -1) - break; - - switch (c) { - case 'a': - af = AF_PACKET; - sock_protocol = sock_protocol_packet; - break; - case '4': - af = AF_INET; - break; - case '6': - af = AF_INET6; - break; - case 'P': - sock_type = SOCK_PACKET; - break; - case 'R': - sock_type = SOCK_RAW; - break; - case 'p': - sock_protocol = atoi(optarg); - printf("using protocol=%d (0x%x)\n", sock_protocol, sock_protocol); - break; - case 't': - timeout = atoi(optarg); - printf("will exit after %d seconds\n", timeout); - break; - default: - printf("invalid argument: '%c'\n", c); - usage(argv[0]); - return -1; - } - } - - printf("Starting...\n"); - - // create RAW socket to capture packets - - sock_raw = socket(af , sock_type , sock_protocol); - if(sock_raw < 0) - { - printf("Socket Error\n"); - return 1; - } - time_t tstop = time(NULL) + timeout; - while(1) - { - int flags = MSG_DONTWAIT; - saddr_size = sizeof saddr; - //Receive a packet - data_size = recvfrom(sock_raw , buffer , 65536 , flags , &saddr , &saddr_size); - if(data_size <0 ) - { - if (EAGAIN == errno || EWOULDBLOCK == errno) { - usleep(15000); - if (timeout > 0 && time(NULL) >= tstop) { - break; - } - continue; - } - - printf("Recvfrom error , failed to get packets\n"); - return 1; - } - - ProcessPacket(buffer , data_size, af); - } - close(sock_raw); - printf("Finished\n"); - return 0; + printf("ERROR: BPF filter is only setup to work for AF_PACKET\n"); + } } -void ProcessPacket(unsigned char* buffer, int size, int af) -{ - ++total; +int main(int argc, char *argv[]) { + int af = AF_INET; + int sock_type = SOCK_RAW; + int sock_protocol_inet = IPPROTO_TCP; + int sock_protocol = sock_protocol_inet; + int timeout = 0; + int isBpfFilterEnabled = 0; - if (AF_PACKET == af) - { - return; // TODO: parse ether - } + int sock_protocol_packet = htons(3); // AF_PACKET - //Get the IP Header part of this packet - struct iphdr *iph = (struct iphdr*)buffer; - switch (iph->protocol) //Check the Protocol and do accordingly... - { - case IPPROTO_ICMP: - ++icmp; - break; - - case IPPROTO_TCP: - ++tcp; - break; - - case IPPROTO_UDP: - ++udp; - break; - - default: - ++others; - break; - } - printf("TCP : %d UDP : %d ICMP : %d Others : %d Total : %d\n",tcp,udp,icmp,others,total); + socklen_t saddr_size; + int data_size; + struct sockaddr saddr; + struct in_addr in; + + unsigned char *buffer = (unsigned char *)malloc(65536); + + int c; + + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "a46p:PRft:", longopts, &option_index); + if (c == -1) + break; + + switch (c) { + case 'a': + af = AF_PACKET; + sock_protocol = sock_protocol_packet; + break; + case '4': + af = AF_INET; + break; + case '6': + af = AF_INET6; + break; + case 'P': + sock_type = SOCK_PACKET; + break; + case 'R': + sock_type = SOCK_RAW; + break; + case 'f': + isBpfFilterEnabled = 1; + break; + case 'p': + sock_protocol = atoi(optarg); + printf("using protocol=%d (0x%x)\n", sock_protocol, sock_protocol); + break; + case 't': + timeout = atoi(optarg); + printf("will exit after %d seconds\n", timeout); + break; + default: + printf("invalid argument: '%c'\n", c); + usage(argv[0]); + return -1; + } + } + + printf("Starting...\n"); + + // create RAW socket to capture packets + + sock_raw = socket(af, sock_type, sock_protocol); + if (sock_raw < 0) { + printf("Socket Error\n"); + return 1; + } + + if (isBpfFilterEnabled) { + SetBpfFilter(sock_raw, af); + } + + // loop to capture packets + + time_t tstop = time(NULL) + timeout; + while (1) { + int flags = MSG_DONTWAIT; + saddr_size = sizeof saddr; + // Receive a packet + data_size = recvfrom(sock_raw, buffer, 65536, flags, &saddr, &saddr_size); + if (data_size < 0) { + if (EAGAIN == errno || EWOULDBLOCK == errno) { + usleep(15000); + if (timeout > 0 && time(NULL) >= tstop) { + break; + } + continue; + } + + printf("Recvfrom error , failed to get packets\n"); + return 1; + } + + ProcessPacket(buffer, data_size, af); + } + close(sock_raw); + printf("Finished\n"); + return 0; +} + +void ProcessPacket(unsigned char *buffer, int size, int af) { + unsigned char *pipbuf = buffer; + ++total; + + if (AF_PACKET == af) { + struct ether_header *eh = (struct ether_header *)buffer; + if (ntohs(eh->ether_type) != ETHERTYPE_IP) { + return; + } + pipbuf = buffer + sizeof(struct ether_header); + } + + // Get the IP Header part of this packet + struct iphdr *iph = (struct iphdr *)pipbuf; + switch (iph->protocol) // Check the Protocol and do accordingly... + { + case IPPROTO_ICMP: + ++icmp; + break; + + case IPPROTO_TCP: + ++tcp; + break; + + case IPPROTO_UDP: + ++udp; + break; + + default: + ++others; + break; + } + printf("TCP : %d UDP : %d ICMP : %d Others : %d Total : %d\n", tcp, + udp, icmp, others, total); } From 44826521e65d8c0191fd97da6666737fa62e4b48 Mon Sep 17 00:00:00 2001 From: Alex M <20775507+packetzero@users.noreply.github.com> Date: Sun, 30 Oct 2022 20:31:35 -0500 Subject: [PATCH 3/4] rename existing linux capture test --- atomics/T1040/T1040.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atomics/T1040/T1040.yaml b/atomics/T1040/T1040.yaml index 2296d265..e3a2e7f3 100644 --- a/atomics/T1040/T1040.yaml +++ b/atomics/T1040/T1040.yaml @@ -1,7 +1,7 @@ attack_technique: T1040 display_name: Network Sniffing atomic_tests: -- name: Packet Capture Linux +- name: Packet Capture Linux using tcpdump or tshark auto_generated_guid: 7fe741f7-b265-4951-a7c7-320889083b3e description: | Perform a PCAP. Wireshark will be required for tshark. TCPdump may already be installed. From f6004e7d91fb950266b3b514eceee2bcfb608e6b Mon Sep 17 00:00:00 2001 From: packetzero <20775507+packetzero@users.noreply.github.com> Date: Fri, 4 Nov 2022 16:56:11 -0500 Subject: [PATCH 4/4] fix prerequisite checks --- atomics/T1040/T1040.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/atomics/T1040/T1040.yaml b/atomics/T1040/T1040.yaml index e3a2e7f3..275cb6c4 100644 --- a/atomics/T1040/T1040.yaml +++ b/atomics/T1040/T1040.yaml @@ -173,7 +173,7 @@ atomic_tests: - description: | compile C program prereq_command: | - exit 1 + if [ -f "#{program_path}" ]; then exit 0; else exit 1; fi get_prereq_command: | cc #{csource_path} -o #{program_path} executor: @@ -202,7 +202,7 @@ atomic_tests: - description: | compile C program prereq_command: | - exit 1 + if [ -f "#{program_path}" ]; then exit 0; else exit 1; fi get_prereq_command: | cc #{csource_path} -o #{program_path} executor: @@ -232,7 +232,7 @@ atomic_tests: - description: | compile C program prereq_command: | - exit 1 + if [ -f "#{program_path}" ]; then exit 0; else exit 1; fi get_prereq_command: | cc #{csource_path} -o #{program_path} executor: @@ -262,7 +262,7 @@ atomic_tests: - description: | compile C program prereq_command: | - exit 1 + if [ -f "#{program_path}" ]; then exit 0; else exit 1; fi get_prereq_command: | cc #{csource_path} -o #{program_path} executor: