viernes, octubre 22, 2010

Antena UHF para TV Digital

DSC00322

(esta es una copia de un mensaje que mandé hacia el grupo Yahoo Ciencia Amateur, si les interesa la ciencia amateur no dejen de entrar.)

Hace unos meses compre en La Plata un dongle USB para ver TV Digital en la PC, pero la antenita que trae el equipo no tiene suficiente ganancia como para recibir la señal desde la Ciudad de Buenos Aires.

Hace poco levantaron una antena en La Plata. Aún así, me quedé con ganas de hacerme una Yagi direccional UHF para 550 Mhz. Hoy aproveché que tenia la tarde libre e hice una.

Usé la calculadora de K7MEM para Yagis VHF/UHF e hice una de 10 elementos ajustada en 550 Mhz, (casi) la frecuencia de Canal 7 en TV Digital. El boom central tiene 1,70 metros de largo y como materia prima aproveché una vieja antena de TV de aire analógica que bajé para instalar la Right VHF.

No la pude terminar ya que el elemento Director no esta en las medidas correctas. Usé un dipolo ajustado a 550 Mhz y tendria que acortarlo 1cm de cada lado.

Algo que noté fue que, si bien tengo una antena de TV Digital a menos de 1 Km de mi casa (antes estaba a 100 Km) apuntando hacia Buenos Aires se recibian imágenes, mientras que apuntando a 90° no se recibia nada. Eso me da una idea de que la Yagi esta funcionando mas o menos bien.

La calculadora que usé es:

 http://www.k7mem.150m.com/Electronic_Notebook/antennas/yagi_vhf.html

Acá pueden ver algunas fotos mientras fui armado la antena:

 http://www.flickr.com/photos/alejolp/sets/72157625095857543/

Entre las fotos se colaron imágenes de la Ringo y el dipolo de 14.1Mhz, aunque está bastante sordo. Al final de todo pueden ver par a par la Yagi UHF contra la omni que trae el aparato.

73's, LU4EXT.

lunes, octubre 11, 2010

Problemas con Speedy


Al igual que Javier, tengo problemas con Speedy: navegando por paginas Web aparece un error aleatorio que me obliga a apretar F5 cada cierto tiempo.

Es bastante molesto hacer eso a mano, asi que hice un mini script para automatizar la tarea. Les presento a Hammer of Thor, un Proxy SOCKS que cuando detecta el error puntual de desconexion de Speedy sigue insistiendo hasta que logra cargar la página.

Técnicamente hablando, de forma aleatoria se genera un paquete IP con el flag RST activado, generando que efectivamente se cierre la conexión. Lo extraño es que ese paquete llega unos pocos milisegundos más tarde del saludo de tres vias. Cosa 'e mandinga dirá alguno.

El programa está hecho en Python usando Twisted, e implementa de forma casera el protocolo SOCKS v5 siguiendo la RFC. Espero poder sacar una nueva version que entienda tambien v4 y Proxy HTTP, pero mi tiempo libre es poco. Se aceptan contribuciones :)

Actualización 12 de Octubre: Parece que la causa de fondo es en realidad un "TCP reset attack"; para mas detalles mirar el thread de la lista de Ubuntu. Una alternativa al Martillo de Thor es filtrar los paquetes RST; por ejemplo con iptables para Linux se puede hacer:
iptables -A OUTPUT -p tcp --dport 80 -m state --state NEW -m recent --set --name thor --rdest -j ACCEPT
iptables -A INPUT -p tcp -m tcp --tcp-flag RST RST -m state --state ESTABLISHED -m recent --name thor --rcheck --rsource --seconds 1 -j DROP
La idea es muy similar al Martillo de Thor: si llega un RST antes de que se cumpla 1 segundo desde que se inició la conexion, descartarlo. Este es un problema que seguro habrá que escalarlo a la CNC, ¿no?.

sábado, octubre 02, 2010

Ejecutando código Python de forma segura con eval()

Respuesta corta


Eval es maligno, no lo intentes.

Respuesta larga


Hace unos días publicaron en la lista de Python Argentina la pregunta de qué tan peligroso era usar la función eval para ejecutar código:

code = raw_input('Ingrese una entrada: ')
eval(code)

Sucede que si el usuario ingresa una expresion similar a:

Ingrese una entrada: __import__('os').system('ls /')

Pueden ocurrir cosas malas; en este ejemplo se esta listando los archivos del directorio raíz, pero ingresando: "rm -rf ~" se van a borrar todos los archivos de la carpeta personal del usuario.

Entonces, eval() parece bastante malo.

