Mostrando entradas con la etiqueta codigo. Mostrar todas las entradas
Mostrando entradas con la etiqueta codigo. Mostrar todas las entradas

15 de junio de 2010

Listando contactos en Android con "Android.Contact"

Como prometí en el primer post sobre Android hoy voy a esplicar un poco de código de Android.

En este ejemplo podremos ver como se obtiene datos de contactos con una API menor de 5, es decir para los moviles con Android 1.x y actualmente se encuentra deprecated (obsoleta), en general esto quere decir que en un futuro será eliminada. Pero no obtante este no es el caso, ya que esta API continuará activa, pero sólo devolverá datos de la primera cuenta de Google creada.

Para el API 5 se creó la clase "ContactsContract", más reciente, que permite acceder a múltiples cuentas y agregar contactos que tengan un apoyo similar.

Para este ejemplo vamos a usar un "Cursor" para acceder a los datos, la clase "Contacts.Phones" para obtener las constantes para acceder a los datos de la agenda. Por supuesto, para poder leer esto es necesario tener permiso para leer la agenda. (Puedes descargar el codigo desde http://pastebin.com/AMGkjhzz)
@SuppressWarnings("finally")
public static boolean isContact(String num, ContentResolver contentResolver) {
 /**
  * Dado un numero de telefono num, retorna si está o no en la agenda
  * Para poder acceder a estos datos es necesario tener permiso de leectura de contactos.
  * ("android.permission.READ_CONTACTS" en AndroidManifest.xml)
  * @param num Numero de telefono
  * @param context Contexto con permisos de leectura de Contactos
  */
 boolean ret = false;
 try{
  Cursor cur = contentResolver.query(Contacts.Phones.CONTENT_URI, null, null, null, null);
  int index = cur.getColumnIndex(Contacts.Phones.NUMBER);
  while (!ret && cur.moveToNext())
   if (num.equals(cur.getString(index)))
    ret = true;
  cur.close();
 } catch (Exception e) {
  e.printStackTrace();
 } finally {
  return ret;
 }
}

Más Información:
http://developer.android.com/guide/appendix/api-levels.html

ContentResolver

9 de mayo de 2010

DNSpython - Sacando información de los DNS con Python

Como ya escribí en el post "Obtención de información de las DNS usando nslookup" desde un servidor DNS se puede obtener mucha información interesante.

El el post pasado para obtener la información usamos la herramienta nslookup. En este caso vamos a implemantar una herramienta que obtenga los datos más interesantes usando la librería DNSPython.

Los datos vamos a mostrar son: CNAME (Nombre canónico) , IPs y sus resoluciones inversas, MX (Registros de correo), NS (Registros de servidores DNS), SOA (marca el inicio de una zona de autoridad), TXT, LOC (localización), MINFO y HINFO (Información del correo y host respectivamente).

Cabe destacar que algunos de estos datos no se encuentran en todos los servidores de nombre.

Además para realizar las consultas vamos a usar en DNS de google situado en 8.8.8.8.

Bueno a continuación el código:

'''
@copyright: GPL v.3
@author: ehooo
@contact: <ehooo|at|rollanwar|dot|net>
'''
import dns.resolver
import dns.reversename
import getopt, sys

if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], "h:", ["host="])
except getopt.error, msg:
print msg
print sys.argv[0] + " [ -h | --host= ]"
exit(2)
host = None
for o, a in opts:
if o in ("-h", "--host"):
host = a
if host is None:
print sys.argv[0] + " [ -h
| --host= ]"
exit(1)

myresolver = dns.resolver.Resolver()
myresolver.domain = dns.name.Name("google-public-dns-a.google.com")
myresolver.nameserver = ['8.8.8.8']

print "DATOS DEL HOST:", host
try:
answers = myresolver.query(host, 'CNAME')
for rdata in answers:
print "CNAME:", str(rdata.target)
except dns.resolver.NoAnswer:
print "CNAME: No se puede obtener"

