Reputation: 667
TL;DR
I can already read packets leaving and entering my system. I can also send raw packets. Now I want to, instead of just being able to read, have the power to change incoming/outgoing.
I have used the AF_PACKET
, SOCK_RAW
and htons(ETH_P_ALL)
mix to be able to see all packets that leave or arrive at my system. I've also made a little program to print information about ethernet frame and underlying protocols (I'm on Ubuntu 16.04.3 64-bit using gcc 5.4.0):
#include <linux/if_packet.h>
#include <netinet/ether.h>
#include <net/ethernet.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include "/home/bar/Libraries/Colors.h" //color macros (cdefault for default terminal color)
#include "/home/bar/Libraries/BitOperations.h" //Get8/Get16/Get32 (value, from, to)
#include "/home/bar/Libraries/EndianConversions.h" //ToLE16/ToLE32 (value)
#include "/home/bar/Libraries/PacketProtocolDefinitions.h" //network structs and defines (EthernetFrame/IpHeader/TCPHeader/UDP_HeaderLength/ICMP_Descriptors/etc..)
#define BuffLen 0x10000
char iface_count;
char** iface_list;
void workPackets(char* buffer, unsigned int BufferLen){
struct EthernetFrame* ethernetFrame = (struct EthernetFrame*)buffer;
char isMine = 0;
for (int i = 0; i < iface_count; ++i){
if(!memcmp(iface_list[i], ethernetFrame->source, 6) || !memcmp(iface_list[i], ethernetFrame->destination, 6)){isMine = 1;break;}
}
printf("%s Ethernet Frame: \n\
Source Mac: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n\
Destination Mac: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n\
Ether Protocol: 0x%.4X\n"cdefault,
isMine?magenta:green,
ethernetFrame->source[0], ethernetFrame->source[1], ethernetFrame->source[2], ethernetFrame->source[3], ethernetFrame->source[4], ethernetFrame->source[5],
ethernetFrame->destination[0], ethernetFrame->destination[1], ethernetFrame->destination[2], ethernetFrame->destination[3], ethernetFrame->destination[4], ethernetFrame->destination[5],
ToLE16(ethernetFrame->ethertype));
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////-------------------IP---------------//////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
if (ToLE16(ethernetFrame->ethertype) == ethertype_IP)
{
struct IpHeader* ipheader = (struct IpHeader*)(buffer+14);
printf(cyan" IP header:\n\
Source IP: %d.%d.%d.%d\n\
Destination IP: %d.%d.%d.%d\n\
Total Size: 0x%X\n\
Protocol: 0x%X\n"cdefault,
ipheader->source[0], ipheader->source[1], ipheader->source[2], ipheader->source[3],
ipheader->destination[0], ipheader->destination[1], ipheader->destination[2], ipheader->destination[3],
ToLE16(ipheader->TotalLength), ipheader->Protocol);
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
//<<<<<<<<<<<<<<-------------TCP----------<<<<<<<<<<<<<//
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
if(ipheader->Protocol == IP_PROTO_TCP){
struct TCPHeader* tcpheader = (struct TCPHeader*)((void*)ipheader+(ipheader->Version__IHL&0xF)*4);
printf(cyan" TCP header:\n\
Source Port: %u\n\
Destination Port: %u\n\
Flags:\n\
SYN: %s\n\
ACK: %s\n\
FIN: %s\n\
Data Offset: 0x%X\n\
Sequence Number: %u\n"cdefault,
tcpheader->source_port, tcpheader->destination_port,
tcpheader->flags.SYN?"SET":"Zero",
tcpheader->flags.ACK?"SET":"Zero",
tcpheader->flags.FIN?"SET":"Zero",
Get8(tcpheader->data_offset__NS, 0, 4), tcpheader->sequence_number);
char* toPrint = (char*)tcpheader;
unsigned short packet_size = ToLE16(ipheader->TotalLength) - Get8(ipheader->Version__IHL, 4, 8)*4 - Get8(tcpheader->data_offset__NS, 0, 4)*4;
printf("data(packet size:0x%.2X): %*.*s", packet_size, packet_size, packet_size, toPrint + Get8(tcpheader->data_offset__NS, 0, 4)*4);
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
//<<<<<<<<<<<<<<-------------UDP----------<<<<<<<<<<<<<//
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
}else if (ipheader->Protocol == IP_PROTO_UDP){
struct UDPHeader* udpheader = (struct UDPHeader*)((void*)ipheader+(ipheader->Version__IHL&0xF)*4);
printf(cyan" UDP header:\n\
Source Port: %u\n\
Destination Port: %u\n\
Length: 0x%X\n"cdefault,
ToLE16(udpheader->source_port), ToLE16(udpheader->destination_port), ToLE16(udpheader->length));
void* toPrint = (void*)udpheader;
unsigned short packet_size = ToLE16(udpheader->length) - UDP_HeaderLength;
printf("data(packet size:0x%.2X) %p: %*.*s",packet_size, toPrint, packet_size, packet_size, (char*)toPrint+UDP_HeaderLength);
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
//<<<<<<<<<<<<<<------------ICMP----------<<<<<<<<<<<<<//
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
}else if(ipheader->Protocol == IP_PROTO_ICMP){
struct ICMPHeader* icmpheader = (struct ICMPHeader*)((void*)ipheader+(ipheader->Version__IHL&0xF)*4);
printf(red" ICMP packet: %s\n"cdefault, ICMP_Descriptors[icmpheader->type][icmpheader->code]);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////------------------ARP---------------//////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
}else if (ToLE16(ethernetFrame->ethertype) == ethertype_ARP){
struct ARPframe* arpframe = (struct ARPframe*)(buffer+14);
printf(yellow" ARP %s:\n\
Sender Mac: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n\
Sender IP: %d.%d.%d.%d\n\
Target IP: %d.%d.%d.%d\n\
Target Mac: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n"cdefault,
ToLE16(arpframe->Operation)==2?"reply":"request",
arpframe->SenderMac[0], arpframe->SenderMac[1], arpframe->SenderMac[2], arpframe->SenderMac[3], arpframe->SenderMac[4], arpframe->SenderMac[5],
arpframe->SenderIP[0], arpframe->SenderIP[1], arpframe->SenderIP[2], arpframe->SenderIP[3],
arpframe->TargetIP[0], arpframe->TargetIP[1], arpframe->TargetIP[2], arpframe->TargetIP[3],
arpframe->TargetMac[0], arpframe->TargetMac[1], arpframe->TargetMac[2], arpframe->TargetMac[3], arpframe->TargetMac[4], arpframe->TargetMac[5]);
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////-----------------SIZE---------------//////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
}else if (ToLE16(ethernetFrame->ethertype) <= 0x5DC){
if(!memcmp(ethernetFrame->source, MAC_STPorLLDP, 6)||!memcmp(ethernetFrame->destination, MAC_STPorLLDP, 6)){
if(ethernetFrame->ethertype != ethertype_LLDP){
printf(blue" STP Packet\n");
}else{
printf(blue" LLDP Packet\n");
}
}else{
printf(red" Unknown Packet Type: Provided Packet Size 0x%.4X:\n ", ToLE16(ethernetFrame->ethertype));
for (int i = 0; i < BufferLen; ++i)
{
printf("%.2X ", buffer[i]&0xFF);
if(!((i+1)%6)){
printf("\n ");
}
}
}
printf(cdefault);
//////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////-----------------IPv6---------------//////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////
}else if(ToLE16(ethernetFrame->ethertype) == ethertype_IPv6){
printf(blue" IPv6 Packet\n"cdefault);
}
return;
}
int main(int argc, char const *argv[])
{
int sock_r;
unsigned char *buffer = (unsigned char *) malloc(BuffLen); //to receive data
memset(buffer,0,BuffLen);
struct sockaddr saddr;
int saddr_len = sizeof (saddr), buflen;
//--------------------------------------------Get My Macs
struct ifaddrs *ifap, *ifaptr;
unsigned char *ptr;
if (getifaddrs(&ifap) == 0) {
for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next)
if(ifaptr->ifa_addr->sa_family == AF_PACKET)
iface_count++;
iface_list = malloc(iface_count*sizeof(void*));
char tempcount = 0;
for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
if(ifaptr->ifa_addr->sa_family == AF_PACKET){
iface_list[tempcount] = malloc(6);
memcpy(iface_list[tempcount++], &(ifaptr->ifa_addr->sa_data[10]), 6);
}
}
freeifaddrs(ifap);
} else {
perror("Getifaddrs");
}
printf("My interfaces: \n");
for (int i = 0; i < iface_count; ++i)
{
printf("%d: %.2X %.2X %.2X %.2X %.2X %.2X\n", i,
iface_list[i][0] & 0xFF, iface_list[i][1] & 0xFF, iface_list[i][2] & 0xFF,
iface_list[i][3] & 0xFF, iface_list[i][4] & 0xFF, iface_list[i][5] & 0xFF);
}
//----------------------------------------------
sock_r = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sock_r<0)
{
perror("SOCKET");
return -1;
}printf("socket connected successfully");
while(1){
//Receive a network packet and copy in to buffer
printf("\n//-----------------------//-----------------------//\n");
buflen=recvfrom(sock_r,buffer,BuffLen,0,&saddr,(socklen_t *)&saddr_len);
if(buflen<0)
{
perror("Receiving");
return -1;
}
workPackets(buffer, buflen);
}
for (int i = 0; i < iface_count; ++i){free(iface_list[i]);}
free(iface_list);
return 0;
}
Along with this I have the ability to send spoofed packets with any information I desire, by just building those from the ground up.
That's all good, but now I want to be able to review and possibly change all packets coming in and out of my system.
What I mean by this is:
If I for example wanted to be edgy and spoof my mac to 13:37:AF:CO:DE:B8
(not sure if that's even valid), I could just find mine in the ethernet frame and change it going out, and do the opposite when coming in.
This would give me a lot of flexibility, if I for example wanted to keep my original MAC/IP for TCP and UDP, but have it spoofed for ARP.
Would be cool to fake being 2 or more different systems at the same time, or even, if doing MiTM, route the traffic through manually while being able to do anything else I want.
Now I've been searching for a bit, and what I've found was netfilter_queue, that seems to be deprecated.
I've also found a kernel netfilter, but if possible I would like to avoid kernel stuff, since I've started using linux only recently and that seems like a pretty big step to take.
In windows there were these system-wide hooks for events like input, which were really intuitive and easy to use, and I was just looking for an equivalent.
Note: I'm doing all this for learning purposes, I do not want to use libraries or frameworks.
So my question is: Is there a valid non-kernel way to scoop up and change packets?
Feel free to request any information you may need or criticize me if you consider I've done something wrong.
Thank you.
Upvotes: 2
Views: 590
Reputation: 3584
Have a look at the non-deprecated APIs for netfilter_queue
, specifically the last three entries on the list (Verdict helpers, Config helpers, Netlink message helper functions). This example could also help.
Upvotes: 1