martes, 22 de agosto de 2017

peligro movil

Alrededor de 500 apps de android estarían robando datos de usuarios


Muchos usuarios conocieron lo que pasó con Droidjack que dejo de ser legal para convertirse en una app criminal y es que hacia muchas cosas "malas"(sin autorización) y empezó a ser tan buena en lo que hacia que se empezó a ofertar a 210 euros en su página puesto que daba los resultados que se ofrecían, lo curioso es que se insertaba en apps legales y de otros proveedores haciendose pasar por app legítima. sin embargo tras los esfuerzos de desarrolladores y nuevas plataformas android se habia inutilizado en gran medida el droidjack. pero recientemente se ha encontrado un kit de software de publicidad integrado en muchas aplicaciones legítimas de Android. Su tarea consiste en robar datos de usuarios y enviarlos a una empresa china. En total ha sido encontrado en más de 500 aplicaciones de este sistema operativo y que estaban en Google Play disponibles para su descarga. Ha sido desarrollado por la empresa china Igexin y ha sido descargado más de 100 millones de veces.

Apps en riesgo

La investigación comenzó tras ciertas solicitudes sospechosas. Los investigadores aseguran que comenzaron su trabajo después de darse cuenta de que estaban descargando muestras de malware conocidos en teléfonos limpios después de que éstos hicieran una petición al servidor de Igexin.
Tras meses de investigación, investigadores de la firma móvil de seguridad Lookout descubrieron que los desarrolladores de Igexin estaban usando funciones legítimas de SDK para enviar comandos maliciosos a aplicaciones
Según los permisos que las aplicaciones legítimas recibieron de los usuarios durante la instalación, Lookout dice que observó que el SDK recopilaba todo tipo de datos de los dispositivos de los usuarios. Especialmente recopilaban los registros de llamadas.
Además, el SDK también descargó de forma forzosa y ejecutó el código contenido en grandes archivos cifrados. Este código ayudó al comportamiento malicioso.

no más descargas

Los investigadores describieron las aplicaciones que contenían este malware y se pusieron en contacto con Google y los desarrolladores. Desactivaron las aplicaciones maliciosas para que no se pudieran descargar de Google Play hasta que los desarrolladores de estos programas hubieran actualizado las versiones y fueran seguras para los usuarios.
Los expertos de Lookout no mencionaron los nombres de las aplicaciones que incluían el SDK de Igexin, ya que no consideraban que esto fuera culpa suya. Sin embargo, proporcionaron una lista genérica de aplicaciones donde encontraron el SDK de Igexin.
Esta lista incluye juegos orientados a adolescentes, aplicaciones para conocer el tiempo, radio por Internet, editores de imágenes y otras aplicaciones relacionadas con la educación, la salud, gimnasio, viajes o de cámara de vídeo. Algunas de estas aplicaciones por sí solas fueron descargadas varios cientos de miles de veces.

Recordemos

Un hecho parecido ocurrió a finales del pasado año. En ese momento, los investigadores de Kryptowire descubrieron que Adups, un desarrollador de firmware chino, había incluido código malicioso en el firmware que entregaron a los fabricantes de teléfonos Android.
El código malicioso recogió un gran número de detalles de usuario y envió los datos a servidores en China. Varias marcas de dispositivos Android de bajo coste se vieron afectadas, principalmente BLU, pero fueron otras.
A principios de este mes, Adups dijo que había detenido el comportamiento de recopilación de datos, pero los expertos no estuvieron de acuerdo.
Estamos por tanto ante una nueva vulnerabilidad para los dispositivos Android y las aplicaciones que podemos encontrar para descargar. Como solemos recomendar siempre, lo mejor es mantener nuestros dispositivos seguros. Tenerlos actualizados y con software que nos permita hacer frente a posibles amenazas es importante.

miércoles, 22 de febrero de 2017

Last Pass

Obteniendo cuentas cifradas con análisis forense de memoria. LastPass

 

