1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-11-20 13:50:12 +01:00

scan APKs for signs of "Master Key" exploit

This exploit is old, and was fixed in 4.4.  But it was easy to exploit,
so it is still worth scanning for it.  It is also easy to scan for, since
valid APKs should not have files with duplicate names.  In theory, this
could look for duplicate file names for any file, but this limits the
false positives by only checking names of files related to executing code.

fdroidclient#40
This commit is contained in:
Hans-Christoph Steiner 2017-06-21 14:01:01 +02:00
parent 5fd014a852
commit 9886e539d3

View File

@ -470,13 +470,24 @@ def sha256sum(filename):
return sha.hexdigest() return sha.hexdigest()
def has_old_openssl(filename): def has_known_vulnerability(filename):
'''checks for known vulnerable openssl versions in the APK''' """checks for known vulnerabilities in the APK
Checks OpenSSL .so files in the APK to see if they are a known vulnerable
version. Google also enforces this:
https://support.google.com/faqs/answer/6376725?hl=en
Checks whether there are more than one classes.dex or AndroidManifest.xml
files, which is invalid and an essential part of the "Master Key" attack.
http://www.saurik.com/id/17
"""
# statically load this pattern # statically load this pattern
if not hasattr(has_old_openssl, "pattern"): if not hasattr(has_known_vulnerability, "pattern"):
has_old_openssl.pattern = re.compile(b'.*OpenSSL ([01][0-9a-z.-]+)') has_known_vulnerability.pattern = re.compile(b'.*OpenSSL ([01][0-9a-z.-]+)')
files_in_apk = set()
with zipfile.ZipFile(filename) as zf: with zipfile.ZipFile(filename) as zf:
for name in zf.namelist(): for name in zf.namelist():
if name.endswith('libcrypto.so') or name.endswith('libssl.so'): if name.endswith('libcrypto.so') or name.endswith('libssl.so'):
@ -485,7 +496,7 @@ def has_old_openssl(filename):
chunk = lib.read(4096) chunk = lib.read(4096)
if chunk == b'': if chunk == b'':
break break
m = has_old_openssl.pattern.search(chunk) m = has_known_vulnerability.pattern.search(chunk)
if m: if m:
version = m.group(1).decode('ascii') version = m.group(1).decode('ascii')
if version.startswith('1.0.1') and version[5] >= 'r' \ if version.startswith('1.0.1') and version[5] >= 'r' \
@ -495,6 +506,11 @@ def has_old_openssl(filename):
logging.warning('"%s" contains outdated %s (%s)', filename, name, version) logging.warning('"%s" contains outdated %s (%s)', filename, name, version)
return True return True
break break
elif name == 'AndroidManifest.xml' or name == 'classes.dex' or name.endswith('.so'):
if name in files_in_apk:
return True
files_in_apk.add(name)
return False return False
@ -1172,7 +1188,7 @@ def scan_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk):
if not common.verify_apk_signature(apkfile): if not common.verify_apk_signature(apkfile):
return True, None, False return True, None, False
if has_old_openssl(apkfile): if has_known_vulnerability(apkfile):
apk['antiFeatures'].add('KnownVuln') apk['antiFeatures'].add('KnownVuln')
apkzip = zipfile.ZipFile(apkfile, 'r') apkzip = zipfile.ZipFile(apkfile, 'r')