├── LEEME.md ├── README.md ├── client ├── Analyzer.py ├── CommonComponent │ ├── AnalyzerDecorator.py │ ├── FuzzingTrigger.py │ ├── InsecureTransmissionAnalyzer.py │ ├── ZIPPathTraversalAnalyzer.py │ └── __init__.py ├── Dispatcher.py ├── DummyFilter │ ├── DummyAnalyzer.py │ ├── DummyReporter.py │ ├── DummyTrigger.py │ └── __init__.py ├── Emulator.py ├── FileSchemeFilter │ ├── FileSchemeAnalyzer.py │ ├── FileSchemeReporter.py │ ├── FileSchemeTrigger.py │ └── __init__.py ├── JavascriptInterfaceFilter │ ├── JavascriptInterfaceAnalyzer.py │ ├── JavascriptInterfaceReporter.py │ ├── JavascriptInterfaceTrigger.py │ └── __init__.py ├── PhonegapFilter │ ├── PhonegapAnalyzerCVE3500.py │ ├── PhonegapAnalyzerJSInjection.py │ ├── PhonegapReporter.py │ ├── PhonegapTriggerCVE3500.py │ ├── PhonegapTriggerJSInjection.py │ └── __init__.py ├── Reporter.py ├── SSLFilter │ ├── SSLAnalyzer.py │ ├── SSLReporter.py │ ├── SSLTrigger.py │ └── __init__.py ├── StorageAnalyzer.py ├── Trigger.py ├── Utils.py ├── VMClient.py ├── client.sh ├── client_setup.sh ├── mitmproxy_setup.sh └── settings.py ├── emulator ├── emulator_setup.sh └── emulator_support_files │ ├── Android-SSL-TrustKiller.apk │ ├── Android-x86-RootScript-4.3 │ ├── install-device.sh │ └── system │ │ ├── app │ │ └── Superuser.apk │ │ ├── bin │ │ ├── .ext │ │ │ └── .su │ │ └── README │ │ ├── etc │ │ └── init.sh │ │ └── xbin │ │ ├── daemonsu │ │ └── su │ ├── Marvin-toqueton.apk │ ├── busybox │ ├── com.saurik.substrate.apk │ └── eu.chainfire.supersu_preferences.xml ├── images └── architecture.jpg └── server ├── DBCache_django.py ├── DBManager_django.py ├── ONE-API ├── NetworkInfo.java ├── SnapshotRestore.java ├── lib │ ├── org.opennebula.client.jar │ ├── ws-commons-util-1.0.2.jar │ ├── xmlrpc-client-3.1.2.jar │ └── xmlrpc-common-3.1.2.jar ├── network_info.jar └── restore.jar ├── Utils.py ├── VMManager.py ├── django_support ├── __init__.py ├── models.py ├── myindices.py └── settings.py └── settings.py /LEEME.md: -------------------------------------------------------------------------------- 1 | # Marvin Dynamic Analyzer # 2 | 3 | Analizador dinámico de vulnerabilidades de aplicaciones Android utilzando OpenNebula y emuladores Android-x86. 4 | 5 | Version: 0.01 6 | 7 | ## Arquitectura general ## 8 | 9 | ![architecture.jpg](https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/master/images/architecture.jpg) 10 | 11 | Las vulnerabilidades analizadas dinámicamente son: 12 | 13 | * JAVASCRIPT_INTERFACE 14 | * PHONEGAP_JS_INJECTION 15 | * PHONEGAP_CVE_3500_URL 16 | * SSL_CUSTOM_TRUSTMANAGER 17 | * SSL_CUSTOM_HOSTNAMEVERIFIER 18 | * SSL_ALLOWALL_HOSTNAMEVERIFIER 19 | * SSL_INSECURE_SOCKET_FACTORY 20 | * SSL_WEBVIEW_ERROR 21 | * WEBVIEW_FILE_SCHEME 22 | 23 | Además, intenta analizar por otro tipo de vulnerabilidades mientras analizando las anteriores: 24 | 25 | * ZIP_PATH_TRAVERSAL 26 | * INSECURE_TRANSMISSION 27 | * INSECURE_STORAGE 28 | 29 | El VMManager esta encargado de buscar nuevas vulnerabilidades en la base de datos de Marvin para analizar dinámicamente. 30 | 31 | Los VMClient reciben las aplicaciones y los tipos de vulnerabilidades a analizar del VMManager. Luego, usa ADB para conectarse al emulador y comenzar el análisis. Además configura al emulador para que él pueda actuar de router para interceptar los pedidos HTTP/HTTPS. Utilizan un fuzzer personalizado [MarvinToqueton](https://github.com/programastic/Marvin-toqueton/) cuyo objetivo es interactuar con la aplicación para disparar las vulnerabilidades. 32 | 33 | Los emuladores corren Android 4.3 x86 para soportar Cydia Substrate. Las próximas versiones soportaran Xposed. 34 | El proyecto está utilizando las imágenes de [Android-x86](www.android-x86.org). 35 | 36 | Hay 3 tipos de emuladores: 37 | * Emuladores sin ningún tipo de verificación SSL 38 | * Emuladores con verificación SSL normal (utilizados para ver si las aplicaciones realizan verificaciones SSL) 39 | * Emuladores corriendo ICS 4.0 (usados para explotar la vulnerabilidad de interfaces Javascript/Java ) 40 | 41 | ## Configuraciones ## 42 | 43 | Las configuraciones de VMManager se encuentran en el archivo settings.py: 44 | 45 | ``` 46 | NET_ID = 1 #ID used de la red Virtual OpenNebula 47 | # IP RPC OpenNebula 48 | ONE_IP = "http://IP:2633/RPC2" 49 | ONE_CREDS = "" #Credenciales OpenNebula xml RPC 50 | ELASTIC_SEARCH_SERVER = "ELASTIC_SEARCH_IP_ADDRESS" 51 | DOWNLOAD_APK_SITE = "http://FRONTEND_IP_ADDRESS/frontpage/" 52 | ANDROID_VM_POOL = {"NONSSL":{"NON_SSL_EMULATOR_IP"},"SSL":{"EMULATOR_IP"},"JAVASCRIPTINTERFACE":{"ICS_EMULATOR_IP"}} 53 | ``` 54 | 55 | **IMPORTANTE:** Bajo el directorio django_support, configurar el archivo settings.py con los SECRET_KEY, contraseñas de la base de datos y URL de BungieSearch correspondientes a la configuración de Marvin-frontend. 56 | 57 | El cliente de VMClient también incluye configuraciones en settings.py: 58 | 59 | ``` 60 | VM_MANAGER_IP = 'VMMANAGER_IP_ADDRESS:8082' 61 | REPORTER_IP = 'localhost:8081' 62 | DEBUG_MODE = False 63 | 64 | #Algunas configuraciones del Fuzzer que pueden ser modificadas en el proyecto Marvin-toqueton. 65 | ``` 66 | 67 | ## Para correrlo ## 68 | 69 | Comenzar el servidor con: 70 | 71 | ``` 72 | python VMManager.py 73 | ``` 74 | 75 | Y los clientes con: 76 | 77 | ``` 78 | ./client.sh 79 | ``` 80 | 81 | ## Análisis de una aplicación ## 82 | 83 | El analizador dinámico obtiene la siguiente aplicación a analizar del conjunto de vulnerabilidades no verificadas en la base de datos de Marvin. Para hacer análisis de una sola aplicación, cambiar DEBUG_MODE a True en settings.py del cliente. Y luego correr el cliente de la siguiente forma: 84 | 85 | ``` 86 | python VMClient.py [VULNERABILITY_ID] [NOMBRE-PAQUETE/NOMBRE ARCHIVO APK] '''{"emulator":"IP_DEL_EMULADOR_A_USAR","count":0}''' 87 | ``` 88 | 89 | En el caso de la vulnerabilidad PHONEGAP_CVE_3500_URL se debe además agregar como parámetro el campo "activity" que referencia a la Activity inicial de la aplicación. Si sólo querés corroborar vulnerabilidades no encontradas estáticamente como ZIP Path Traversal o transmisión y almacenamiento inseguro, utiliza "dummy" como ID de la vulnerabilidad. 90 | 91 | **IMPORTANTE:** El nombre del archivo APK debe coincidir con el nombre del paquete de la aplicación. 92 | 93 | ## Deployment ## 94 | 95 | VMManager y VMClient pueden utilizar cualquier VM hosteada en OpenNebula. Los autores utilizaron ttylinux - kvm del Marketstore de OpenNebula como template. 96 | 97 | ### Dependencias de VMManager ### 98 | 99 | Correr los siguientes comandos para instalar las dependencias: 100 | 101 | ``` 102 | pip install bungiesearch 103 | 104 | sudo apt-get install python-mysqldb 105 | 106 | apt-get install default-jre 107 | 108 | apt-get install python-django 109 | ``` 110 | 111 | ### Dependencias de VMClient ### 112 | 113 | Correr los siguientes comandos para instalar las dependencias: 114 | 115 | ``` 116 | apt-get install mitmproxy 117 | ``` 118 | ### Crear un emulador ### 119 | 120 | Descargar desde el sitio de Android-x86 el ISO para el release 4.3 o 4.0. Instalar en un disco con una herramienta de virtualización. Los autores utilizamos VirtualBox para ello. 121 | 122 | Convierte la imagen instalada del disco a formato qemu de KVM para soportar snapshots en OpenNebula. Puedes utilizar el siguiente comando: 123 | 124 | ``` 125 | qemu-img convert -O qcow2 original-image image-converted.qcow 126 | ``` 127 | Sube la imagen a OpenNebula y create un nuevo template para el emulador. 128 | 129 | Ejemplo de template: 130 | 131 | ``` 132 | CONTEXT=[NETWORK="YES",SSH_PUBLIC_KEY="$USER[SSH_PUBLIC_KEY]"] 133 | 134 | CPU="2" 135 | 136 | DISK=[DRIVER="qcow2",IMAGE="Image Name",IMAGE_UNAME="Your user"] 137 | 138 | GRAPHICS=[LISTEN="0.0.0.0",TYPE="VNC"] 139 | 140 | HYPERVISOR="kvm" 141 | 142 | MEMORY="512" 143 | 144 | NIC=[MODEL="pcnet",NETWORK="Your Virtual Network Name",NETWORK_UNAME="Your user"] 145 | 146 | SUNSTONE_CAPACITY_SELECT="YES" 147 | 148 | SUNSTONE_NETWORK_SELECT="YES" 149 | ``` 150 | 151 | **IMPORTANTE:** COnfigurar QCOW2 como el driver de disco para soportar snapshots. 152 | 153 | **IMPORTANTE:** Al crear un dispositivo Android ICS, configurar el driver de red a pcnet. 154 | 155 | Crear una VM utilizando el template creado anteriormente y prende el dispositivo. 156 | 157 | **IMPORTANT:** Iniciar la primera vez en modo Debug y editar el archivo /mnt/grub/menu.lst. Agregar "quiet nomodeset vga=788" antes de video=-16 a los argumentos del kernel. 158 | 159 | Por ahora no hay soporte de contextualización para las VMs de Android corriendo en OpenNebula así que las interfaces de red deben ser configuradas manualmente para poder conectarse remotamente desde ADB. Para correr comandos como root desde la aplicación Terminal utilizar: 160 | 161 | ``` 162 | adb root 163 | 164 | adb kill-server 165 | 166 | adb start-server 167 | 168 | adb shell 169 | ``` 170 | 171 | Los siguientes comandos deberían funcionar para configurar la red corriendo en la aplicación de Terminal como root: 172 | 173 | ``` 174 | ifconfig eth0 [IP] netmask [NETMASK] 175 | 176 | busybox route add default gw [GATEWAY] dev eth0 177 | ``` 178 | 179 | 180 | Una vez terminada la configuración de la red, **salir de la terminal en modo root** e utilizar ADB para permitir depuración remota con: 181 | 182 | ``` 183 | adb tcpip 5556 184 | ``` 185 | 186 | Manualmente configurar: 187 | * Deshabilitar el sonido en el Emulador (Settings->Sound) 188 | * Destildar la verificación de las aplicaciones en Settings->Security->Uncheck Verify Apps 189 | 190 | Correr android_setup_client.sh en un host con los siguientes parámetros: 191 | 192 | ``` 193 | emulator_setup.sh {SSL/NOSSL} {IP-EMULADOR} {ROUTER-PARA-EMULADOR} 194 | ``` 195 | 196 | Seleccionar {SSL/NOSSL} para desplegar un emulador de tipo 1 o 2. 197 | 198 | El script: 199 | * Rootea el emulador 200 | * Agrega una configuración de red estática 201 | * Instala SuperUser y CydiaSubstrate, linkea Substrate y le permite correr como root. 202 | * Si es de tipo NOSSL, instala Android-SSL-TrustKiller para dejar de validar la verificación SSL. 203 | * Instala el fuzzer Marvin-toqueton. 204 | * Deshabilita el bloqueo de pantalla y la suspensión. 205 | 206 | 207 | Por último, crear un snapshot de la VM corriendo con el nombre que prefieras. 208 | 209 | ### Requerimientos ### 210 | 211 | * Marvin-frontend instalado en alguna VM de OpenNebula. 212 | * Python 2.7.x (No usar Python 3.X) 213 | 214 | ### Créditos ### 215 | * Joaquín Rinaudo ([@xeroxnir](https://www.twitter.com/xeroxnir)) 216 | * Juan Heguiabehere ([@jheguia](https://www.twitter.com/jheguia)) 217 | 218 | ### Contacto ### 219 | * Mandar un correo a stic en el dominio fundacionsadosky.org.ar 220 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Marvin Dynamic Analyzer # 2 | 3 | Dynamic android vulnerability scanner using OpenNebula and Android-x86 emulators. 4 | 5 | Version: 0.01 6 | 7 | ## General architecture ## 8 | 9 | ![architecture.jpg](https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/master/images/architecture.jpg) 10 | 11 | Vulnerabilities analyzed dynamically are: 12 | 13 | * JAVASCRIPT_INTERFACE 14 | * PHONEGAP_JS_INJECTION 15 | * PHONEGAP_CVE_3500_URL 16 | * SSL_CUSTOM_TRUSTMANAGER 17 | * SSL_CUSTOM_HOSTNAMEVERIFIER 18 | * SSL_ALLOWALL_HOSTNAMEVERIFIER 19 | * SSL_INSECURE_SOCKET_FACTORY 20 | * SSL_WEBVIEW_ERROR 21 | * WEBVIEW_FILE_SCHEME 22 | 23 | Also it checks for new vulnerabilities that might be found while searching for others: 24 | 25 | * ZIP_PATH_TRAVERSAL 26 | * INSECURE_TRANSMISSION 27 | * INSECURE_STORAGE 28 | 29 | VMManager is in charge of searching new vulnerabilities to be analyzed in Marvin's Django database. 30 | 31 | VMClients receive the app and type of vulnerability to analyze and emulator to use from VMManager. It uses ADB to connect to the emulator and start the analysis. It also configures the emulator to act as its gateway to intercept HTTP/HTTPS requests. 32 | 33 | VMClients uses a custom fuzzer [MarvinToqueton](https://github.com/programastic/Marvin-toqueton/) developed for interacting with the application to try triggering the vulnerabilities. 34 | 35 | The emulators are running Android 4.3 x86 because of Cydia Substrate support. The project is using [Android-x86](www.android-x86.org) images. 36 | 37 | There are 3 types of emulator: 38 | * Emulators with no SSL verification 39 | * Emulators with normal SSL verification (They are used to check if an application does proper SSL verification) 40 | * Emulators running ICS 4.0 (Used for exploiting Javascript Interface vulnerability) 41 | 42 | ## Configurations ## 43 | 44 | VMManager configurations are in settings.py: 45 | 46 | ``` 47 | NET_ID = 1 #ID used for OpenNebula virtual network 48 | #credentials for RPC 49 | ONE_IP = "http://IP:2633/RPC2" 50 | ONE_CREDS = "" #OpenNebula xml RPC credentials 51 | ELASTIC_SEARCH_SERVER = "ELASTIC_SEARCH_IP_ADDRESS" 52 | DOWNLOAD_APK_SITE = "http://FRONTEND_IP_ADDRESS/frontpage/" 53 | ANDROID_VM_POOL = {"NONSSL":{"NON_SSL_EMULATOR_IP"},"SSL":{"EMULATOR_IP"},"JAVASCRIPTINTERFACE":{"ICS_EMULATOR_IP"}} 54 | ``` 55 | 56 | **IMPORTANT:** Under the django_support directory, also configure the settings.py file with the corresponding SECRET_KEY, database password and BungieSearch URL from Marvin-frontend configuration. 57 | 58 | VMClient configurations are also in the settings.py file: 59 | 60 | ``` 61 | VM_MANAGER_IP = 'VMMANAGER_IP_ADDRESS:8082' 62 | REPORTER_IP = 'localhost:8081' 63 | DEBUG_MODE = False 64 | 65 | #Some fuzzer configurations that can be modified in Marvin-toqueton project. 66 | ``` 67 | 68 | ## Running ## 69 | 70 | Start the server with: 71 | 72 | ``` 73 | python VMManager.py 74 | ``` 75 | 76 | And VMClients with: 77 | 78 | ``` 79 | ./client.sh 80 | ``` 81 | ## Running a single analysis ## 82 | 83 | Marvin dynamic analyzer obtains the next app to analyze from the pool of unverified vulnerabilities on Marvin's database. If you want to run an analysis on a single app you can change the variable DEBUG_MODE to True in settings.py of the client. Then you can run an analysis with: 84 | 85 | 86 | ``` 87 | python VMClient.py [VULNERABILITY_ID] [PACKAGE-NAME/FILE-NAME] '''{"emulator":"EMULATOR_IP_ADDRESS","count":0}''' 88 | ``` 89 | In the case of PHONEGAP_CVE_3500_URL, you must also include the parameter "activity" with a reference to the initial application activity. If you only want to run checks against vulnerabilities not found statically such as ZIP path traversal, insecure storage and insecure transmission use "dummy" as the vulnerability ID. 90 | 91 | 92 | **IMPORTANT:** The filename of the APK must be the same as the package name. 93 | 94 | ## Deployment ## 95 | 96 | VMManager and VMClient can use any Linux virtual machine hosted in OpenNebula. Authors used ttylinux - kvm from OpenNebula Marketstore as template. 97 | 98 | ### VMManager dependencies ### 99 | 100 | Run to install dependencies: 101 | 102 | ``` 103 | pip install bungiesearch 104 | 105 | sudo apt-get install python-mysqldb 106 | 107 | apt-get install default-jre 108 | 109 | apt-get install python-django 110 | ``` 111 | 112 | ### VMClient dependencies ### 113 | 114 | Run to install dependencies: 115 | 116 | ``` 117 | apt-get install mitmproxy 118 | ``` 119 | ### Creating an emulator ### 120 | 121 | Download from the Android-x86 site the ISO for 4.3 or 4.0 release. Install it in a disk with your favorite virtualization tool. Authors used VirtualBox. 122 | 123 | Convert the installed image to KVM qemu's format in order to support OpenNebula's snapshot feature. You can use the following command: 124 | 125 | ``` 126 | qemu-img convert -O qcow2 original-image image-converted.qcow 127 | ``` 128 | 129 | Upload the image to OpenNebula and create a new template for the emulator. 130 | 131 | Example of template: 132 | 133 | ``` 134 | CONTEXT=[NETWORK="YES",SSH_PUBLIC_KEY="$USER[SSH_PUBLIC_KEY]"] 135 | 136 | CPU="2" 137 | 138 | DISK=[DRIVER="qcow2",IMAGE="Image Name",IMAGE_UNAME="Your user"] 139 | 140 | GRAPHICS=[LISTEN="0.0.0.0",TYPE="VNC"] 141 | 142 | HYPERVISOR="kvm" 143 | 144 | MEMORY="512" 145 | 146 | NIC=[MODEL="pcnet",NETWORK="Your Virtual Network Name",NETWORK_UNAME="Your user"] 147 | 148 | SUNSTONE_CAPACITY_SELECT="YES" 149 | 150 | SUNSTONE_NETWORK_SELECT="YES" 151 | ``` 152 | 153 | **IMPORTANT:** Set QCOW2 as the image mapping driver for snapshot support. 154 | 155 | **IMPORTANT:** When creating a ICS Android device, set the network interface driver to pcnet. 156 | 157 | Create a VM using the previous template and boot the device. 158 | 159 | **IMPORTANT:** Start the kernel in debug mode the first time and edit the file /mnt/grub/menu.lst. Add "quiet nomodeset vga=788" before the option video=-16 to kernel arguments. 160 | 161 | So far there is no contextualization support for Android VMs running in OpenNebula so the network interfaces have to be configured manually. First to run commands as root run in Terminal application the following commands: 162 | 163 | ``` 164 | adb root 165 | 166 | adb kill-server 167 | 168 | adb start-server 169 | 170 | adb shell 171 | ``` 172 | 173 | The following commands should work to set the network if run using the Terminal application as root. 174 | 175 | ``` 176 | ifconfig eth0 [IP] netmask [NETMASK] 177 | 178 | busybox route add default gw [GATEWAY] dev eth0 179 | ``` 180 | 181 | Once the network configuration is set,**exit the Terminal in root mode** and use adb to allow remote debugging with: 182 | 183 | ``` 184 | adb tcpip 5556 185 | ``` 186 | 187 | Manually configure: 188 | 189 | * Disable all sound in emulator (Settings->Sound) 190 | * Unverify appplications with Settings->Security->Uncheck Verify Apps 191 | 192 | Then run android_setup_client.sh in a host with the following parameters: 193 | 194 | ``` 195 | emulator_setup.sh {SSL/NOSSL} {EMULATOR-IP} {GATEWAY-IP-FOR-EMULATOR} 196 | ``` 197 | 198 | Choose {SSL/NOSSL} to emulator deploy type 1 or 2. 199 | 200 | This script: 201 | 202 | * Roots the emulator 203 | * Adds network static configuration 204 | * Installs SuperUser and CydiaSubstrate, links Substrate and allows it to run as root. 205 | * If it's NOSSL type, install Android-SSL-TrustKiller to skip SSL verification 206 | * Install the Marvin-toqueton fuzzer. 207 | * Disables lockscreen and suspension 208 | 209 | Lastly, create a snapshot of the VM running with any name you refer. 210 | 211 | ### Requirements ### 212 | 213 | * Deployed Marvin-frontend 214 | * Python 2.7.x (DO NOT USE Python 3.X) 215 | 216 | ### Credits ### 217 | * Joaquín Rinaudo ([@xeroxnir](https://www.twitter.com/xeroxnir)) 218 | * Juan Heguiabehere ([@jheguia](https://www.twitter.com/jheguia)) 219 | 220 | ### Who do I talk to? ### 221 | * Send an email to stic at fundacionsadosky.org.ar 222 | 223 | -------------------------------------------------------------------------------- /client/Analyzer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import os 27 | import sys 28 | import json 29 | import abc 30 | import traceback 31 | import threading 32 | sys.path.append(os.getcwd()) 33 | import Utils 34 | from Dispatcher import * 35 | from StorageAnalyzer import StorageAnalyzer 36 | from Emulator import Emulator 37 | from libmproxy import controller, proxy 38 | from libmproxy.proxy.server import ProxyServer 39 | from libmproxy.flow import FlowWriter 40 | import logging 41 | 42 | class Analyzer(controller.Master): 43 | acting_component = None 44 | 45 | def __init__(self, filter_id, package_name, description): 46 | self.report = {} 47 | self.filter_id = filter_id 48 | self.package_name = package_name 49 | self.description = description 50 | config = proxy.ProxyConfig(port=8080,mode="transparent") 51 | server = ProxyServer(config) 52 | controller.Master.__init__(self, server) 53 | flow_dump_file = open(self.get_package_name()+"_network_traffic", "wb") 54 | self.network_flow = FlowWriter(flow_dump_file) 55 | self.should_exit = None 56 | self.extra_analyzers = [InsecureTransmissionAnalyzer(self),ZIPPathTraversalAnalyzer(self)] 57 | logging.debug("Init analyzer") 58 | 59 | def get_extra_analyzers(self): 60 | return self.extra_analyzers 61 | 62 | def get_package_name(self): 63 | return self.package_name 64 | 65 | def get_filter_id(self): 66 | return self.filter_id 67 | 68 | def get_description(self): 69 | return self.description 70 | 71 | def start_analyzer(self, error_message_queue, signal_init, signal_close): 72 | try: 73 | signal_init.set() 74 | self.should_exit = signal_close 75 | controller.Master.run(self) 76 | StorageAnalyzer(self).analyze_storage() 77 | self.send_report() 78 | except Exception: 79 | print traceback.format_exc() 80 | error_message_queue.put(traceback.format_exc()) 81 | finally: 82 | self.shutdown() 83 | 84 | @staticmethod 85 | def get_analyzer(): 86 | if not Analyzer.acting_component: 87 | logging.error("Analyzer: analyzer not initialized") 88 | raise Exception("Analyzer not initialized") 89 | return Analyzer.acting_component 90 | 91 | 92 | @staticmethod 93 | def get_analyzer_for(filter_id, package_name, description): 94 | if not Analyzer.acting_component: 95 | analyzer_class = Dispatcher.get_component_for(filter_id, Analyzer) 96 | logging.debug("Analyzer class: "+ repr(analyzer_class)) 97 | Analyzer.acting_component = analyzer_class(filter_id, package_name, description) 98 | logging.debug("Analyzer acting component: "+ repr(Analyzer.acting_component)) 99 | return Analyzer.acting_component 100 | 101 | @staticmethod 102 | def is_for(FILTER_ID): 103 | return False 104 | 105 | def add_to_report(self, key, value): 106 | self.report[key] = value 107 | 108 | def get_report(self): 109 | return self.report 110 | 111 | def handle_request(self, flow): 112 | for analyzer in self.get_extra_analyzers(): 113 | analyzer.handle_request(flow) 114 | self.network_flow.add(flow) 115 | flow.reply() 116 | 117 | def handle_response(self, flow): 118 | logging.debug("Analyzer: analyzing "+ str(flow)) 119 | for analyzer in self.get_extra_analyzers(): 120 | try: 121 | analyzer.handle_response(flow) 122 | except Exception: 123 | print traceback.format_exc() 124 | self.network_flow.add(flow) 125 | flow.reply() 126 | 127 | def send_report(self): 128 | Utils.notify(Utils.get_reporter(), 'analyzer', Analyzer.get_analyzer().get_report()) 129 | 130 | 131 | from CommonComponent.ZIPPathTraversalAnalyzer import ZIPPathTraversalAnalyzer 132 | from CommonComponent.InsecureTransmissionAnalyzer import InsecureTransmissionAnalyzer 133 | from DummyFilter.DummyAnalyzer import * 134 | from SSLFilter.SSLAnalyzer import * 135 | from PhonegapFilter.PhonegapAnalyzerJSInjection import * 136 | from PhonegapFilter.PhonegapAnalyzerCVE3500 import * 137 | from FileSchemeFilter.FileSchemeAnalyzer import * 138 | from JavascriptInterfaceFilter.JavascriptInterfaceAnalyzer import * 139 | 140 | 141 | -------------------------------------------------------------------------------- /client/CommonComponent/AnalyzerDecorator.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | 27 | # DummyAnalyzer for testing 28 | class AnalyzerDecorator(): 29 | 30 | def __init__(self,analyzer): 31 | self.analyzer = analyzer 32 | 33 | def handle_request(self, flow): 34 | return 35 | 36 | def handle_response(self, flow): 37 | return 38 | 39 | def add_to_report(self, key, value): 40 | self.analyzer.add_to_report(key,value) 41 | -------------------------------------------------------------------------------- /client/CommonComponent/FuzzingTrigger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import subprocess 27 | import random 28 | from Trigger import Trigger 29 | import logging 30 | 31 | THROTTLE = 1000 32 | EVENTS_PER_TEST = 100 33 | 34 | 35 | class FuzzingTrigger(Trigger): 36 | def __init__(self, filter_id, package_name, description): 37 | Trigger.__init__(self, filter_id, package_name, description) 38 | self.throttle = 1000 39 | self.event_per_test = 100 40 | 41 | def set_throttle(self, delay): 42 | self.throttle = delay 43 | 44 | def set_events_per_test(self, event): 45 | self.event_per_test = event 46 | 47 | def trigger(self): 48 | print "Running FuzzingTrigger" 49 | seed = int(random.SystemRandom().getrandbits(32)) 50 | logging.debug ("Lanzando shell monkey -p %s -v --throttle %d -s %d %d" % ( 51 | self.get_package_name(), self.throttle, seed, self.event_per_test)) 52 | self.get_emulator().run_adb_command('shell monkey -p %s -v --throttle %d -s %d %d' % ( 53 | self.get_package_name(), self.throttle, seed, self.event_per_test)) 54 | # add events to notify to server 55 | 56 | -------------------------------------------------------------------------------- /client/CommonComponent/InsecureTransmissionAnalyzer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from AnalyzerDecorator import AnalyzerDecorator 27 | import settings 28 | 29 | 30 | # DummyAnalyzer for testing 31 | class InsecureTransmissionAnalyzer(AnalyzerDecorator): 32 | 33 | def sensitiveValues(self): 34 | return settings.FUZZER_VALUES 35 | 36 | def privateValues(self): 37 | merged = {} 38 | for values in settings.FUZZER_PRIVACY_VALUES.values(): 39 | merged.update(values) 40 | return merged 41 | 42 | def keywords(self): 43 | values = self.sensitiveValues() 44 | values.update(self.privateValues()) 45 | return values 46 | 47 | def requestContains(self, flow, keyword_value): 48 | inPath = keyword_value in flow.request.path 49 | inContent =flow.request.content is not None and keyword_value in flow.request.content 50 | inHeaders = keyword_value in str(flow.request.headers) 51 | return inPath or inContent or inHeaders 52 | 53 | def handle_request(self, flow): 54 | print flow.request.headers["Host"][0] 55 | if flow.request.scheme.endswith("http"): 56 | for (key_info, private_info_fuzzer) in self.keywords().iteritems(): 57 | if (self.requestContains(flow,private_info_fuzzer)): 58 | requested_site = flow.request.headers["Host"][0] 59 | self.add_to_report("INSECURE_TRANSMISSION", 60 | "Application has leaked sensitive information via insecure transmission. Type: "+key_info+". Value leaked: " + private_info_fuzzer + " in request " + requested_site + flow.request.path) 61 | AnalyzerDecorator.handle_request(self,flow) 62 | -------------------------------------------------------------------------------- /client/CommonComponent/ZIPPathTraversalAnalyzer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from AnalyzerDecorator import AnalyzerDecorator 27 | 28 | class ZIPPathTraversalAnalyzer(AnalyzerDecorator): 29 | 30 | def handle_response(self, flow): 31 | #check for zip file 32 | if flow.request.scheme.endswith("http") and flow.response.headers.get("Content-Type")[0].endswith("application/zip"): 33 | self.add_to_report("ZIP_PATH_TRAVERSAL","A ZIP file was downloaded using HTTP, application could be vulnerable to ZIP path traversal which could cause remote code execution or at least allowing an attacker to write to any file") 34 | AnalyzerDecorator.handle_response(self,flow) -------------------------------------------------------------------------------- /client/CommonComponent/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | -------------------------------------------------------------------------------- /client/Dispatcher.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import Utils 27 | 28 | 29 | class Dispatcher(object): 30 | # find the right trigger, analyzer and reporter according to filter_ID 31 | @staticmethod 32 | def get_component_for(filter_ID, claz): 33 | #ask for subclasses and find wich one can validate the element 34 | subclasses = Utils.get_subclasses(claz) 35 | #print subclasses 36 | for subclass in subclasses: 37 | if subclass.is_for(filter_ID): 38 | return subclass #create subclass 39 | return claz 40 | -------------------------------------------------------------------------------- /client/DummyFilter/DummyAnalyzer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Analyzer import Analyzer 27 | 28 | #DummyAnalyzer for testing 29 | class DummyAnalyzer(Analyzer): 30 | 31 | @staticmethod 32 | def is_for(filter_id): 33 | #attend Dummy 34 | return "dummy" == filter_id 35 | 36 | 37 | -------------------------------------------------------------------------------- /client/DummyFilter/DummyReporter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Reporter import Reporter 27 | 28 | #Dummy DummyReporter for testing 29 | class DummyReporter(Reporter): 30 | 31 | @staticmethod 32 | def is_for(filter_id): 33 | #attend Dummy 34 | return "dummy" == filter_id 35 | 36 | #overrides 37 | def create_vm_reports(self,trigger,analyzer): 38 | return {'status':'UNKNOWN','description':{'trigger':trigger,'analyzer':analyzer}} 39 | 40 | -------------------------------------------------------------------------------- /client/DummyFilter/DummyTrigger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import subprocess 27 | import random 28 | from CommonComponent.FuzzingTrigger import FuzzingTrigger 29 | 30 | THROTTLE = 1000 31 | EVENTS_PER_TEST = 100 32 | 33 | class DummyTrigger(FuzzingTrigger): 34 | 35 | @staticmethod 36 | def is_for(filter_id): 37 | #attend Dummy 38 | return filter_id == "dummy" 39 | 40 | #overrides 41 | def trigger(self): 42 | FuzzingTrigger.trigger(self) 43 | 44 | 45 | -------------------------------------------------------------------------------- /client/DummyFilter/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | -------------------------------------------------------------------------------- /client/Emulator.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import abc 27 | import time 28 | import Utils 29 | import netifaces 30 | import logging 31 | 32 | class Emulator(object): 33 | acting_component = None 34 | 35 | def __init__(self, filter_id, package_name, description): 36 | self.remote_ip = description['emulator'] + ':5556' 37 | Utils.run_blocking_cmd("adb connect {0}:5556".format(self.remote_ip)) 38 | Utils.run_blocking_cmd("adb root") 39 | logging.debug("waiting to connect to adb as root") 40 | time.sleep(5) 41 | Utils.run_blocking_cmd("adb connect {0}:5556".format(self.remote_ip)) 42 | logging.debug("connected to adb as root") 43 | 44 | @staticmethod 45 | def get_emulator_for(filter_id, package_name, description): 46 | if not Emulator.acting_component: 47 | Emulator.acting_component = Emulator(filter_id, package_name, description) 48 | return Emulator.acting_component 49 | 50 | @staticmethod 51 | def get_emulator(): 52 | return Emulator.acting_component 53 | 54 | def set_localhost_as_gateway(self): 55 | local_address = netifaces.ifaddresses('eth0')[netifaces.AF_INET][0]['addr'] 56 | return self.run_adb_command('shell \"busybox route add -host {0} dev eth0; busybox route delete default; route add default gw {0} dev eth0\"'.format(local_address)) 57 | 58 | def disconnect(self): 59 | return Utils.run_blocking_cmd("adb disconnect") 60 | 61 | def get_remote_ip(self): 62 | return self.remote_ip.split(":")[0] 63 | 64 | def run_adb_command(self, command): 65 | return Utils.run_blocking_cmd("adb -s {0} ".format(self.remote_ip) + command) 66 | -------------------------------------------------------------------------------- /client/FileSchemeFilter/FileSchemeAnalyzer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Analyzer import Analyzer 27 | import base64 28 | 29 | # DummyAnalyzer for testing 30 | class FileSchemeAnalyzer(Analyzer): 31 | @staticmethod 32 | def is_for(filter_id): 33 | # attend Dummy 34 | return "WEBVIEW_FILE_SCHEME" == filter_id 35 | 36 | def handle_request(self, flow): 37 | if flow.request.path.find("?vulnerable_file_scheme") != -1: 38 | activity_index = flow.request.path.find("&activity=") 39 | self.add_to_report(self.get_filter_id(), 40 | "Dynamically verified that Javascript can be inyected running as file:// scheme via an Intent to " + base64.b64decode( 41 | flow.request.path[ 42 | activity_index + len( 43 | "&activity="):])) 44 | Analyzer.handle_request(self,flow) -------------------------------------------------------------------------------- /client/FileSchemeFilter/FileSchemeReporter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Reporter import Reporter 27 | 28 | # Dummy DummyReporter for testing 29 | class FileSchemeReporter(Reporter): 30 | @staticmethod 31 | def is_for(filter_id): 32 | #attend Dummy 33 | return "WEBVIEW_FILE_SCHEME" == filter_id 34 | 35 | #overrides 36 | def create_vm_reports(self, trigger, analyzer): 37 | if len(analyzer.keys()): 38 | return {'status': 'SUCCESS', 'description': analyzer} 39 | else: 40 | return {'status': 'UNKNOWN', 'description': analyzer} 41 | -------------------------------------------------------------------------------- /client/FileSchemeFilter/FileSchemeTrigger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import time 27 | import base64 28 | from Trigger import Trigger 29 | 30 | THROTTLE = 1000 31 | EVENTS_PER_TEST = 500 32 | 33 | 34 | class FileSchemeTrigger(Trigger): 35 | @staticmethod 36 | def is_for(filter_id): 37 | # attend Dummy 38 | return "WEBVIEW_FILE_SCHEME" == filter_id 39 | 40 | 41 | def get_exported_activity(self): 42 | return self.get_description()["activity"] 43 | 44 | # overrides 45 | def trigger(self): 46 | html = ''' 47 | 48 | 49 | 50 | Webview File Scheme Test 51 | 52 | 53 | 54 | 62 |

