System Overview

SVGTranslate, developed by Wikimedia, converts SVG files into PNG images while allowing for language-based text substitutions within the SVG content. This PHP backend application is structured with functionality managed through ApiController.php and Renderer.php. The source code is hosted on SVGTranslate’s GitHub repository.

Component Path

  • API Controller: /src/Controller/ApiController.php
  • Rendering Service: /src/Service/Renderer.php

Vulnerability Description

The application is vulnerable to unauthenticated remote code execution (RCE) due to improper handling of the language parameter in the PNG generation process. The vulnerability arises from how shell commands are constructed and executed in the rendering service.

Vulnerability Analysis

Entry Point

The API endpoint defined as follows processes file names and language parameters to serve PNG files, without adequate validation of the lang parameter:

/**
 * Serve a PNG rendering of the given SVG in the given language.
 * @Route("/api/file/{filename}/{lang}.png", name="api_file", methods="GET")
 * @param string $filename
 * @param string $lang
 * @return Response
 */
public function getFile(string $filename, string $lang): Response
{
    $filename = Title::normalize($filename);
    $content = $this->svgRenderer->render($this->cache->getPath($filename), $lang);
    return new Response($content, 200, ['Content-Type' => 'image/png', 'X-File-Hash' => sha1($content)]);
}

Renderer Vulnerability

In Renderer.php, the lang parameter is unsafely included in a shell command, allowing command injection:

/**
 * Render a SVG file to PNG.
 * @param string $file Full filesystem path to the SVG file to render.
 * @param string $lang Language code for rendering.
 * @param string $outFile File path for the output PNG.
 * @throws ProcessFailedException If conversion fails.
 * @return string The PNG image contents.
 */
public function render(string $file, string $lang, ?string $outFile = null) : string
{
    $command = $this->rsvgCommand.' "$SVG"';
    if ('fallback' !== $lang) {
        $command .= " --accept-language=$lang";
    }
    if ($outFile) {
        $command .= ' > "$PNG"';
    }
    $process = Process::fromShellCommandline($command);
    $process->mustRun(null, ['SVG' => $file, 'PNG' => $outFile]);
    return $process->getOutput();
}

Proof of Concept

The vulnerability was tested directly on the Wikimedia instance, demonstrating unauthorized command execution as evidenced by the system user information in the server response:

GET /api/file/SI_base_unit1.svg/fr;id;.png HTTP/2
Host: svgtranslate.toolforge.org

Timeline of Events

Mitigation Recommendations

  1. Input Validation: Implement strict validation for all input parameters, especially those being incorporated into command line operations.
  2. Secure Command Execution: Use array parameters for command execution to ensure separation between commands and arguments, preventing injection.
  3. Security Audit and Testing: Conduct a thorough security review and penetration testing to identify and fix potential vulnerabilities.

Thanks to the Wikimedia Community

Big thanks to everyone at Wikimedia for their hard work on so many helpful tools. Their quick teamwork in fixing this vulnerability really shows their commitment to keeping things secure and running smoothly. Your efforts are greatly appreciated!