September 3, 2026
TED 1 CTF Walkthrough
I recently came across the TED 1 CTF on VulnHub and tried my hand at solving this challenge. It took me some time, and I referred to some…

By Om D. Chalke
7 min read
I recently came across the TED 1 CTF on VulnHub and tried my hand at solving this challenge. It took me some time, and I referred to some walkthroughs, but here is mine.
Link if you want to try it yourself before reading spoilers: TED: 1 — VulnHub
I worked through the TED 1 CTF on a local Ubuntu 16.04 machine. The screenshots show the target at 192.168.118.140 and an Apache 2.4.18 web server. I'd describe the box as beginner-friendly, although the session-poisoning step was the part that required the most thought.
Finding the machine on the network
I started on my Kali machine by checking which address I was using:
ip a
That gave me:
192.168.118.137/24
I knew the CTF target would be somewhere on that local network, so rather than guessing an IP, I used ARP discovery:
sudo netdiscover -r 192.168.118.137/24
The scan returned a few hosts, including:
192.168.118.1 192.168.118.2 192.168.118.140 192.168.118.254
192.168.118.140 stood out as the likely target, so I moved on to service enumeration.
Recon: only HTTP was exposed
I ran a version scan because I wanted to know what was actually listening before touching the web application:
sudo nmap -sV 192.168.118.140
The result was very small:
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
There weren't a bunch of services to investigate, so the attack surface was clearly going to be the web server.
I opened:
and got a login page.
At that point, I switched to Burp Suite so I could see exactly what the application was sending rather than treating the login form as a black box.
The first login attempt failed, but the error gave me something useful
My first guess was deliberately simple:
username=root password=root
I sent it through Burp and the server came back with:
Username is not correct.
So root wasn't a valid application username.
I then tried:
username=admin password=admin
This time the application gave me a much more interesting response:
Password hash is not correct, make sure to hash it before submit.
That was actually helpful. The application wasn't just saying "wrong password" it was telling me exactly what format it expected.
Weak authentication
At this point I suspected the application was using a predictable hash rather than a proper password-hashing scheme. I tested admin through a SHA-256 generator, which produced:
8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
I sent that as the password instead of admin.
That worked, and I reached the authenticated area as admin.
The "file browser" was more interesting than it looked
After logging in, I was dropped into a page called Simple File Browser.
There was a search box, so I started thinking about what the backend might actually be doing with that input.
Instead of searching for a normal filename, I tried an absolute path:
/etc/passwd
The application returned the contents of /etc/passwd.
That immediately changed the attack direction.
I wasn't just searching a directory anymore; I was making the server read a file I chose.
Vulnerability: Local File Inclusion / arbitrary local file read
This was a classic LFI-style file inclusion flaw. The application was taking user-controlled input and using it as a filesystem path without properly restricting what could be accessed.
The reason /etc/passwd was such a useful test is that it's a harmless, well-known Linux file. If the server returns it, I know the path isn't being restricted the way it should be.
Reading the PHP session file
Once I knew arbitrary local files could be read, I started looking for something more useful than /etc/passwd.
The application was using PHP sessions, and I knew PHP commonly stores those under:
/var/lib/php/sessions/
So I tried reading the session file for my current session:
/var/lib/php/sessions/sess_a323c3vosg5re7ftia9vpfn9i6
The application returned session data that looked like:
loggedin|b:1; name|s:5:"admin"; id|i:1; user_pref|s:1:"/";
The part that caught my attention was:
user_pref
I was already controlling that value through the application, and I could also read the server-side session file.
That combination was the clue I needed.
Turning the LFI into code execution
I started with a small proof of concept rather than immediately throwing a reverse shell at it.
I changed the user_pref cookie so it contained:
The idea was simple:
- Put PHP code into data I controlled.
- Get that data written into the session file.
- Use the LFI to make the application include that session file.
- If PHP parsed the session file as code, my command should run.
I URL-encoded the payload and sent it through Burp.
It worked.
The response contained the output of ifconfig, including the target's address:
192.168.118.140
That was the point where I knew I had gone beyond file reading.
Vulnerability: LFI-to-RCE via PHP session poisoning
This was a classic LFI-to-RCE via session poisoning.
The LFI alone gave me file access. The session poisoning part gave me a way to place PHP code inside a file that the server could later include. Once the PHP interpreter processed that file, the injected system() call executed with the web server's privileges.
From command execution to a real shell
Once ifconfig worked, I knew I could execute commands, but a one-command-at-a-time setup would be awkward.
So I replaced the test payload with a reverse shell:
I chose my Kali address, 192.168.118.137, because that was the machine I was attacking from.
Before triggering the payload, I opened a listener:
nc -lvp 4848
The listener showed:
listening on [any] 4848 …
I then sent the poisoned request.
The connection came back:
connect to [192.168.118.137] from (UNKNOWN) [192.168.118.140]
I had a shell.
I checked exactly what level of access I had
The first thing I wanted to know was who I was running as.
whoami
The answer was:
www-data
That made sense because the PHP application was running through Apache.
I also ran:
uname -a
and got:
Linux ubuntu 4.4.0–21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
So I had a shell, but it was only the low-privileged web-server account.
That meant the next problem was privilege escalation.
sudo -l gave me the exact path to root
Rather than blindly trying kernel exploits, I checked what the current account was allowed to run through sudo:
sudo -l
The result was the line I was looking for:
User www-data may run the following commands on ubuntu: (root) NOPASSWD: /usr/bin/apt-get
That was a huge mistake in the machine's configuration.
www-data could execute apt-get as root, without entering a password.
At that point I didn't need to hunt for another vulnerability. I just needed to find a way to make the permitted command execute something of my choosing.
Why I chose apt-get for privilege escalation
apt-get supports configuration hooks that can run commands during an update operation.
Since sudo was launching apt-get as root, anything executed by that root-owned apt-get process would also run with root privileges.
I used the Pre-Invoke hook to launch Bash:
sudo apt-get update -o APT::Update::Pre-Invoke::=/bin/bash
I wasn't interested in actually updating packages. The update command was just the mechanism for getting apt-get to process the hook.
Bash spawned from that root apt-get process gave me a root shell.
I checked:
whoami
and got:
root
I then verified it more explicitly:
id
which returned:
uid=0(root) gid=0(root) groups=0(root)
That was the point where I had full control of the machine.
Vulnerability: dangerous sudoers configuration
The root cause here wasn't a bug in apt-get itself. The problem was that the administrator gave www-data permission to execute a powerful package-management program as root with NOPASSWD.
Because that binary has features capable of invoking other commands, it effectively became a path to arbitrary root command execution.
Looking for the flag
Once I was root, I searched for likely flag/proof files:
find / -iname "flag" -o -iname "proof" 2>/dev/null
The screenshot shows the command running and returning a large number of paths under /sys, such as files named flags. But none of them are actual flags, and I suppose the aim was to attain root privileges only. We have it, and thus we can conclude that we have solved the CTF challenge.
What I learned from this machine
- I got more value from the application's error messages than from guessing passwords. The
rootattempt failed, but theadminresponse told me the password needed to be hashed. - The LFI was much more powerful than it first appeared. Reading
/etc/passwdwas only the proof. Reading the PHP session file was what opened the door to code execution. - I deliberately tested command execution with
ifconfigfirst. That was safer and easier to verify than jumping straight to a reverse shell. - For privilege escalation, I checked
sudo -lbefore trying anything complicated. That single command exposed the intended path to root.