def scan(filenames): """Scan the provided files for vulnerabilities""" for filename in filenames: click.echo('\nProcessing file: ' + filename) # Try to open the provided file as a ZIP, fail otherwise zip_file = TruegazeUtils.open_file_as_zip(filename) if zip_file is None: click.echo( 'ERROR: Unable to open file - please check to make sure it is an APK or IPA file' ) sys.exit(-1) # Detect manifest is_android = False is_ios = False android_manifest = TruegazeUtils.get_android_manifest(zip_file) ios_manifest = TruegazeUtils.get_ios_manifest(zip_file) # Set flags, error out if no manifest is found if android_manifest: click.echo( 'Identified as an Android application via a manifest located at: ' + android_manifest) is_android = True elif ios_manifest: click.echo( 'Identified as an iOS application via a manifest located at: ' + ios_manifest) is_ios = True else: click.echo( 'ERROR: Unable to identify the file as an Android or iOS application' ) sys.exit(-2) # Pass the filename to the individual modules for scanning for PLUGIN in ACTIVE_PLUGINS: click.echo() click.echo('Scanning using the "' + PLUGIN.name + '" plugin') instance = PLUGIN(filename, is_android, is_ios) # Show error if OS is not supported # TODO: Add tests if instance.is_os_supported(): instance.scan() else: click.echo('-- OS is not supported by this plugin, skipping') click.echo("Done!")
def scan(self): # On Android, the config file is usually in the assets folder but can be placed elsewhere. # On iOS the configuration file can be anywhere. # Load file self.zip_file = TruegazeUtils.open_file_as_zip(self.filename) # Search all paths for the config file paths = AdobeMobileSdkPlugin.get_paths(self.zip_file) if len(paths) == 0: click.echo( '-- Cannot find the "ADBMobileConfig.json" file, skipping') return # Loop through files, parse the JSON and analyze click.echo('-- Found ' + str(len(paths)) + ' configuration file(s)') for path in paths: click.echo('-- Scanning "' + path + "'") # Try to parse the data parsed_data = AdobeMobileSdkPlugin.parse_data(self.zip_file, path) if not parsed_data: click.echo( '---- ERROR: Unable to parse config file - will skip. File: ' + path) continue # Validate the file messages = AdobeMobileSdkPlugin.validate(parsed_data) if len(messages) > 0: click.echo("-- Found " + str(len(messages)) + ' issues') for message in messages: click.echo(message) else: click.echo("-- No issues found")
def test_valid_file_in_directory(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('test/test.txt', 'testdata') paths = TruegazeUtils.get_matching_paths_from_zip( zip_file, re.compile(r'.*est.*\.txt')) assert len(paths) == 1 assert paths[0] == 'test/test.txt'
def test_valid_one_file(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('test', '') paths = TruegazeUtils.get_matching_paths_from_zip( zip_file, re.compile(r'.*')) assert len(paths) == 1 assert paths[0] == 'test'
def test_valid_three_files(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('test1.txt', '') zip_file.writestr('test/test2.doc', '') zip_file.writestr('test/test/test3.md', '') paths = TruegazeUtils.get_matching_paths_from_zip( zip_file, re.compile(r'.*est/test.*\..*')) assert len(paths) == 2 assert paths[0] == 'test/test2.doc' assert paths[1] == 'test/test/test3.md'
def test_not_empty(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr("test", 'testdata') assert TruegazeUtils.get_android_manifest(zip_file) is None
def test_empty(self): zip_file = ZipFile(io.BytesIO(), 'a') assert TruegazeUtils.get_android_manifest(zip_file) is None
def test_junk_manifest(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('Payload/Test.app/Info.plist', '<junk></junk>') assert TruegazeUtils.get_ios_manifest(zip_file) is None
# software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # import sys import click from beautifultable import BeautifulTable from truegaze.plugins import ACTIVE_PLUGINS from truegaze.utils import TruegazeUtils @click.version_option(version=TruegazeUtils.get_version(), prog_name='truegaze') @click.group() def cli(): """ truegaze - A static analysis tool for Android and iOS applications focusing on security issues outside the source code such as resource strings, third party libraries and configuration files. Copyright (c) 2019 Nightwatch Cybersecurity. Source code: https://github.com/nightwatchcybersecurity/truegaze """ @cli.command('list') def list_plugins(): """List supported plugins""" click.echo("Total active plugins: " + str(len(ACTIVE_PLUGINS)))
from setuptools import find_packages, setup from truegaze.utils import TruegazeUtils with open("README.md", "r") as fh: long_description = fh.read() setup( name='truegaze', version=TruegazeUtils.get_version(), description= 'Static analysis tool for Android/iOS apps focusing on security issues outside the source code.', long_description=long_description, long_description_content_type="text/markdown", url='https://github.com/nightwatchcybersecurity/truegaze', author='Nightwatch Cybersecurity', author_email='*****@*****.**', license='Apache', packages=find_packages( exclude=["scripts.*", "scripts", "tests.*", "tests"]), include_package_data=True, install_requires=open('requirements.txt').read().splitlines(), entry_points={'console_scripts': ['truegaze = truegaze.cli:cli']}, classifiers=[ 'Environment :: Console', 'Development Status :: 3 - Alpha', 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', ], python_requires='>=3.6',
def test_directory_with_right_name(self): info = ZipInfo('assets/' + ANDROID_MANIFEST) info.external_attr = 16 zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr(info, '') assert TruegazeUtils.get_android_manifest(zip_file) is None
def test_empty(self): zip_file = ZipFile(io.BytesIO(), 'a') paths = TruegazeUtils.get_matching_paths_from_zip( zip_file, re.compile(r'.*')) assert len(paths) == 0
def test_valid(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('Payload/Test.app/Info.plist', TestUtilsGetiOSManifest.make_ios_manifest()) assert TruegazeUtils.get_ios_manifest( zip_file) == 'Payload/Test.app/Info.plist'
def test_manifest_with_some_keys3(self): buffer = io.BytesIO() plistlib.dump(dict(CFBundleShortVersionString='some app'), buffer) zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('Payload/Test.app/Info.plist', buffer.getvalue()) assert TruegazeUtils.get_ios_manifest(zip_file) is None
def test_manifest_with_no_keys(self): buffer = io.BytesIO() plistlib.dump({}, buffer) zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('Payload/Test.app/Info.plist', buffer.getvalue()) assert TruegazeUtils.get_ios_manifest(zip_file) is None
def test_empty_manifest(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr(ANDROID_MANIFEST, '') assert TruegazeUtils.get_android_manifest(zip_file) is None
def test_wrong_directory(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('assets/' + ANDROID_MANIFEST, 'manifest data') assert TruegazeUtils.get_android_manifest(zip_file) is None
def test_format_valid(self): pattern = re.compile(r'^(\d+\.)?(\d+\.)?(\*|\d+)$') assert pattern.match(TruegazeUtils.get_version()) is not None
def test_valid(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr(ANDROID_MANIFEST, 'manifest data') assert TruegazeUtils.get_android_manifest(zip_file) == ANDROID_MANIFEST
def test_not_found(self): assert TruegazeUtils.open_file_as_zip('blablabla') is None
def test_invalid_empty(self): assert TruegazeUtils.open_file_as_zip(io.BytesIO()) is None
def test_invalid_not_zip(self): assert TruegazeUtils.open_file_as_zip( io.StringIO('foobar data')) is None
def get_paths(zip_file): return TruegazeUtils.get_matching_paths_from_zip( zip_file, CONFIG_FILE_PATTERN)
def test_valid_zip(self): zip_buffer = io.BytesIO() zip_file = ZipFile(zip_buffer, 'a') zip_file.writestr('testfile', 'testdata') zip_file.close() assert TruegazeUtils.open_file_as_zip(zip_buffer) is not None
# software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # import sys import click from beautifultable import BeautifulTable from truegaze.plugins import ACTIVE_PLUGINS from truegaze.utils import TruegazeUtils @click.version_option(version=TruegazeUtils.get_version(), prog_name='truegaze') @click.group() def cli(): """ truegaze - A static analysis tool for Android and iOS applications focusing on security issues outside the source code such as resource strings, third party libraries and configuration files. Copyright (c) 2019 Nightwatch Cybersecurity. Source code: https://github.com/nightwatchcybersecurity/truegaze """ @cli.command('list') def list_plugins(): """List supported plugins"""
def test_wrong_directory3(self): zip_file = ZipFile(io.BytesIO(), 'a') zip_file.writestr('Payload/Testapp/Info.plist', TestUtilsGetiOSManifest.make_ios_manifest()) assert TruegazeUtils.get_ios_manifest(zip_file) is None