Webview File Scheme Test

63 | 64 | 65 | ''' 66 | with open("test.html", "w") as local: 67 | local.write(html) 68 | self.get_emulator().run_adb_command('push test.html /mnt/sdcard/test.html') 69 | self.get_emulator().run_adb_command( 70 | "shell am start -n {0}/{1} file://mnt/sdcard/test.html".format( 71 | self.get_package_name(), self.get_exported_activity())) 72 | time.sleep(4) 73 | print "shell am start -n {0}/{1} file://mnt/sdcard/test.html".format( 74 | self.get_package_name(), self.get_exported_activity()) 75 | self.get_emulator().run_adb_command( 76 | "shell am start -a android.intent.action.VIEW -a android.intent.category.BROWSEABLE -n {0}/{1} \"file://mnt/sdcard/test.html\"".format( 77 | self.get_package_name(), self.get_exported_activity())) 78 | # TODO: how to wait for app trying to load? 79 | time.sleep(4) -------------------------------------------------------------------------------- /client/FileSchemeFilter/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | -------------------------------------------------------------------------------- /client/JavascriptInterfaceFilter/JavascriptInterfaceAnalyzer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Analyzer import Analyzer 27 | from libmproxy.protocol.http import decoded 28 | import lxml.html 29 | import base64 30 | import sys 31 | 32 | 33 | class JavascriptInterfaceAnalyzer(Analyzer): 34 | 35 | @staticmethod 36 | def is_for(filter_id): 37 | # attend Dummy 38 | return "JAVASCRIPTINTERFACE" == filter_id 39 | 40 | def handle_request(self, flow): 41 | if flow.request.path.find("?vulnerable_javascript_injection") != -1: 42 | visited_url_index = flow.request.path.find("&url=") 43 | interface_url_index = flow.request.path.find("&interface=") 44 | self.add_to_report(self.get_filter_id(), 45 | "Dynamically verified that malicious Javascript can be injected via HTTP via url %s and can run arbitrary code via the Javascript Interface %s" % ( 46 | base64.b64decode( 47 | flow.request.path[visited_url_index + len("&url="):]), 48 | flow.request.path[interface_url_index + len("&interface="):visited_url_index])) 49 | Analyzer.handle_request(self,flow) 50 | 51 | def handle_response(self, flow): 52 | print "request path is %s " % flow.request.path 53 | # If it's injectable and it's not the injected request 54 | requested_site = flow.request.headers["Host"][0] 55 | if flow.request.scheme.endswith("http") and requested_site.find("www.fundacionsadosky.org.ar") == -1: 56 | visited_url = base64.b64encode(requested_site + flow.request.path) 57 | #taken from www.droidsec.org/tests/addjsif/ 58 | script = '''vulnerable=[];for(i in top){el=top[i];if(el==null){continue};if(typeof(el)==='function'){continue}try{top[i].getClass().forName('java.lang.Runtime');vulnerable.push(i)}catch(e){}}if(vulnerable.length>0){var request=new XMLHttpRequest();request.open("GET","http://www.fundacionsadosky.org.ar/?vulnerable_javascript_injection=true&interface="+vulnerable.join()+"&url=''' + visited_url + '''",true);request.onreadystatechange=function(){};request.send()}''' 59 | content_type = flow.response.headers.get("Content-Type") 60 | if not content_type: 61 | content_type = flow.response.headers.get("Content-type") 62 | if content_type and "text/html" in content_type[0]: 63 | with decoded(flow.response): # automatically decode gzipped responses. 64 | if flow.response.content: 65 | try: 66 | response = flow.response.content 67 | print "Response is "+response 68 | root = lxml.html.fromstring(response) 69 | if root.find('.//*') is not None: 70 | print "TRIED MODIFYING /html " + requested_site+ flow.request.path 71 | # is HTML, use lxml to insert to head, body or script 72 | append_in = root.find('.//head') 73 | if append_in is None: 74 | append_in = root.find('.//body') 75 | elif append_in is None: 76 | append_in = root.find('.//script').getparent() 77 | else: 78 | append_in = root 79 | script = lxml.html.fromstring('') 80 | if append_in is not None: 81 | append_in.append(script) 82 | flow.response.content = lxml.html.tostring(root) 83 | except: 84 | print "There was a problem parsing the html response, skip it" 85 | # mimetype may be application/javascript or text/javascript 86 | elif content_type and "javascript" in content_type[0]: 87 | with decoded(flow.response): # automatically decode gzipped responses. 88 | print "TRIED MODIFYING /javascript " + requested_site + flow.request.path 89 | # is searching for library .JS (both cases sensitive) or JQUERY 90 | flow.response.content = script.encode("utf-8") + flow.response.content 91 | Analyzer.handle_response(self,flow) 92 | -------------------------------------------------------------------------------- /client/JavascriptInterfaceFilter/JavascriptInterfaceReporter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Reporter import Reporter 27 | 28 | # Dummy DummyReporter for testing 29 | class JavascriptInterfaceAnalyzer(Reporter): 30 | @staticmethod 31 | def is_for(filter_id): 32 | # attend Dummy 33 | return "JAVASCRIPTINTERFACE" == filter_id 34 | 35 | # overrides 36 | def create_vm_reports(self, trigger, analyzer): 37 | if len(analyzer.keys()): 38 | return {'status': 'SUCCESS', 'description': analyzer} 39 | else: 40 | return {'status': 'UNKNOWN', 'description': analyzer} 41 | -------------------------------------------------------------------------------- /client/JavascriptInterfaceFilter/JavascriptInterfaceTrigger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import Utils 27 | import time 28 | from CommonComponent.FuzzingTrigger import FuzzingTrigger 29 | import logging 30 | 31 | THROTTLE = 1000 32 | EVENTS_PER_TEST = 500 33 | 34 | 35 | class JavascriptTrigger(FuzzingTrigger): 36 | def __init__(self, filter_id, package_name, description): 37 | FuzzingTrigger.__init__(self, filter_id, package_name, description) 38 | 39 | @staticmethod 40 | def is_for(filter_id): 41 | # attend Dummy 42 | return "JAVASCRIPTINTERFACE" == filter_id 43 | 44 | def trigger(self): 45 | # delay throttle since there's a lot of communication to be made probably 46 | self.set_throttle(4000) 47 | #halve the events so it doesn't take that much 48 | self.set_events_per_test(50) 49 | logging.debug("Lanzando FuzzingTrigger") 50 | FuzzingTrigger.trigger(self) 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /client/JavascriptInterfaceFilter/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | -------------------------------------------------------------------------------- /client/PhonegapFilter/PhonegapAnalyzerCVE3500.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Analyzer import Analyzer 27 | from libmproxy.protocol.http import decoded 28 | import lxml.html 29 | import base64 30 | 31 | # DummyAnalyzer for testing 32 | class PhonegapAnalyzer(Analyzer): 33 | def __init__(self, filter_id, package_name, description): 34 | Analyzer.__init__(self,filter_id, package_name, description) 35 | # number of ocurrences of inyection, to add in report 36 | self.inyection_count = 0 37 | 38 | @staticmethod 39 | def is_for(filter_id): 40 | # attend Dummy 41 | return "PHONEGAP_CVE_3500_URL" == filter_id 42 | 43 | def handle_request(self, flow): 44 | if flow.request.path.find("?vulnerable_cve_3500") != -1: 45 | self.add_to_report(self.get_filter_id(), "Dynamically verified that application is vulnerable to CVE-3500") 46 | Analyzer.handle_request(self,flow) 47 | -------------------------------------------------------------------------------- /client/PhonegapFilter/PhonegapAnalyzerJSInjection.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Analyzer import Analyzer 27 | from libmproxy.protocol.http import decoded 28 | import lxml.html 29 | import base64 30 | from JavascriptInterfaceFilter.JavascriptInterfaceAnalyzer import JavascriptInterfaceAnalyzer 31 | # DummyAnalyzer for testing 32 | class PhonegapAnalyzer(JavascriptInterfaceAnalyzer): 33 | 34 | @staticmethod 35 | def is_for(filter_id): 36 | # attend Dummy 37 | return "PHONEGAP_JS_INJECTION" == filter_id 38 | 39 | def handle_request(self, flow): 40 | if flow.request.path.find("?vulnerable_javascript_injection") != -1: 41 | visited_url_index = flow.request.path.find("&url=") 42 | self.add_to_report(self.get_filter_id(), 43 | "Dynamically verified that malicious Javascript can be injected via HTTP via url %s" % base64.b64decode( 44 | flow.request.path[visited_url_index + 5:])) 45 | Analyzer.handle_request(self,flow) 46 | 47 | -------------------------------------------------------------------------------- /client/PhonegapFilter/PhonegapReporter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Reporter import Reporter 27 | 28 | # Dummy DummyReporter for testing 29 | class PhonegapReporter(Reporter): 30 | @staticmethod 31 | def is_for(filter_id): 32 | # attend Dummy 33 | return "PHONEGAP_JS_INJECTION" == filter_id or \ 34 | "PHONEGAP_CVE_3500_URL" == filter_id 35 | 36 | # overrides 37 | def create_vm_reports(self, trigger, analyzer): 38 | if len(analyzer.keys()): 39 | return {'status': 'SUCCESS', 'description': analyzer} 40 | else: 41 | return {'status': 'UNKNOWN', 'description': analyzer} 42 | -------------------------------------------------------------------------------- /client/PhonegapFilter/PhonegapTriggerCVE3500.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import Utils 27 | import time 28 | import traceback 29 | from CommonComponent.FuzzingTrigger import FuzzingTrigger 30 | 31 | THROTTLE = 1000 32 | EVENTS_PER_TEST = 500 33 | 34 | 35 | class PhonegapTrigger(FuzzingTrigger): 36 | @staticmethod 37 | def is_for(filter_id): 38 | # attend Dummy 39 | return "PHONEGAP_CVE_3500_URL" == filter_id 40 | 41 | def get_main_activity(self): 42 | return self.get_description()["activity"] 43 | 44 | def test_cve_3500(self): 45 | html = ''' 46 | 47 | 48 | 49 | Testing Phonegap CVE-2014-3500 50 | 51 | 52 | 53 | 65 |