try:
answers = myresolver.query(host, 'A')
ip = []
for rdata in answers:
n = dns.reversename.from_address(rdata.address)
try:
answers_inv = myresolver.query(n, 'PTR')
for rdata_inv in answers_inv:
ip += [(rdata.address, str(rdata_inv.target))]
except dns.resolver.NoAnswer:
ip += [(rdata.address, "PTR: Sin respuesta "+str(n))]
except dns.resolver.NXDOMAIN:
ip += [(rdata.address, "PTR: Dominio NX "+str(n))]
print "IPs:", ip
except dns.resolver.NoAnswer:
print "IPs: No se puede obtener"

try:
answers = myresolver.query(host, 'MX')
mx = []
for rdata in answers:
mx += [str(rdata.exchange)]
print "MXs:", mx
except dns.resolver.NoAnswer:
print "MXs: No se puede obtener"

try:
answers = myresolver.query(host, 'NS')
ns = []
for rdata in answers:
ns += [str(rdata.target)]
print "NSs:", ns
except dns.resolver.NoAnswer:
print "NSs: No se puede obtener"

try:
answers = myresolver.query(host, 'SOA')
for rdata in answers:
print "SOA:", str(rdata.mname), str(rdata.rname)
except dns.resolver.NoAnswer:
print "SOA: No se puede obtener"

try:
answers = myresolver.query(host, 'TXT')
for rdata in answers:
print "TXT:", rdata.strings
except dns.resolver.NoAnswer:
print "TXT: No se pueden obtener"

try:
answers = myresolver.query(host, 'LOC')
for rdata in answers:
print "LOC:", "Latitud",rdata.float_latitude,"Logitud", rdata.float_longitude
except dns.resolver.NoAnswer:
print "LOC: No se pueden obtener"

try:
answers = myresolver.query(host, 'MINFO')
for rdata in answers:
print "MINFO:", rdata.to_text()
except dns.resolver.NoAnswer:
print "MINFO: No se pueden obtener"

try:
answers = myresolver.query(host, 'HINFO')
for rdata in answers:
print "HINFO:", rdata.to_text()
except dns.resolver.NoAnswer:
print "HINFO: No se pueden obtener"


El resultado sería el siguiente:

DATOS DEL HOST: www.rollanwar.net
CNAME: rollanwar.net.
IPs: [('66.98.184.55', 'hispla.com.')]
MXs: ['rollanwar.net.']
NSs: ['ns1.hispla.com.', 'ns2.hispla.com.']
SOA: ns1.hispla.com. sistema.hispla.com.
TXT: No se pueden obtener
LOC: No se pueden obtener
MINFO: No se pueden obtener
HINFO: No se pueden obtener

Más info:

http://www.iana.org/assignments/dns-parameters
http://www.dnspython.org/

29 de noviembre de 2009

Script para obtener versión de WP

Haciendo uso de lo explicado en el articulo "Saca la versión de Wordpress gracias a Gears" he decidido crear un pequeño script en Python que nos diga la versión de WP que se está usando.


La manera más simple para obtener este dato de la versión es buscar el valor del campo "generator" en los metadatos del sitio:


<meta name="generator" content="WordPress 2.8.6" />

Dependiendo del tema que tengamos este dato de meta puede o no estar.


'''
@copyright: GPL v.3
@author: ehooo
@contact: <vvillahoz|at|malwareint|dot|com>
'''

from HTMLParser import HTMLParser
from httplib import HTTPException
import httplib, os, string

class ParseMetaGen(HTMLParser):
'''
Parser para obtener el metadato "generator"
'''

def __init__(self):
HTMLParser.__init__(self)
self.generator = False

def handle_startendtag(self, tag, attrs):
if tag == 'meta':
if len(attrs) == 2 and attrs[0][0] == 'name' and attrs[0][1] == 'generator':
self.generator = attrs[1][1]

