Port scanning without an IP address

Re-evaluating how some actions are performed can sometimes lead to new insights, which is exactly the reason for this blog post. Be aware that I’ve only tested this on two ‘test’ networks, so I cannot guarantee this will always work. Worst scenario you’ll read an (hopefully) out-of-the-box blog entry about an alternative port scan method that maybe only works in weird corner cases. The source for the script can be found on my gist, if you prefer to skip my ramblings and jump directly to the source.

One of the things I usually do is sniff traffic on the network that I am connected to with either my laptop or a drop device. At that point the output of the ifconfig command usually looks similar to this:

 eth0 Link encap:Ethernet HWaddr 00:0c:29:4b:e7:35 
 inet6 addr: fe80::20c:29ff:fe4b:e735/64 Scope:Link
 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
 RX packets:386316 errors:0 dropped:0 overruns:0 frame:0
 TX packets:25286 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000 
 RX bytes:390745367 (390.7 MB) TX bytes:4178071 (4.1 MB)

Like you will notice the interface has no IPv4 IP address assigned, you can ignore the IPv6 address for now. Normally I determine which IP address or MAC address to clone based on the traffic that I captured and analysed previously. Then I’m all set to start port scanning or performing other type of attacks.

This time however I wondered what type of activities I could perform without an IP address. I mean it would be pretty interesting to talk IP to devices, somehow see a response and not be traceable, right? So I decided to see if it would for example be possible to perform a port scan on the network without having an IP address configured on my network interface.

Since usually when you want to perform non-standard, weird or nifty tricks with TCP/IP you have to resort to raw sockets I decided to directly jump to scapy to build a POC. My working theory was as follow:

Normally when I am just sniffing traffic I see all kind of traffic that gets send to the broadcast address, so what if we perform a port scan and we specify the broadcast address as the source?

I decided to test this using two virtual machine (ubuntu & Windows 10) with the network settings on ‘NAT’ and also tested with the same virtual machines while bridged to a physical network. The following oneliners can be used to transmit the raw packet:

pkt = Ether(dst='00:0c:29:f6:a5:65',src='00:08:19:2c:e0:15') / IP(dst='172.16.218.178',src='172.16.218.255') / TCP(dport=445,flags='S')
sendp(pkt,iface='eth0')

Running tcpdump will confirm if this works or not, moment of truth:

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
23:27:21.903583 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto TCP (6), length 40)
 172.16.218.255.20 > 172.16.218.178.445: Flags [S], cksum 0x803e (correct), seq 0, win 8192, length 0
23:27:21.904440 IP (tos 0x0, ttl 128, id 31823, offset 0, flags [DF], proto TCP (6), length 44)
 172.16.218.178.445 > 172.16.218.255.20: Flags [S.], cksum 0x03be (correct), seq 3699222724, ack 1, win 65392, options [mss 1460], length 0
23:27:24.910050 IP (tos 0x0, ttl 128, id 31824, offset 0, flags [DF], proto TCP (6), length 44)
 172.16.218.178.445 > 172.16.218.255.20: Flags [S.], cksum 0x03be (correct), seq 3699222724, ack 1, win 65392, options [mss 1460], length 0
23:27:30.911092 IP (tos 0x0, ttl 128, id 31825, offset 0, flags [DF], proto TCP (6), length 44)
 172.16.218.178.445 > 172.16.218.255.20: Flags [S.], cksum 0x03be (correct), seq 3699222724, ack 1, win 65392, options [mss 1460], length 0
23:27:42.911498 IP (tos 0x0, ttl 128, id 31829, offset 0, flags [DF], proto TCP (6), length 40)
 172.16.218.178.445 > 172.16.218.255.20: Flags [R], cksum 0x1af8 (correct), seq 3699222725, win 0, length 0

wOOOOOOOt!! It seems to work. We can clearly see the packet being sent to the ‘.178’ IP address from the broadcast (.255) source address and then we see the response flowing back to the broadcast address.

Now that’s pretty interesting right? Essentially we can now perform port scans without being really traceable on the network. Somehow this still feels ‘weirdish’ because it just works on first try…so still thinking I missed something :/