Descripción


Esta publicación se me ocurrió debido a que esta semana leí la noticia PSA: LastPass Does Not Encrypt Everything In Your Vault básicamente LastPass no está cifrando todos los datos del usuario.
LastPass es una de las plataformas más utilizadas para almacenar nuestras cuentas de usuario y contraseñas y poder acceder a ellas siempre que lo necesitemos.
LastPass guarda con un cifrado AES de 256 bits toda la información más sensible de sus usuarios, como sus nombres, el usuario y la contraseña de una página web, la dirección URL de la página web asociada a dichos datos no se guarda de forma cifrada.



En la imagen anterior se puede visualizar que el parámetro URL está cifrado de forma diferente que los parámetros anteriores a él, esto es debido a que esta pasado por hexadecimal si se utiliza un conversor se podrá ver en texto plano la URL.


Básicamente de esto se trata la investigación...

Para lograr obtener algo “hacer hack” no necesariamente debes ir en contra y romper puedes hacer bypass y en este caso le haremos honor al título de esta publicación utilizaremos el análisis forense para obtener todas las cuentas y contraseñas del usuario.

¿Pensamos?
En algún momento toda esta data debe ser descifrada, debe mostrarse tal cual es para poder ser tratada, pues aquí viene una de las cosas que más me gusta jugar y es investigar la memoria y descubrir que pasa en ella como en la publicación Hackeando la memoria de Windows en tiempo real (POC)