def error(msg=""):
print "ERROR"+str(msg)
exit(1)

if __name__ == '__main__':
site = None

try:
opts, args = getopt.getopt(sys.argv[1:], "s", ["site="])
except getopt.error, msg:
print msg
error(": Use -s URL or --site=URL")

for o, a in opts:
if o in ("-s","--site"):
site = string.replace(a,"http://","")
else:
error(": Unhandled Option")

if site is not None:
path = site.find("/")
if path != -1:
host = site[:path]
path = site[path:]
else:
path = "/"
headers = {"Host": host, "Accept": "*/*", "User-Agent":"Spider/WP-version"}

try:
conn = httplib.HTTPConnection(host)
conn.request("GET", path, headers=headers)
respuesta = conn.getresponse()
if respuesta.status == httplib.OK:
pmg = ParseMetaGen()
pmg.feed(respuesta.read())
version = pmg.generator
if not version:#Si no obtenemos el generador buscamos en el Gears
conn.request("GET", path+"/wp-admin/gears-manifest.php", headers=headers)
respuesta = conn.getresponse()
if respuesta.status == httplib.OK:
data = respuesta.read()
if data.find("ae52efa2f066ffc235840dc615f051d7"):
version = "Wordpress 2.8.1-2.8.6"
elif data.find("068d0a4281fb2a342ba6fd73b6b93982"):
version = "Wordpress 2.8.0"
elif data.find("thickbox.js?ver=3.1-20080430"):
version = "Wordpress 2.7.0"
elif data.find("thickbox.js?ver=3.1-20090123"):
version = "Wordpress 2.7.1"
elif data.find("_20080710a"):
version = "Wordpress 2.6.0"
elif data.find("wp-admin.css?ver=2.6.1"):
version = "Wordpress 2.6.1"
elif data.find("wp-admin.css?ver=2.6.2"):
version = "Wordpress 2.6.2"
elif data.find("wp-admin.css?ver=2.6.3"):
version = "Wordpress 2.6.3"
elif data.find("wp-admin.css?ver=2.6.4"):
version = "Wordpress 2.6.4"
elif data.find("wp-admin.css?ver=2.6.5"):
version = "Wordpress 2.6.5"
else:
version = "Not Found"
elif respuesta.status == httplib.NOT_FOUND:
print "Gears Not Supported"
print "Version: "+version
except HTTPException:
error('Conection probles')
else:
error()

Para evitar que nuestro wordpress muestre la versión en los metadatos de nuestro sitio, podemos crear un simple plugin que nos eliminará este metadato, o añadir la siguiente linea a un plugin cualquiera.


remove_action('wp_head', 'wp_generator');

2 de noviembre de 2009

HTML5 el nuevo estándar de Internet

HTML5 es el nuevo estándar del W3C para la web. Este estándar está pensado para la web 2.0 o web semántica, como queráis llamarla.

HTML5 está todavía en versión de pruebas, pero ya podemos ver ejemplos de como funciona en sitios como Youtube (http://www.youtube.com/html5). Cabe destacar que a la par de HTML5 se está desarrollando XHTML5 que aúna XHTML1.1 y HTML5.

-Eliminado en HTML5

  • Tags de estilo:
    HTML5 elimina ciertas etiquetas típicas de estilo dado que ya no son necesarias al establecer el estándar de estilo CSS como son: <center>, <font>, <s>, <strike>, <tt>, <big>, <basefont> y <u>)

  • Tags dobles:
    Se eliminan ciertas etiquetas duplicadas en las versiones anteriores como: <acronym>, <xmp> y <dir>

  • Tags para frames:
    Adios a los viejos frames como: <noframes>, <frameset> y <frame>

  • Tags especificos:
    Sustituye la etiqueta <applet> usada para insertar applets de java por la generica <object>.