sudo ./ipless-scan.py 172.16.218.178 00:0c:29:f6:a5:65 -p 445 3389 5000 -i eth0
2017-10-26 23:13:33,559 - INFO - Started ipless port scan
2017-10-26 23:13:33,559 - INFO - Started sniffer and waiting 10s
2017-10-26 23:13:43,568 - INFO - Starting port scan
2017-10-26 23:13:43,604 - INFO - Found open port - 445
2017-10-26 23:13:43,628 - INFO - Found open port - 3389
2017-10-26 23:13:43,645 - INFO - Found closed port - 5000
2017-10-26 23:13:43,654 - INFO - Finished port scan, waiting 5s for packets
2017-10-26 23:13:52,626 - INFO - Stopped sniffer

Python raw sockets sniffing & pcap saving

Even though we are pretty used to it, libpcap is not always present on systems. Usually, regardless of your goal, looking at traffic is actually pretty useful. In my experience this applies to offensive (pentesting, red team) work as well as defensive (incident response, network monitoring) work.

One of the first things that comes to mind, when libpcap is not available, is of course raw sockets, since these seem to be always available as long as you have the correct privileges. I’ve written previously about them as well as created some POC for backdoor purposes. Up until now raw sockets haven’t failed me, so when during a recent assignment I had to sniff traffic without libpcap I decided to write some Python code to achieve this. In case you are wondering, yes this was to further gather juicy information from unencrypted protocols like telnet, http and ftp.

A script nowadays never starts without a quick google query to save yourself the trouble of writing everything from scratch. So even though I enjoy writing a lot of things from scratch to learn, in this case I mainly adjusted an excellent example script from: http://askldjd.com/2014/01/15/a-reasonably-fast-python-ip-sniffer/

Adjusting the above script to save the data in pcap format was an easy undertaking and immediately useful. After waiting for a couple of minutes I got myself a nice pcap file which I could analyse on another machine with regular tools like tcpdump or wireshark.

You can find the script on the following gist

raw sockets, pcap files & wireshark quirks?

So this is one of those things that you don’t stumble upon until you are playing with it. When writing sniffers you can use libpcap or it’s win32 version winpcap. Now that doesn’t really do the job in all circumstances, since sometimes you don’t want to install an additional library. Lucky for us you can also sniff traffic by using raw sockets(I’m assuming win32, for the rest of the blog entry). Usually when sniffing with raw sockets you are looking for something specific in the stream of data and can just output it to a good old plain text file…however sometimes you’d just like to capture everything that comes through the network interface. That’s when it becomes interesting to save packets in the PCAP format, so I decided to write my own quick & dirty implementation.

Now here comes the fun part, when sniffing the packets you get everything above the Physical Layer. This means that the Ethernet header is lost and you directly receive the IP header. I did not realize this until I had written the packets into a PCAP file. When trying to open the file with Wireshark, it tries to interpret the first bytes as a Ethernet header which fails horribly. So I came up with two possible solutions:

  • Understand / Configure wireshark to start directly with the IP interpretation
    • Tried this for a while, then decided to go for the second option just for fun.
  • Add a fake Ethernet header to each packet.
    • As previously stated this was the final choice

Well it worked like a charm, Wireshark correctly interpreted the packets and dissected the rest of the contents just as I was used to with normal captures. If anyone knows how to correct the problem WITHOUT writing the fake Ethernet header, by just configuring wireshark correctly DO share.

Here is one last and small warning/readme, before I post the src, for those wanting to play with raw sockets…I’ve noticed some weird behavior myself when testing the sniffer on Win7.

http://www.nirsoft.net/utils/smsniff.html#problems

Under Windows 2000/XP (or greater), SmartSniff also allows you to capture TCP/IP packets without installing any capture driver, by using ‘Raw Sockets’ method. However, this capture method has some limitations and problems:

  • Outgoing UDP and ICMP packets are not captured.
  • On Windows XP SP1 outgoing packets are not captured at all – Thanks to Microsoft’s bug that appeared in SP1 update…
    This bug was fixed on SP2 update, but under Vista, Microsoft returned back the outgoing packets bug of XP/SP1.
  • On Windows Vista with SP1, only UDP packets are captured. TCP packets are not captured at all.
  • On Windows 7, it seems that ‘Raw Sockets’ method works properly again, at least for now…

