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 machinesudo su -— to work as root when neededipandnmap— for network discoverytcpdump— for packet capture and live sniffingWireshark— for pcap analysismacof— for MAC floodingettercapandetterfilter— for ARP spoofing and packet manipulationcurlandnetcat— 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
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.
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.
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
Task 4: Passive Network Sniffing
This task focused on passive sniffing — only listening on eth1 without changing any traffic.
I started by watching the traffic live using tcpdump:
tcpdump -i eth1
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.
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
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
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.
Task 6: Man-In-The-Middle: Intro to ARP Spoofing
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.
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
I then scanned both systems for open services:
nmap -sV 192.168.12.10 192.168.12.20
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
The server replied with "no auth header received" — the service was reachable but protected.
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
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.
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}
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.
When stopping ettercap with q, the tool gracefully re-ARPed the victims to restore the ARP state.
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:
etterfilter whoami.ecf -o whoami.ef
I disabled ufw to ensure the incoming reverse shell would not be blocked:
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:
Flag: THM{wh4t_an_ev1l_M!tM_u_R}
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
eht1instead ofeth1and forgot the-woption 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: Basicheader 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 byetterfilter.
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
Let's do a nmap scan on the box and see what we get back.
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:
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.
So when we enter the IP address into the browser URL it automatically changes it to “2million.htb”.
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…
We are in!
We see the old Hack The Box webpage, let's travel to /invite to see what is there.
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.
We see 2 Javascript, one for some frontend, and the other one for inviteapi.
Let's open the second one:
Well that's a mess, we need to make the JS code more readable.
We can do it using the option marked on red to „pretty source code”.
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.
And here is the JS code that we got back:
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:
We get error „405 Method Not Allowed”, let's modify our request using burpsuite.
We see that the GET request is not accepted, let's try to experiment with other request types.
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.
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:
Some code that is clearly BASE64 encoded, copy paste into cyberchef, select operation „From Base64” and…
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.
Let's fill up the form and create a new user account.
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.
When we click on it it downloads VPN configuration file for us. It doesn't work unfortunately.
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.
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.
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.
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.
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.
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.
>
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
If they are not checking the value inside username, what else can there be here?
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.
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
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.
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.
We are in.
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)
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
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:
Copy it via scp to the target:
Now that we have the files on the server, we can follow the author's github usage section.
Step1:
Run this
Step2:
Run this but in a different shell (create a new one by doing SSH to it while holding the first session still)
Step3:
Profit
We are now root, we can cd to /root and ls the content of it.
And we have flag for the root user.