-Lo nuevo en HTML5
HTML5 añade nuevas etiquetas pensadas principalmente el mundo del bloging y la web 2.0 como <article>, <time> o <details> entre otros; pero donde más destaca es en lo referido a multimedia con nuevas etiquetas como <audio>, <canvas>, <embed> o <video> entre otros.
También añade nuevos parámetros como "draggable", "contenteditable" o "ref" entre otros.
Pero donde más ha cambiado el estándar es en los eventos script, 17 nuevos eventos han sido añadidos (y solo uno eliminado "onreset") que permiten por ejemplo generar eventos al mover el scroll o al producirse un error.
Quizás lo que más llamará la atención a algunos será el rediseño de la estructura básica de HTML, de este modo un código básico en HTML5 quedaría algo así:
<html>
<head>
</head>
<body>
<header><!-- Cabecera del site -->
</header>
<nav><!-- Menu de navegación -->
</nav>
<article>
<section>
</section>
</article>
<footer><!-- Pie del site -->
</footer>
</body>
<html

- Lo malo en HTML5
Desde el punto de vista de accesibilidad web se ha eliminado el parámetro "accesskey" que facilita el acceso a un punto concreto de la web mediante un acceso por rápido por teclado.

De momento pocos navegadores dan un soporte correcto a este estandar.

Referencias:

HTML5 en W3c
Protocolo HTML5

12 de octubre de 2009

Saca la versión de Wordpress gracias a Gears

Bueno, cuando estuve mirando el error del FPD observé que existía un fichero que mostraba con un contenido demasiado explicito, en un principio pesé que podía ser un fallo por el que se descubriría la versión del wordpress instalada, movido por un dato muy claro:
"version" : "ae52efa2f066ffc235840dc615f051d7"

En ese momento me mosqueo un poco y me puse a buscar. Que diablos era ese fichero con referencias a javascripts.

Resulta que es un fichero usado por Gears, antes Google Gears.

¿Que es Gears? os preguntareis algunos. Pues bien Gears es un programa diseñado en principio por Google y que posteriormente ha liberado para la comunidad. Este es el software que en el que se basa Gmail offline o GoogleDocs offline entre otros. Con estos datos el software se descarga de tu sitio la información para que puedas usarla sin conexión y luego se sincroniza con el servidor.

Pero vamos a lo interesante.

¿Como calcula la versión?


En la versión 2.8.x el calculo se realiza a partir de:
//El generador que da soporte a Gears usa estos datos
md5( $tinymce_version . $manifest_version ) //Como aparece en manifest.php
//Donde:
$tinymce_version = "3241-1141" //Para 2.8.x
//Y
$manifest_version = "20090610" //Para 2.8
$manifest_version = "20090616" //Para v2.8.1 - 2.8.4
//Por tanto:
//En v2.8 podriamos poner
echo '"version" : "068d0a4281fb2a342ba6fd73b6b93982"';
//En v2.8.1-2.8.4
echo '"version" : "ae52efa2f066ffc235840dc615f051d7"';

En las versiones 2.6.x y 2.7.x el valor de $man_version se calcula mediante unas funciones que crean un dato que parece aleatorio; pero bueno eso no es un muy importante ya que se le añade un "_DIGITOS" que nos indica la versión. Haciendo por tanto que el valor de $man_version sea poco relevante.
//En v2.7.x
$man_version = bucles_1($man_version)//Por resumir
$man_version = md5($man_version);
//Podríamos poner algo asi en v2.7.x
echo '"version" : "'."$man_version"."_20081201".'"';
//En v2.6
$man_version = bucles_2($man_version)//Por resumir
$man_version = md5($man_version);
echo '"version" : "'."$man_version"."_20080710a".'"';
//En v2.6.1 - 2.6.5
echo '"version" : "'."$man_version"."_20080810".'"';

