Cybersecurity Challenge Write-Ups

Group Project

Ali — Man-In-The-Middle Attack: ARP Spoofing

Platform: TryHackMe  |  Room: L2 MAC Flooding & ARP Spoofing

Introduction

For this write-up I chose the TryHackMe room "L2 MAC Flooding & ARP Spoofing". I picked this room because it taught me something practical about layer 2 attacks, especially the difference between passive sniffing, MAC flooding, and ARP spoofing. The room starts with simple enumeration and then gradually moves into man-in-the-middle traffic interception and packet manipulation.

Challenge Summary

In this room, the attacker first gains SSH access to a dual-homed Linux host called eve. From there, the internal network connected to eth1 can be explored. The room then moves through four main ideas:

  • Discovering live hosts on the internal subnet.
  • Passively sniffing traffic and understanding what can be seen without interfering with the network.
  • Using MAC flooding to force a switch into a more hub-like behavior and expose additional traffic.
  • Using ARP spoofing / ARP poisoning to become a man-in-the-middle and finally manipulating packets to obtain the final flag.

Environment and Tools Used

I completed the room from my Kali VM connected to the TryHackMe VPN. The main tools I used were:

  • ssh — for access to the room machine
  • sudo su - — to work as root when needed
  • ip and nmap — for network discovery
  • tcpdump — for packet capture and live sniffing
  • Wireshark — for pcap analysis
  • macof — for MAC flooding
  • ettercap and etterfilter — for ARP spoofing and packet manipulation
  • curl and netcat — for testing services and interacting with the target

Step-by-Step Solution

Task 1: Getting Started

The first task was just preparation. I started the room machine and made sure my Kali VM was connected to the TryHackMe VPN. I also waited a few minutes before connecting, because the room specifically mentions that the services may need time to start.

openvpn <your_tryhackme_config>.ovpn
VPN terminal output VPN connection command Room instructions to start machine

Task 2: Initial Access

After the target IP appeared, I connected to the first machine by SSH using the credentials given in the room. Once I logged in as admin, I switched to root because the room itself suggests using the root user for the remaining steps.

ssh -o StrictHostKeyChecking=accept-new admin@10.128.176.177
sudo su -

At this point the task was simply to confirm that I could gain access. The correct answer for this step was that access was successful.

SSH login and root access

Task 3: Network Discovery

The next step was to inspect the second network interface, because the room states that the network of interest is connected to eth1. I first checked the interface configuration to find my internal IP address and subnet.

eth1 interface configuration

From this output I identified eve's internal address as 192.168.12.66/24. That immediately answered the first two questions: the IP address was 192.168.12.66 and the CIDR prefix was /24.

To discover the other hosts on the subnet, I used an Nmap ping scan. This showed three hosts in total: alice, bob, and eve.

nmap -sn 192.168.12.0/24
Nmap scan showing alice, bob, eve Task 3 answers

Task 4: Passive Network Sniffing

This task focused on passive sniffing — only listening on eth1 without changing any traffic.

Network topology diagram

I started by watching the traffic live using tcpdump:

tcpdump -i eth1
tcpdump live capture showing ICMP traffic

To get a more readable view of the payload, I also used the ASCII option. The useful part was the first line of each packet, which showed bob sending ICMP echo requests to eve and eve replying.

tcpdump ASCII output

After that, I wrote the packets to a pcap file so I could inspect them in Wireshark more comfortably:

tcpdump -i eth1 -w /tmp/tcpdump.pcap
tcpdump writing to pcap SCP file transfer Wireshark showing ping packets Correct answers for Task 4

Task 5: Sniffing While MAC Flooding

The next idea was to increase the amount of visible traffic by using MAC flooding. The theory is that if the switch's MAC table becomes overloaded, it may temporarily act more like a hub and forward extra frames to other ports. I used two SSH sessions: one to capture, one to flood.

First SSH session:

tcpdump -i eth1 -w /tmp/tcpdump2b.pcap

Second SSH session:

macof -i eth1

After around one minute I stopped both, copied the capture back to Kali, and opened it in Wireshark:

scp admin@10.128.176.177:/tmp/tcpdump2b.pcap .
wireshark tcpdump2b.pcap
SCP copy of tcpdump2b.pcap

I could now see additional ICMP traffic between Alice and Bob. The correct packet type was ICMP and the data size was 1337 bytes. This task also taught me that MAC flooding is noisy, unreliable, and not something a real attacker would prefer if a quieter method is available.

Answer: 1337 bytes

Task 6: Man-In-The-Middle: Intro to ARP Spoofing

ARP cache poisoning diagram

Since MAC flooding is noisy, the room moved to ARP spoofing. The idea was to poison the ARP caches of Alice and Bob and place eve in the middle of their communication.

ettercap -T -i eth1 -M arp