The header file:


/*

DiabloHorn, fun with pcap and raw sockets

*/

#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
#include <time.h>

typedef struct pcap_hdr_s {
 unsigned int magic_number;   /* magic number */
 unsigned short version_major;  /* major version number */
 unsigned short version_minor;  /* minor version number */
 int  thiszone;       /* GMT to local correction */
 unsigned int sigfigs;        /* accuracy of timestamps */
 unsigned int snaplen;        /* max length of captured packets, in octets */
 unsigned int network;        /* data link type */
} pcap_hdr;

typedef struct pcaprec_hdr_s {
 unsigned int ts_sec;         /* timestamp seconds */
 unsigned int ts_usec;        /* timestamp microseconds */
 unsigned int incl_len;       /* number of octets of packet saved in file */
 unsigned int orig_len;       /* actual length of packet */
} pcaprec_hdr;

HANDLE openpcap(LPCWSTR);
void writepcaprec(HANDLE,void *,int);
void closepcap(HANDLE);

The C file


/*

 DiabloHorn, fun with pcap and raw sockets

 */
#include "pcap.h"

/*
 Opens a pcap file for appending, file is set to +S +H.
 Writes the general header.
*/
HANDLE openpcap(LPCWSTR filename){
 HANDLE hFile = NULL;
 pcap_hdr *genHeader;
 DWORD numWritten;
 //create file with shared read access and set it's attrib to +S +H
 hFile = CreateFile(filename,FILE_APPEND_DATA,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,NULL);
 if(hFile == INVALID_HANDLE_VALUE){
 return hFile;
 }else if(GetLastError() == ERROR_ALREADY_EXISTS){
 printf("Appending to existing pcap file\n");
 return hFile;
 }
 printf("Created empty pcap file\n");
 genHeader = (pcap_hdr *)malloc(sizeof(pcap_hdr));
 memset(genHeader,0,sizeof(pcap_hdr));
 genHeader->magic_number = 0xa1b2c3d4;
 genHeader->network = 1;//ethernet
 genHeader->sigfigs = 0;
 genHeader->snaplen = 65535;
 genHeader->thiszone = 0;
 genHeader->version_major = 2;
 genHeader->version_minor = 4;
 printf("Writing general pcap header\n");

 if(WriteFile(hFile,genHeader,sizeof(pcap_hdr),&numWritten,NULL) == 0){
 //need something more sexy here
 return INVALID_HANDLE_VALUE;
 }
 free(genHeader);
 return hFile;
}

/*
 Write the record of the pcap file
 Write record header (does not take into account the time stuff)
 Write fake eth header
 Write actual ip load data
 NOTE: supplied data must be max size 65521, due to specs in general header
 reason cause of fakeeth and me liking 65535 as a number :-)
*/
void writepcaprec(HANDLE hFile,void *data,int datalen){
 pcaprec_hdr *recHeader;
 DWORD numWritten;
 time_t seconds;
 //fake eth header
 byte fakeeth[14] = {0xde,0xde,0xde,0xde,0xde,0xad,0xbe,0xbe,0xbe,0xbe,0xbe,0xef,0x08,0x00};
 seconds = time(NULL);
 //write pcap record header stuff
 recHeader = (pcaprec_hdr *)malloc(sizeof(pcaprec_hdr));
 memset(recHeader,0,sizeof(pcaprec_hdr));
 recHeader->incl_len = datalen+sizeof(fakeeth);
 recHeader->orig_len = datalen+sizeof(fakeeth);
 recHeader->ts_sec = (unsigned int)seconds;
 recHeader->ts_usec = 0;
 printf("Writing record pcap header\n");
 WriteFile(hFile,recHeader,sizeof(pcaprec_hdr),&numWritten,NULL);
 free(recHeader);
 printf("Writing fake eth header\n");
 //write fake eth header, to fix wireshark
 WriteFile(hFile,fakeeth,sizeof(fakeeth),&numWritten,NULL);
 printf("Writing record data\n");
 //write pcap data stuff
 WriteFile(hFile,data,datalen,&numWritten,NULL);
}

/*
 Prolly hardly used but ohwell...
*/
void closepcap(HANDLE hFile){
 CloseHandle(hFile);
}