Lo curioso del caso es que según las especificaciones de Geads el valor de "version" es un String, que solo indica si ha cambiado o no el contenido del fichero. Por tanto sería igual de valido hacer un md5 de la fecha de la ultima actualización si es que esta actualización cambiase el valor de este fichero. O incluso lo que seria más óptimo, cuando se instalase el blog se creara un fichero estático y se modificara en caso de ser necesario en vez de estar generandose continuamente con cada petición.

Pero aun hay algo más, dado que en los datos mostrados existe más información sobre versiones de los distintos ficheros js, podríamos obtener más diferencias.

En la versión 2.7 thickbox.js?ver=3.1-20080430

En la versión 2.7.1 thickbox.js?ver=3.1-20090123

Si en el caso de 2.7.x hay que mirar en "thickbox", en las versiones 2.6.x lo tenemos aún más fácil, ya que podemos encontrar varios sitios donde viene directamente la versión, por ejemplo, wp-admin.css?ver=2.6.x entre otros.
//Parte del código de gears-manifest.php en la versión 2.6.1
foreach ( $wp_scripts->registered as $script ) {
if ( empty($script->src) || strpos($script->src, 'tiny_mce_config.php') ) continue;
$ver = empty($script->ver) ? $wp_version : $script->ver; //Aquí en ocasiones se incluye la versión
$src = str_replace( array( '/wp-admin/', '/wp-includes/' ), array( '', '../wp-includes/' ), $script->src );
$defaults .= '{ "url" : "' . $src . '?ver=' . $ver . '" },' . "\n";
$man_version .= $ver;
}

Conclusiones


Aunque en principio podría haber parecido un vector claro para conocer la versión del Wordpress, parece que en las versión 2.8.x estos datos claros se han eliminando.

Aún así en la versión actual sigue dejando un claro punto para saber si la versión en cuanto cambien los valores de $tinymce_version o $manifest_version.

Bajo mi punto de vista, como ya he dicho antes, modificaría el modo de crear el valor de "version" y lo convertiría en un dato aleatorio, que solo cambien cuando sea necesario.
De este modo un posible atacante solo sería capaz de conocer si un wordpress se ha actualizado conociendo el valor anterior de "version" si esa actualización cambiara este dato, hecho que no siempre ocurre.

Referencia:
Tutorial de gears
Arquitectura de gears
Aplicaciones disposibles

11 de octubre de 2009

Lista de Password

Tras leer en varios sitio sobre la publicación de más de 10.000 contraseñas de cuentas de correo, y tras confirmar por distintas fuentes que las contraseñas suelen ser las mismas y que en su mayor parte son simples de sacar con un ataque de fuerza bruta por diccionario, me he propuesto hacer un diccionario ordenado por probabilidad de aparición de las contraseñas.