In this first scenario, the hosts were using ARP validation, so ettercap could not establish a proper MITM. The answer to whether MITM could be established was therefore Nay.

Ettercap output with ARP validation Task 6 answers

Task 7: Man-In-The-Middle: Sniffing

Task 7 introduced a new machine with Alice and Bob running Ubuntu with default ARP behavior, making a successful ARP spoofing attack realistic.

ssh -o StrictHostKeyChecking=accept-new admin@10.129.153.233
sudo su -
ip a s eth1
nmap -sn 192.168.12.0/24
Nmap scan showing two hosts IP addresses of Alice and Bob

I then scanned both systems for open services:

nmap -sV 192.168.12.10 192.168.12.20
Nmap service scan results

Alice had 4444/tcp open, Bob had 80/tcp open. I tested direct access to Bob's web service:

curl http://192.168.12.20
curl response: no auth header received

The server replied with "no auth header received" — the service was reachable but protected.

Task answer: Nay

I then launched ARP spoofing with ettercap and monitored HTTP traffic in a second terminal:

First SSH session:

ettercap -T -i eth1 -M arp

Second SSH session:

tcpdump -i eth1 -A port 80
Passive tcpdump before ARP spoofing

This time the attack worked. I could see Alice sending HTTP requests to Bob through eve. The request showed the Host header was www.server.bob, the requested file was /test.txt, and the HTTP response body contained OK. I also spotted a Basic Authorization header which decoded to admin:s3cr3t_P4zz.

HTTP request visible after ARP spoofing HTTP response body Authorization header captured

Using those credentials I was able to retrieve the first flag:

curl -u admin:s3cr3t_P4zz -H "Host: www.server.bob" http://192.168.12.20/user.txt

Flag: THM{wh0s_$n!ff1ng_0ur_cr3ds}

Flag: user.txt

I also observed Bob connecting back to Alice on port 4444, revealing a reverse shell. By watching the stream I saw the commands executed in order: whoami, pwd, and ls. The file of interest was root.txt.

TCP stream showing whoami Connection between 192.168.12.10 and 192.168.12.20 ls command in reverse shell stream

When stopping ettercap with q, the tool gracefully re-ARPed the victims to restore the ARP state.

Ettercap graceful shutdown Task 7 answers part 1 Task 7 answers part 2

Task 8: Man-In-The-Middle: Manipulation

The final task moved from passive interception to active packet manipulation. I created an etterfilter source file (whoami.ecf) to replace Alice's whoami command with a reverse shell payload, then compiled it:

whoami.ecf filter source
etterfilter whoami.ecf -o whoami.ef
etterfilter compilation output

I disabled ufw to ensure the incoming reverse shell would not be blocked:

ufw disabled

I started a Netcat listener on port 6666, then launched ettercap with the compiled filter:

ettercap -T -i eth1 -M arp /192.168.12.10// /192.168.12.20// -F whoami.ef

The filter worked. Alice's whoami command was replaced by my reverse shell payload, and Bob connected back to my Netcat listener. I had remote shell access as root and found the final flag:

Netcat listener

Flag: THM{wh4t_an_ev1l_M!tM_u_R}

Root shell with final flag

What Failed and How I Corrected It

  • My Kali VM lost internet access at one point — I had to fix the VMware/NAT setup and restart services on my host before continuing.
  • I mistyped eht1 instead of eth1 and forgot the -w option on some tcpdump commands, causing errors until I corrected them.
  • When copying pcap files with scp, I first ran the command in the wrong session and had to correct the direction of the file copy.
  • In Task 7, I initially wrote down the decoded credentials incorrectly. After inspecting the Authorization: Basic header more carefully, I got the right username and password.
  • In Task 8, I mistakenly left the placeholder <reverse_shell> in the filter file instead of replacing it with an actual payload.
  • I also initially confused the etterfilter source file (.ecf) with the compiled file (.ef). Ettercap requires the compiled version produced by etterfilter.

What I Learned

  • Passive sniffing on a switched network only shows traffic already destined for my interface.
  • MAC flooding can expose more traffic but is noisy and unreliable.
  • ARP spoofing is a much more practical MITM approach when hosts do not validate ARP replies.
  • Packet captures are more useful when you understand exactly which field you are looking at.
  • MITM attacks are not only about listening — with the right tool they can also manipulate live network traffic.

Conclusion

Overall, I found this room very useful because it connected networking theory to practical attack behavior. I started by identifying hosts on an internal subnet, then moved to passive analysis, MAC flooding, ARP spoofing, and finally packet manipulation. The room clearly showed the difference between a network where traffic is merely visible and one where the attacker can actively interfere with what two hosts send to each other.