Si estas en una investigación y en el equipo te encuentras con el siguiente directorio:
C:\Users\El_Usuario\AppData\Local\Google\Chrome\User Data\Default\Extensions\hdokiejnpimakedhajhdlcegeplioahd
Debes saber que está presente LastPass en Chrome y si el equipo aún se encuentra encendido debemos hacer un dump de memoria *.raw/*.mem como ya se ha realizado en otras publicaciones.

Para este análisis utilizaremos Volatility y el siguiente plugin de Kevin Breen que utiliza reglas de Yara para poder llevar a cabo la tarea.
# Donated under Volatility Foundation, Inc. Individual Contributor Licensing Agreement
# LastPass - Recover memory resident account information.
# Author: Kevin Breen
# Thanks to the guide on http://www.ghettoforensics.com/2013/10/dumping-malware-configuration-data-from.html

import volatility.plugins.taskmods as taskmods
import volatility.win32.tasks as tasks
import volatility.utils as utils
import volatility.debug as debug
import volatility.plugins.malware.malfind as malfind
import volatility.conf as conf
import re
import json
import string

try:
    import yara
    has_yara = True
except ImportError:
    has_yara = False


signatures = {
    'lastpass_struct_a': 'rule lastpass_strcuta {strings: $a = /{"reqinfo":.*"lplanguage":""}/ condition: $a}\n'
                         'rule lastpass_strcutb {strings: $a = /"tld":".*?","unencryptedUsername":".*?","realmmatch"/ condition: $a}\n'
                         'rule lastpass_strcutc {strings: $a = /{"cmd":"save"(.*?)"tld":"(.*?)"}/ condition: $a}\n'
                         'rule lastpass_strcutd {strings: $a = /"realurl":"(.*?)"domains":"(.*?)"/ condition: $a}\n'
                         'rule lastpass_strcute {strings: $a = /{"cmd":"save"(.*?)"formdata"(.*?)}/ condition: $a}\n'
                         'rule lastpass_priv1 {strings: $a = /LastPassPrivateKey<(.*?)>LastPassPrivateKey/ condition: $a}'
}

config = conf.ConfObject()
config.add_option('CONFSIZE', short_option='C', default=4000,
                           help ='Config data size',
                           action ='store', type='int')
config.add_option('YARAOFFSET', short_option='Y', default=0,
                           help ='YARA start offset',
                           action ='store', type='int')

class LastPass(taskmods.PSList):
    """ Extract lastpass data from process. """

    def calculate(self):
        """ Required: Runs YARA search to find hits """
        if not has_yara:
            debug.error('Yara must be installed for this plugin')

        addr_space = utils.load_as(self._config)
        rules = yara.compile(sources = signatures)
        for task in self.filter_tasks(tasks.pslist(addr_space)):
            if not task.ImageFileName.lower() in ['chrome.exe', 'firefox.exe', 'iexplore.exe']:
                continue
            scanner = malfind.VadYaraScanner(task=task, rules=rules)
            for hit, address in scanner.scan():
                yield task, address

    def string_clean_hex(self, line):
        line = str(line)
        new_line = ''
        for c in line:
            if c in string.printable:
                new_line += c
            else:
                new_line += '\\x' + c.encode('hex')
        return new_line


    def clean_json(self, raw_data):
        # We deliberately pull in too much data to make sure we get it all.
        # Now parse it out again

        if raw_data.startswith('LastPassPrivate'):
            pattern = 'LastPassPrivateKey<(.*?)>LastPassPrivateKey'
            key_data = re.search(pattern, raw_data).group(0)
            if not any(substring in key_data for substring in ['"+a+"', 'indexOf']):
                return self.string_clean_hex(key_data)

        else:

            if raw_data.startswith("{\"cmd"):
                if 'formdata' in raw_data:
                    pattern = '{"cmd":"save"(.*?)"formdata"(.*?)}'
                    val_type = 'mixedform'
                else:
                    pattern = '{"cmd":"save"(.*?)"tld":"(.*?)"}'
                    val_type = 'mixed'

            elif raw_data.startswith("\"realurl"):
                pattern = '"realurl":"(.*?)"domains":"(.*?)"'
                val_type = 'mixed'

            elif raw_data.startswith("\"tld"):
                pattern = '"tld":".*?","unencryptedUsername":".*?","realmmatch"'
                val_type = 'username'

            elif raw_data.startswith('{"reqinfo"'):
                pattern = '{"reqinfo":.*?"lplanguage":""}'
                val_type = 'password'
            else:
                pass

            match = re.search(pattern, raw_data)
            real_data = self.string_clean_hex(match.group(0))

            if val_type == 'username':
                vars = real_data.split(',')
                tld = vars[0].split(':')[1].strip('"')
                username = vars[1].split(':')[1].strip('"')
                return {'type': 'username', 'username': username, 'tld': tld}

            elif val_type == 'mixedform':
                if '"tld":"' in real_data:
                    # Try to parse as json
                    try:
                        json_data = json.loads(real_data)
                        tld = json_data['tld']
                        password = json_data['password']
                        username = json_data['username']
                        clean_data = {'type': 'mixed', 'username': username, 'tld': tld, 'password': password}
                    except ValueError:
                        # Json fails manual parse
                        tld = re.search('"tld":"(.*?)"', real_data)
                        if not tld:
                            tld = re.search('"domains":"(.*?)"', real_data)
                        if tld:
                            tld = tld.group(0).split(':')[-1].strip('"')
                        else:
                            tld = 'unknown'
                        try:
                            password = re.search('"password":"(.*?)"', real_data).group(0).split(':')[-1].strip('"')
                        except:
                            password = 'Unknown'
                        try:
                            username = re.search('"username":"(.*?)"', real_data).group(0).split(':')[-1].strip('"')
                        except:
                            username = 'Unknown'
                        clean_data = {'type': 'mixed', 'username': username, 'tld': tld, 'password': password}

                else:
                    tld = re.search('"url":"(.*?)"', real_data).group(0).split(':')[-1].strip('"')
                    username = re.search('login(.*?)text', real_data).group(0).split('\\t')[1]
                    password = re.search('password(.*?)password', real_data).group(0).split('\\t')[1]
                    clean_data = {'type': 'mixed', 'username': username, 'tld': tld, 'password': password}

                return clean_data

            elif val_type == 'mixed':
                # Try to parse as json
                try:
                    json_data = json.loads(real_data)
                    tld = json_data['tld']
                    password = json_data['password']
                    username = json_data['username']
                    clean_data = {'type': 'mixed', 'username': username, 'tld': tld, 'password': password}
                except ValueError:
                    # Json fails manual parse
                    tld = re.search('"tld":"(.*?)"', real_data)
                    if not tld:
                        tld = re.search('"domains":"(.*?)"', real_data)

                    if tld:
                        tld = tld.group(0).split(':')[-1].strip('"')
                    else:
                        tld = 'unknown'

                    try:
                        password = re.search('"password":"(.*?)"', real_data).group(0).split(':')[-1].strip('"')
                    except:
                        password = 'Unknown'
                    try:
                        username = re.search('"username":"(.*?)"', real_data).group(0).split(':')[-1].strip('"')
                    except:
                        username = 'Unknown'

                    clean_data = {'type': 'mixed', 'username': username, 'tld': tld, 'password': password}
            else:
                # Try to parse as json
                try:
                    json_data = json.loads(real_data)
                    tld = json_data['domains']
                    password = json_data['value']
                    clean_data = {'type': 'password', 'password': password, 'tld': tld}
                except ValueError:
                    # Json fails manual parse
                    password = re.search('"value":"(.*?)"', real_data).group(0).split(':')[-1].strip('"')
                    tld = re.search('"domains":"(.*?)"', real_data).group(0).split(':')[-1].strip('"')
                    clean_data = {'type': 'password', 'password': password, 'tld': tld}

            return clean_data

    def render_text(self, outfd, data):
        """ Required: Parse data and display """
        outfd.write("Searching for LastPass Signatures\n")

        rules = yara.compile(sources=signatures)

        results = {}
        priv_keys = []

        for task, address in data:  # iterate the yield values from calculate()
            outfd.write('Found pattern in Process: {0} ({1})\n'.format(task.ImageFileName, task.UniqueProcessId))
            proc_addr_space = task.get_process_address_space()
            raw_data = proc_addr_space.read(address + self._config.YARAOFFSET, self._config.CONFSIZE)
            if raw_data:
                clean_data = self.clean_json(raw_data)
                if not clean_data:
                    continue
                if 'PrivateKey' in clean_data:
                    priv_keys.append(clean_data)
                else:
                    # If we already created the dict
                    if clean_data['tld'] in results:
                        if 'username' in clean_data:
                            if results[clean_data['tld']]['username'] == 'Unknown':
                                results[clean_data['tld']]['username'] = clean_data['username']
                        if 'password' in clean_data:
                            if results[clean_data['tld']]['password'] == 'Unknown':
                                results[clean_data['tld']]['password'] = clean_data['password']
                    # Else create the dict
                    else:
                        if 'username' in clean_data:
                            username = clean_data['username']
                        else:
                            username = 'Unknown'
                        if 'password' in clean_data:
                            password = clean_data['password']
                        else:
                            password = 'Unknown'
                        results[clean_data['tld']] = {'username': username, 'password': password}

        for k, v in results.iteritems():
            outfd.write("\nFound LastPass Entry for {0}\n".format(k))
            outfd.write('UserName: {0}\n'.format(v['username']))
            outfd.write('Pasword: {0}\n'.format(v['password']))
        outfd.write('\n')

        for key in priv_keys:
            outfd.write('\nFound Private Key\n')
            outfd.write('{0}\n'.format(key))
El comando que utilizaremos es el siguiente:
volatility --plugins='/root/VolPlugins' --profile=Win7SP1x64 -f '/root/Escritorio/POC_CASE01 /TESTING-PC-20170125-023936.raw' lastpass
Donde invocamos a volatility, le indicamos la ruta del plugin, indicamos el perfil del OS, la ruta en donde se encuentra el volcado y finalmente invocamos el plugin lastpass.

Después de algunos minutos obtendremos las cuentas del usuario almacenadas con el Plugin LastPass en Chrome Browser