Se que existen infinidad de diccionarios en muchos sitios (http://passwords.openwall.net) pero yo quiero crear uno bastante reducido, para tardar a menos y realizar el menor numero de intentos posibles.

¿Porque usar diccionarios?


Existen varias razones pero las dos principales son estas:

  • La primera es la más clara, intentar probar todas requiere demasiado tiempo. El calculo es simple, simplemente hay que hacer la variación con repetición. He realizado un test empírico de un algoritmo que calcula 10000 contraseñas (solo números) tarda 0.047 segundos y por tanto en generar y probar un millón vamos a suponer que tarda 5 segundos; con estos cálculos aproximados veamos la siguiente tabla:

    Tamaño Posibles (85)Tiempo (85) Posibles (26)Tiempo (26)
    4 caracteres52 millones4,33 minutos456.9762,28 segundos
    5 caracteres4.437 millones6,16 horas11 millones59,40 segundos
    6 caracteres377.149 millones21,83 días308 millones25,74 min
    7 caracteres32 billones5,07 años8.031 millones11,15 horas
    8 caracteres2.724 billones4,31 siglos208.827 millones12,08 días
    9 caracteres231.616 billones36,6 milenios5 billones10,47 meses
    10 caracteres19 trillones3010 milenios141 billones22,69 años
    11 caracteres1.673 trillones265070 milenios3.670 billones5,9 siglos

    NOTA: 2x(26 Letras + ñ) + (21 caracteres especiales [!|"@.#$~%&/()=?¿'*+] ) + (10 digitos) = 85
    Como se observa en la tabla cuando la contraseña tiene más de 5 caracteres el tiemplo empleado para obtener la contraseña es de más de una semana, tiempo de sobra para ser detectados sin problema.

  • Otra de las razones por las que realizar un diccionario está relacionado con el modo que tenemos de recordar las personas. Dado que recordar "E46!fhJd9@" nos resulta muy complicado usamos otras contraseñas simples de recordad, normalmente palabras que existen. Por tanto podemos reducir el número de intento a tantas como palabras hay en el diccionario, más de 160 mil palabras según la rae, lo que reduce en numero de posibles claves.Lo mejor a la hora de usar diccionarios es realizar uno personalizado para cada objetivo, no es igual intentar sacar el password de un ingles o americano que de un egipcio o un español, ya que las palabras que usarán serán distintas.


Creación del Diccionario


Para crear mi diccionario he cogido los datos desde los siguientes sitios:

  1. Top 10 contraseñas de PC Magazine

  2. Contraseñas más usadas de las publicadas de "Flirtlife.de"

  3. Contraseñas más usadas en las publicadas de "Hotmail.com"

  4. Contraseñas por defecto de los Routers


Para que me resultara más simple la creación del diccionario he programado en python un simple código para ordenar:
'''
Programa que descarga y ordena las contraseñas más por defecto de los routers
@author: ehooo
'''

import httplib, os
from HTMLParser import HTMLParser
class ParseTable(HTMLParser):
'''
Parser para obtener los datos de la columna indicada
'''

def __init__(self, columna):
HTMLParser.__init__(self)
self._columna = columna
self._colAct = 0
self._data = False
self._fin = True
self.lista = {}

def handle_data(self, data):
if self._data:
dato = str(data)
if dato in self.lista:
self.lista[dato] += 1
else:
self.lista[dato] = 1
self._data = False

def handle_starttag(self, tag, attrs):
if self._fin:
if tag == "table":
self._fin = False
elif tag == "tr":
self._colAct = 0
elif tag == "td":
if self._colAct == self._columna:
self._data = True
self._colAct += 1

def handle_endtag(self, tag):
if self._fin:
pass
elif tag == "tr":
self._colAct = 0
elif tag == "table":
self._fin = True

if __name__ == '__main__':
conn = httplib.HTTPConnection("www.phenoelit-us.org")
conn.request("GET", "/dpl/dpl.html")
res = conn.getresponse()
if res.status == httplib.OK:
html = res.read()
p = ParseTable(5)
p.feed(html)
lista = p.lista
n_elem = len(lista)
list = {}
try:
while True:#Cuando se queda sin elementos lanza una Excepcion "KeyError"
(key, valor) = lista.popitem()
if valor in list:
list[valor] += ", "+str(key)
else :
list.setdefault(valor, str(key))
except(KeyError):
valores = list.keys()
valores.sort()
valores.reverse()
ord = []
for valor in valores:
ord += [list[valor]]
elem = 0
for passwords in ord:
porcentaje = (valores[elem]* 100) / n_elem
print valores[elem],"de",n_elem,"son",porcentaje,'% ->',passwords
elem += 1
else:
print "ERROR en la descarga"

La lista que he obtenido ha quedado reducida ha 46 palabras, cogiendo las más comunes de los routers y de las personas; eliminando ciertas passwords sin demasiada relevancia. Puedes descargar el diccionario desde http://rollanwar.net/pass+usados.txt. De todos modos puedes crear el diccionario de routers usando parte del código publicado.

Otros datos de interés:
http://onemansblog.com/2007/03/26/how-id-hack-your-weak-passwords/