The challenge also reinforced a good lesson as a beginner: solving labs is rarely just about one perfect command. A lot of the work comes from observing outputs carefully, correcting small mistakes, comparing approaches, and understanding why one method fails while another succeeds.

Bozhidar

Danil

Mateusz — HackTheBox Challenge

Platform: HackTheBox  |  Challenge: TwoMillion

screenshot

Let's do a nmap scan on the box and see what we get back.

screenshot

Arguments used by me:

-p- all ports

-T4 speed 4

-A additional information gathering scripts

-v verbose (to show information gathered as the program runs)

-oN to save the result

Result:

screenshot

So we see that we have SSH service on port 22 and a HTTP service on port 80. The rest are filtered so we only have two open TCP ports on this machine.

screenshot screenshot

So when we enter the IP address into the browser URL it automatically changes it to “2million.htb”.

screenshot

And we don't get anything back.

This is because when we enter the IP address (in my case “10.129.3.184”), it redirects us to the URL “2million.htb”, which goes towards your general DNS servers. Because the DNS servers don't know which IP is for this URL, they can't redirect us to the IP and we end up not getting anything back.

So for us to fix that we have to configure manually this IP address to this URL, we can do this in the hosts file which are located on:

Linux: /etc/hosts

Windows: C:\Windows\System32\drivers\etc\hosts

I am working on Windows so for me I will open the file using notepad as administrator:

Windows key + R

Type „Powershell”

Press Ctrl + Shift + Enter to run the selected application as admin.

And then just open the file using notepad.exe: notepad.exe C:\Windows\System32\drivers\etc\hosts

Once you are in the file, add the IP address followed by the URL name.

[IP] [URL]

So for me it will be: 10.129.3.184 2million.htb

Save and exit.

Now when we try to connect to this IP address or to just the URL…

screenshot

We are in!


We see the old Hack The Box webpage, let's travel to /invite to see what is there.

screenshot

So for us to sign up back in the day to the Hack The Box, you had to do a mini challenge to get the invite code.

Let's see what is being loaded with this site using our development tools that come with firefox.

screenshot

We see 2 Javascript, one for some frontend, and the other one for inviteapi.

Let's open the second one:

screenshot

Well that's a mess, we need to make the JS code more readable.

screenshot

We can do it using the option marked on red to „pretty source code”.

screenshot

Now it's better, but it is still a mess. Let's try to make it prettier using some other tool on the web.

After trying a few websites I found https://jsbeautify.org/ to only make the pasted JS readable.

screenshot

And here is the JS code that we got back:

function verifyInviteCode(code) { var formData = { "code": code }; $.ajax({ type: "POST", dataType: "json", data: formData, url: '/api/v1/invite/verify', success: function(response) { console.log(response) }, error: function(response) { console.log(response) } }) } function makeInviteCode() { $.ajax({ type: "POST", dataType: "json", url: '/api/v1/invite/how/to/generate', success: function(response) { console.log(response) }, error: function(response) { console.log(response) } }) }

We see two functions, one to verify the invite code and one to create it. Both of them use API to do this.

Let's try to enter /api/v1/invite/how/to/generate:

screenshot

We get error „405 Method Not Allowed”, let's modify our request using burpsuite.

screenshot

We see that the GET request is not accepted, let's try to experiment with other request types.

screenshot

I have changed GET request with POST request and we have a response!

We got some data back that is ciphered with ROT13 (aka the Caesar cipher with key 13).

We open cyberchef and select operation „ROT13” and paste the encoded data.

screenshot

We get the following response back:

„In order to generate the invite code, make a POST request to \/api\/v1\/invite\/generate”

You can see in the path „\/”, because we got the response back using JSON, to input / in JSON you need to write \ before it to make sure it won't get intercepted as a part of the code, or in other words:

In JSON put \ before any special sign to make it literal.

So it is just a normal path like this: /api/v1/invite/generate

We try to make the request using GET to this path but it doesn't work, we switch to POST and we get back this:

screenshot

Some code that is clearly BASE64 encoded, copy paste into cyberchef, select operation „From Base64” and…

screenshot

We got the invite code, note that it changes so everytime you want to create a new user you have to make a post request to /api/v1/invite/generate, and base64 decode the response.

With that we have gained the invite code to register our new account.

screenshot

Let's fill up the form and create a new user account.

screenshot screenshot screenshot

And we are in!

Let's now search for the „Connection Pack”, on the left navigation sidebar we can find section „Labs”, with subsection „Access”, where we see the searched button.

screenshot

When we click on it it downloads VPN configuration file for us. It doesn't work unfortunately.

screenshot

When we inspect this button using our built in dev tools, we see that it leads to an API function.

Now we need to enumerate this API to see what else is hidden in it.

screenshot

When we go to “2million.htb/api/v1”, we get a response of possible paths to other functions.

