CVE-2022-46165: XSS in Syncthing webUI

Moritz Kaumanns

21. Jul. 2023

Github Repository von syncthing
SeverityMedium
TypeCross-site scripting (XSS)
Affected ProductSyncthing < 1.23.5
CVE ID(s)CVE-2022-46165

Syncthing is a free continuous file synchronization application. It synchronizes files between one or more peers on all major operating systems. Syncthing (prior version 1.23.5) provides a local administration panel that is served via a built-in web service that is subject to a cross-site scripting (XSS) vulnerability.

Impact

A compromised instance with shared folders enabled could synchronize malicious files with arbitrary HTML and JavaScript in the name. If an owner of another device visits the shared folder settings and hovers over the last sync, a script could be executed to change shared folder settings or automatically add devices.

In addition, adding a new device with a malicious name could embed HTML or JavaScript in parts of the page.

Details

The following two sections provide a brief overview of the bugs and how they could be exploited by an attacker. Two locally hosted instances were used for this test.

Field “Latest Change”

  • Open the webUI at http://127.0.0.1:8384/.
  • Create/Delete a file named <img src=a onerror=alert(123)> and synchronize the changes with another instance.
  • Hover over the last change to trigger the tooltip.

HTML Source (Web Browser)

The payload <img src=a onerror=alert(123)> is directly embedded in the web page and finally executed. No input sanitization or output encoding was performed.

<span tooltip="" data-original-title="\&quot;><img src=a onerror=alert(123)> @ 2022-11-30 16:58:43"
    aria-describedby="tooltip409527">
    <!-- ngIf: !folderStats[folder.id].lastFile.deleted --><span translate=""
        translate-value-file="&quot;><img src=a onerror=alert(123)>" ng-if="!folderStats[folder.id].lastFile.deleted"
        class="ng-scope">Updated "&gt;&lt;img src=a onerror=alert(123)&gt;</span>
    <!-- end ngIf: !folderStats[folder.id].lastFile.deleted -->
    <!-- ngIf: folderStats[folder.id].lastFile.deleted -->
</span>
<div class="tooltip fade top in" role="tooltip" id="tooltip409527"
    style="top: 446.033px; left: 318.3px; display: block;">
    <div class="tooltip-arrow" style="left: 50%;"></div>
    <div class="tooltip-inner">\"&gt;<img src="a" onerror="alert(123)"> @ 2022-11-30 16:58:43</div>
</div>

Corresponding code in the project

gui/default/index.html:

<tr ng-if="folder.type != 'sendonly' && folder.type != 'receiveencrypted' && folderStats[folder.id].lastFile && folderStats[folder.id].lastFile.filename">
    <th><span class="fas fa-fw fa-exchange-alt"></span>&nbsp;<span translate>Latest Change</span></th>
    <td class="text-right">
        <span tooltip data-original-title="{{folderStats[folder.id].lastFile.filename}} @ {{folderStats[folder.id].lastFile.at | date:'yyyy-MM-dd HH:mm:ss'}}">
        <span translate translate-value-file="{{folderStats[folder.id].lastFile.filename | basename}}" ng-if="!folderStats[folder.id].lastFile.deleted">Updated {%file%}</span>
        <span translate translate-value-file="{{folderStats[folder.id].lastFile.filename | basename}}" ng-if="folderStats[folder.id].lastFile.deleted">Deleted {%file%}</span>
        </span>
    </td>
</tr>

gui/default/syncthing/core/tooltipDirective.js:

angular.module('syncthing.core')
    .directive('tooltip', function () {
        return {
            restrict: 'A',
            link: function (scope, element, attributes) {
                $(element).tooltip({
                    html: 'true'
                });
            }
        };
    });

Field “Shared With”

  • Open the webUI at http://127.0.0.1:8384/.
  • Create a device with the following name test"'><h1>Headline</h1><img src=x><script>alert(1)</script>.
  • Add the device on another instance and share a folder.
  • Hover over the malicious device name to trigger the tooltip.

HTML Source (Web Browser)

The payload is embedded and executed on mouse hover.

<span tooltip="" data-original-title="test&quot;'><h1>Headline</h1><img src=x><script>alert(1)</script>  "
    ng-bind-html="sharesFolder(folder)" class="ng-binding" aria-describedby="tooltip348410">test"'&gt;<h1>Headline
    </h1><img src="x"></span>
<div class="tooltip fade top" role="tooltip" id="tooltip348410" style="top: 0px; left: 0px; display: block;">
    <div class="tooltip-arrow" style="left: 50%;"></div>
    <div class="tooltip-inner">test"'&gt;<h1>Headline</h1><img src="x">
        <script>alert(1)</script>
    </div>
</div>

Corresponding code in the project

gui/default/index.html:

<tr>
    <th><span class="fas fa-fw fa-share-alt"></span>&nbsp;<span translate>Shared With</span></th>
    <td class="text-right">
        <span tooltip data-original-title="{{sharesFolder(folder)}} {{folderHasUnacceptedDevices(folder) ? '<br/>(<sup>1</sup>' + ('The remote device has not accepted sharing this folder.' | translate) + ')' : ''}} {{folderHasPausedDevices(folder) ? '<br/>(<sup>2</sup>' + ('The remote device has paused this folder.' | translate) + ')' : ''}}" ng-bind-html="sharesFolder(folder)"></span>
    </td>
</tr>

gui/default/syncthing/core/tooltipDirective.js:

angular.module('syncthing.core')
    .directive('tooltip', function () {
        return {
            restrict: 'A',
            link: function (scope, element, attributes) {
                $(element).tooltip({
                    html: 'true'
                });
            }
        };
    });

Recommendation

This issue has been addressed in version 1.23.5 of Syncthing. Users are advised to upgrade. Users unable to upgrade should avoid sharing folders with untrusted devices/users.

References

Seien Sie Cyber-Bedrohungen einen Schritt voraus und identifizieren Sie Schwachstellen in Ihrer Applikation