- General documentation
- Related topic : Open Search
Introduction
In Firefox, search engines have been widely abused in the past. The curent complexity comes from hijacking-protection. ( source )
What does NOT work anymore
- user_pref
Keyword.Url
â BUG#873734 - user_pref
browser.search.selectedEngine
â BUG#1029148
So, how does it works?
- AT STARTUP, preferences âbrowser.urlbar.placeholderNameâ and âbrowser.search.defaultenginenameâ are used to display the search engine name⊠but itâs ONLY a label (see BUG #1449317 ).
- THEN, when the
search.json.mozlz4
has been read from the profile, the full configuration is loaded and the previous preferences are not used anymore during the session.
Decoding/Encoding search.json.mozlz4
search.json.mozlz4
has a magic number header ( âmozLz40\0â ) followed by the content itself, encoded using LZ4 algorithm. Thatâs why, in order to decompress the file, the magic number needs to be skiped, then the remaining bytes can be extracted using LZ4 algorithm.
This python program does the job well ( source )
#!/usr/bin/env python
from sys import stdin, stdout, argv, stderr
import os
try:
import lz4.block as lz4
except ImportError:
import lz4
stdin = os.fdopen(stdin.fileno(), 'rb')
stdout = os.fdopen(stdout.fileno(), 'wb')
if argv[1:] == ['-c']:
stdout.write(b'mozLz40\0' + lz4.compress(stdin.read()))
elif argv[1:] == ['-d']:
assert stdin.read(8) == b'mozLz40\0'
stdout.write(lz4.decompress(stdin.read()))
else:
stderr.write('Usage: %s -c|-d < infile > outfile\n' % argv[0])
stderr.write('Compress or decompress Mozilla-flavor LZ4 files.\n\n')
stderr.write('Examples:\n')
stderr.write('\t%s -d < infile.json.mozlz4 > outfile.json\n' % argv[0])
stderr.write('\t%s -c < infile.json > outfile.json.mozlz4\n' % argv[0])
exit(1)
Requires
- Python2 :
python-lz4
package - Python3 :
python3-lz4tools
package
Reverse engineering of search.json.mozlz4 Json schema
- SearchService.jsm â see comments!
- SearchEngine.jsm
Hash computation
Hashes are used to ensure that the file has been updated by Firefox itself. So computing hashes has been designed to be difficult, in order to prevent malicious program from updating silently search.json.mozlz4
file.
Source code
- SearchService.jsm
- SearchEngine.jsm â
function getVerificationHash(name)
Using jsconsole
- prerequisite: inside Firefox, set the
devtools.chrome.enabled
preference totrue
insideabout:config
to enable the browser console command line ( source ). - run
firefox -jsconsole
from the command line - copy/paste
getVerificationHash
function from SearchEngine.jsm . - ask for
getVerificationHash("Qwant")
Using Bash commands
# getVerificationHash("Qwant")
name=Qwant
# OS.Path.basename(OS.Constants.Path.profileDir) â profile dependent, of course
profileDir="fbhzwefm.default"
# Services.appinfo.name
appName="Firefox"
# Copy/Pasted disclaimer from https://hg.mozilla.org/mozilla-central/file/tip/toolkit/components/search/SearchEngine.jsm#l228
disclaimer="By modifying this file, I agree that I am doing so only within $appName itself, using official, user-driven search engine selection processes, and in a way which does not circumvent user consent. I acknowledge that any attempt to change this file from outside of $appName is a malicious act, and will be responded to accordingly."
salt=$profileDir$name$(echo $disclaimer | sed "s/\$appName/$appName/g")
printf "$salt" | openssl sha256 -binary | base64
Appendix
Here is getVerificationHash
function from SearchEngine.jsm .
function getVerificationHash(name) {
let disclaimer
"By modifying this file, I agree that I am doing so " +
"only within $appName itself, using official, user-driven search " +
"engine selection processes, and in a way which does not circumvent " +
"user consent. I acknowledge that any attempt to change this file " +
"from outside of $appName is a malicious act, and will be responded " +
"to accordingly.";
let salt
OS.Path.basename(OS.Constants.Path.profileDir) +
name +
disclaimer.replace(/\$appName/g, Services.appinfo.name);
let converter = Cc[
"@mozilla.org/intl/scriptableunicodeconverter"
].createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
// Data is an array of bytes.
let data = converter.convertToByteArray(salt, {});
let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
Ci.nsICryptoHash
);
hasher.init(hasher.SHA256);
hasher.update(data, data.length);
return hasher.finish(true);
}