Por diferentes motivos muchas apps necesitan detectar si el teléfono ha sido “rooteado”, por lo que en este artículo veremos diferentes técnicas para averiguarlo. He pensado que un post sobre el tema sería de interés para muchos lectores, ya que es frecuente ver este tipo de preguntas en foros de desarrollo.
En este post de StackOverflow podemos encontrar técnicas comúnmente utilizadas en apps para detectar Rooted. El siguiente código hace uso de 3 métodos para detectar Rooted: el primero busca la cadena “test-keys”, que es una llave genérica para firmar paquetes; el segundo método comprueba si existe el Superuser.apk en el disco, esta app gestiona el acceso al comando su (privilegios administrador) para las demás apps; y por último el tercer método llama directamente a su y ejecuta un comando con privilegios de root.
/**
* @author Kevin Kowalewski
*
*/
public class Root {
private static String LOG_TAG = Root.class.getName();
public boolean isDeviceRooted() {
if (checkRootMethod1()){return true;}
if (checkRootMethod2()){return true;}
if (checkRootMethod3()){return true;}
return false;
}
public boolean checkRootMethod1(){
String buildTags = android.os.Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
return true;
}
return false;
}
public boolean checkRootMethod2(){
try {
File file = new File("/system/app/Superuser.apk");
if (file.exists()) {
return true;
}
} catch (Exception e) { }
return false;
}
public boolean checkRootMethod3() {
if (new ExecShell().executeCommand(SHELL_CMD.check_su_binary) != null){
return true;
}else{
return false;
}
}
}
/**
* @author Kevin Kowalewski
*
*/
public class ExecShell {
private static String LOG_TAG = ExecShell.class.getName();
public static enum SHELL_CMD {
check_su_binary(new String[] {"/system/xbin/which","su"}),
;
String[] command;
SHELL_CMD(String[] command){
this.command = command;
}
}
public ArrayList
<string> executeCommand(SHELL_CMD shellCmd){
String line = null;
ArrayList
<string> fullResponse = new ArrayList<string>();
Process localProcess = null;
try {
localProcess = Runtime.getRuntime().exec(shellCmd.command);
} catch (Exception e) {
return null;
//e.printStackTrace();
}
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
try {
while ((line = in.readLine()) != null) {
Log.d(LOG_TAG, "--> Line received: " + line);
fullResponse.add(line);
}
} catch (Exception e) {
e.printStackTrace();
}
Log.d(LOG_TAG, "--> Full response was: " + fullResponse);
return fullResponse;
}
}
</string></string></string>
Las apps en un teléfono que no este rooteado no podrían ejecutar cualquiera de estos métodos, ya que todas las apps en Android están dentro de una sandbox (un sistema de aislamiento de procesos) y con privilegios limitados.
Los tres métodos descritos posiblemente sean los más comunes y si hacemos ingeniería inversa de forma frecuente los podemos encontrar en conocidas apps.
Otras técnicas incluyen el uso de la fantástica librería RootTools que facilita el desarrollo de apps que necesitan root ofreciendo diversas herramientas. Muchas apps utilizan esta librería.
Las funcionalidades de esta librería incluyen comprobar si existe o instalar BusyBox (programa que combina muchas utilidades estándares de Unix en un solo ejecutable pequeño), comprobar si existe o instalar SuperUser, comprobar que se tiene acceso root, herramientas nativas o suficiente espacio en la SD Card.
Como ejercicio para comprobar las técnicas de detección de Rooted he escrito el VULNEX ROOT TESTER que combina diferentes técnicas, desde las básicas aquí presentadas a algunas más sofisticadas de las que hablaremos en otro post. A continuación algunas capturas de la herramienta.
Sin duda la capacidad de detectar Rooted puede ser necesaria para determinadas apps que requieren un elevado nivel de seguridad, pero igualmente para muchas apps legítimas, como diversas apps de seguridad, requieren de root para funcionar correctamente o aprovechar al máximo la plataforma Android.
Tenemos que tener en cuenta que a la hora de escribir una app segura no es suficiente con detectar Rooted, sino que también debemos pensar en realizar un modelo de amenazas de los posibles riesgos para nuestra app, desarrollar de forma segura (por ejemplo OWASP Mobile) y aplicar técnicas de ofuscación de código (entre otras muchas medidas) para mitigar vulnerabilidades y dificultar la ingeniería inversa. Mi recomendación es que si no estamos familiarizados con estos conceptos hablemos con algún profesional en desarrollo seguro.
¿Y vuestra app que técnicas utiliza para detectar Rooted?
El pasado 10 y 11 de mayo se celebró el curso de Perito Informático Forense en Reus (España) por la Asociación Nacional de Tasadores y Peritos Judiciales Informáticos (ANTPJI), de la que soy miembro y profesor en los cursos, donde tuve el placer de dar una charla sobre dos de mis pasiones como son Python y OSINT (Open Source Intelligence).
Python es un estupendo lenguaje para desarrollar rápidamente todo tipo de aplicaciones potentes y con multitud de librerías para realizar exploits, ingeniería inversa, herramientas web y más. Sin duda un conocimiento útil para cualquier experto en seguridad.
Internet es inmenso y abarca toda información inimaginable, por ello las técnicas OSINT son de vital importancia para recolectar, analizar y presentar esta información.
Para este curso decidí que sería interesante para los asistentes aprender a desarrollar simples herramientas (scripts) que les permitan realizar OSINT mediante Python con una serie de ejercicios prácticos con un objetivo concreto cada uno.
La presentación y el código están disponibles en la web de VULNEX.
Nota: he quitado la query de Google Hacking de los scripts para que el lector pueda meter su propia query.
Herramienta #1
Objetivo: buscar miembros ANTPJI en LinkedIn mediante Google Custom Search API
Estos scripts son muy sencillos y hacen lo mismo pero de forma diferente. El primero utiliza el Google API Client, mientras que el segundo utiliza la fantástica librería Requests.
En este script estamos utilizando un poco de Google Hacking para buscar miembros de la asociación en LinkedIn.
# File: ex1_a.py
# Date: 05/14/13
# Author: Simon Roses Femerling
# Desc: Basic Google Hacking
#
# VULNEX (C) 2013
# www.vulnex.com
import const
from apiclient.discovery import build
import pprint
# your google hacking query
query=''
query_params=''
doquery=query+query_params
service = build("customsearch","v1",developerKey=const.cse_token)
res = service.cse().list(
q=doquery,
cx=const.cse_id,
num=10).execute()
pprint.pprint(res)
# VULNEX EOF
Al ejecutar cualquiera de estos scripts obtenemos el siguiente resultado:
No demasiado interesante por el momento :)
Herramienta #2
Objetivo: obtener las fotos de los miembros ANTPJI en LinkedIn mediante Google Custom Search API.
El siguiente script obtiene las fotos de los miembros de la asociación en LinkedIn y de paso sacamos los metadatos ;) El script genera una página HTML con todas las fotos.
# File: ex2.py
# Date: 05/14/13
# Author: Simon Roses Femerling
# Desc: Download picture and extract metadata
#
# VULNEX (C) 2013
# www.vulnex.com
import const
from apiclient.discovery import build
import pprint
import os
from PIL import Image
from StringIO import StringIO
from PIL.ExifTags import TAGS
import requests
import markup
def do_query(istart=0):
if istart == 0:
res = service.cse().list(
q=doquery,
cx=const.cse_id,
num=10).execute()
else:
res = service.cse().list(
q=doquery,
cx=const.cse_id,
num=10,
start=istart).execute()
return res
pic_id=1
do_stop=10
cnt=1
page=markup.page()
# Set page title
page.init(title="ANTPJI OSINT")
page.h1("ANTPJI OSINT")
# Set output directory
out_dir = "pics_gepl"
# Your Google Hacking query
query=''
query_params=''
doquery=query+query_params
service = build("customsearch","v1",developerKey=const.cse_token)
if not os.path.exists(out_dir):
os.makedirs(out_dir)
res=[]
while True:
if cnt==1:
res = do_query()
else:
if not res['queries'].has_key("nextPage"): break
res = do_query(res['queries']['nextPage'][0]['startIndex'])
cnt+=1
if cnt > do_stop: break
if res.has_key("items"):
for item in res['items']:
name=""
if not item.has_key('pagemap'): continue
if not item['pagemap'].has_key('hcard'): continue
hcard = item['pagemap']['hcard']
for card in hcard:
pic_url=""
if 'title' in card:
if 'fn' in card: name = card['fn']
if 'photo' in card: pic_url = card['photo']
if pic_url != "":
image = requests.get(pic_url)
pic_n = os.path.join(out_dir,"%s.jpg") % pic_id
file = open(pic_n,"w")
pic_id+=1
try:
i = Image.open(StringIO(image.content))
if hasattr(i,"_getexif"):
ret = {}
info = i._getexif()
if info:
for k,v in info.items():
decode = TAGS.get(k,v)
ret[decode] = v
print ret
i.save(file,"JPEG")
page.p(name.encode('ascii','ignore'))
page.img(src=pic_n)
page.br()
page.br()
except IOError, e:
print "error: %s" % e
file.close()
# Set your output filename
with open('index_gepl.html','w') as fp:
fp.write(str(page))
# VULNEX EOF
Y este es el resultado:
Con unas pocas líneas de código hemos conseguido una herramienta bastante interesante.
Herramienta #3
Objetivo: ¿cuál es la relación de los miembros de ANTPJI en LinkedIn?
Con este script buscamos la relación entre los miembros de la asociación en LinkedIn y creamos un gráfico que relaciona las palabras.
# File: ex3.py
# Date: 05/14/13
# Author: Simon Roses Femerling
# Desc: Build graph from profiles
#
# VULNEX (C) 2013
# www.vulnex.com
import const
from apiclient.discovery import build
import networkx as nx
import matplotlib.pyplot as plt
def do_query(istart=0):
if istart == 0:
res = service.cse().list(
q=doquery,
cx=const.cse_id,
num=10).execute()
else:
res = service.cse().list(
q=doquery,
cx=const.cse_id,
num=10,
start=istart).execute()
return res
do_stop=10
cnt=1
# Your Google Hacking query here
query=''
query_params=''
doquery=query+query_params
service = build("customsearch","v1",developerKey=const.cse_token)
G=nx.DiGraph()
res=[]
while True:
if cnt==1:
res = do_query()
else:
if not res['queries'].has_key("nextPage"): break
res = do_query(res['queries']['nextPage'][0]['startIndex'])
cnt+=1
if cnt > do_stop: break
if res.has_key("items"):
for item in res['items']:
name=""
if not item.has_key('pagemap'): continue
if not item['pagemap'].has_key('hcard'): continue
hcard = item['pagemap']['hcard']
for card in hcard:
if 'title' in card:
if 'fn' in card: name = card['fn']
G.add_edge(name,card["fn"])
plt.figure(figsize=(30,30))
nx.draw(G)
# Set your output filename
plt.savefig('antpji_rela_map.png')
# VULNEX EOF
Y este es el gráfico que se genera:
Herramienta #4
Objetivo: ¿de qué se habla en el Twitter de la asociación?
Este script baja los últimos tweets de la cuenta de la asociación y genera una nube de palabras. Útil para ver rápidamente de qué se habla.
# File: ex4.py
# Date: 05/14/13
# Author: Simon Roses Femerling
# Desc: Create word cloud
#
# VULNEX (C) 2013
# www.vulnex.com
import requests
import json
import urllib
import const
from pytagcloud import create_tag_image, make_tags
from pytagcloud.lang.counter import get_tag_counts
site="http://search.twitter.com/search.json?q="
# Your query here
query=""
url=site+urllib.quote(query)
response = requests.get(url)
tag = []
for res in response.json["results"]:
tag.append(res["text"].encode('ascii','ignore'))
text = "%s" % "".join(tag)
tags = make_tags(get_tag_counts(text),maxsize=100)
# Set your output filename
create_tag_image(tags,"antpji_word_cloud.png", size=(600,500), fontname="Lobster")
# VULNEX EOF
Y esta es la nube de palabras:
Herramienta #5
Objetivo: ¿dónde tienen cuenta los alias que participan en el Twitter de ANTPJI?
El siguiente script extrae los alias que han publicado o mencionado en el Twitter de la asociación y se comprueba si existe ese alias en 160 redes sociales.
# File: ex5.py
# Date: 05/14/13
# Author: Simon Roses Femerling
# Desc: Check usernames on 160 social network sites
#
# VULNEX (C) 2013
# www.vulnex.com
import requests
import json
import urllib
import const
import pprint
site="http://search.twitter.com/search.json?q="
# Your query here
query=""
url=site+urllib.quote(query)
print "Recolectando alias en Twitter: %s\n" % query
response = requests.get(url)
users = []
for res in response.json["results"]:
if res.has_key('to_user'):
if not res['to_user'] in users: users.append(str(res["to_user"]))
if res.has_key('from_user'):
if not res['from_user'] in users: users.append(str(res["from_user"]))
print "ALIAS-> %s" % users
print "\nComprobrando alias en 160 websites\n"
for username in users:
for service in const.services:
try:
res1 = requests.get('http://checkusernames.com/usercheckv2.php?target=' + service + '&username=' + username, headers={'X-Requested-With': 'XMLHttpRequest'}).text
if 'notavailable' in res1:
print ""
print username + " -> " + service
print ""
except Exception as e:
print e
# VULNEX EOF
Y el resultado es el siguiente:
Herramienta #6
Objetivo: ¿se pueden obtener los metadatos en las fotos de ANTPJI?
Este script baja las fotos relacionadas con la asociación en Google y extrae los metadatos.
# File: ex6.py
# Date: 05/14/13
# Author: Simon Roses Femerling
# Desc: Download pictures from Google and extract metadata
#
# VULNEX (C) 2013
# www.vulnex.com
import const
from apiclient.discovery import build
import pprint
import os
from PIL import Image
from StringIO import StringIO
from PIL.ExifTags import TAGS
import requests
import markup
def do_query(istart=0):
if istart == 0:
res = service.cse().list(
q=doquery,
cx=const.cse_id,
num=10).execute()
else:
res = service.cse().list(
q=doquery,
cx=const.cse_id,
num=10,
start=istart).execute()
return res
pic_id=1
do_stop=10
cnt=1
page=markup.page()
# Set your page title
page.init(title="ANTPJI OSINT")
page.h1("ANTPJI OSINT")
# Set output directory
out_dir = "pics_gepl"
# Define your Google hacking query here
query=''
query_params=''
doquery=query+query_params
service = build("customsearch","v1",developerKey=const.cse_token)
if not os.path.exists(out_dir):
os.makedirs(out_dir)
res=[]
while True:
if cnt==1:
res = do_query()
else:
if not res['queries'].has_key("nextPage"): break
res = do_query(res['queries']['nextPage'][0]['startIndex'])
cnt+=1
if cnt > do_stop: break
if res.has_key("items"):
for item in res['items']:
name=""
if not item.has_key('pagemap'): continue
if not item['pagemap'].has_key('hcard'): continue
hcard = item['pagemap']['hcard']
for card in hcard:
pic_url=""
if 'title' in card:
if 'fn' in card: name = card['fn']
if 'photo' in card: pic_url = card['photo']
if pic_url != "":
image = requests.get(pic_url)
pic_n = os.path.join(out_dir,"%s.jpg") % pic_id
file = open(pic_n,"w")
pic_id+=1
try:
i = Image.open(StringIO(image.content))
if hasattr(i,"_getexif"):
ret = {}
info = i._getexif()
if info:
for k,v in info.items():
decode = TAGS.get(k,v)
ret[decode] = v
print ret
i.save(file,"JPEG")
page.p(name.encode('ascii','ignore'))
page.img(src=pic_n)
page.br()
page.br()
except IOError, e:
print "error: %s" % e
file.close()
# Set your output filename
with open('index_gepl.html','w') as fp:
fp.write(str(page))
# VULNEX EOF
Una imagen vale más que mil palabras!
Como hemos visto a lo largo de este artículo podemos escribir sofisticadas herramientas de OSINT de forma sencilla con un poco de Python que nos
permiten recabar mucha información sobre individuos o colectivos.
Si te gustaría que profundizara sobre algún tema en Python y OSINT por favor házmelo saber :)
Hace un par de semanas los medios publicaban que el US Air Force ha clasificado 6 herramientas como ciberarmas, sin duda un hot topic. Para este post yo haré lo mismo y pondré un listado de 10 herramientas que podrían ser ciberarmas.
Mi criterio de selección va en función de su utilidad, capacidades y que sea open source o gratis por lo menos.
Lógicamente existen más herramientas que me gustan o que utilizo, pero creo que esta lista es una estupenda recopilación para realizar ataques en red y sistemas, ingeniería inversa, análisis de tráfico, ingeniería social, búsqueda de vulnerabilidades y desarrollo de exploits, vamos, que debería estar en el toolkit de todo pentester :)
Metasploit: la herramienta de hacking ético por excelencia.
SET: amplias opciones para realizar ataques de ingeniería social.
Dsploit: nada como llevar un toolkit de hacking ético en el móvil, para Android.