Una primera mejora a la fución eval() es asignarle dos parámetros adicionales para anular las funciones de importacion de módulos. Haciendo:

eval(code, {'__import__': None}, {})

El anterior ejemplo ya no funciona:

>>> eval('__import__("os")', {'__import__': None}, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not callable

Pero hay otra forma de pasar por arriba la limitacion de __import__:

>>> eval('__builtins__["__import__"]("os")', {'__import__': None}, {})
<module 'os' from '/usr/lib/python2.6/os.pyc'>

Ya tenemos de vuelta la referencia a __import__. Entonces como bien comentaron en la lista, una solución sería anular la referencia a __builtins__:

>>> eval('__builtins__["__import__"]("os")', {'__import__': None, '__builtins__': None}, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
TypeError: 'NoneType' object is unsubscriptable

Y si queremos abrir un arhivo tampoco se puede:

>>> eval('open("/tmp/aa", "w")', {'__import__': None, '__builtins__': None}, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'open' is not defined

Con la función 'file' tampoco funciona. El módulo __builtins__ es el módulo donde residen todas las funciones predefinidas de Python: file, open, list, dir, etc. Si queremos construir una lista tampoco se puede:

>>> eval('list()', {'__import__': None, '__builtins__': None}, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'list' is not defined

Pero hay otras formas de pasar por arriba las limitaciones de no tener __builtins__. Solo se necesita tener una referencia a un objeto que referencie a una función que nos permita hacer cosas no permitidas.

Una forma es usar las operaciones de 'introspection' de Python. Dada una instancia de un objeto podemos saber su clase, la clase padre y las subclases. Por ejemplo:

>>> (1).__class__.__bases__[0].__subclasses__()
[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, ...]

En este caso estamos tomando la instancia del objeto int(1), preguntando su clase, luego la clase padre (object), y luego las subclases de ésta (¡un montón!).

Mirando atentamente la lista encontramos dos referencias interesantes:

  • <type 'file'>
  • <type 'zipimport.zipimporter'>

La primera es el tipo file(), la misma que se usa para leer y escribir archivos. La segunda es la clase zipimporter, que permite importar módulos dentro de un zip.

Lo interesante es que aún anulando la referencia al módulo __builtins__, las anteriores operaciones siguen funcionando:

>>> eval("""[x for x in (1).__class__.__bases__[0].__subclasses__() if x.__name__=='file'][0]("/proc/version")""", {'__import__': None, '__builtins__': None, }, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
IOError: file() constructor not accessible in restricted mode

Intentando usar la referencia a 'file' tenemos un problema: el IOError.

El mensaje de error IOError aparece porque dentro de la función file() hay un bloque de código que verifica que __builtins__ no sea None.

Años atrás, el equipo de Python intentó agregarle al lenguaje la funcionalidad de ejecución restringida de código, que permita ejecutar código no seguro de forma segura.

Luego de varios intentos desistieron, justamente porque no era posible contemplar todos los casos. Hoy en día esas funciones están deprecated y en Python 3 fueron completamente removidas del código fuente.

Entonces, en Python 3 es posible importar cualquier módulo y ejectar codigo malicioso fácilmente, aún anulando __builtins__:

>>> (eval('[x for x in (1).__class__.__bases__[0].__subclasses__() if x.__name__ == "ImpImporter"][0]().find_module("os").load_module("os").system("id")', {'__import__': None, '__builtins__': None}, {}))
uid=1000(alejo) gid=1000(alejo) groups=1000(alejo),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),44(video),46(plugdev)

En este caso, la clase ImpImporter es una subclase de object que abstrae todo el comportamiento de importación de módulos. Acceder a esa clase es algo que no se puede evitar usando las opciones de eval().

Quedó pendiente la instancia de zipimporter.

Importar modulos con esa clase no imposible, la única complicación es conocer la ruta de algún egg o zip dentro del sistema que nos ofrezca alguna referencia a __builtins__, __import__, al módulo 'os' o a otro módulo que nos interese.

Por ejemplo, hacer un zip que tenga dentro un modulo que importe el módulo 'os' es bastante trivial:

>>> eval("""[x for x in (1).__class__.__bases__[0].__subclasses__() if x.__name__=='zipimporter'][0]("/tmp/modulo1.zip").find_module("modulo1").load_module("modulo1").os.system("id")""", {'__import__': None, '__builtins__': None, }, {})
uid=1000(alejo) gid=1000(alejo) groups=1000(alejo),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),44(video),46(plugdev)

En resumen, no se confíen de eval() para ejecutar código potencialmente inseguro. En la wiki de Python.org hay una sección dedicada exclusivamente a la seguridad, aquellos que estén interesados no dejen de leerla.