There are 2 sections, one for “user” and the other one for “admin”. That sounds interesting.

“Update user settings” in an admin section, sounds good, let's see what it does and how to use it.

As it's using PUT request, we need to edit our response in Burp suite.

screenshot

We get a response that the content type of our request is invalid, that is because my previous request was captured when we wanted to get website content, here the application expects from us JSON content as seen in the “Content-Type: application/json” response header and the response.

In my request we don't have any content-type specification, so let's add it, specify that it's JSON format and send the request again.

screenshot

Now we get a response from the server that our request is missing “email” parameter, let's add it then and fill it with our user's email.

screenshot

So now when we send the email parameter, it says that it also wants “is_admin” parameter, let's add it then and experiment what should be the value in it.

screenshot

is_admin with value 1 works! It now responds to us with status message of our user and that “is_admin” variable is set to 1. Let's see if there is anything different on the main page.

> screenshot

Not really…

Maybe we need to explore the API(s) a little bit more.

After experimenting a little bit, I have discovered that “/api/v1/admin/vpn/generate”, expects from us username variable, but when we enter it, it doesn't reject non string values

screenshot

If they are not checking the value inside username, what else can there be here?

screenshot

Also, I have noticed that our session cookie, starts with “PHP”, which probably means that the backend on the server is running PHP.

So we know that there could be an injection vulnerability in the “username” variable, and that the backend is running PHP.

I got stuck here for a while because when I tried to escape the variable and run for example “whoami”, I didn't get any response back except the headers, which made me think that this is not working. But then I realized that the command could work, but that just nothing was reflected back to me.

So I have run a netcat listener on my machine and injected a ping request to my machine to see if the injection is actually working, but I am just not seeing it.

screenshot

So I have decided to run a ping command on the server and observe my network card that is for openvpn service. And we get ping requests to our targeted machine!

Note: When you test for things like that, you may want to set the ping command to only send one packet, as the Linux ping command will just run forever unlike the Windows ping command which sends 4 packets only by default. Because:

1. Very loud as the network gets flooded with ping requests.

2. Can be a step towards DOS, which we don't want as we want the targeted machine and our machine to run smoothly.

To send only 4 ping requests:

Linux: ping -c 4 [IP]

Windows: ping -n 4 [IP]

Now that we know that we can run commands on it, let's insert a reverse shell and see if we get it back.

To generate reverse shell script for PHP you will find many websites on the internet.

https://payloadplayground.com/generators/reverse-shell screenshot

First type of reverse shell didn't give me anything back, but after trying the second type it worked and we have a reverse shell to the server!

Note: That this is not a stable shell and you might need to rerun it a couple of times if it disconnects.

screenshot

After listing files in it we see that there is .env in it, and we can also cat into it to see what's in it.

And there we have compromised database admin credentials.

Username: admin

Password: SuperDuperPass123

As we remember from the beginning, we have SSH service on this server, let's try to use these credentials to log into it.

screenshot

We are in.

screenshot

And we have flag for user account.


After we explore a little, we can find a file called “admin” in /var/mail (this is where mail is stored on Linux)

screenshot

So from this mail we can read that CVEs for OverlayFS and FUSE look “nasty”.

We have two "nasty" services, but first we have to understand what they do, second see the versions of them so that we can search for CVEs for them if there are any.

So OverlayFS allows us to layer multiple filesystems on top of each other (huh), thing is that it is used for things like Docker and most importantly, it is an inseparable thing that comes with the Linux kernel.

Let's check our Linux kernel version using “uname -r” and see on the internet if there are any exploits for it.

uname -r

screenshot screenshot

There are, a lot.

Following our HTB guided tasks, we need to find a CVE from 2023 about OverLay system.

After searching for it, there is only one CVE related to this Linux kernel version that is about the OverLay system.

Now that we know what we have to use, we need to understand how to use it.

Link: https://securitylabs.datadoghq.com/articles/overlayfs-cve-2023-0386/

After reading a blog post about this vulnerability, I understand that we can fake a file ownership to make it run as the root user, but honestly, I have no idea how to use OverLay nor FUSE, so for this case we will just download a ready privilege escalation script from github.

After a while I have found some ready to go code on github: https://github.com/xkaneiki/CVE-2023-0386

Let's git clone the code on our machine (since the target machine doesn't have git installed) and then copy it using scp to the target machine.

Download it via git clone:

screenshot

Copy it via scp to the target:

screenshot screenshot

Now that we have the files on the server, we can follow the author's github usage section.

Step1:

Run this

screenshot

Step2:

Run this but in a different shell (create a new one by doing SSH to it while holding the first session still)

screenshot

Step3:

Profit

screenshot

We are now root, we can cd to /root and ls the content of it.

screenshot

And we have flag for the root user.

screenshot
fullscreen

Nikita