Merge pull request #2213 from packetzero/am_t1040_linux_pcap
Add Linux T1040 Packet Capture using raw sockets and filtering
This commit is contained in:
+119
-1
@@ -1,7 +1,7 @@
|
||||
attack_technique: T1040
|
||||
display_name: Network Sniffing
|
||||
atomic_tests:
|
||||
- name: Packet Capture Linux
|
||||
- name: Packet Capture Linux using tshark or tcpdump
|
||||
auto_generated_guid: 7fe741f7-b265-4951-a7c7-320889083b3e
|
||||
description: |
|
||||
Perform a PCAP. Wireshark will be required for tshark. TCPdump may already be installed.
|
||||
@@ -222,3 +222,121 @@ atomic_tests:
|
||||
rm -f #{program_path}
|
||||
name: bash
|
||||
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: |
|
||||
if [ -f "#{program_path}" ]; then exit 0; else exit 1; fi
|
||||
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: |
|
||||
if [ -f "#{program_path}" ]; then exit 0; else exit 1; fi
|
||||
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: |
|
||||
if [ -f "#{program_path}" ]; then exit 0; else exit 1; fi
|
||||
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: |
|
||||
if [ -f "#{program_path}" ]; then exit 0; else exit 1; fi
|
||||
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
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/filter.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#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'},
|
||||
{"filter", no_argument, NULL, 'f'},
|
||||
{"time", required_argument, NULL, 't'},
|
||||
{0, 0, 0, 0}};
|
||||
|
||||
static void usage(const char *progname) {
|
||||
printf("usage: %s <options>\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 <int value> 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 <num seconds> 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;
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
// instructions have to be modified to offset from IP layer
|
||||
|
||||
printf("ERROR: BPF filter is only setup to work for AF_PACKET\n");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
int sock_protocol_packet = htons(3); // AF_PACKET
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user