[Replicant] [vendor_replicant-scripts] [PATCH v5 4/4] Add new script to extract certificates from /data/system/packages.xml
Denis 'GNUtoo' Carikli
GNUtoo at cyberdimension.org
Fri Oct 9 16:55:58 UTC 2020
Signed-off-by: Denis 'GNUtoo' Carikli <GNUtoo at cyberdimension.org>
---
.../gen_key_migration_script/extract_certs.py | 150 ++++++++++++++++++
1 file changed, 150 insertions(+)
create mode 100755 images/gen_key_migration_script/extract_certs.py
diff --git a/images/gen_key_migration_script/extract_certs.py b/images/gen_key_migration_script/extract_certs.py
new file mode 100755
index 0000000..a08d3e1
--- /dev/null
+++ b/images/gen_key_migration_script/extract_certs.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+# Copyright (C) 2020 Denis 'GNUtoo' Carikli <GNUtoo at cyberdimension.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import os
+import re
+import sh
+import sys
+
+# Etree isn't subject to any data leak vulnerabilities:
+# +---------------------------+------------+-----------------------------------+
+# | Issue | Etree | Issue description |
+# +---------------------------+------------+-----------------------------------+
+# | billion laughs | Vulnerable | DDOS by huge CPU and memory usage |
+# +---------------------------+------------+-----------------------------------+
+# | quadratic blowup | Vulnerable | DDOS by huge CPU and memory usage |
+# +---------------------------+------------+-----------------------------------+
+# | external entity expansion | Safe (1) | Data leak |
+# +---------------------------+------------+-----------------------------------+
+# | DTD retrieval | Safe | Data leak |
+# +---------------------------+------------+-----------------------------------+
+# | decompression bomb | Safe | DDOS by huge amount of CPU |
+# +---------------------------+------------+-----------------------------------+
+# (1) xml.etree.ElementTree doesn’t expand external entities and raises a
+# ParserError when an entity occurs.
+#
+# Other XML parsers like sax, minidom, pulldom, xmlrpc have similar security
+# properties: they are not vulnerable (anymore) to data leaks but they are still
+# vulnerable to DDOS attacks.
+# Reference: https://docs.python.org/3.8/library/xml.html#xml-vulnerabilities
+import xml.etree.ElementTree
+
+import jinja2
+
+def usage(progname):
+ print ('Usage:')
+ print ('\t{} <path/to/packages.xml> <path/to/out/dir/>'.format(progname))
+ sys.exit(1)
+
+def find_keys(results, root):
+ # Error if there is no keyset-settings or keys
+ keyset_settings = root.findall('keyset-settings')
+ assert (len(keyset_settings) == 1)
+
+ keys = keyset_settings[0].findall('keys')
+ assert (len(keys) == 1)
+
+ public_key_entries = keys[0].findall('public-key')
+ assert (len(public_key_entries) >= 1)
+
+ for public_key in public_key_entries:
+ identifier = public_key.get('identifier')
+ value = public_key.get('value')
+ results[identifier] = {
+ 'PEM-pubkey' : value,
+ 'packages' : [],
+ }
+
+ return results
+
+def parse_packages_xml_file(results, path):
+ xml_file = open(path, 'r')
+ tree = xml.etree.ElementTree.parse(xml_file)
+ root = tree.getroot()
+
+ find_keys(results, root)
+
+ # elm.iter(<tag>) iterates over the elm element and all elements below it
+ for package in root.iter('package'):
+ package_name = package.get('name')
+ proper_signing_keyset = package.findall('proper-signing-keyset')
+ assert(len(proper_signing_keyset) == 1)
+
+ identifier = proper_signing_keyset[0].get('identifier')
+
+ results[identifier]['packages'].append(package_name)
+
+ for cert in package.iter('cert'):
+ if 'key' in cert.keys():
+ results[identifier]['DER-cert'] = cert.get('key')
+
+ xml_file.close()
+
+def write_pem_cert(der_cert, path):
+ openssl = sh.Command('openssl')
+ data = bytes.fromhex(der_cert)
+
+ output = openssl('x509',
+ '-inform', 'DER',
+ '-outform', 'PEM',
+ '-out', path, _in=data)
+
+def get_filename(id, data):
+ if 'android' in packages:
+ return 'platform.x509.pem'
+ elif 'com.cyanogenmod.trebuchet' in packages:
+ return 'releasekey.x509.pem'
+ elif 'com.android.contacts' in packages:
+ return 'shared.x509.pem'
+ elif 'com.android.providers.media' in packages:
+ return 'media.x509.pem'
+ elif len (data['packages']) == 1:
+ return str(packages[0]) + '.x509.pem'
+ else:
+ return str(id) + '.x509.pem'
+
+if __name__ == '__main__':
+ # The packages.xml file has the certificate keys encoded twice:
+ # - Once in PEM format in the value= in:
+ # <keyset-settings version="1">
+ # <keys>
+ # <public-key identifier="1" value="..." />
+ # - Once in DER format in key= in:
+ # <package name="..." [...]>
+ # <sigs [...]>
+ # <cert [...] key="..." />
+ if len(sys.argv) != 3:
+ usage(sys.argv[0])
+
+ packages_xml_path = sys.argv[1]
+ out_dir_path = sys.argv[2]
+
+ results = {}
+ parse_packages_xml_file(results, packages_xml_path)
+
+ for id, data in results.items():
+ packages = data['packages']
+
+ base_path = out_dir_path + os.sep + get_filename(id, data)
+
+ if len(packages) > 1:
+ file = open(base_path + '.txt', 'w')
+ file.write('Packages:' + os.linesep)
+ for package in packages:
+ file.write("- " + package + os.linesep)
+ file.close()
+
+ write_pem_cert(data['DER-cert'], base_path)
--
2.28.0
More information about the Replicant
mailing list