Testing Phonegap CVE-2014-3500

66 | 67 | 68 | ''' 69 | with open("test.html", "w") as local: 70 | local.write(html) 71 | self.get_emulator().run_adb_command('push test.html /sdcard/test.html') 72 | self.get_emulator().run_adb_command( 73 | "shell am start -a android.intent.action.MAIN -n {0}/{1} --es url file:///sdcard/test.html".format( 74 | self.get_package_name(), self.get_main_activity())) 75 | time.sleep(3) 76 | 77 | 78 | def trigger(self): 79 | self.test_cve_3500() 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /client/PhonegapFilter/PhonegapTriggerJSInjection.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import Utils 27 | import time 28 | from CommonComponent.FuzzingTrigger import FuzzingTrigger 29 | 30 | THROTTLE = 1000 31 | EVENTS_PER_TEST = 500 32 | 33 | 34 | class PhonegapTrigger(FuzzingTrigger): 35 | @staticmethod 36 | def is_for(filter_id): 37 | # attend Dummy 38 | return "PHONEGAP_JS_INJECTION" == filter_id 39 | 40 | def test_javascript_inyection(self): 41 | # delay throttle since there's a lot of communication to be made probably 42 | self.set_throttle(4000) 43 | # halve the events so it doesn't take that much 44 | self.set_events_per_test(100) 45 | FuzzingTrigger.trigger(self) 46 | 47 | def trigger(self): 48 | self.test_javascript_inyection() 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /client/PhonegapFilter/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | -------------------------------------------------------------------------------- /client/Reporter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import SimpleHTTPServer 27 | import SocketServer 28 | import os 29 | import abc 30 | import sys 31 | import logging 32 | import traceback 33 | import json 34 | import threading 35 | import multiprocessing 36 | from Dispatcher import * 37 | from Emulator import Emulator 38 | 39 | 40 | class ReporterHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 41 | report = {} 42 | 43 | def is_full_report(self): 44 | return len(ReporterHandler.report.keys()) == 2 45 | 46 | # only has POST 47 | 48 | def do_POST(self): 49 | # logging.warning("======= POST STARTED =======") 50 | try: 51 | if self.path == '/trigger' or self.path == '/analyzer': 52 | length = int(self.headers['Content-Length']) 53 | content = json.loads(self.rfile.read(length).decode('utf-8')) 54 | #save any of those two reports 55 | ReporterHandler.report[self.path[1:]] = content 56 | logging.warning(ReporterHandler.report) 57 | if self.is_full_report(): #have both reports already 58 | Reporter.get_reporter().send_report(ReporterHandler.report['trigger'], 59 | ReporterHandler.report['analyzer']) 60 | self.send_response(200) 61 | else: 62 | self.send_response(404) 63 | return 64 | except: 65 | logging.warning(traceback.format_exc()) 66 | self.send_response(500) 67 | 68 | 69 | class Reporter(object): 70 | acting_component = None 71 | 72 | def __init__(self, filter_id, package_name, description): 73 | self.filter_id = filter_id 74 | self.package_name = package_name 75 | self.description = description 76 | 77 | @staticmethod 78 | def get_reporter(): 79 | if not Reporter.acting_component: 80 | raise Exception("Reporter not initialized") 81 | return Reporter.acting_component 82 | 83 | @staticmethod 84 | def get_reporter_for(filter_id, package_name, description): 85 | if not Reporter.acting_component: 86 | reporter_class = Dispatcher.get_component_for(filter_id, Reporter) 87 | Reporter.acting_component = reporter_class(filter_id, package_name, description) 88 | return Reporter.acting_component 89 | 90 | @staticmethod 91 | # returns if its for FILTER_ID 92 | def is_for(filter_id): 93 | return False 94 | 95 | def get_package_name(self): 96 | return self.package_name 97 | 98 | def get_filter_id(self): 99 | return self.filter_id 100 | 101 | def get_description(self): 102 | return self.description 103 | 104 | def start_reporter(self, error_message_queue,signal_init,signal_close): 105 | try: 106 | Handler = ReporterHandler 107 | SocketServer.TCPServer.allow_reuse_address = True 108 | server = SocketServer.TCPServer(('localhost', 8081), Handler) 109 | server_thread = threading.Thread(target=server.serve_forever,args=()) 110 | server_thread.start() 111 | signal_init.set() 112 | signal_close.wait() 113 | server.shutdown() 114 | except Exception: 115 | print traceback.format_exc() 116 | error_message_queue.put(traceback.format_exc()) 117 | 118 | @abc.abstractmethod 119 | def create_vm_reports(self, trigger_rep, analyzer_rep): 120 | # create final report to send VM Manager 121 | ip = Emulator.get_emulator().get_remote_ip() 122 | return {'status': 'ERROR', 'emulator': ip, 'description': {'errors': 'Called abstract method from reporter'}} 123 | 124 | def send_report(self, trigger, analyzer): 125 | filter_report = self.create_vm_reports(trigger, analyzer) 126 | ip = Emulator.get_emulator().get_remote_ip() 127 | filter_report['emulator'] = ip 128 | if Utils.isTestingMode(): 129 | print "FINAL REPORT " 130 | print filter_report 131 | else: 132 | Utils.notify(Utils.get_VM_Manager(), 'report', filter_report) 133 | 134 | 135 | from DummyFilter.DummyReporter import * 136 | from SSLFilter.SSLReporter import * 137 | from FileSchemeFilter.FileSchemeReporter import * 138 | from PhonegapFilter.PhonegapReporter import * 139 | from JavascriptInterfaceFilter.JavascriptInterfaceReporter import * 140 | 141 | -------------------------------------------------------------------------------- /client/SSLFilter/SSLAnalyzer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Analyzer import Analyzer 27 | 28 | # DummyAnalyzer for testing 29 | class SSLAnalyzer(Analyzer): 30 | @staticmethod 31 | def is_for(filter_id): 32 | # attend Dummy 33 | return filter_id in [ 34 | "SSL_CUSTOM_TRUSTMANAGER", 35 | "SSL_CUSTOM_HOSTNAMEVERIFIER", 36 | "SSL_ALLOWALL_HOSTNAMEVERIFIER", 37 | "SSL_INSECURE_SOCKET_FACTORY", 38 | "SSL_WEBVIEW_ERROR" 39 | ] 40 | 41 | def handle_request(self, flow): 42 | if flow.request.scheme.endswith("https"): 43 | self.add_to_report(self.get_filter_id(), 44 | 'SSL connection to host %s, app not validating certificates properly' % ( 45 | flow.request.headers["Host"][0] + flow.request.path)) 46 | Analyzer.handle_request(self,flow) 47 | 48 | -------------------------------------------------------------------------------- /client/SSLFilter/SSLReporter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from Reporter import Reporter 27 | 28 | # Dummy DummyReporter for testing 29 | class SSLReporter(Reporter): 30 | @staticmethod 31 | def is_for(filter_id): 32 | # attend Dummy 33 | return filter_id in [ 34 | "SSL_CUSTOM_TRUSTMANAGER", 35 | "SSL_CUSTOM_HOSTNAMEVERIFIER", 36 | "SSL_ALLOWALL_HOSTNAMEVERIFIER", 37 | "SSL_INSECURE_SOCKET_FACTORY", 38 | "SSL_WEBVIEW_ERROR" 39 | ] 40 | 41 | #overrides 42 | def create_vm_reports(self, trigger, analyzer): 43 | if len(analyzer.keys()): 44 | return {'status': 'SUCCESS', 'description': analyzer} 45 | else: 46 | return {'status': 'UNKNOWN', 'description': analyzer} 47 | -------------------------------------------------------------------------------- /client/SSLFilter/SSLTrigger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import subprocess 27 | import random 28 | from Trigger import Trigger 29 | from CommonComponent.FuzzingTrigger import FuzzingTrigger 30 | 31 | THROTTLE = 1000 32 | EVENTS_PER_TEST = 500 33 | 34 | 35 | class SSLTrigger(FuzzingTrigger): 36 | def __init__(self, filter_id, package_name, description): 37 | FuzzingTrigger.__init__(self, filter_id, package_name, description) 38 | self.set_events_per_test(200) 39 | 40 | @staticmethod 41 | def is_for(filter_id): 42 | # attend Dummy 43 | return filter_id in [ 44 | "SSL_CUSTOM_TRUSTMANAGER", 45 | "SSL_CUSTOM_HOSTNAMEVERIFIER", 46 | "SSL_ALLOWALL_HOSTNAMEVERIFIER", 47 | "SSL_INSECURE_SOCKET_FACTORY", 48 | "SSL_WEBVIEW_ERROR" 49 | ] 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /client/SSLFilter/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | -------------------------------------------------------------------------------- /client/StorageAnalyzer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import subprocess 27 | import shlex 28 | import os 29 | import sqlite3 30 | import traceback 31 | from Emulator import Emulator 32 | import settings 33 | 34 | 35 | class StorageAnalyzer(object): 36 | def __init__(self, analyzer): 37 | self.analyzer = analyzer 38 | 39 | def sensitiveValues(self): 40 | return settings.FUZZER_VALUES 41 | 42 | def extracted_storage_dir(self): 43 | return self.analyzer.get_package_name() + "_files" 44 | 45 | def internal_storage_dir(self): 46 | return self.extracted_storage_dir() + "/internal_storage/" 47 | 48 | def modified_sdcard_dir(self): 49 | return self.extracted_storage_dir() + "/sdcard_modified_files/" 50 | 51 | def extract_storage(self): 52 | emulator = Emulator.get_emulator() 53 | directory = self.extracted_storage_dir() 54 | if not os.path.exists(directory): 55 | os.makedirs(directory) 56 | # dump internal memory and sdcard 57 | emulator.run_adb_command( 58 | "pull /data/data/" + self.analyzer.get_package_name() + " " + self.internal_storage_dir()) 59 | emulator.run_adb_command("pull /sdcard/" + " " + self.modified_sdcard_dir()) 60 | 61 | def is_database(self, fileName): 62 | cmd = shlex.split('file {0}'.format(fileName)) 63 | result = subprocess.check_output(cmd) 64 | type = result.split(':')[1] 65 | return 'SQLite' in type 66 | 67 | def get_filter_id(self): 68 | return "INSECURE_STORAGE" 69 | 70 | def search_for_plaintext_in_db(self, connection, table, database): 71 | cursor = connection.cursor() 72 | cursor.execute("SELECT * FROM '{0}';".format(table)) 73 | for row in cursor: 74 | for keyword, value in self.sensitiveValues().iteritems(): 75 | if value in row: 76 | self.analyzer.add_to_report(self.get_filter_id(), 77 | "Found plaintext {0} (value {1}) in table {2} of database {3}".format(keyword, value, table, 78 | database)) 79 | 80 | def search_for_plaintext(self, file): 81 | content = open(file).read() 82 | for keyword, value in self.sensitiveValues().iteritems(): 83 | if value in content: 84 | self.analyzer.add_to_report(self.get_filter_id(), 85 | "Found plaintext {0} (value {1}) in file {2}".format(keyword, value, file)) 86 | 87 | def analyze_shared_preferences(self, path): 88 | self.analyze_files(path) 89 | 90 | def analyze_files(self, path): 91 | for subdir, dirs, files in os.walk(path): 92 | for file in files: 93 | filePath = os.path.join(subdir, file) 94 | self.search_for_plaintext(filePath) 95 | 96 | def analyze_storage(self): 97 | self.extract_storage() 98 | self.analyze_database(self.internal_storage_dir() + 'databases/') 99 | self.analyze_shared_preferences(self.internal_storage_dir() + 'shared_prefs/') 100 | self.analyze_files(self.internal_storage_dir() + 'files/') 101 | 102 | def analyze_database(self, path): 103 | for subdir, dirs, files in os.walk(path): 104 | for file in files: 105 | filePath = os.path.join(subdir, file) 106 | try: 107 | if self.is_database(filePath): 108 | con = sqlite3.connect(filePath) 109 | cursor = con.cursor() 110 | cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") 111 | for table in cursor: 112 | self.search_for_plaintext_in_db(con, table[0], filePath) 113 | except: 114 | print "Error " + filePath 115 | print traceback.format_exc() 116 | -------------------------------------------------------------------------------- /client/Trigger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import abc 27 | import traceback 28 | from Dispatcher import * 29 | from Emulator import Emulator 30 | # Import Filters after defining trigger 31 | 32 | class Trigger(object): 33 | # only need one component 34 | acting_component = None 35 | 36 | def __init__(self, filter_id, package_name, description): 37 | self.report = {} 38 | self.filter_id = filter_id 39 | self.package_name = package_name 40 | self.description = description 41 | 42 | def get_package_name(self): 43 | return self.package_name 44 | 45 | def get_filter_id(self): 46 | return self.filter_id 47 | 48 | def get_description(self): 49 | return self.description 50 | 51 | def get_emulator(self): 52 | return Emulator.get_emulator() 53 | 54 | @staticmethod 55 | def get_trigger(): 56 | if not Trigger.acting_component: 57 | raise Exception("Trigger not initialized") 58 | return Trigger.acting_component 59 | 60 | @staticmethod 61 | def get_trigger_for(filter_id, package_name, description): 62 | if not Trigger.acting_component: 63 | trigger_class = Dispatcher.get_component_for(filter_id, Trigger) 64 | Trigger.acting_component = trigger_class(filter_id, package_name, description) 65 | return Trigger.acting_component 66 | 67 | @staticmethod 68 | # returns if its for FILTER_ID 69 | def is_for(FILTER_ID): 70 | return False 71 | 72 | @abc.abstractmethod 73 | def trigger(self): 74 | print "Trigger Base class" 75 | 76 | def add_to_report(self, key, value): 77 | if self.report[key]: 78 | self.report[key] = value 79 | else: 80 | self.report[key].append(value) 81 | 82 | def get_report(self): 83 | return self.report 84 | 85 | def send_report(self): 86 | Utils.notify(Utils.get_reporter(), 'trigger', self.get_report()) 87 | 88 | def start_trigger(self, error_message_queue, signal_init, signal_close): 89 | try: 90 | # do not block VMclient 91 | signal_init.set() 92 | self.trigger() 93 | self.send_report() 94 | except Exception: 95 | print traceback.format_exc() 96 | error_message_queue.put(traceback.format_exc()) 97 | 98 | 99 | from DummyFilter.DummyTrigger import * 100 | from SSLFilter.SSLTrigger import * 101 | from PhonegapFilter.PhonegapTriggerJSInjection import * 102 | from PhonegapFilter.PhonegapTriggerCVE3500 import * 103 | from FileSchemeFilter.FileSchemeTrigger import * 104 | from JavascriptInterfaceFilter.JavascriptInterfaceTrigger import * 105 | 106 | 107 | -------------------------------------------------------------------------------- /client/Utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import urllib2 27 | import subprocess 28 | import shlex 29 | import traceback 30 | import json 31 | import settings 32 | import logging 33 | 34 | def isTestingMode(): 35 | return settings.DEBUG_MODE 36 | 37 | 38 | def notify(address, path, params): 39 | logging.debug("Notifying "+address+'/'+path) 40 | data = json.dumps(params) 41 | url = '''http://''' + address + '/' + path 42 | try: 43 | logging.debug("Trigger: notify "+url +", data: "+data) 44 | request = urllib2.Request(url, data=data, headers={'Content-Type': 'application/json'}) 45 | return urllib2.urlopen(request).read() 46 | except: 47 | print traceback.format_exc() 48 | logging.error(traceback.format_exc()) 49 | pass 50 | 51 | 52 | def run_non_blocking_cmd(command): 53 | logging.debug("Running non-blocking command: "+command) 54 | return subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE) 55 | 56 | 57 | def log_process(p): 58 | if not p: 59 | return "process is null" 60 | s = None 61 | if p.poll() is not None: 62 | s = "process has finished already. Return code: %s\n" % p.returncode 63 | else: 64 | s = "process hasn't finished. Terminating it." 65 | p.kill() 66 | o, e = p.communicate() 67 | s += "STDOUT: %s, STDERR : %s " % (o, e) 68 | return s 69 | 70 | 71 | def run_blocking_cmd(command): 72 | logging.debug("Running blocking command: "+command) 73 | return subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() 74 | 75 | 76 | def get_subclasses(c): 77 | subclasses = c.__subclasses__() 78 | #logging.debug("Subclassing " + repr(c)+". First list: " + repr(subclasses)) 79 | for d in list(subclasses): 80 | subclasses.extend(get_subclasses(d)) 81 | #logging.debug ("Subclassing "+repr(c)+" : Final list: " + repr(subclasses)) 82 | return subclasses 83 | 84 | 85 | def get_VM_Manager(): 86 | return settings.VM_MANAGER_IP 87 | 88 | 89 | def get_reporter(): 90 | return settings.REPORTER_IP 91 | -------------------------------------------------------------------------------- /client/VMClient.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | # -*- coding: utf-8 -*- 27 | import time 28 | import Utils 29 | import urllib 30 | import urllib2 31 | import traceback 32 | import sys 33 | import json 34 | import multiprocessing 35 | from Reporter import Reporter 36 | from Trigger import Trigger 37 | from Analyzer import Analyzer 38 | from Emulator import Emulator 39 | import Queue 40 | import threading 41 | import logging 42 | 43 | 44 | class VMClient(object): 45 | # since it's not thread safe, no modification of VMClient should occur during running of Trigger or Reporter 46 | vm_client = None 47 | 48 | @classmethod 49 | def get_VM_client(cls): 50 | if cls.vm_client is None: 51 | cls.vm_client = VMClient() 52 | return cls.vm_client 53 | 54 | def __init__(self): 55 | logging.basicConfig(filename="/tmp/VMClient.log", level=logging.DEBUG) 56 | self.get_apk_from_manager() 57 | self.trigger = Trigger.get_trigger_for(self.get_filter_tag(), self.get_package_name(), self.get_description()) 58 | self.reporter = Reporter.get_reporter_for(self.get_filter_tag(), self.get_package_name(), 59 | self.get_description()) 60 | self.analyzer = Analyzer.get_analyzer_for(self.get_filter_tag(), self.get_package_name(), 61 | self.get_description()) 62 | self.emulator = Emulator.get_emulator_for(self.get_filter_tag(), self.get_package_name(), 63 | self.get_description()) 64 | self.error_queue = multiprocessing.Queue() 65 | self.setup_device() 66 | logging.debug("Init successful") 67 | logging.debug("Trigger: " + repr(self.trigger)) 68 | logging.debug("Reporter: " + repr(self.reporter)) 69 | logging.debug("Analyzer: " + repr(self.analyzer)) 70 | logging.debug("Emulator: " + repr(self.emulator)) 71 | 72 | def get_emulator(self): 73 | return self.emulator 74 | 75 | def get_package_name(self): 76 | return self.package_name 77 | 78 | def get_filter_tag(self): 79 | return self.filter_id 80 | 81 | def get_description(self): 82 | return self.description 83 | 84 | def get_apk_from_manager(self): 85 | if Utils.isTestingMode(): 86 | print "VMClient is being tested" 87 | self.filter_id = sys.argv[1] 88 | self.package_name = sys.argv[2] 89 | self.description = json.loads(sys.argv[3]) 90 | logging.debug("VULNERABILITY: " + self.filter_id) 91 | logging.debug("PACKAGE: " + self.package_name) 92 | logging.debug("DESCRIPTION: " + json.dumps(self.description)) 93 | else: 94 | url = '''http://''' + Utils.get_VM_Manager() + '/apk' 95 | # Open the url 96 | try: 97 | response = urllib.urlopen(url) 98 | self.filter_id = response.info().dict['filter'] 99 | self.package_name = response.info().dict['apk'] # save only name 100 | download_url = response.info().dict['download_url'] # save only name 101 | content = response.read() 102 | self.description = json.loads(content) 103 | response = urllib.urlopen(download_url) 104 | # Open our local file for writing 105 | with open(self.package_name + ".apk", "w") as local_file: 106 | local_file.write(response.read()) 107 | local_file.flush() 108 | local_file.close() 109 | # handle errors 110 | print "HANDLING:" 111 | print "VULNERABILITY: " + self.filter_id 112 | print "PACKAGE: " + self.package_name 113 | print "DESCRIPTION: " + json.dumps(self.description) 114 | except urllib2.HTTPError, e: 115 | print "HTTP Error:", e.code, url 116 | except urllib2.URLError, e: 117 | print "URL Error:", e.reason, url 118 | 119 | def send_FAIL_signal(self,description): 120 | headers = {'status': 'ERROR', 'emulator': self.get_emulator().get_remote_ip(), 121 | 'description': description} 122 | logging.error(description) 123 | Utils.notify(Utils.get_VM_Manager(), 'error', headers) 124 | 125 | def setup_device(self): 126 | emulator = self.get_emulator() 127 | # install application 128 | (output, err) = emulator.run_adb_command('install -r %s' % (self.package_name + ".apk")) 129 | self.add_error_message(output) 130 | self.add_error_message(err) 131 | logging.debug(err) 132 | #set client as gateway so analyzer intercepts traffic 133 | message = self.emulator.set_localhost_as_gateway() 134 | logging.debug("Setting localhost as gateway: "+ str(message)) 135 | 136 | 137 | def start_trigger_process(self): 138 | return self.start_component_and_wait_for_initialization(self.trigger.start_trigger) 139 | 140 | def start_analyzer_process(self): 141 | return self.start_component_and_wait_for_initialization(self.analyzer.start_analyzer) 142 | 143 | def start_reporter_process(self): 144 | return self.start_component_and_wait_for_initialization(self.reporter.start_reporter) 145 | 146 | def start_component_and_wait_for_initialization(self, target): 147 | signal_init = multiprocessing.Event() 148 | signal_close = multiprocessing.Event() 149 | process = multiprocessing.Process(target=target, args=(self.error_queue, signal_init, signal_close)) 150 | process.start() 151 | # wait for initialization 152 | signal_init.wait() 153 | return (process, signal_close) 154 | 155 | def wait_for_component(self, component): 156 | component[1].set() 157 | component[0].join() 158 | 159 | def time_out(self): 160 | print "A time out has occurred, the analysis failed" 161 | self.add_error_message("THE TEST TIMED OUT") 162 | logging.error("Test timed out") 163 | self.send_errors() 164 | 165 | def add_error_message(self, message): 166 | logging.error(message) 167 | self.error_queue.put(message) 168 | 169 | def send_errors(self): 170 | errors = [] 171 | while True: 172 | try: 173 | errors.append(self.error_queue.get_nowait()) 174 | except Queue.Empty: 175 | break 176 | self.send_FAIL_signal({"errors": errors}) 177 | 178 | def start_timeout_error_handler(self): 179 | self.timer = threading.Timer(300, self.time_out) 180 | self.timer.start() 181 | 182 | def finish_timeout_error_handler(self): 183 | self.timer.cancel() 184 | 185 | def get_max_tries(self): 186 | return 1000 187 | 188 | def start_components(self): 189 | try: 190 | self.start_timeout_error_handler() 191 | if (Utils.isTestingMode() == False) and int(self.get_description()['count']) > self.get_max_tries(): 192 | self.send_FAIL_signal({'tries': 'Couldn\'t verify the vulnerability dinamically'}) 193 | else: 194 | analyzer = self.start_analyzer_process() 195 | reporter = self.start_reporter_process() 196 | # waits for reporter and analyzer to start before trigger. 197 | trigger = self.start_trigger_process() 198 | # wait trigger process to finish 199 | self.wait_for_component(trigger) 200 | logging.debug("already closed trigger") 201 | print "already closed trigger" 202 | # signal end of trigger 203 | self.wait_for_component(analyzer) 204 | print "already closed analyzer" 205 | logging.debug("already closed analyzer") 206 | self.wait_for_component(reporter) 207 | print "already closed reporter" 208 | logging.debug("already closed reporter") 209 | self.finish_timeout_error_handler() 210 | except: 211 | self.error_queue.put(traceback.format_exc()) 212 | self.send_errors() 213 | finally: 214 | self.emulator.disconnect() 215 | 216 | 217 | if __name__ == '__main__': 218 | VMClient.get_VM_client().start_components() 219 | -------------------------------------------------------------------------------- /client/client.sh: -------------------------------------------------------------------------------- 1 | adb start-server; 2 | while true; do 3 | sleep 10; 4 | python VMClient.py; 5 | done 6 | 7 | -------------------------------------------------------------------------------- /client/client_setup.sh: -------------------------------------------------------------------------------- 1 | echo 1 > /proc/sys/net/ipv4/ip_forward 2 | iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080 3 | iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8080 4 | -------------------------------------------------------------------------------- /client/mitmproxy_setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # Get mitmproxy files 4 | wget https://github.com/mitmproxy/mitmproxy/archive/v0.11.3.tar.gz 5 | wget https://github.com/mitmproxy/netlib/archive/v0.11.2.tar.gz 6 | 7 | # Install requirements 8 | sudo apt-get update 9 | sudo apt-get -y install python-pip 10 | sudo apt-get -y install python-cffi 11 | sudo apt-get -y install libffi-dev 12 | sudo apt-get -y install python-dev 13 | sudo apt-get -y install libssl-dev 14 | sudo apt-get -y install libxml2-dev 15 | sudo apt-get -y install libxslt-dev 16 | sudo apt-get -y install android-tools-adb 17 | sudo pip install --upgrade six 18 | sudo pip install netifaces 19 | sudo pip install --upgrade setuptools 20 | 21 | tar xvzf v0.11.2.tar.gz 22 | cd netlib-0.11.2 23 | sudo python setup.py install 24 | cd .. 25 | 26 | tar xvzf v0.11.3.tar.gz 27 | cd mitmproxy-0.11.3 28 | sudo python setup.py install 29 | 30 | 31 | -------------------------------------------------------------------------------- /client/settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | VM_MANAGER_IP = 'IP_VM_MANAGER:8082' 27 | REPORTER_IP = 'localhost:8081' 28 | DEBUG_MODE = False 29 | FUZZER_VALUES={ 30 | 'PASSWORD': 's3cr3tpass', 31 | 'MAIL' : 'fakeemailandroid@gmail.com', 32 | 'PHONE' : '1112341234', 33 | 'CONTACTNAME' : 'C0ntactFuzz', 34 | 'CONTACTPHONE' : '1107060504' 35 | } 36 | FUZZER_PRIVACY_VALUES={ 37 | "android.telephony.TelephonyManager" : { 38 | "getSimOperatorName" : "QUAM-SIM", 39 | "getSimOperator" : "72201", 40 | "getNetworkOperatorName" : "QUAM-NETWORK", 41 | "getNetworkOperator" : "72207" 42 | }, 43 | "com.android.internal.telephony.gsm.GSMPhone" : { 44 | "getDeviceId" : "352738061926670", 45 | "getImei" : "352738061926671", 46 | "getSubscriberId" : "722010000906017", 47 | "getLine1Number" : "1112341234" 48 | }, 49 | 50 | "com.android.internal.telephony.gsm.CDMAPhone" : { 51 | "getDeviceId" : "352738061926672", 52 | "getMeid" : "352738061926673", 53 | "getSubscriberId" : "722010000906017", 54 | "getLine1Number" : "541101010101" 55 | }, 56 | "com.android.internal.telephony.PhoneBase" : 57 | { 58 | "getIccSerialNumber" : "8954010111009060172f" 59 | 60 | }, 61 | "android.net.wifi.WifiInfo" : { 62 | "getBSSID" : "de:ad:be:ef:aa:aa", 63 | "getIpAddress" : "192.168.0.2", 64 | "getMacAddress" : "de:ad:be:ef:bb:bb", 65 | "getSSID" : "ThizIsMyWifi" 66 | }, 67 | 68 | "com.android.settings.Utils" : { 69 | "getWifiIpAddresses" : "192.168.0.2", 70 | "getDefaultIpAddresses" : "192.168.0.2" 71 | }, 72 | "android.location.LocationManager" : { 73 | "requestLocationUpdates" : "-34.603773, -58.381700", 74 | "getLastKnownLocation" : "-34.603773, -58.381700" 75 | }, 76 | "android.net.wifi.WifiManager" : { 77 | "getConfiguredNetworks" : "Not applicable yet", 78 | "getScanResults" : "Not applicable yet" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /emulator/emulator_setup.sh: -------------------------------------------------------------------------------- 1 | #WORKING_FOLDER_PATH=/home/android/Android 2 | echo "Run with {SSL/NOSSL} {EMULATOR-IP} {GATEWAY-IP-FOR-EMULATOR} as args" 3 | WORKING_FOLDER_PATH=emulator_support_files 4 | EMULATOR_TYPE=$1 5 | emulator=$2:5556 6 | GW_FOR_EMULATOR=$3 7 | echo "Using emulator address $emulator" 8 | #Rooting, substrate, installing ssl bypass and fuzzer then linking 9 | #rooting the emulator 10 | connect_adb () 11 | { 12 | adb disconnect 13 | adb connect "$1" 14 | sleep 8 15 | adb -s "$1" root 16 | #wait for adb to restart 17 | sleep 8 18 | adb connect "$1" 19 | } 20 | 21 | connect_adb "$emulator" 22 | #disabling lockscreen and suspension 23 | adb -s "$emulator" shell "sqlite3 /data/system/locksettings.db \"UPDATE locksettings SET value = '1' WHERE name = 'lockscreen.disabled'\"" 24 | #adb -s "$emulator" shell "sqlite3 /data/data/com.android.providers.settings/databases/settings.db \"update system set value='-1' where name='screen_off_timeout'\"" 25 | #rooting emulator 26 | adb -s "$emulator" push $WORKING_FOLDER_PATH/Android-x86-RootScript-4.3/ /data/local/ 27 | adb -s "$emulator" shell "cd /data/local; sh install-device.sh" 28 | #add to init.sh remote adb, configure static ip to emulator and remove return 0 29 | adb -s "$emulator" remount 30 | adb -s "$emulator" shell "sed -i 's/return 0//g' /system/etc/init.sh" 31 | adb -s "$emulator" shell "echo 'adb tcpip 5556 &' >> /system/etc/init.sh" 32 | adb -s "$emulator" shell "echo 'ifconfig eth0 $2 netmask 255.255.255.0 &' >> /system/etc/init.sh" 33 | adb -s "$emulator" shell "echo 'busybox route add default gw $GW_FOR_EMULATOR dev eth0 &' >> /system/etc/init.sh" 34 | adb -s "$emulator" shell "echo 'return 0' >> /system/etc/init.sh" 35 | #reboot needed for SuperUser.apk to be instaled 36 | adb -s "$emulator" reboot & 37 | sleep 60 38 | connect_adb "$emulator" 39 | #start supersu to add shared_prefs configuration 40 | adb -s "$emulator" shell "am start -a android.intent.action.MAIN -n eu.chainfire.supersu/.MainActivity" 41 | sleep 8 42 | #change shared_pref to allow all apps 43 | adb -s "$emulator" push $WORKING_FOLDER_PATH/eu.chainfire.supersu_preferences.xml /data/data/eu.chainfire.supersu/shared_prefs/eu.chainfire.supersu_preferences.xml 44 | #reboot needed for grant access to be resolved 45 | adb -s "$emulator" reboot & 46 | sleep 60 47 | connect_adb "$emulator" 48 | #installing cydia 49 | adb -s "$emulator" install -r $WORKING_FOLDER_PATH/com.saurik.substrate.apk 50 | #installing extensions 51 | #if first emulator, install trust-killer 52 | if [[ "$EMULATOR_TYPE" == *"NOSSL"* ]] 53 | then 54 | adb -s "$emulator" install -r $WORKING_FOLDER_PATH/Android-SSL-TrustKiller.apk 55 | fi 56 | adb -s "$emulator" install -r $WORKING_FOLDER_PATH/Marvin-toqueton.apk 57 | #linking cydia 58 | adb -s "$emulator" shell /data/data/com.saurik.substrate/lib/libSubstrateRun.so do_link 59 | #rm all sdcard content 60 | adb -s "$emulator" shell "ls -d /sdcard/* | grep -v Android | xargs rm -r" 61 | #open substrate to update permitted.list (wait some seconds)pm 62 | adb -s "$emulator" shell am start -a android.intent.action.MAIN -n com.saurik.substrate/.SetupActivity 63 | sleep 8 64 | #copy the privacy.json file to sdcard 65 | adb -s "$emulator" shell am start -a android.intent.action.MAIN -n ar.fsadosky.marvintoqueton/.MainActivity 66 | #clear gapps and google play services because of crashes 67 | adb -s "$emulator" shell "pm uninstall com.google.android.gms" 68 | adb -s "$emulator" shell "pm clear com.google.android.gms" 69 | adb -s "$emulator" shell "pm clear com.google.android.gsf" 70 | sleep 8 71 | #rebooting phone 72 | adb -s "$emulator" shell /system/bin/setprop ctl.restart zygote 73 | #Busybox already installed in android-x86, no longer needs installing 74 | connect_adb "$emulator" 75 | -------------------------------------------------------------------------------- /emulator/emulator_support_files/Android-SSL-TrustKiller.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/emulator/emulator_support_files/Android-SSL-TrustKiller.apk -------------------------------------------------------------------------------- /emulator/emulator_support_files/Android-x86-RootScript-4.3/install-device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | clear 3 | echo "-----------------------------" 4 | echo "Root script for Android 4.3" 5 | echo " By Quinny899 @ XDA" 6 | echo " Root by Chainfire @ XDA" 7 | echo "-----------------------------" 8 | echo "Script loaded" 9 | echo "Installing to device from device" 10 | echo "Mounting..." 11 | mount -o rw,remount /system 12 | echo "Removing old files..." 13 | rm -r -f /system/bin/.ext 14 | rm -f /system/bin/.ext/.su 15 | rm -f /system/xbin/daemonsu 16 | rm -f /system/xbin/su 17 | #rm -f /system/etc/init.sh 18 | rm -f /system/app/Superuser.apk 19 | echo "Copying files..." 20 | mkdir /system/bin/.ext 21 | cp system/bin/.ext/.su /system/bin/.ext/ 22 | cp system/xbin/su /system/xbin/ 23 | cp system/xbin/daemonsu /system/xbin/ 24 | cp system/app/Superuser.apk /system/app/ 25 | cat system/etc/init.sh >> /system/etc/init.sh 26 | echo "Setting permissions..." 27 | chmod 06755 /system/xbin/su 28 | chmod 06755 /system/xbin/daemonsu 29 | chmod 06755 /system/bin/.ext/.su 30 | chmod 777 /system/bin/.ext 31 | chmod 755 /system/etc/init.sh 32 | echo "Cleaning up..." 33 | echo "Finished. You need to reboot for root to be available" 34 | -------------------------------------------------------------------------------- /emulator/emulator_support_files/Android-x86-RootScript-4.3/system/app/Superuser.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/emulator/emulator_support_files/Android-x86-RootScript-4.3/system/app/Superuser.apk -------------------------------------------------------------------------------- /emulator/emulator_support_files/Android-x86-RootScript-4.3/system/bin/.ext/.su: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/emulator/emulator_support_files/Android-x86-RootScript-4.3/system/bin/.ext/.su -------------------------------------------------------------------------------- /emulator/emulator_support_files/Android-x86-RootScript-4.3/system/bin/README: -------------------------------------------------------------------------------- 1 | This isn't blank, there's a hidden folder called .ext -------------------------------------------------------------------------------- /emulator/emulator_support_files/Android-x86-RootScript-4.3/system/etc/init.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2013 The Android-x86 Open Source Project 3 | # 4 | # License: GNU Public License v2 or later 5 | # 6 | 7 | function set_property() 8 | { 9 | # this must be run before post-fs stage 10 | echo $1=$2 >> /x86.prop 11 | } 12 | 13 | function init_misc() 14 | { 15 | # a hack for USB modem 16 | lsusb | grep 1a8d:1000 && eject 17 | } 18 | 19 | function init_hal_audio() 20 | { 21 | case "$PRODUCT" in 22 | VirtualBox*|Bochs*) 23 | [ -d /proc/asound/card0 ] || modprobe snd-sb16 isapnp=0 irq=5 24 | ;; 25 | *) 26 | ;; 27 | esac 28 | [ -d /proc/asound/card0 ] || modprobe snd-dummy 29 | 30 | for c in $(grep '\[.*\]' /proc/asound/cards | awk '{print $1}'); do 31 | alsa_ctl init $c 32 | alsa_amixer -c $c set Master on 33 | alsa_amixer -c $c set Master 100 34 | alsa_amixer -c $c set Headphone on 35 | alsa_amixer -c $c set Headphone 100 36 | alsa_amixer -c $c set Speaker 100 37 | alsa_amixer -c $c set Capture 100 38 | alsa_amixer -c $c set Capture cap 39 | alsa_amixer -c $c set PCM 100 unmute 40 | alsa_amixer -c $c set 'Mic Boost' 2 41 | done 42 | } 43 | 44 | function init_hal_bluetooth() 45 | { 46 | for r in /sys/class/rfkill/*; do 47 | type=$(cat $r/type) 48 | [ "$type" = "wlan" -o "$type" = "bluetooth" ] && echo 1 > $r/state 49 | done 50 | 51 | # these modules are incompatible with bluedroid 52 | rmmod ath3k 53 | rmmod btusb 54 | rmmod bluetooth 55 | } 56 | 57 | function init_hal_camera() 58 | { 59 | [ -c /dev/video0 ] || modprobe vivi 60 | } 61 | 62 | function init_hal_gps() 63 | { 64 | # TODO 65 | return 66 | } 67 | 68 | function set_drm_mode() 69 | { 70 | case "$PRODUCT" in 71 | ET1602*) 72 | drm_mode=1366x768 73 | ;; 74 | *) 75 | ;; 76 | esac 77 | 78 | [ -n "$drm_mode" ] && set_property debug.drm.mode.force $drm_mode 79 | } 80 | 81 | function init_uvesafb() 82 | { 83 | case "$PRODUCT" in 84 | *Q550) 85 | UVESA_MODE=${UVESA_MODE:-1280x800} 86 | ;; 87 | ET2002*) 88 | UVESA_MODE=${UVESA_MODE:-1600x900} 89 | ;; 90 | T91*) 91 | UVESA_MODE=${UVESA_MODE:-1024x600} 92 | ;; 93 | VirtualBox*|Bochs*) 94 | UVESA_MODE=${UVESA_MODE:-1024x768} 95 | ;; 96 | *) 97 | ;; 98 | esac 99 | 100 | modprobe uvesafb mode_option=${UVESA_MODE:-800x600}-16 ${UVESA_OPTION:-mtrr=3 scroll=redraw} 101 | } 102 | 103 | function init_hal_gralloc() 104 | { 105 | case "$(cat /proc/fb | head -1)" in 106 | 0*inteldrmfb|0*radeondrmfb) 107 | set_property hal.gralloc drm 108 | set_drm_mode 109 | ;; 110 | 0*svgadrmfb) 111 | ;; 112 | "") 113 | init_uvesafb 114 | ;& 115 | 0*) 116 | [ "$HWACCEL" = "1" ] || set_property debug.egl.hw 0 117 | ;; 118 | esac 119 | } 120 | 121 | function init_hal_hwcomposer() 122 | { 123 | # TODO 124 | return 125 | } 126 | 127 | function init_hal_lights() 128 | { 129 | chown 1000.1000 /sys/class/backlight/*/brightness 130 | } 131 | 132 | function init_hal_power() 133 | { 134 | # TODO 135 | case "$PRODUCT" in 136 | *) 137 | ;; 138 | esac 139 | } 140 | 141 | function init_hal_sensors() 142 | { 143 | case "$(cat $DMIPATH/uevent)" in 144 | *ICONIA*W*) 145 | set_property hal.sensors w500 146 | ;; 147 | *S10-3t*) 148 | set_property hal.sensors s103t 149 | ;; 150 | *Inagua*) 151 | #setkeycodes 0x62 29 152 | #setkeycodes 0x74 56 153 | set_property hal.sensors kbd 154 | set_property hal.sensors.kbd.type 2 155 | ;; 156 | *TEGA*|*2010:svnIntel:*) 157 | set_property hal.sensors kbd 158 | set_property hal.sensors.kbd.type 1 159 | io_switch 0x0 0x1 160 | setkeycodes 0x6d 125 161 | ;; 162 | *MS-N0E1*) 163 | ;; 164 | *) 165 | set_property hal.sensors kbd 166 | ;; 167 | esac 168 | } 169 | 170 | function init_ril() 171 | { 172 | case "$PRODUCT" in 173 | TEGA*|Intel*) 174 | set_property rild.libpath /system/lib/libreference-ril.so 175 | set_property rild.libargs "-d /dev/ttyUSB2" 176 | ;; 177 | *) 178 | ;; 179 | esac 180 | } 181 | 182 | function do_init() 183 | { 184 | init_misc 185 | init_hal_audio 186 | init_hal_bluetooth 187 | init_hal_camera 188 | init_hal_gps 189 | init_hal_gralloc 190 | init_hal_hwcomposer 191 | init_hal_lights 192 | init_hal_power 193 | init_hal_sensors 194 | init_ril 195 | chmod 640 /x86.prop 196 | post_init 197 | } 198 | 199 | function do_netconsole() 200 | { 201 | modprobe netconsole netconsole="@/,@$(getprop dhcp.eth0.gateway)/" 202 | } 203 | 204 | function do_bootcomplete() 205 | { 206 | for bt in $(lsusb -v | awk ' /Class:.E0/ { print $9 } '); do 207 | chown 1002.1002 $bt && chmod 660 $bt 208 | done 209 | } 210 | 211 | PATH=/system/bin:/system/xbin 212 | 213 | DMIPATH=/sys/class/dmi/id 214 | BOARD=$(cat $DMIPATH/board_name) 215 | PRODUCT=$(cat $DMIPATH/product_name) 216 | 217 | # import cmdline variables 218 | for c in `cat /proc/cmdline`; do 219 | case $c in 220 | androidboot.hardware=*) 221 | ;; 222 | *=*) 223 | eval $c 224 | ;; 225 | esac 226 | done 227 | 228 | [ -n "$DEBUG" ] && set -x || exec &> /dev/null 229 | 230 | # import the vendor specific script 231 | hw_sh=/vendor/etc/init.sh 232 | [ -e $hw_sh ] && source $hw_sh 233 | 234 | case "$1" in 235 | netconsole) 236 | [ -n "$DEBUG" ] && do_netconsole 237 | ;; 238 | bootcomplete) 239 | do_bootcomplete 240 | ;; 241 | init|"") 242 | do_init 243 | ;; 244 | esac 245 | daemonsu --daemon & 246 | return 0 247 | -------------------------------------------------------------------------------- /emulator/emulator_support_files/Android-x86-RootScript-4.3/system/xbin/daemonsu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/emulator/emulator_support_files/Android-x86-RootScript-4.3/system/xbin/daemonsu -------------------------------------------------------------------------------- /emulator/emulator_support_files/Android-x86-RootScript-4.3/system/xbin/su: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/emulator/emulator_support_files/Android-x86-RootScript-4.3/system/xbin/su -------------------------------------------------------------------------------- /emulator/emulator_support_files/Marvin-toqueton.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/emulator/emulator_support_files/Marvin-toqueton.apk -------------------------------------------------------------------------------- /emulator/emulator_support_files/busybox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/emulator/emulator_support_files/busybox -------------------------------------------------------------------------------- /emulator/emulator_support_files/com.saurik.substrate.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/emulator/emulator_support_files/com.saurik.substrate.apk -------------------------------------------------------------------------------- /emulator/emulator_support_files/eu.chainfire.supersu_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | grant 4 | 3 5 | 6 | light 7 | access 8 | 31122359 9 | 10 | 11 | 12 | 13 | 10 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /images/architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/images/architecture.jpg -------------------------------------------------------------------------------- /server/DBCache_django.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | class DBCache_django(object): 27 | def __init__(self): 28 | self.cache = {} 29 | 30 | def cache_dynamic_test_for_vm(self, vm_id, dynamic_test_object): 31 | self.cache[vm_id] = dynamic_test_object 32 | 33 | def get_dynamic_test_for_vm(self, vm_id): 34 | if not vm_id in self.cache.keys(): 35 | raise Exception("Cache corrupted") 36 | return self.cache[vm_id] 37 | 38 | -------------------------------------------------------------------------------- /server/DBManager_django.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import Utils 27 | import json 28 | import os 29 | import sys 30 | sys.path.append(os.path.join(os.path.dirname(__file__), 'django_support')) 31 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_support.settings") 32 | from django.conf import settings 33 | from django.db import connections 34 | from django_support.models import VulnerabilityResult 35 | from django_support.models import App 36 | from django_support.models import DynamicTestResults 37 | from DBCache_django import DBCache_django 38 | import settings 39 | import random 40 | 41 | class DBManager_django(object): 42 | 43 | def __init__(self): 44 | self.cache = DBCache_django() 45 | 46 | def default_filter_state(self, filter_id, apk_name): 47 | return {"status":"UNKNOWN", "count":0, "description":{}} 48 | 49 | def pick_dynamic_test_with_status_unknown(self,vm_client_id): 50 | dynamic_test = DynamicTestResults.objects.filter(status__exact='UNKNOWN').order_by('?').first() 51 | package_name = dynamic_test.vuln.app.package_name 52 | vulnerability_type = dynamic_test.vuln.name 53 | if(dynamic_test.vuln.dynamic_test_params is not None): 54 | dynamic_test_params = json.loads(dynamic_test.vuln.dynamic_test_params) 55 | else: 56 | dynamic_test_params = {} 57 | dynamic_test_params['count'] = dynamic_test.count 58 | download_url = settings.DOWNLOAD_APK_SITE + '%d'%dynamic_test.vuln.app.id + '/apk/?' 59 | self.cache.cache_dynamic_test_for_vm(vm_client_id,dynamic_test) 60 | print (vulnerability_type, package_name, download_url, dynamic_test_params) 61 | return (vulnerability_type, package_name, download_url, dynamic_test_params) 62 | 63 | def update_success(self,vm_client_id,description): 64 | dynamic_test = self.cache.get_dynamic_test_for_vm(vm_client_id) 65 | for vuln_type in description.keys(): 66 | if vuln_type == dynamic_test.vuln.name: 67 | dynamic_test.status = "SUCCESS" 68 | dynamic_test.count +=1 69 | dynamic_test.description = {vuln_type : description[vuln_type]} 70 | dynamic_test.save() 71 | else: 72 | for vulnerability in description[vuln_type]: 73 | #its one of the dynamic only tested vulnerabilities, create a new vulnerability with its 74 | #description 75 | vulnerability = VulnerabilityResult(name = vuln_type, 76 | description = vulnerability, 77 | confidence = 1, 78 | dynamicTest = False, 79 | dynamic_test_params = None, 80 | app = dynamic_test.vuln.app) 81 | vulnerability.save() 82 | 83 | 84 | def log_failure(self,vm_client_id,status,extras): 85 | dynamic_test = self.cache.get_dynamic_test_for_vm(vm_client_id) 86 | dynamic_test.status = status 87 | dynamic_test.count +=1 88 | dynamic_test.description = extras 89 | dynamic_test.save() 90 | 91 | -------------------------------------------------------------------------------- /server/ONE-API/NetworkInfo.java: -------------------------------------------------------------------------------- 1 | import org.opennebula.client.Client; 2 | import org.opennebula.client.vnet.VirtualNetwork; 3 | import org.opennebula.client.OneResponse; 4 | import org.opennebula.client.host.HostPool; 5 | 6 | /** 7 | * 8 | * author oneadmin 9 | */ 10 | public class NetworkInfo { 11 | 12 | public static void main(String[] args) throws Exception { 13 | //password and URL standard 14 | Client client = new Client( args[0],args[1] ); 15 | //network ID 16 | int net_id = Integer.parseInt(args[2]); 17 | OneResponse or = VirtualNetwork.info(client,net_id); 18 | if (or.isError()) { 19 | System.out.println(or.getErrorMessage()); 20 | } 21 | else{ 22 | System.out.println(or.getMessage()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/ONE-API/SnapshotRestore.java: -------------------------------------------------------------------------------- 1 | import org.opennebula.client.Client; 2 | import org.opennebula.client.vm.VirtualMachine; 3 | import org.opennebula.client.OneResponse; 4 | import org.opennebula.client.host.HostPool; 5 | 6 | /** 7 | * 8 | * author oneadmin 9 | */ 10 | public class SnapshotRestore { 11 | 12 | public static void main(String[] args) throws Exception { 13 | Client client = new Client( args[0],args[1] ); 14 | int vm_id = Integer.parseInt(args[2]); 15 | int snapshot_id = Integer.parseInt(args[3]); 16 | OneResponse or = VirtualMachine.snapshotRevert(client,vm_id,snapshot_id); 17 | if (or.isError()) { 18 | System.out.println(or.getErrorMessage()); 19 | } 20 | else{ 21 | System.out.println(or.getMessage()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/ONE-API/lib/org.opennebula.client.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/server/ONE-API/lib/org.opennebula.client.jar -------------------------------------------------------------------------------- /server/ONE-API/lib/ws-commons-util-1.0.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/server/ONE-API/lib/ws-commons-util-1.0.2.jar -------------------------------------------------------------------------------- /server/ONE-API/lib/xmlrpc-client-3.1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/server/ONE-API/lib/xmlrpc-client-3.1.2.jar -------------------------------------------------------------------------------- /server/ONE-API/lib/xmlrpc-common-3.1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/server/ONE-API/lib/xmlrpc-common-3.1.2.jar -------------------------------------------------------------------------------- /server/ONE-API/network_info.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/server/ONE-API/network_info.jar -------------------------------------------------------------------------------- /server/ONE-API/restore.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programa-stic/Marvin-dynamic-Analyzer/add9f6612206275c8e19157123731762a9504234/server/ONE-API/restore.jar -------------------------------------------------------------------------------- /server/Utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | import subprocess 27 | import shlex 28 | 29 | def run_get_output(command): 30 | try: 31 | return subprocess.check_output(shlex.split(command),stderr=subprocess.STDOUT) 32 | except subprocess.CalledProcessError as grepexc: 33 | return grepexc.output 34 | 35 | def run_non_blocking_cmd(command): 36 | return subprocess.Popen(shlex.split(command),stdout=subprocess.PIPE,stderr=subprocess.PIPE) 37 | 38 | -------------------------------------------------------------------------------- /server/VMManager.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | #!/usr/bin/env python 27 | import SimpleHTTPServer 28 | import SocketServer 29 | import logging 30 | from DBManager_django import DBManager_django 31 | import Utils 32 | import thread 33 | import traceback 34 | import xml.etree.ElementTree as ET 35 | import json 36 | import settings 37 | import random 38 | 39 | 40 | class VMManager(object): 41 | __vmm = None 42 | 43 | def __init__(self): 44 | self.db = DBManager_django() 45 | self.running_emulators = set() 46 | 47 | @staticmethod 48 | def get_vmm(): 49 | if not VMManager.__vmm: 50 | VMManager.__vmm = VMManager() 51 | return VMManager.__vmm 52 | 53 | def restore_vm(self, ip): 54 | thread.start_new_thread(self.one_restore_vm, (ip,)) 55 | 56 | def one_restore_vm(self, ip): 57 | self.restore_host_to_snapshot(self.get_vm_id_from_ip(ip)) 58 | 59 | def get_vm_id_from_ip(self, ip): 60 | xml_response = Utils.run_get_output( 61 | 'java -jar ONE-API/network_info.jar %s %s %s' % (settings.ONE_CREDS, settings.ONE_IP, settings.NET_ID)) 62 | et = ET.fromstring(xml_response) 63 | for lease in et.iter("LEASE"): 64 | if lease.find("IP") is not None and lease.find("IP").text == ip: 65 | return int(lease.find("VM").text) 66 | raise Exception("NO VM ID found for %s" % ip) 67 | 68 | def restore_host_to_snapshot(self, vm_emulator_id): 69 | # hardcoding snapshot ID to 1 70 | return Utils.run_get_output( 71 | 'java -jar ONE-API/restore.jar %s %s %d %d' % (settings.ONE_CREDS, settings.ONE_IP, vm_emulator_id, 0)) 72 | 73 | def save_report(self, vm_client_id, content): 74 | print content 75 | status = content['status'] 76 | extras = content['description'] 77 | if status == 'SUCCESS': 78 | self.db.update_success(vm_client_id, extras) 79 | else: 80 | # there was an error in the report 81 | self.db.log_failure(vm_client_id, status, extras) 82 | 83 | def find_target_for_VM_client(self,vm_client_id): 84 | return self.db.pick_dynamic_test_with_status_unknown(vm_client_id) 85 | 86 | def add_running_emulator(self, emulator): 87 | self.running_emulators.add(emulator) 88 | 89 | def remove_running_emulator(self, emulator): 90 | self.running_emulators.remove(emulator) 91 | 92 | def find_emulator_for(self, vulnerability_type): 93 | possibleEmulators = settings.ANDROID_VM_POOL 94 | if "SSL" in vulnerability_type: 95 | key = "SSL" 96 | elif vulnerability_type in ["PHONEGAP_JS_INJECTION", "JAVASCRIPTINTERFACE"]: 97 | key = "JAVASCRIPTINTERFACE" 98 | else: 99 | key = "NONSSL" 100 | print set(possibleEmulators[key]) - self.running_emulators 101 | return random.sample(set(possibleEmulators[key]) - self.running_emulators, 1)[0] 102 | 103 | def get_VMManager_request_handler(self): 104 | return VMManagerRequestHandler 105 | 106 | 107 | class VMManagerRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 108 | def end_headers(self, filter, apk, download_url): 109 | # set extra headers for apk 110 | self.send_header('Content-Type', 'application/json') 111 | # set to "dummy" for testing 112 | self.send_header("filter", filter) 113 | self.send_header("apk", apk) 114 | self.send_header("download_url", download_url) 115 | SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self) 116 | 117 | def do_GET(self): 118 | logging.warning("======= GET STARTED =======") 119 | if self.path.startswith('/apk'): 120 | vmm = VMManager.get_vmm() 121 | vm_client_id = self.client_address[0] 122 | filter, apk, download_url, description = vmm.find_target_for_VM_client(vm_client_id) 123 | description['emulator'] = vmm.find_emulator_for(filter) 124 | vmm.add_running_emulator(description['emulator']) 125 | description = json.dumps(description) 126 | self.send_response(200) 127 | self.end_headers(filter, apk, download_url) 128 | self.wfile.write(description) 129 | else: 130 | self.send_response(404) 131 | 132 | def do_POST(self): 133 | logging.warning("======= POST STARTED =======") 134 | try: 135 | length = int(self.headers['Content-Length']) 136 | if self.path == '/report' or self.path == '/error': 137 | content = json.loads(self.rfile.read(length).decode('utf-8')) 138 | # TODO: should be an asynchrous request so the do_POST doesn't get hang waiting 139 | # VM ID matchs the vm ip for now 140 | vm_emulator_id = content['emulator'] 141 | vm_client_id = self.client_address[0] 142 | vmm = VMManager.get_vmm() 143 | vmm.remove_running_emulator(vm_emulator_id) 144 | vmm.save_report(vm_client_id, content) 145 | vmm.restore_vm(vm_emulator_id) 146 | self.send_response(200) 147 | except: 148 | print traceback.format_exc() 149 | # if server failed, still restore VM ? 150 | VMManager.get_vmm().restore_vm(vm_emulator_id) 151 | self.send_response(500) 152 | 153 | 154 | def start_VMManager(): 155 | SocketServer.TCPServer.allow_reuse_address = True 156 | server = SocketServer.TCPServer(('0.0.0.0', 8082), VMManager.get_vmm().get_VMManager_request_handler()) 157 | try: 158 | server.serve_forever() 159 | except KeyboardInterrupt: 160 | server.shutdown() 161 | server.socket.close() 162 | 163 | 164 | if __name__ == '__main__': 165 | start_VMManager() 166 | -------------------------------------------------------------------------------- /server/django_support/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | -------------------------------------------------------------------------------- /server/django_support/models.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from django.db import models 27 | from bungiesearch.managers import BungiesearchManager 28 | # Create your models here. 29 | 30 | class App(models.Model): 31 | QUEUED_STATUS = 'QUEUED' 32 | DOWNLOADING = 'DOWNL' 33 | DOWNLOADED = 'DLED' 34 | START_STAT = 'STATBEG' 35 | START_STAT = 'STATEND' 36 | START_VF = 'STARTVF' 37 | END_VF = 'ENDVF' 38 | 39 | 40 | # app_id = models.AutoField(db_index=True, blank=True) 41 | status = models.CharField(default = "QUEUED", max_length=20) 42 | package_name = models.CharField(db_index=True, max_length=100) 43 | app_name = models.CharField(blank = True, db_index=True, max_length=100) 44 | version = models.CharField(blank = True, max_length=30) 45 | md5 = models.CharField(db_index=True, max_length=100) 46 | sha1 = models.CharField(db_index=True, max_length=100) 47 | uploaded = models.DateField(auto_now_add = True, blank=True) 48 | targetSDK = models.CharField(blank = True, max_length=10) 49 | minSDK = models.CharField(blank = True, max_length=10) 50 | bayesResult = models.NullBooleanField(blank=True) 51 | bayesConfidence = models.DecimalField(blank=True, null=True, max_digits=4, decimal_places=3) 52 | 53 | objects = BungiesearchManager() 54 | 55 | def __unicode__(self): 56 | return self.package_name + ' ' + self.version 57 | 58 | class Meta: 59 | app_label = 'frontpage' 60 | 61 | class Sourcefile(models.Model): 62 | file_name = models.CharField(max_length=200) 63 | file_contents = models.TextField() 64 | app = models.ForeignKey(App) 65 | 66 | objects = BungiesearchManager() 67 | 68 | def __unicode__(self): 69 | return self.file_name 70 | 71 | class Meta: 72 | app_label = 'frontpage' 73 | 74 | 75 | class Permission(models.Model): 76 | name = models.CharField(max_length=100) 77 | perm_description = models.TextField() 78 | perm_danger = models.TextField() 79 | app = models.ManyToManyField(App) 80 | 81 | objects = BungiesearchManager() 82 | 83 | def __unicode__(self): 84 | return self.name 85 | 86 | class Meta: 87 | app_label = 'frontpage' 88 | 89 | 90 | class Activity(models.Model): 91 | name = models.CharField(max_length=150) 92 | app = models.ForeignKey(App) 93 | objects = BungiesearchManager() 94 | 95 | def __unicode__(self): 96 | return self.name 97 | 98 | class Meta: 99 | app_label = 'frontpage' 100 | 101 | class Receiver(models.Model): 102 | name = models.CharField(max_length=150) 103 | app = models.ForeignKey(App) 104 | objects = BungiesearchManager() 105 | 106 | def __unicode__(self): 107 | return self.name 108 | 109 | class Meta: 110 | app_label = 'frontpage' 111 | 112 | class Provider(models.Model): 113 | name = models.CharField(max_length=150) 114 | app = models.ForeignKey(App) 115 | objects = BungiesearchManager() 116 | 117 | def __unicode__(self): 118 | return self.name 119 | 120 | class Meta: 121 | app_label = 'frontpage' 122 | 123 | class Service(models.Model): 124 | name = models.CharField(max_length=150) 125 | app = models.ForeignKey(App) 126 | 127 | objects = BungiesearchManager() 128 | 129 | def __unicode__(self): 130 | return self.name 131 | 132 | class Meta: 133 | app_label = 'frontpage' 134 | 135 | class VulnerabilityResult(models.Model): 136 | name = models.CharField(max_length=150) 137 | description = models.TextField() 138 | confidence = models.CharField(max_length=10, null=True) 139 | dynamicTest = models.BooleanField(default=False) 140 | dynamic_test_params = models.TextField(null=True) 141 | app = models.ForeignKey(App) 142 | 143 | objects = BungiesearchManager() 144 | 145 | def __unicode__(self): 146 | return self.name 147 | 148 | class Meta: 149 | app_label = 'frontpage' 150 | 151 | class DynamicTestResults(models.Model): 152 | name = models.CharField(max_length=150) 153 | status = models.CharField(max_length=150) 154 | count = models.IntegerField() 155 | description = models.TextField() 156 | vuln = models.ForeignKey(VulnerabilityResult) 157 | objects = BungiesearchManager() 158 | #app = models.ForeignKey(App) 159 | 160 | def __unicode__(self): 161 | return self.name 162 | 163 | class Meta: 164 | app_label = 'frontpage' 165 | 166 | 167 | 168 | class NoUpdatedField(models.Model): 169 | package_name = models.CharField(max_length=150, db_index=True) 170 | 171 | objects = BungiesearchManager() 172 | 173 | class Meta: 174 | app_label = 'frontpage' 175 | 176 | 177 | class ManagedButEmpty(models.Model): 178 | package_name = models.CharField(max_length=150, db_index=True) 179 | 180 | objects = BungiesearchManager() 181 | 182 | class Meta: 183 | app_label = 'frontpage' 184 | 185 | class Unmanaged(models.Model): 186 | package_name = models.CharField(max_length=150, db_index=True) 187 | 188 | objects = BungiesearchManager() 189 | 190 | class Meta: 191 | app_label = 'frontpage' 192 | 193 | -------------------------------------------------------------------------------- /server/django_support/myindices.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | from models import App 27 | from models import Sourcefile 28 | from models import Permission 29 | from models import Service 30 | from models import Activity 31 | from models import Provider 32 | from bungiesearch.fields import StringField 33 | from bungiesearch.indices import ModelIndex 34 | 35 | 36 | class AppIndex(ModelIndex): 37 | #effectived_date = DateField(eval_as='obj.created if obj.created and obj.published > obj.created else obj.published') 38 | meta_data = StringField(eval_as='" ".join([fld for fld in [obj.package_name, obj.app_name, obj.md5, obj.sha1] if fld])') 39 | 40 | class Meta: 41 | model = App 42 | #exclude = [] 43 | #hotfixes = {} 44 | default = True 45 | 46 | class SourcefileIndex(ModelIndex): 47 | #effectived_date = DateField(eval_as='obj.created if obj.created and obj.published > obj.created else obj.published') 48 | meta_data = StringField(eval_as='" ".join([fld for fld in [obj.file_name, obj.file_contents] if fld])') 49 | 50 | class Meta: 51 | model = Sourcefile 52 | exclude = ('file_contents') 53 | #hotfixes = {} 54 | default = True 55 | 56 | class PermissionIndex(ModelIndex): 57 | #effectived_date = DateField(eval_as='obj.created if obj.created and obj.published > obj.created else obj.published') 58 | #meta_data = StringField(eval_as='" ".join([fld for fld in [obj.link, str(obj.tweet_count), obj.raw] if fld])') 59 | 60 | class Meta: 61 | model = Permission 62 | #exclude = ('raw', 'missing_data', 'negative_feedback', 'positive_feedback', 'popularity_index', 'source_hash') 63 | #hotfixes = {} 64 | default = True 65 | 66 | class ActivityIndex(ModelIndex): 67 | #effectived_date = DateField(eval_as='obj.created if obj.created and obj.published > obj.created else obj.published') 68 | #meta_data = StringField(eval_as='" ".join([fld for fld in [package_name, app_name, md5, sha1] if fld])') 69 | 70 | class Meta: 71 | model = Activity 72 | #exclude = ('raw', 'missing_data', 'negative_feedback', 'positive_feedback', 'popularity_index', 'source_hash') 73 | #hotfixes = {} 74 | default = True 75 | 76 | class ServiceIndex(ModelIndex): 77 | #effectived_date = DateField(eval_as='obj.created if obj.created and obj.published > obj.created else obj.published') 78 | #meta_data = StringField(eval_as='" ".join([fld for fld in [obj.link, str(obj.tweet_count), obj.raw] if fld])') 79 | 80 | class Meta: 81 | model = Service 82 | #exclude = ('raw', 'missing_data', 'negative_feedback', 'positive_feedback', 'popularity_index', 'source_hash') 83 | #hotfixes = {} 84 | default = True 85 | 86 | class ProviderIndex(ModelIndex): 87 | #effectived_date = DateField(eval_as='obj.created if obj.created and obj.published > obj.created else obj.published') 88 | #meta_data = StringField(eval_as='" ".join([fld for fld in [obj.link, str(obj.tweet_count), obj.raw] if fld])') 89 | 90 | class Meta: 91 | model = Provider 92 | #exclude = ('raw', 'missing_data', 'negative_feedback', 'positive_feedback', 'popularity_index', 'source_hash') 93 | #hotfixes = {} 94 | default = True 95 | 96 | 97 | -------------------------------------------------------------------------------- /server/django_support/settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | """ 27 | Django settings for marvin project. 28 | 29 | For more information on this file, see 30 | https://docs.djangoproject.com/en/1.6/topics/settings/ 31 | 32 | For the full list of settings and their values, see 33 | https://docs.djangoproject.com/en/1.6/ref/settings/ 34 | """ 35 | 36 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 37 | import os 38 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 39 | SITE_ID = 1 40 | 41 | # Quick-start development settings - unsuitable for production 42 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/ 43 | 44 | # SECURITY WARNING: keep the secret key used in production secret! 45 | SECRET_KEY = '*' 46 | 47 | # SECURITY WARNING: don't run with debug turned on in production! 48 | DEBUG = True 49 | 50 | TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')] 51 | TEMPLATE_DEBUG = True 52 | TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth", 53 | "django.core.context_processors.debug", 54 | "django.core.context_processors.i18n", 55 | "django.core.context_processors.media", 56 | "django.core.context_processors.static", 57 | "django.core.context_processors.tz", 58 | "django.contrib.messages.context_processors.messages") 59 | 60 | ALLOWED_HOSTS = ['*'] 61 | 62 | 63 | # Application definition 64 | 65 | INSTALLED_APPS = ( 66 | 'django.contrib.admin', 67 | 'django.contrib.auth', 68 | 'django.contrib.contenttypes', 69 | 'django.contrib.sessions', 70 | 'django.contrib.messages', 71 | 'django.contrib.staticfiles', 72 | 'bungiesearch', 73 | 'djgpa', 74 | 'preferences', 75 | 'django.contrib.sites', 76 | 'frontpage', 77 | ) 78 | 79 | MIDDLEWARE_CLASSES = ( 80 | 'django.contrib.sessions.middleware.SessionMiddleware', 81 | 'django.middleware.common.CommonMiddleware', 82 | 'django.middleware.csrf.CsrfViewMiddleware', 83 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 84 | 'django.contrib.messages.middleware.MessageMiddleware', 85 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 86 | ) 87 | 88 | BUNGIESEARCH = { 89 | 'URLS': ['IP_ELASTICSEARCH_SERVER'], # No leading http:// or the elasticsearch client will complain. 90 | 'INDICES': {'apps2': 'django_support.myindices'}, # Must be a module path. 91 | #'ALIASES': {'bsearch': 'marvin.search_aliases'}, 92 | 'SIGNALS': {'BUFFER_SIZE': 1}, 93 | 'TIMEOUT': 5 94 | } 95 | 96 | # Database 97 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases 98 | 99 | DATABASES = { 100 | 'default': { 101 | 'ENGINE': 'django.db.backends.mysql', 102 | 'NAME': 'marvin', 103 | 'USER': 'marvin', 104 | 'PASSWORD': '', 105 | 'HOST': 'IP_DJANGO_SERVER', 106 | 'PORT': '3306' 107 | } 108 | } 109 | 110 | DEFAULT_INDEX_TABLESPACE='' 111 | 112 | # Internationalization 113 | # https://docs.djangoproject.com/en/1.6/topics/i18n/ 114 | 115 | LANGUAGE_CODE = 'en-us' 116 | 117 | TIME_ZONE = 'UTC' 118 | 119 | USE_I18N = True 120 | 121 | USE_L10N = True 122 | 123 | USE_TZ = True 124 | 125 | -------------------------------------------------------------------------------- /server/settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Fundacion Dr. Manuel Sadosky 2 | # All rights reserved. 3 | 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are met: 6 | 7 | # 1. Redistributions of source code must retain the above copyright notice, this 8 | # list of conditions and the following disclaimer. 9 | 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | 26 | NET_ID = 1 #ID used for the framework virtual network 27 | #credentials for RPC 28 | ONE_IP = "http://OPEN_NEBULA_HOST:2633/RPC2" 29 | ONE_CREDS = "oneadmin:ONEADMINCREDENTIALS" #OpenNebula xml RPC credentials 30 | ELASTIC_SEARCH_SERVER = "IP_ELASTICSEARCH_SERVER" 31 | DOWNLOAD_APK_SITE = "http://FRONTEND_IP_ADDRESS:8080/frontpage/" 32 | ANDROID_VM_POOL = {"NONSSL":{"NON_SSL_EMULATOR_IP"},"SSL":{"EMULATOR_IP"},"JAVASCRIPTINTERFACE":{"ICS_EMULATOR_IP"}} 33 | --------------------------------------------------------------------------------