Escribiendo tus propias historias con git

Buenas con todos :) Antes de continuar con los textos de la lista de pedidos, quiero celebrar el lanzamiento de git 2.16 agradeciendo a cada uno de los que mandó un parche y a cada uno de los usuarios, en total tuvimos como 4000 líneas entre actualizaciones y correcciones, lo cual no habla muy bien de mi primera versión, pero si de la amabilidad de ustedes :) ¡Gracias! Ahora bien, les contaré un pequeño secreto, hasta ahora no ha habido una vez en la que no me haya sentado a escribir un artículo y haya pensado mucho al respecto, normalmente solo escribo de corrido, y después el buen lagarto se toma la amabilidad de corregir mis faltas de tipeo :) así que gracias a él también.

Esto no es lo mejor cuando hablamos de escribir artículos, supuestamente debería tener un objetivo y armar una estructura, y marcar pequeños puntos y revisiones y etc etc… Ahora bien, esto no solo aplica para los blogs en general, sino que es fundamental en un software que pretende ser bueno :) Para esta tarea, y tras algunos problemas con el software de control de versiones que se usaba en el desarrollo del kernel hace unos años, nació git :)

¿Dónde aprender git?

La cantidad de documentación existente en torno a git es descomunal, incluso si solo tomamos las páginas de manual que vienen con la instalación, tendríamos una cantidad inmensa de lectura. Yo personalmente encuentro el libro de git bastante bien diseñado, incluso yo he traducido algunos de los segmentos de la sección 7, todavía me faltan unos cuantos, pero denme tiempo :P tal vez en este mes pueda traducir lo que queda de esa sección.

¿Qué hace git?

Git está diseñado para ser rápido, eficiente, simple y soportar grandes cargas de información, después de todo, la comunidad del kernel lo creo para su software, el cual es uno de los trabajos conjuntos más grandes del software libre del mundo y cuenta con cientos de contribuciones por hora en una base de código que supera el millón de líneas.

Lo interesante de git es su forma de mantener las versiones de la data. Antiguamente (otros programas de control de versiones) tomaban comprimidos de todos los archivos existentes en un punto de la historia, como hacer un backup. Git tiene un enfoque diferente, al realizar un commit se marca un punto en la historia, ese punto en la historia cuenta con una serie de modificaciones y trabajos, al final del día, se juntan todas las modificaciones a lo largo del tiempo y se obtienen los archivos para poder comprimir o marcar como hitos de versiones. Como sé que todo esto suena complicado, voy a llevarlos por un mágico viaje en un ejemplo super básico.

Pequeño proyecto de calculamática

La calculamática será un programa que encontrará los cuadrados de un número dado, lo haremos en C y será lo más simple posible, así que no esperen muchos controles de seguridad de mi parte. Primero vamos a crear un repositorio, lo haré con Github para matar dos pájaros de un tiro:

Diseño propio. Christopher Díaz Riveros

Hemos agregado un par de cosas bastante simples como la licencia (muy importante si quieres proteger tu trabajo, en mi caso, obligarlos a compartir los resultados si lo quieren usar de base :P)

Ahora vamos a ir a nuestra querida terminal, git clone es el comando que se encarga de descargar el repositorio ubicado en la url asignada y crear una copia local en nuestro equipo.

Diseño propio. Christopher Díaz Riveros

Ahora vamos a revisar con git log lo que ha ocurrido en la historia de nuestro proyecto:

Aquí tenemos mucha información en diversos colores :) vamos a tratar de explicarla:

la primer línea amarilla es el “código de barras del commit” cada commit tiene su propio identificador único, con el cual puedes hacer bastantes cosas, pero lo vamos a dejar para después. Ahora tenemos HEAD de celeste y master de verde. Estos son “punteros” su función es apuntar a la ubicación actual de nuestra historia (HEAD) y la rama en la que estamos trabajando en nuestra computadora (master).

origin/master es la contraparte de internet, origin es el nombre por defecto que se ha asignado a nuestra URL, y master es la rama en la que está trabajando… para hacerlo sencillo, los que tienen un / son aquellos que no se encuentran en nuestro equipo, sino que son referencias a lo que está en internet.

Después tenemos el autor, la fecha y hora y el resumen del commit. Esta es una pequeña reseña de lo que ha sucedido en ese punto de la historia, muy importante en muchos proyectos y ahí se condenza bastante informaicón. Vamos a ver más de cerca lo que sucedió en el commit con el comando git show <código-de-commit>

 

Diseño propio. Christopher Díaz Riveros

El comando git show nos lleva a esta pantalla en formato parche, donde se aprecia lo que se ha agregado y lo que se ha quitado (si se hubiese quitado algo) en ese momento de la historia, hasta aquí solo nos muestra que se agregaron los archivos .gitignore,README.mdLICENSE.

Ahora vamos a lo nuestro, vamos a escribir un archivo :) crearemos el primer hito en nuestra historia :D :

Diseño propio. Christopher Díaz Riveros

Brevemente, vamos a crear un programa que nos muestre la cantidad de argumentos pasados al momento de ejecutarlo, simple :)

Diseño propio. Christopher Díaz Riveros

Eso fue fácil :) ahora vamos a ver el siguiente comando útil: git status

Diseño propio. Christopher Díaz Riveros

Algún alma de buen corazón ha traducido git para hacerlo sencillo de seguir, aquí tenemos mucha información útil, sabemos que estamos en la rama master, que estamos actualizados con origin/master(la rama de Github), ¡que tenemos archivos sin seguimiento! y que para agregarlos tenemos que usar git add, vamos a probar :)

Diseño propio. Christopher Díaz Riveros

Ahora tenemos un nuevo espacio de verde, en el cual se muestra el archivo que hemos agregado a la zona de trabajo. En este lugar podemos agrupar nuestros cambios para poder realizar un commit, el commit consiste en un hito a lo largo de la historia de nuestro proyecto, vamos a crear el commit :) git commit

Diseño propio. Christopher Díaz Riveros

Brevemente explicado, la línea amarilla es el título de nuestro commit, yo escribo main.c por una mera referencia visual. El texto de negro es la explicación de los cambios realizados desde el commit anterior hasta ahora :) guardamos el archivo y veremos nuestro commit guardado en el registro.

Diseño propio. Christopher Díaz Riveros

Ahora vamos a ver la historia de nuestro proyecto con git log

Diseño propio. Christopher Díaz Riveros

Nuevamente en el log, ahora podemos ver que la línea verde y la roja han diferido, eso se debe a que en nuestra computadora, estamos un commit por encima de los almacenados en internet :) vamos a seguir el trabajo, supongamos que ahora quiero mostrar un mensaje en caso de que el usuario ponga más de un argumento en el programa (lo cual haría que la calculadora se confunda :) )

Como podemos ver, nuestro programa ha crecido bastante :D , ahora tenemos la función imprimir_ayuda() que muestra un mensaje sobre cómo usar la calculamatica, y en el bloque main() ahora hacemos una revisión con if(algo que veremos en un tutorial de programación en otro momento, por ahora solo es necesario saber que si se ingresan más de 2 argumentos a la calculamática, que el programa termine y se muestre la ayuda. Vamos a ejecutarlo:

Diseño propio. Christopher Díaz Riveros

Como pueden ver ahora imprime el número que ha sido entregado en lugar de la cantidad de argumentos, pero eso yo no se los había contado antes :) para los curiosos echo $? muestra el código de salida del último programa ejecutado, el cual es 1 porque ha terminado en error. Ahora vamos a revisar cómo va nuestra historia:

Diseño propio. Christopher Díaz Riveros

Ahora sabemos que estamos 1 commit delante de Github, que el archivo main.c ha sido modificado, vamos a crear el siguiente commit  haciendo git add main.c  y luego git commit:)

Diseño propio. Christopher Díaz Riveros

Ahora hemos sido un poco más específicos, puesto que hemos implementado una función y cambiado el código de validación. Ahora que se ha guardado vamos a revisar nuestro último cambio. Podemos verlo con git show HEAD

Diseño propio. Christopher Díaz Riveros

Ahora se pueden apreciar las líneas rojas y verdes, hemos agregado la biblioteca stdlib.h, modificado gran parte del código y agregado la función a nuestra historia.

Ahora vamos a ver el log: (git log)

Diseño propio. Christopher Díaz Riveros

Podemos ver que estamos dos commits adelante de la versión de Github, vamos a igualar un poco el marcador :) para eso usamos git push origin master

Con esto decimos, envía mis commits al url origin en la rama master

Diseño propio. Christopher Díaz Riveros

¡Felicidades! Ahora sus cambios están en Github, ¿no me creen? vamos a revisarlo ;)

Diseño propio. Christopher Díaz Riveros

Ahora tenemos los 3 commits en Github :)

Resumen

Hemos tocado los aspectos más básicos de git, ahora pueden crear un flujo de trabajo simple en sus proyectos, esto no es casi nada de toda la gran variedad de cosas que pueden hacerse con git, pero ciertamente es lo más práctico y de todos los días para un desarrollador o blogger. No hemos llegado al final de la calculadora, pero eso lo vamos a dejar para otro momento ;) Muchas gracias por llegar hasta aquí y espero les ayude a participar en varios proyectos :D Saludos

 

¿Cómo saber que estás listo para contribuir?

Hola a todos :) estos días he cumplido con varios logros personales y ciertamente me han dejado pensando un poco, así que quiero compartir con ustedes los resultados de mi divagar, además de responder de manera indirecta a algunos correos que me llegan al buzón cada cierto tiempo :)

Todos tenemos un principio

Esta es una anécdota que ya he contado en mi primer artículo, pero hasta el día de hoy me sigue impactando en esos momentos que me tomo para reflexionar sobre mi camino en el desarrollo de software. Cuando recién tenía Ubuntu en mi laptop, recuerdo un día haber estado en la biblioteca y haber querido actualizar mi computadora, nunca lo había hecho, pero no sé por qué en ese momento lo necesitaba… creo que había algo que quería instalar para un curso y no aparecía en los repositorios cuando se suponía que debía estar… todavía me acuerdo de la frustración que sentí y el desanimo con el que recorría las listas de resultados de google hasta que encontré la solución… me faltaba ejecutar el oscuro y misterioso comando:

sudo apt-get update

Evidentemente en ese tutorial seguía la línea:

sudo apt-get upgrade

al poco tiempo y leyendo en otros lugares había incluso visto :

sudo apt-get update && sudo apt-get upgrade

pero recuerdo curiosamente haber escrito:

sudo apt-get update && upgrade

pensando que de esa manera se ejecutaría lo mismo :) qué tiempos aquellos…

Todos tenemos más de un principio

Ahora en inevitable que llegue a mi mente el primer momento en que escuché de Kali Linux, ciertamente estaba maravillado por esto de la seguridad, había leído un post que trataba sobre descifrado de claves de redes inhalámbricas, me sentía todo un hacker al momento de ejecutar john.

Horas pasaron en el primer intento por descubrir la clave de una red WEP que se encontraba en las cercanías de mi tarteja de wifi… me llevó un buen tiempo descubrir que las listas de claves por defecto de john solo tenían palabras en inglés, algo que ciertamente no es muy útil en mi ciudad, y mucho menos en las cercanías de donde vivo…

Mi primer libro de ‘hacker’

Recuerdo con mucho cariño mi primer libro de hacker, ciertamente fue todo un desafío… primero porque en ese momento todavía no estaba acostumbrado a leer en inglés, segundo… y más importante aún, porque cada línea de texto me parecía chino mezclado con algún tipo de lenguaje alienígena. Para todos aquellos que se estén preguntando qué libro es ese… la respuesta está aquí :)

Y fue ese un punto interesante en mi camino de aprendizaje, porque ese fue el momento en que descubrí que no me gustaba usar Kali Linux sin saber qué estaba sucediendo a cada paso, ciertamente es interesante correr cosas como nmap o burp o mil y un herramientas más que vienen por defecto. Descubrí que quería saber por qué funcionaban, y cómo lo hacían. Desde ese momento dejé de practicar con las herramientas de Kali y empecé a leer sobre lenguajes de programación.

Y volvimos al primer instante donde todo parecía chino alienígena :) ciertamente entendía poco o nada de lo que leía, y al mismo tiempo seguía y seguía, devorando información en cada rincón de internet a más no poder… evidentemente me preocupaba por conseguir la mejor fuente posible para llenarme de información.

Entrar en lo profundo

Pasó un poco de tiempo y ya estaba en Gentoo, y me llamaba mucho la curiosidad de muchas cosas, y con el pasar de los días aprendía mucho sobre compilación y sobre construcción, y sobre seguridad, y sobre muchas cosas. Pero evidentemente al principio, como en todas las experiencias previas, sentía que leía chino alienígena.

¿Por qué cuento esto?

Pues porque estos días empecé a mandar mis primeros parches (cosas bastante pequeñas) a la comunidad del kernel, hacía mucho tiempo había escuchado que era una comunidad de comentarios despiadados, que no eran el lugar para un novato en el mundo FOSS, que era muy selectivos con lo que se aplicaba y ¿saben lo que he descubierto? que no es nada de eso, si conoces las reglas :)

Ya en otro momento hablamos sobre el entrar a casa a ajena, y no respetar las reglas de casa… evidentemente me ha tomado tiempo aprender estas reglas, aprender a usar git lo suficientemente bien como para mandar un parche adecuadamente, aprender a usar un software de análisis estático de código, aprender a revisar mis trabajos con detenimiento, aprender a comunicarme con la comunidad, aprender a usar vim, aprender C… y sí, al principio todo puede parecer chino alienígena, pero conforme van pasando los días, todo esto cobra sentido y te das cuenta de cuánto has avanzado y cuánto has aprendido.

Hoy

Hoy conozco más comandos y formas de actualizar un sistema de las que podría haber imaginado, lo mismo que hoy conozco y domino en cierta medida el flujo de trabajo colaborativo en una comunidad… hoy leo aquellas páginas (o incluso algunas más complicadas) y no me pierdo en el camino…

Mañana

Si hablamos de mañana… pues todavía hay mucho que quiero aprender, quiero conocer nuevas tecnologías, quiero dominar nuevos lenguajes, quiero construir nuevas comunidades, quiero enseñar a más personas, y probablemente pasará lo que ha pasado en cada primer paso de mi descubrir tecnológico… que no voy a entender nada al principio :) y a esto quería llegar con tantas palabras, mucho se habla de la zona de confort, yo creo que ese es el lugar a donde llegan todos aquellos que creen que han dominado algo… porque tan solo creer que lo has dominado, es ciertamente motivo y razón suficiente para descubrir que te equivocas, y que todavía te falta mucho por recorrer. Al principio tal vez no entiendas, tal vez te equivoques, tal vez incluso quieras tirar la toalla, pero todo eso es necesario para no llegar jamás a la zona de confort, porque si solo haces lo que conoces, ¿qué más confortable que eso?

Este lo dejo hasta aquí porque solo es una pequeña opinión… no quiero que piensen que sé más de lo que en realidad sé, lo poco que he aprendido es porque me he dado el trabajo de nunca estar en una zona de confort por tiempo suficiente como para creer que “domino” algún tema :) y para los que me preguntan que cuándo estarán listos para colaborar en un proyecto o comunidad, pues la respuesta es sencilla…

Si te sientes listo, ya estás tarde.

Gran parte de la aventura está en descubrir cosas :) si ya todo lo conoces y dominas, todo pierde sentido :) es por esto que disfruto tanto aprendiendo sobre GNU/Linux, porque es un mundo que no parece acabar. Cierto es que puedes dedicarte a hacer la misma labor por muchos días o años sin crecer, pero también es cierto que puedes hacer una labor sin dominarla, pero aprendiendo mucho cada día :) Gracias a los que lleguen hasta aquí, y saludos y cuidado con su zona de confort

cómo proteger tu equipo de ataques

Muy buenas con todos, antes de entrar al hardening de tu equipo, quiero contarles que el installador que estoy desarrollando para Gentoo ya está en su fase pre-alpha :D esto quiere decir que el prototipo es lo suficientemente robusto como para poder ser probado por otros usuarios, pero al mismo tiempo todavía falta mucho por recorrer, y el feedback de estas etapas (pre-alpha,alpha,beta) ayudará a definir rasgos importantes sobre el proceso :) Para los interesados…

https://github.com/ChrisADR/installer

PS. todavía tengo la versión exclusivamente en inglés, pero espero que para el beta ya tenga su traducción a español también (estoy aprendiendo esto de las traducciones en tiempo de ejecución en python, así que todavía hay mucho por descubrir)

Hardening

Cuando hablamos de hardening, nos referimos a una gran variedad de acciones o procedimientos que dificultan el acceso a un sistema informático, o red de sistemas. Precisamente por eso es que es un tema vasto y lleno de matices y detalles. En este artículo voy a listar algunas de las cosas más importantes o recomendables para tener en cuenta al momento de proteger un sistema, intentaré ir de lo más crítico a lo menos crítico, pero sin ahondar mucho en el tema puesto que cada uno de estos puntos sería motivo de un artículo propio.

Acceso físico

Este es sin dudas el primer y más importante problema de los equipos, puesto que si el atacante cuenta con fácil acceso físico al equipo, ya puede contarse como un equipo perdido. Esto es verdad tanto en grandes centros de data como en laptops dentro de una empresa. Una de las principales medidas de protección para este problema son las claves a nivel de BIOS, para todos aquellos a los que suene nuevo esto, es posible poner una clave al acceso físico de la BIOS, de esta manera si alguien quiere modificar los parámetros de inicio de sesión y arrancar el equipo desde un sistema live, no será trabajo sencillo.

Ahora bien, esto es algo básico y ciertamente funciona si es realmente requerido, yo he estado en varias empresas en las que no importa esto, porque creen que el “guardia” de seguridad de la puerta es más que suficiente para poder evitar el acceso físico. Pero vamos a un punto un poco más avanzado.

LUKS

Supongamos por un segundo que un “atacante” ya ha conseguido acceso físico al equipo, el siguiente paso es cifrar cada disco duro y partición existente. LUKS (Linux Unified Key Setup) es una especificación de cifrado, entre otras cosas LUKS permite cifrar con clave una partición, de esta manera, al iniciar el sistema si no se conoce la clave, la partición no puede ser montada ni leída.

Paranoia

Ciertamente existe gente que necesita un nivel “máximo” de seguridad, y esto lleva a resguardar hasta el aspecto más mínimo del sistema, pues bien, este aspecto llega a su cúspide en el kernel. El kernel de linux es la manera en la que tu software va a interactuar con el hardware, si tu evitas que tu software “vea” al hardware, este no podrá hacer daño al equipo. Para poner un ejemplo, todos conocemos lo “peligrosos” que son los USB con vírus cuando hablamos de Windows, pues ciertamente los usb pueden contener código en linux que podría o no ser perjudicial para un sistema, si hacemos que el kernel solo reconozca el tipo de usb (firmware) que deseamos, cualquier otro tipo de USB simplemente sería obviado por nuestro equipo, algo ciertamente un poco extremo, pero podría servir dependiendo de las circunstancias.

Servicios

Cuando hablamos de servicios, la primera palabra que me viene a la mente es “supervisión”, y esto es algo bastante importante, puesto que una de las primeras cosas que hace un atacante al entrar a un sistema es mantener la conexión. Realizar análisis periódicos de las conexiones entrantes y sobre todo salientes es algo muy importante en un sistema.

Iptables

Ahora bien, todos hemos oído sobre iptables, es una herramienta que permite generar reglas de ingreso y salida de data a nivel de kernel, esto es ciertamente útil, pero también es un arma de doble filo. Muchas personas creen que por tener el “firewall” ya están libres de cualquier tipo de ingreso o salida del sistema, pero nada más alejado de la realidad, esto solo puede servir de efecto placebo en muchos casos. Es conocido que los firewalls funcionan a base de reglas, y estas ciertamente pueden ser evitadas o engañadas para permitir transportar data por puertos y servicios por los cuales las reglas considerarían que es algo “permitido”, solo es cuestión de creatividad :)

Estabilidad vs rolling-release

Ahora este es un punto bastante polémico en muchos lugares o situaciones, pero permítanme explicar mi punto de vista. Como miembro de un equipo de seguridad que vela por muchos de los problemas de la rama estable de nuestra distribución, estoy al tanto de muchas, casi todas las vulnerabilidades existentes en los equipos Gentoo de nuestros usuarios. Ahora bien, distribuciones como Debian, RedHat, SUSE, Ubuntu y muchas otras pasan por lo mismo, y sus tiempos de reacción pueden variar dependiendo de muchas circunstancias.

Vayamos a un ejemplo claro, seguro todos han oído hablar de Meltdown, Spectre y toda una serie de noticias que han volado por internet en estos días, pues bien, la rama más “rolling-release” del kernel ya está parchada, el problema radica en llevar esas correcciones a kernels anteriores, ciertamente el “backporting” es un trabajo pesado y difícil. Ahora bien, después de eso, todavía tienen que ser probados por los desarrolladores de la distribución, y una vez que se han completado las pruebas, recién estará a disposición de los usuarios normales. ¿A qué quiero llegar con esto? A que el modelo rolling-release nos exige conocer más sobre el sistema y formas de rescatarlo si algo falla, pero eso es bueno, porque mantener la pasividad absoluta en el sistema tiene varios efectos negativos tanto para quien lo administra como para los usuarios.

Conoce tu software

Este es un adicional bastante valioso al momento de administrar, cosas tan simples como subscribirse a las noticias del software que utilizas puede ayudar a conocer de antemano los avisos de seguridad, de esta manera se puede generar un plan de reacción y al mismo tiempo ver cuánto tiempo toma para cada distribución resolver los problemas, siempre es mejor se proactivo en estos temas porque más del 70% de los ataques a empresas se llevan a cabo por software no actualizado.

Reflexión

Cuando la gente habla de hardening, muchas veces se cree que un equipo “resguardado” es a prueba de todo, y no hay nada más falso. Como su traducción literal lo indica, hardening implica hacer las cosas más difíciles, NO imposibles… pero muchas veces mucha gente piensa que esto conlleva magia oscura y muchos trucos como los honeypots… esto es un adicional, pero si no se puede con lo más básico como mantener actualizado un software o un lenguaje de programación… no existe necesidad de crear redes fantasma y equipos con contramedidas… lo digo porque he visto varias empresas donde preguntan por versiones de PHP 4 a 5 (evidentemente descontinuadas)… cosas que hoy por hoy son conocidas por tener cientos sino miles de fallas de seguridad, pero si la empresa no puede seguir el ritmo de la tecnología, pues de nada sirve que hagan lo demás.

Además, si todos estamos usando software libre o abierto, el tiempo de reacción para los errores de seguridad suele ser bastante corto, el problema viene cuando estamos tratando con software privativo, pero eso lo dejo para otro artículo que sigo esperando poder escribir pronto.

Muchas gracias por llegar hasta aquí :) saludos

Variables 101: Conociendo tu computadora

Tristemente he visto que no muchos desean aprender a programar este 2018 :( pero aunque solo hubiese leído mi artículo anterior una persona y tras un poco de tiempo esta sea capaz de mandar un commit a algún proyecto de software libre, yo me daría por satisfecho con mi labor :)

Para los amantes de la seguridad, les prometo que el siguiente será un post sobre seguridad :) así todos contentos, si alguno desea aprender otra cosa (como git, administración de servidores, o yo que sé :p ), o que comente algún otro tema que no pueda responderse de manera sencilla en el cuadro de comentarios, avísenme y vemos cómo podemos trabajarlo ;)

Bueno, ahora si vamos a lo nuestro, anteriormente hablamos sobre tipado, y que este tenía que ver con la forma en la que guardamos nuestras variables en un programa, ahora vamos a revisar un poco de qué ocurre en el interior y esperemos que pueda ser lo suficientemente claro.

Bits

Creo que este es un tema que siempre toco cuando escribo sobre programación, ciertamente es algo que me fascina y que me ha ayudado a comprender muchas cosas, ahora intentaré explicar un poco cómo son, cómo se leen, y para qué sirven :)

Piensen en un interuptor de luz, cuando el circuito está cerrado, tenemos un 0 en la pantalla, cuando cambiamos de posición el interruptor, pues un 1:) ¿sencillo no es cierto?

Ahora, un 0 y un 1 pueden significar muchas cosas, todo depende de la creatividad con que lo tomes, supongamos que yo quiero saber si alguien va a ir al Norte o al Sur, 1 puede significar norte y 0, sur :) digamos que yo quiero saber si alguien es hombre o mujer, 1 puede ser hombre y 0, mujer :) . Ahora quiero saber si esta persona es jóven o mayor (>22), 0 puede significar jóven y 1, mayor. Sigamos imaginando… ¿Tiene alguna mascota? 1 diría que sí, mientras que 0 diría que no. Ahora quiero que lean conmigo la siguiente línea:

1001

Esto es la forma breve de decir…

Una jóven mujer de no más de 22 años se dirige al norte acompañada de su mascota.

lo que es muy diferente a:

0110 o Un hombre con más de 22 años de edad se dirige solo hacia el sur.

Bytes

Ahora vamos a ir un paso adelante, vamos a aprender a leer bytes. Un byte es la sucesión de 8 bits, los cuales se leen de derecha a izquierda y cada 1 representa una potencia de 2 elevado a la n donde n es la posición del bit. Como suena a chino, vamos a poner un pequeño ejemplo :)

01001011 Tenemos este byte, ahora vamos a ir de derecha a izquierda ( <- ) yo voy a ponerlos de arriba a abajo para poder escribir su signifiacdo:

1: el bit al estar en la posición 0 nos indica que tenemos lo siguiente 2 elevado a la cero o 2^0. Esto bien sabemos que equivale a 1.

1: el segundo bit, ahora la posición 12^1 que es lo mismo que decir 2

0: tercer bit… aquí debería ser 2^2, mas como no está encendido, vamos a dejarlo en 0

1:cuarto bit, 2^3 u 8 :)

0: lo mismo que 0

0: otro 0

1:ahora estamos en 2^6 o 64

y finalmente 0 , que ya sabemos lo que significa :) ahora vamos a sumar nuestros resultados y compararlos con la siguiente tabla :) Tenemos un 75 por lo que vamos a buscarlo en la columna Decimal y veremos lo que aparece en Char

Resultado de imagen para ascii table

¡¡Tenemos una K!! Felicidades, ya saben leer en binario :) Pero los más sagaces habrán podido notar que también hemos obtenido un número decimal, y que este tiene un límite (cuando todos los valores son 1) Ese límite se encuentra en el número 255.

Word

Ahora más de uno me dirá, pero ¿qué pasa si necesito un número mayor a 255? o ¿dónde puedo encontrar otros caractéres como los japoneces? Pues la respuesta es simple, juntemos 2 bytes. Ahora que tenemos dos, la cantidad posible de combinaciones que tenemos es 2^16 o 65536 posibles resultados, como el 0 es uno de esos, el máximo posible es 65535. ¿A alguno le suena ese número? ¿Recuerdan la cantidad máxima de puertos en un sistema linux? Se los dejo de tarea ;)

Double word & quad word

Para los más matemáticos también existen formatos específicos, los double word contienen, como muchos ya habrán imaginado 2 word o 4 bytes (o 32 bits) de información, lo mismo que decir:

11111111111111111111111111111111 o de 0 a 4 294 967 295

A estas alturas muchos se preguntarán qué sucede con los números negativos, es decir, en algún lugar deben estar contemplados ¿no es cierto? Para poder almacenar un número negativo, los desarrolladores de procesadores eligieron ocupar el primer bit de la izquierda como un valor de signo. Esto quiere decir que si el primero bit es 0 hablamos de un número positivo, pero si es 1 tenemos un negativo. Ahora ven por qué los bits son tan especiales, pueden ser lo que tu quieras :D

¡Pero esto evidentemente nos deja con una posición menos para realizar la multiplicación! Por lo que nuestro0 a 4 294 967 295 se convierte en:

-2,147,483,648 a +2,147,483,647

Ahora bien, muchos ya tenemos procesadores de 64 bits, y este es el valor de un quad word, podemos tener valores que van desde el 0 a 18 446 744 073 709 551 615. Ese sí es un número grande :)

¿Por qué 8 bits?

Esto es algo que ya más de uno se estará preguntando, y la respuesta está en el hardware. Desde su origen, los procesadores necesitaban data para poder realizar operaciones. La data se almacena en la memoria de la computadora y cada vez que el procesador la requiere utiliza los buses de data para conseguirla. En la antigüedad, estos buses podían comunicar un máximo de 8 bits por ciclo, esto quiere decir que el máximo y más eficiente modo de mover data, era agrupando 8 bits y mandando estos al procesador.

Con el paso del tiempo, hasta el día de hoy, los procesadores han desarrollado la hablidad de mover 16 bits, 32bits y… 64 bits.

¿Qué tiene que ver con el tipado?

Ya llegamos a la parte donde todo cobra sentido :) El tipado es una propiedad que utilizan los lenguajes de programación para denominar estos espacios de memoria. Todas las variables tienen su contraparte en alguno de estos tipos de data, sin importar cómo sean llamadas. Estos se conocen como tipos de dato primitivos, cada lenguaje fuertemente tipado tiene su concepción de estos valores, y la cantidad que representan. Por ejemplo en C tenemos la biblioteca limits.h que nos muestra la cantidad máxima y mínima de los valores primitivos.

Veamos lo que sucede si intentamos romper uno de los valores:

Diseño propio. Christopher Díaz Riveros

A la derecha tenemos los valores del archivo limits.h y a la izquierda hemos tomado uno de estos valores (unsigned short int) y le hemos asignado un número mayor al correspondiente. Como resultado el compilador nos advierte que estamos utilizando mal la memoria porque la forma binaria de 66666 no puede caber en la forma binaria de 65535. Esto nos lleva a una lección de performance cuando programamos, si tu valor no va a crecer mucho a lo largo del tiempo, o si no requieres valores tan grandes como los de un double o quad word, utilizar el tipo correcto reduce la cantidad de memoria solicitada por el CPU, lo que implica una mayor velocidad de obtención de data si está bien calculada.

En el lado de los intérpretes esto es más sencillo debido a las conversiones implícitas. Cuando definimos una variable en lenguajes como javascript o Python, el intérprete se encarga de entender qué tipo es, y asignar el espacio suficiente de memoria para realizar las operaciones. Veamos un ejemplo sencillo :)

Diseño propio. Christopher Díaz Riveros

Como pueden ver, no tenemos que explicar al intérprete de python el tipo de nuestra variable, porque él mismo se encarga de asignar un tipo y almacenarlo en memoria :)

Conoce tus variables

Esto depende del lenguaje y tipo de implementación que vayas a utilizar, pero el primer paso para poder programar es aprender las variables que puedes utilizar :) Una vez comprendas las variables, estarás en situación de poder utilizarlas de manera eficiente y lógica para almacenar información (provista por un usuario o por el sistema). Este es el primer paso en la escalera de la programación y con suerte, tras leer este artículo, habrás comprendido un poco mejor cómo es que funciona tu computadora y cómo almacena la información. Conmigo será hasta el siguiente artículo, recuerden dejar sus comentarios para saber si hay que reforzar o comentar algún punto en específico. Saludos

Eligiendo tu primer lenguaje de programación

Ciertamente esta es una de las preguntas que más llega a mi bandeja de entrada al momento de hablar de programación. Si vamos a comenzar una serie de artículos que les permitan aprender a programar y devolver el conocimiento gratuito en forma de contribuciones a comunidades de software libre/open source en el mundo, es necesario responder a esta básica aunque un poco difícil pregunta. ¿Qué lenguaje de programación debo aprender?

Un poco de historia

Para poder empezar a comprender y elegir un lenguaje de programación, primero debemos conocer un poco sobre la historia de los mismos, sus usos y funciones, y cómo resuelven distintas necesidades a lo largo del tiempo.

Lenguajes de máquina (bajo nivel)

Conocidos comunmente como Assembly, son lenguajes de programación que podríamos definir como dialectos de un idioma más general… Esto suena un poco complicado pero lo voy a ejemplificar… Sabemos que el lenguaje universal de la computación es la electricidad, esto quiere decir que en última instancia lo que una computadora lee son 0s y 1s, vamos a denominar esto como español de computadora. En este ejemplo, el español es la regla básica, pero como bien sabemos, no es lo mismo el español que hablan los latinos al hablado en España, e incluso así, no es lo mismo el español de Perú con el español de Argentina. Evidentemente todos tenemos casi las mismas palabras (0s y 1s), mas el uso y significado pueden variar de acuerdo al contexto.

Esto sucede a nivel de procesador. Cuando hablamos de arquitecturas de computación, (amd64, intel, arm,…) nos referimos al dialecto de ese español de computadora. Esto se debe a que diversas empresas entienden el orden y significado a su manera, por lo que algunos varian en detalles como el flujo de la corriente, o el orden con el que se van a guardar los 0s y 1s.

Estos lenguajes de programación son sumamente veloces, puesto que trabajan al nivel más bajo posible de programación, pero son sumamente dependientes de la arquitectura y ciertamente son un poco más complicados de aprender que el resto. Estos suelen requerir de una base más amplia de conceptos para poder transformar la data y poder ejecutar cosas útiles en ellas. Para los amantes de los videojuegos, un ejemplo serían las consolas SEGA, las cuales utilizaban Assembly para programar sus juegos. Evidentemente en esa época la cantidad de memoria era mínima comparada con hoy, y era necesario dominar un lenguaje que pudiera ser veloz y producir programas ligeros.

Lenguajes de alto nivel

Este gran grupo contempla aquellos lenguajes que vinieron después de Assembly. La necesidad de obtener codigo portable hizo que surgiera un grupo de lenguajes denominados compilados. Entre estos el primero en tomar ventaja fue C, el cual ha tenido predominancia en la programación a nivel de sistema operativo desde los 70s.

Lenguajes compilados

Vamos a ver un ejemplo práctico de lo que comento. Veamos un programa muy simple en lenguaje C que imprime una línea de código.

Diseño propio. Christopher Díaz Riveros

Tras compilarlo tenemos lo siguiente:

Diseño propio.Christopher Díaz Riveros

Pero ahora veamos lo que tendríamos que escribir para replicar el mismo resultado en código Assembly:

Diseño propio. Christopher Díaz Riveros

Esta es la traducción de nuestras 3 líneas de código de simple.c, el archivo simple.s es creado mediante el comando gcc -S simple.c y es lo que entendería nuestro procesador en un dialecto Assembly. Evidentemente para poder crear un ejecutable que conste de 0s y 1s es necesario procesar el archivo simple.s y conectarlo con las bibliotecas compartidas de nuestro sistema. Esto se hace mediante un ensamblador (as) y un conector (ld).

Los lenguajes compilados brindan una gran ventaja sobre los de bajo nivel, son portables. La portabilidad entrega código que puede ser ejecutado en distintos procesadores sin la necesidad de generar código específico para cada arquitectura. Otra ventaja evidente es la simplicidad que emplea al momento de leer y escribir código. Dentro de sus principales desventajas tenemos una elevada complejidad, puesto que comparado con el siguiente tipo de lenguajes que veremos, la libertad que brinda C puede ser perjudicial si no se sabe controlar, ciertamente es como entregar una pistola, podría suceder que en la falta de experiencia una persona termine disparando a su propio pie en el intento de limpiar el arma.

Lenguajes interpretados

Dentro de este grupo tenemos una gran variedad de lenguajes, entre los más importantes contamos Python, Ruby, Javascript, PHP, etc… La idea básica de estos lenguajes es brindar una forma rápida de creación y ejecución de programas, esto se debe a que muchos de los procesos difíciles son llevados a cabo en el intérprete, y la programación de la lógica es la que se implementa en el código. Veamos el mismo ejemplo anterior pero esta vez escrito en Python:

Diseño propio. Christopher Díaz Riveros

Dentro de las cosas más resaltantes podemos ver que la primer línea se encarga de llamar al intérprete ( el programa que va a ejecutar nuestra aplicación) y el subsiguiente código es más “simple” que su versión en C, puesto que todo el trabajo pesado se realiza en el intérprete.

Diseño propio. Christopher Díaz Riveros

Los lenguajes interpretados brindan al desarrollador una capa de seguridad mayor, puesto  cuentan con controles de seguridad más rigurosos (OJO que no son perfectas, puesto que hasta los mejores pueden cometer errores) y ya no sufrimos el riesgo de disparar un arma sin darnos cuenta, puesto que al primer intento, el intérprete soltaría una alerta y se cancelaría la ejecución. La principal desventaja se hace evidente al momento de ejecutar el programa, puesto que este es más lento que su contraparte binaria, esto precisamente debido a la mayor cantidad de procesamiento para poder asegurar que el código funciona. Si el programa no requiere de plazos extremadamente cortos, la diferencia puede pasar desapercibida, pero si hablamos de miles o millones de datos por segundo, la diferencia se hace exponencialmente notable en los lenguajes compilados.

Tipado

Esta es una caracteríscia de los lenguajes de programación, estos pueden ser fuertemente débilmente tipados. Este tema lo voy a dejar para otro post, puesto que es necesario y curioso entender cómo se almacena la memoria en un programa, pero por ahora solo necesitamos hacer la distincion: Los lenguajes fuertemente tipados son aquellos que requieren conocer el tipo de dato que va a trabajarse en una variable o constante, mientras que los débilmente tipados pueden realizar conversiones de manera implícita y todo dependerá de una jerarquía de conversión seguida por el lenguaje. (si no se entiende ahora, no hay problema, lo dejaremos para después)

Paradigmas

Al igual que todo en el mundo GNU/Linux, los lenguajes de programación se basan de acuerdo a paradigmas, y se generan comunidades en torno a estos. Por ejemplo tenemos la Fundación Python o Ruby o PHP o Bash (en cuyo caso es la comunidad GNU). A lo que quiero llegar con esto es que no puedo expresar la gran cantidad de pros y contras que tiene cada uno, pero si puedo decirles que donde existe un lenguaje de programación libre, existe una comunidad donde aprender y participar. Vale la pena mencionar que muchos si es que no son todos los intérpretes de lenguajes están escritos en C, o algún derivado cercano, y el desarrollo de los mismos suele llevarse a cabo por un grupo más reducido de la comunidad, quienes se encargan de tomar decisiones que afectarán a todos los usuarios del lenguaje. Pueden incluso formarse instituciones que velen por el desarrollo correcto del lenguaje, como es el caso de C.

¿Cuál elegir?

Ya hemos hablado bastante sobre los lenguajes y todavía no respondo a lo más importante :P . Pero espero que tras haber revisado este pequeño artículo no sea necesario que sea yo mismo quien te diga qué lenguaje elegir, puesto que con esta información estás en toda la capacidad de buscar uno que te genere curiosidad. Evidentemente si deseas aprender a programar en un lenguaje Assembly requerirás de bastante tiempo antes de poder tener algo funcional, el tiempo se reducirá bastante si optas por un lenguaje compilado, donde además de contar con la portabilidad en sistemas *NIX, podrás aprender información referente a el funcionamiento del mismo sistema, puesto que estar en contacto con C o derivados te hace de una manera u otra aprender cómo funciona de manera general un sistema operativo. Por último, si lo que quieres es aprender algo ligero y que te permita hacer mucho sin la necesidad de comprender mucho, los lenguajes interpretados son una manera entretenida de aprender y desarrollar habilidades de programación.

Aprende con algo emocionante

Este es el mejor consejo que puedo darles, si quieren aprender algo, es necesario encontrar algo apasionante primero, sino será bastante difícil sobrepasar la curva de aprendizaje típica de todo lenguaje de programación. Supongamos que administran sistemas, en ese caso tal vez sea necesario aprender un lenguaje ideal para scripting (interpretado), dentro de estos contamos con Perl, Python, Bash, etc etc… Tal vez lo tuyo son los juegos, existen muchos proyectos en lenguajes como Javascript, Lua, C++, dependiendo del tipo de juego que desees realizar. Tal vez te gustaría crear una herramienta a nivel de sistema, pues tenemos C, Python, Perl, como verás algunos se repiten, y esto es debido a que muchos lenguajes pueden ser utilizados para muchas tareas, por eso la definición de lenguajes multipropósitos en la mayoría de estos.

Comienza un proyecto

Con esto no me refiero a que crees el siguiente compilador, o incluso el siguiente lenguaje de programación, un proyecto puede ser arreglar un pequeño bug en tu programa favorito, tal vez incluso ayudar a mejorar la documentación. ¿Por qué la documentación? porque no hay mejor forma de aprender cómo funciona el software que leyendo y ayudando a escribir su documentación, porque luego del código fuente, es la mayor fuente de información que se va a encontrar sobre el programa. En otro momento veremos cómo leer el código de un proyecto y entender las funciones y valores que adquieren.

Muchas gracias por haber llegado hasta aquí y como siempre, sus comentarios me ayudan a generar mejor contenido y saber dónde enfocar la atención, Saludos.

Comencemos el año programando

Es sorprendente la acogida que ha tenido el buen Mar.io y a decir verdad es el primer artículo que publico que genera más de 10 mil vistas, esto pone la valla algo alta para los siguientes y espero no defraudarlos con este :) Muchas gracias por encontrar mis escritos lo suficientemente interesantes como para compartirlos dicho sea de paso :)

Programación

Este es un tema de moda, todo el mundo quiere programar, o al menos todo el mundo piensa que es una habilidad cada vez más necesaria, y a decir verdad a mi me gustaría escribir todo un libro sobre programación, GNU/Linux, seguridad, y tal vez en algún momento lo pueda hacer, apenas aprenda cómo escribir libros libres y en formato agradable :P .

La tecnología avanza rápidamente

Este es uno de los motivos por los que no he escrito el libro todavía :P puesto que quiero hacer algo que pueda superar la barrera del tiempo en un campo en el que las cosas no suelen durar más de unos días de forma vigente. Es por esto que en este artículo quiero contarles un poco de los conceptos más que las implementaciones, de esta manera podremos volver a leer estas líneas en un tiempo y seguirán siendo vigentes.

Los principios se mantienen más tiempo

A pesar de que existen muchos lenguajes de programación hoy por hoy, muchos de los conpceptos se remontan a los mismos orígenes. Con esto quiero decir que muchas de las cosas que hoy se aprenden, han sido válidas por mucho tiempo, y probablemente lo seguirán siendo, esto debido a que la programación es hecha por personas y mientras sigan siendo ellos los que desarrollen, algunos conceptos se mantendrán.

Conociendo las bases

Ya existen muchos cursos, algunos gratuitos y otros no, que exponen gran parte de la sintaxis de muchos de los lenguajes de programación más populares de hoy en día. Pero no vamos a hacer esto aquí :) yo quiero contarles un poco de lo que todo pogramador debería pensar antes de empezar a programar para poder hacer un trabajo decente.

Entrar en la mente del programador es ciertamente algo necesario, ya en un artículo un poco antiguo tratamos el tema. Ahora vamos a entrar un poco en los conceptos que nos permiten escribir el código.

Variables y funciones

Las variables son espacios de memoria, pensemos en los buzones que tienen los grandes edificios, están diseñados para almacenar cierto tipo de objetos, los hay grandes y pequeños, pueden estar solos o en grupo. Una variable es un valor que tu sabes que se usará a lo largo del tiempo, aunque exactamente no conoces su valor en el principio, si lo conoces y sabes que no va a variar, estamos frente a una constante.

Las funciones por otra parte, son conjuntos de instrucciones. Una instrucción es lo más básico que puede hacer un procesador, la razón de ser de las funciones es permitir al programador agrupar conjuntos de ordenes para poder repetirlas a lo largo de un programa. veamos un ejemplo sencillo y a la vez lleno de detalles.

Diseño propio. Christopher Díaz Riveros

Este es un pequeño programa escrito en C, tenemos la función main, la variable saludo, y la función printf que proviene de la biblioteca stdio.h. Vamos a modificar un poco el ejemplo y luego compilarlo para ver qué sucede.

Diseño propio. Christopher Díaz Riveros

Hemos agregado una pequeña función llamada saludar la cual toma como argumento una variable llamada saludo y la imprime. Esto no cambia mucho el resultado final del programa pero nos permite mostrar un gran y útil principio de la programación, la abstracción. Veamos el resultado:

Diseño propio. Christopher Díaz Riveros

Un simple programa, que está lleno de conocimiento y trabajo.

Bibliotecas

El motivo por el cual creé la función saludar fue simplemente para mostrar uno de los principios más grandes del desarrollo de software, que ya hemos nombrado: la abstracción. Así como hemos definido saludarprintf() ha sido definido en algún lugar de nuestro sistema operativo (la biblioteca standard de C de GNU), este lugar se conoce comúnmente como biblioteca. Las bibliotecas son conjuntos de funciones que nos permiten agregar funcionalidad a nuestros programas sin tener que volver a inventar la rueda. En este caso, gracias a printf no tenemos que preocuparnos de toda la lógica necesaria para poder mostrar en una terminal el mensaje que deseamos.

Las bibliotecas están presentes en casi todos los lenguajes de programación actuales, puesto que al poder contar con secciones de código para elegir e implementar es más sencillo que crear cada función desde cero.

Abstracción

Imaginemos el sistema de correo, nosotros no necesitamos conocer toda la logística necesaria para poder enviar o recibir una carta, lo mismo sucede con la programación, abstraer es esencial para generar código duradero y elegante. Este proceso permite utilizar nombres generales para definir procesos generales.  En otras palabras, si creamos la función enviarCarta() sabemos de manera general que dicha función se encargará de enviar una carta, pero no necesariamente qué pasos se requieren para hacerlo. Y este es otro punto por el cual la abstracción es tan buena, puesto que nos permite encapsular segmentos de procesos.

Encapsulamiento

Nuestra función saludar es un claro ejemplo de encapsulamiento, nos permite tener un bloque cerrado con instrucciones específicas que podemos usar una o mil veces dentro de un programa. Esto hace que el código sea más fácil de leer y que sea más fácil de depurar puesto que si algún error surge, sabemos exactamente cuáles son los límites de nuestra función, y conocemos cada instrucción en un espacio reducido. Esto nos lleva a un principio de la programación bastante común en UNIX

Haz una cosa, hazla muy bien

Una buena función es aquella que solamente hace una cosa, pero la hace muy bien. Pensemos en esto por unos instantes… enviarCarta() probablemente haría muchas cosas, lo que no puede ser bueno si queremos depurar el proceso, mientras saludar() solo hace una. A lo largo del tiempo, si surgen problemas la segunda será más fácil de reparar que la primera. Una opción para evitar este problema sería generar distintos niveles de abstracción para enviarCarta(), esto quiere decir que dentro de la función existirían otras como verificarSobre() y tal vez dentro de esta una como verificarRemitente(). En definitiva esta última función (verificarRemitente()) es mucho más específica que solo enviarCarta() y de esta manera podemos encapsular partes del código para que hagan lo que es necesario y solo una cosa a la vez.

Practicar

Para aprender el arte de la programación es necesario practicar, y puesto que yo ahora he dado un vistazo muy general al tema, es necesario que practiquen con diversos lenguajes, o diversos problemas. Primero intentando generar funciones específicas, luego aumentando la complejidad. Como siempre, si surgen dudas, o sugerencias o comentarios, me ayudan mucho a saber qué aspectos reforzar. Muchas gracias y que este 2018 esté lleno de éxitos y proyectos asombrosos. Saludos

La siguiente generación de ciberdelincuentes

Muy buenas con todos, un título más que sugestivo, y quiero empezar con este pequeño video que vi hace un buen tiempo, una de esas joyas que te hacen desconfiar de la tecnología y te ponen la piel de gallina.

Pese a su inofensivo aspecto, este video es sin duda algo que todos nosotros como personas relacionadas al TI debemos temer y conocer. Pero vamos a revisar unos pocos detalles antes.

Mar.IO

El autor del video nos cuenta la historia del jugador que ha conseguido vencer ese nivel del tan conocido juego Super Mario World. En el proceso nos explica que dicho jugador no es humano, más bien un programa de computadora que ha conseguido aprender por su cuenta el proceso del juego.

Neuroevolución

Este es el proceso que ha seguido Mar.io desde no cononcer absolutamente nada sobre el juego a completar exitosamente el nivel. Este proceso emula a los cerebros humanos y genera una red neural. Dicha red se puede apreciar en la parte superior derecha de Mar.io y es la que se genera tras una larga secuencia de intentos y errores.

Resultado

Tras 24 horas de evolución neuronal, Mar.io ha sido capaz de completar exitosamente el nivel, esto debido a una serie de generaciones que han aprendido que el camino a seguir está a la derecha, que existen cosas que pueden lastimar a Mar.io y que puede evitarlas con comandos como saltar y demás.

Todo está en los números

Si han visto el video completo sabrán que existe un diagrama en el que se muestra una línea azul (4:06). Este cuadro muestra el fitness alcanzado en cada generación. Fitness es un resultado que se obtiene de una función que toma, entre otras cosas, la distancia y tiempo que demora Mar.io en morir. Como pueden ver, existen puntos donde se estanca en su evolución, pero eventualmente encuentra la solución y sigue evolucionando. Tras 32 generaciones de Mar.ios se consigue el objetivo de completar el nivel.

¿Qué tiene que ver con seguridad?

Muchos ya lo estarán preguntando a estas alturas, pero creo que la respuesta es más que evidente, cambiemos un poco de contexto a Mar.io, supongamos que en lugar de jugar su inofensivo juego, le entregamos una computadora con mmm… ¿Kali Linux?

Kali Linux

Todo buen profesional de TI debe conocer este nombre, así como Ubuntu es sinónimo de computadoras de mesa y los nombres Red Hat y SUSE grandes empresas que giran en torno a Linux. Lo primero que suele venir a la mente de muchos cuando hablamos de seguridad informática es Kali Linux.

Simplifica el pentesting

Para los que hemos jugado un poco con la distro, sabemos que Kali simplifica mucho los pasos del pentesting, puesto que nos entrega una suite completa de herramientas que podemos empezar a usar tanto desde su entorno live como instalando en un disco duro. Algunas de estas herramientas se instalan de manera manual me dirán más de uno, pero si lo vemos de forma un poco simplista, con lo que tenemos pre-instalado estamos más que preparados para un pentesting “normal”.

Pentesting

Este es el proceso que realiza un analista de seguridad, algunos de forma defensiva, pero si estás en Kali, probablemente de manera ofensiva. A lo largo de un pentesting el analista hace un reconocimiento del objetivo, encuentra posibles vectores de ataque, realiza ataques dirigidos en entornos lo más “controlados” posibles, y tras su largo esfuerzo genera un reporte detallado de todo el proceso y apunta los posibles fallos que puede tener un sistema/software/equipo/persona.

Mar.io pentester

Supongamos por un segundo que Mar.io decide dedicar su vida al análisis de seguridad, él no duerme, no come, no juega, solo requiere de tiempo para procesar cosas y números para analizar sus resultados. Imaginemos qué sucederá tras unos cuantos meses de estudios de Kali Linux. Con un poco de tiempo aprenderá a usar nmap, tal vez después le interese probar metasploit, y quién sabe, tal vez con el tiempo genere su propio programa para hacer las cosas más eficientes. Esto me recuerda mucho al programa de AI de Facebook que decidió crear su propio idioma de negociación porque el inglés era muy poco “eficiente” ( y no, no era esperanto tampoco por si se lo preguntan :P ).

El futuro de la seguridad

Imaginemos ahora por un instante que después solo existirán Mar.ios trabajando en seguridad, algunos atacando, otros defendiendo, pero eso ya no importaría. ¿Por qué? pues porque si tenemos ambos bandos peleando a ese nivel, sin dormir, sin comer, sin nada… ¿qué podría hacer un humano para estar a su altura? Recordemos a la AI de Google que pudo vencer al mejor jugador de Go en lo que se supone es el juego más complicado del planeta para una máquina :).

Esto nos lleva al mundo empresarial, en el que los pentesters ya no serán necesarios, ni para auditar, ni para defender, y las grandes empresas tendrán servidores dedicados al análisis continuo de sus programas y redes.

¿Debo continuar mi carrera en seguridad?

Pues esto es algo un poco complicado de responder :) si seguimos la misma premisa para cualquier campo, vamos a ver que el 90% de trabajos del futuro serán o podrían ser realizados por pequeños Mar.ios, desde la psicología, pasando por el derecho y medicina, hasta finalmente llegar al software, digo finalmente porque el punto en el que un programa sea capaz de modificarse a sí mismo, ese será el punto de fin de nuestro control sobre los programas, ellos se mejorarán a sí mismos y en ese entonces serán incontrolables. Suena aterrador, lo sé :) pero déjenme soñar un poco :P

Centrando en el tema nuevamente, si vale la pena o no aprender a hacer esto, yo creo que sí y no. Sí vale la pena si realmente vas a meterte de lleno en el tema, y vas a investigar y aprender cosas que vayan más allá del mero hecho de repetir un proceso una y mil veces esperando obtener el mismo resultado.

Esto aplica tanto para los pentesters como para los desarrolladores, y los administradores de sistemas. Aquel que solamente conozca usar una herramienta será fácilmente remplazado por un Mar.io en el futuro. Los que, por otro lado, puedan diseñar herramientas (verdaderos hackers :P) serán quienes entrenen y mejoren a los pequeños Mar.ios, no tendrán el futuro asegurado, pero mientras sean mejores que los programas, podrán llevarse un pan a la mesa :)

Reflexión

Bueno, hasta aquí  será por hoy, gracias por leer y me gustaría pedirles un favor. Sé que muchos leen sin comentar nada al respecto, y es verdad que ya les debo varios temas para escribir o continuar, pero nunca está de más un pequeño feedback para saber si hay dudas o no, si se puede comentar algo más o no, si ustedes tienen un aporte sustancial al texto, o lo que se les ocurra :) Así me motivan a mí a seguir escribiendo y al mismo tiempo me dan nuevas ideas para otros artículos. Saludos.

 

El 80/20 también afecta la programación

Todos hemos oído sobre la regla del 80/20, aquella que dice que el 80% de nuestro éxito (efectos) proviene tan solo del 20% de nuestras acciones (causas). Pues bien, esta universal verdad también afecta al desarrollo de software, y en este artículo vamos a desgranar un poco de los fundamentos de esta afirmación.

BPM

Business Process Managment, por su siglas en inglés, es una disciplina de gestión (entre otras cosas) que permite comprender de manera visual los procesos que deben realizarse en un negocio (o en muchos otros lugares). Entre sus cualidades principales está el hecho de poder analizar procesos complejos y hacerlos “simples”.

Existen muchas herramientas open source que permiten desarrollar diagramas BPM, la que yo he usado para este artículo es BonitaSoft. Si desean aprender un poco más sobre la gestión de procesos existen muchos tutoriales en internet y libros referentes al tema. Ahora volvamos al tema central.

Proyectos de software

Hoy en día existen muchas metodologías para desarrollar proyectos, están las ágiles, las tradicionales, las mixtas, etc, etc. Un punto que todas tienen en común es la preparación. ¿Qué quiero decir con esto? Que el 80% de tu éxito en este proyecto de software se basará en el primer 20% de todo el proceso, la preparación. 

La preparación de un proyecto

Esto es algo lógico que en la realidad se aplica muy poco (como muchas otras cosas lógicas que son ilógicas en la práctica). Cuando hablamos de preparación debemos entender la capacidad de comprender el problema, entender la solución y sobre todo, el proceso que la solución aplica. Una de las cosas que menos se encuentran en proyectos de software poco profesionales es la falta de documentación sobre dicho tema. Esto normalmente aparece en empresas privadas puesto que el deseo de vender supera al proceso de creación.

Como muchos de los que leen estos artículos trabajan o están relacionados con la tecnología, no está de más mencionar que si en algún momento de sus vidas laborales se encuentran con una empresa/proveedor que no cumple con una buena preparación, es casi 80% seguro :P que el proyecto no va a salir bien.

La abstracción es la clave

Esto es algo que he aprendido de mi tiempo usando GNU/Linux, y que demuestra una y otra vez ser clave en el proceso de creación de software. La capacidad de abstraer problemas para convertirlos en cosas más “simples” es vital para poder generar código elegante, y sobre todo duradero. Y tal vez esta es una de las principales diferencias de los grandes proyectos profesionales y los proyectos que crecen sin control alguno. Los primeros piensan, comprenden y estructuran el proceso mientras que los segundos lo mantienen funcionando sin necesidad de entenderlo.

Stager

Este es el nombre del proyecto que desarrolla el instalador de Gentoo, como pueden imaginar, este es un proceso bastante complejo, puesto que soporta una gran cantidad de arquitecturas. Otro factor a tener en cuenta es la cantidad de configuraciones que soporta, a nivel de kernel, init system, etc. Y les cuento todo esto porque además es mi proyecto de tesis, el cual debo acabar antes de terminar de estudiar. Evidentemente no puedo hacer un programa que contemple absolutamente todas las opciones posibles en tan poco tiempo ( hasta julio del próximo año), pero al menos puedo generar uno que permita instalar de manera muy básica un sistema funcional.

Entendiendo el proceso de instalación

Gracias a las herramientas de BPM, se puede generar un diagrama de proceso que nos permite entender los pasos necesarios para la instalación exitosa de Gentoo en un equipo.

proceso de instalación de Gentoo
Diseño propio. Christopher Díaz Riveros

A pesar de contener varios procesos y subprocesos, evidentemente se ha resumido bastante y se puede apreciar que contamos con 18 pasos lineales. Esto es importante porque una aplicación que cuenta con una estructura lineal es sencilla de implementar, y al mismo tiempo se puede generar paralelismo en alguno o varios de los subprocesos en caso de ser necesario.

Otro factor importante es que nos permite abstraer conjuntos de procesos por tipo, por ejemplo, definir un subproceso kernel nos permite saber que existen tareas específicas dentro del mismo que están directamente relacionadas con el proceso de instalación de un kernel de manera exitosa.

Sub-proceso "kernel"
Diseño propio. Christopher Díaz Riveros

De esta manera cada paso “complejo” se convierte en uno “simple” de manera global, sin perder los detalles necesarios. Esto facilita la visibilidad del conjunto sin disminuir el nivel de especificación necesario para cumplir el proceso de manera exitosa. Y tampoco podemos negar que es más sencillo ver la imagen que leer todo el Handbook de golpe :)

Ahorra tiempo

Otra ventaja evidente es que al no contar con un lenguaje de programación directamente conectado, es posible realizar el análisis de la lógica sin necesariamente perder tiempo en la implementación del lenguaje. Esto es una ventaja comparado con la cantidad de tiempo que se puede invertir en implementar una funcionalidad para al final descubrir que va a ser descartada porque existe una solución más eficiente. Como lo que serían las soluciones en pseudo-código (algo que también es ignorado por muchos “desarrolladores” pero que no debería serlo).

Dirigir proyectos se hace fácil

Teniendo en cuenta estos conceptos, la dirección de proyectos (de cualquier índole) se hace más sencilla, porque enfocamos los esfuerzos donde realmente son necesarios, y si esta parte es hecha de manera correcta, el resto cae por su propio peso. Espero que les ayude a la curiosidad y los motive a investigar sobre el BPM, la algoritmia y quién sabe, tal vez los anime a ayudarme con mi tesis :P Muchas gracias por llegar hasta aquí y ya nos estamos viendo pronto. Saludos

 

Una nueva historia para Gentoo

Esta semana, como siempre, las listas de correo de Gentoo están llenas de conversaciones referentes al futuro de la distribución, y una de ellas llamó mucho mi atención, al punto de ser el tema central de este artículo. Pero antes de eso vamos a conocer un poco de historia sobre la distribución:

Su creador

Nos remontamos al milenio pasado, en 1999 Daniel Robbins, lanza la primera versión de Enoch Linux, una distribuación que deseaba romper con los estándares hasta ese momento concebidos por todas las demás distribuciones, crear paquetes en lugar de recibirlos precompilados.  La idea principal era crear un sistema que se acomode al hardware del usuario, y que no tuviera paquetes innecesarios.

FreeBSD

Tras unos cuantos problemas con Enoch, Daniel migró a FreeBSD, un sistema operativo UNIX, y fue donde conoció Ports, la herramienta de control de paquetes de la distribución. Como podrán imaginar, ports se encarga de compilar los programas en lugar de conseguir binarios, para binarios se utiliza la herramienta pkg.

Gentoo 1.0

Ya en el año 2002, tras haber solucionado el elusivo bug, Gentoo ya había adquirido su nombre oficial, así llamado por la especie de pingüino más rápida de todas, y mostraba al mundo su primer versión oficial. Este hito fue el primer paso de una larga serie de cambios y modificaciones que surgieron a lo largo de los años, pero vamos a centrarnos en las más importantes.

Gestión Comunitaria

Esta es una característica peculiar en Gentoo, puesto que al no haber una compañía específica dirigiendo, la comunidad es la que decide en última instancia lo mejor tanto para desarrolladores como para usuarios. Mas cabe mencionar que grandes empresas como Sony y Google se han valido del paradigma de Gentoo para mejorar sus sistemas.

2004

Este fue un año particularlmente complicado para Gentoo, puesto que su fundador debía ceder la dirección a la Fundación Gentoo, debido a temas personales. Debido a la explosión de popularidad que estaba teniendo Gentoo en ese momento, la gente empezaba a usar Gentoo cada vez más y los números se veían prometedores, pero un crecimiento tan acelerado hacía difícil acomodar la estructura a la escala correcta. Teniendo siempre en cuenta que muchos de estos proyectos se realizan en “tiempos libres”, una explosión de fama no podía ser tan buena si no iba a haber suficiente gente para controlar las riendas.

2007

Otro año complicado, puesto que debido a la falta de estructura adecuada, y con una especie de serie de guerrillas internas, Gentoo se hundía en el mundo GNU/Linux y pasaba a ser una distribución “secundaria”. En este ambiente Daniel decide regresar al desarrollo activo como developer, pero tras muchas diferencias personales y ataques por ambos bandos, decide retirarse poco después de su reingreso. Poco después nace Funtoo Linux, una distro basada en Gentoo, pero con algunas modificaciones esenciales que no superaron la poco estable estructura de aquel entonces.

GLEP 39

Gentoo Linux Enhancement Proposal (GLEP) son documentos en los que se proponen cambios, tanto técnicos como estructurales, a la comunidad. Un GLEP pasa por continuos procesos de elaboración, revisión, votación, y puede o no ser implementado, dependiendo de la necesidad de la comunidad y la viabilidad de la propuesta. En particular la GLEP 39 es un proyecto que desea implementar una nueva estructura para Gentoo Linux, en esta se redefine el orden y la manera de proceder de muchos proyectos y developers. Comenzó en 2005, y siguió su proceso de desarrollo hasta ser aprobada en 2008. Definitivamente fue la respuesta de la comunidad, tanto desarrolladores como usuarios, por mejorar los complicados problemas estructurales que durante años la habían estado afectando.

El daño era evidente

Para este momento, Gentoo ya había sufrido mucho debido a las guerrillas internas y la falta de dirección. Muchos usuarios y desarrolladores se habían retirado y se convertía en un pequeño proyecto que esperaba su muerte. Pero lo sorprendente es que a pesar de todo, y contra todo pronóstico, la serie de cambios hicieron que Gentoo cuente con una estructura más estable, y gracias también a la disminución de desarrolladores y usuarios (posibles puntos de vista contradictorios al momento de desarrollar) se pudo empezar a trabajar en nuevos proyectos y mejorar Gentoo en su núcleo.

La prueba definitiva, los años

Ya han pasado 10 años desde ese momento en el tiempo, y mucho ha cambiado, y otras cosas no tanto, la estructura definida en aquel entonces ya se ha establecido, y se ha aprendido mucho en el proceso, nuevos desarrolladores han llegado y otros se han retirado. En resumen, Gentoo no ha muerto (sorprendentemente). Y esta nueva sabiduría se refleja en las formas y modelos de selección, resolución de problemas, presentación de proyectos, en fin, ya se han hecho a la idea. Y esto nos lleva a esta semana nuevamente.

“A plan for Gentoo”

Este ha sido el título del hilo de la conversación que ha causado este artículo, aunque todavía no están los registros completos, esto es un poco de lo que ha sucedido. Daniel desea volver a contribuir al proyecto, generar más conexión entre Gentoo y Funtoo y resolver algunos pendientes en diversos proyectos de la comunidad.

Esto se está conversando en estos instantes en las listas, y la primer impresión es que Daniel desea retornar de manera más que activa y así ayudar a la dirección de Gentoo ( como miembro de concilio). Para esto ya está tomando el quiz de developer sin commit-access, en el cual se realizan una serie de entrevistas vía IRC entre un reclutador de Gentoo (usualmente un developer) y el aspirante. En estas entrevistas se revisan una a una las preguntas del quiz, que giran en torno a la nueva estructura de la comunidad, cómo proceder, cómo proponer y cómo arreglar cosas.

Solo como nota extra, existe un quiz especialmente diseñado para tener commit-access, esto implica poder editar directamente los archivos .ebuild que vienen a ser los .deb o .rpm en debian o redhat respectivamente. Este es mucho más riguroso en temas técnicos y procesos de mantenimiento de programas.

Para poder realizar la entrevista es necesario haber sido mentorado por algún developer de Gentoo, quien explica al aspirante los procesos y lo guía en el proceso de encontrar las respuestas (todo está tan bien documentado que se puede hacer sin un mentor, pero es necesario contar con uno para que él/ella sea quien solicite un entrevistador).

Aprender de la historia

Yo no me considero un amante de la historia, pero he aprendido que es necesario conocerla si no queremos cometer los mismos errores, y al igual que la programación, saber qué sucedió en el pasado nos enseña a entender mejor el futuro. Este será un tema constante en las listas de correo de Gentoo en los siguientes días o tal vez semanas, y esperemos que sea para bien, puesto que los años no pasan en vano y ambos lados ya cuentan con la experiencia de la edad.  En última instancia todos busacmos lo mismo, seguir construyendo un Gentoo cada vez mejor. Saludos y gracias por llegar hasta aquí :)

Un vistazo a la explotación de vulnerabilidades

Como me quedé con ganas de seguir tratando de este tema, permítanme contarles un poco de historia, teoría y práctica sobre las vulnerabilidaes. Todos hemos oído a estas alturas que  los fallos de seguridad pueden costar mucho, todos sabemos que debemos mantener nuestro software actualizado, todos sabemos que muchas actualizaciones se producen por errores de seguridad. Pero hoy les contaré un poco sobre cómo es que se encuentran y se explotan dichos errores :) Pero antes de esto vamos a aclarar unos cuantos detalles para poder tener un mejor panorama.

Antes de empezar

Primero quiero decirles que nos vamos a centrar en la primer vulnerabilidad que aprendí a explotar, los conocidos Buffer Overflows, en esta vulnerabilidad aprovechamos una falta de verificación en la memoria para hacer cosas divertidas :) Pero vamos a aclarar un poco más al respecto.

Esto no va a ser un escenario del mundo real

No puedo darme el lujo de enseñarles a romper cualquier programa que vean :) primero porque es peligroso para sus computadoras, segundo porque eso me tomaría más de mi acostumbrada cuota de palabras.

Nos vamos de viaje a los 80s

Esto que les voy a mostrar lo puedo hacer en mi laptop, pero no quiere decir que se pueda realizar hoy por hoy de manera sencilla :) muchos de estos conceptos ya han sido explotados tantas veces que han surgido nuevos métodos de protección y nuevos métodos para evadirlos :P pero eso nos regresa al mismo lugar, falta espacio para poder contar todo eso :)

Tal vez no funcione en tu procesador

Aunque voy a usar un ejemplo muy simple, quiero que desde el principio quede bastante claro que los pormenores de esto son tantos y tan variados que así como puede salirte igual que a mí, si deseas intentarlo, también puede que no se consiga el efecto deseado :) Pero ya se imaginarán que eso no puedo explicarlo en este espacio, sobre todo porque con esta introducción ya me llevé más de 300 palabras, así que directo a lo nuestro.

Qué es un Buffer Overflow

Para responder esto primero tenemos que comprender la primera mitad de esta combinación.

Buffers

Como todo se trata de memoria en un equipo computacional, es lógico que debe existir algún tipo de contenedor de información. Cuando hablamos de inputs outputs, llegamos directamente al concepto de buffers. Para hacerlo corto, un buffer es un espacio de memoria de tamaño definido en el que vamos a almacenar una cantidad de información, simple :)

Los overflow ocurren, como su nombre lo indica, cuando un buffer se llena con más información de la que puede aguantar. Pero, ¿por qué es esto importante?

Stack

También conocido como pilas, son un tipo de datos abstracto en el que podemos apilar información, su principal característica es que cuentan con un ordenamiento LIFO (Last In First Out). Pensemos por un segundo en una pila de platos, nosotros los ponemos por encima uno a uno, y luego los sacamos uno a uno desde arriba, esto hace que el último plato que hayamos puesto (el que se encuentra hasta arriba) sea el primer plato que vamos a sacar, evidentemente si solo podemos sacar un plato a la vez y decidimos hacerlo en ese orden :P.

Ahora que ya conocen estos dos conceptos, tenemos que ordenarlos. Las pilas son importantes porque cada programa que ejecutamos tiene su propia pila de ejecución. Pero esta pila tiene una característica particularcrece hacia abajo. Lo único que deben saber de esto es que mientras un programa se ejecuta, cuando una función es llamada, la pila pasa de un número X de memoria a un número (X-n). Pero para poder seguir debemos comprender un concepto más.

Punteros

Este es un concepto que vuelve loco a muchos programadores cuando empiezan en el mundo de C, a decir verdad la gran potencia de la programación en C se debe en parte al uso de punteros. Para hacerlo simple, un puntero apunta a una dirección de memoria. Esto suena complejo, pero no lo es tanto, todos tenemos RAM en nuestras máquinas ¿cierto? Pues esta puede definirse como un arreglo consecutivo de bloques, normalmente dichas ubicaciones se expresan en números hexadecimales ( del 0 a 9 y luego de eso de A a F, como por ejemplo 0x0, 0x1, 0x6, 0xA, 0xF, 0x10). Aquí como nota curiosa, 0x10 NO es igual a 10 :P si lo convertimos al orden decimal sería lo mismo que decir 15. Esto es algo que también confunde a más de uno al principio, pero vamos a lo nuestro.

Registros

Los procesadores trabajan con una serie de registros, los cuales funcionan para transmitir ubicacioines desde la memoria física al procesador, para arquitecturas que usan 64-bits, la cantidad de registros es grande y difícil de describir aquí, pero para hacernos a la idea, los registros son como punteros, indican entre otras cosas, un espacio en memoria (ubicación).

Ahora la práctica

Sé que ha sido mucha información para procesar hasta ahora, pero en realidad son temas algo complejos que intento explicar de manera muy simple, vamos a ver un pequeño programa que usa buffers y lo vamos a romper para entender esto de los overflows, evidentemente este no es un programa real, y vamos a “evadir” muchas de las contramedidas que se usan hoy en día, solo para mostrar cómo se hacían las cosas antes :) y porque algunos de estos principios son necesarios para poder aprender cosas más complejas ;)

GDB

Un gran programa que es sin duda uno de los más usados por programadores en  C. Entre sus múltiples virtudes tenemos el hecho de que nos permite ver todo esto que hemos estado conversando hasta ahora, registros, la pila, buffers, etc :) Vamos a ver el programa que vamos a usar para nuestro ejemplo.

retinput.c

Diseño propio. Christopher Díaz Riveros

Este es un programa bastante simple, vamos a usar la librería stdio.h para poder obtener información y mostrarla en un terminal. Podemos ver una función llamada return_input la cual genera un buffer llamado array, que tiene una longitud de 30 bytes (el tipo de dato char es de 1 byte de largo).

La función gets(array); solicita información por consola y la función printf() devuelve el contenido de array y lo muestra en pantalla.

Todo programa escrito en C comienza por la función main(), esta solo se va a encargar de llamar a return_input, ahora vamos a compilar el programa.

Diseño propio. Christopher Díaz Riveros

Vamos a desprender un poco de lo que acabo de hacer. La opción -ggdb le indica a gcc que tiene que compilar el programa con información para que gdb sea capaz de realizar un debug adecuado. -fno-stack-protector es una opción que evidentemente no deberíamos estar usando, pero que vamos a usar porque en caso contrario nos sería posible genererar el buffer overflow en el stack. Al final he probado el resultado. ./a.out solo ejecuta lo que acabo de compilar, me pide información y la devuele. Funcionando :)

Advertencias

Otra nota aquí. ¿Pueden ver las advertencias? claramente es algo a tener en cuenta cuando trabajamos con código o compilamos, esta es un poco obvia y son pocos los programas que hoy en día tienen la función gets() en el código. Una ventaja de Gentoo es que al compilar cada programa, puedo ver lo que puede estar mal, un programa “ideal” no debería tenerlas, pero les sorprendería cuántos programas grandes tienen estas advertencias porque simplemente son MUY grandes y es difícil mantener el rastro de las funciones peligrosas cuando son muchas advertencias al mismo tiempo. Ahora si sigamos

Depurando el programa

Diseño propio. Christopher Díaz Riveros

Ahora, esta parte puede ser un poco confusa, pero como ya he escrito bastante, no puedo darme el lujo de explicarlo todo, así que perdón si ven que voy muy rápido :)

Desarmando el código

Vamos a empezar viendo nuestro programa compilado en lenguaje máquina.

Diseño propio. Christopher Díaz Riveros

Este es el código de nuestra función main en Assembly, esto es lo que entiende nuestro procesador, la línea de la izquierda es la dirección física en memoria, el <+n> se conoce como offset, básicamente la distancia desde el principio de la función (main) hasta esa instrucción (conocido como opcode). Luego vemos el tipo de instrucción (push/mov/callq…) y uno o más registros. Resumido podemos decir que es la indicación seguida de la fuente/origen y el destino. <return_input> hace referencia a nuestra segunda función, vamos a darle un vistazo.

Return_input

Diseño propio. Christopher Díaz Riveros

Esta es un poco más compleja, pero solo quiero que revisen un par de cosas, existe un label llamado <gets@plt> y un último opcode llamado retq que indica el final de la función. Vamos a poner un par de breakpoints, uno en la función gets y otro en el retq.

Diseño propio. Christopher Díaz Riveros

Run

Ahora vamos a correr el programa para ver cómo empieza a comenzar la acción.

Diseño propio. Christopher Díaz Riveros

Podemos ver que aparece una pequeña flecha que indica el opcode donde nos encontramos, quiero que tengan en cuenta la dirección 0x000055555555469b, esta es la dirección que se encuentra tras la llamada a return_input en la función main , esto es importante puesto que ahí es donde debería regresar el programa cuando acabe de recibir el input, vamos a entrar en la función. Ahora vamos a revisar la memoria antes de entrar a la función gets.

Diseño propio. Christopher Díaz Riveros

Les he vuelto a poner la función main arriba, y he resaltado el código al que me refería, como pueden ver, debido al endianess se ha separado en dos segmentos, quiero que tengan en cuenta la dirección 0x7fffffffdbf0 (la primera de la izquierda tras el commando x/20x $rsp) puesto que esta es la ubicación que tenemos que usar para revisar el resultado de gets, sigamos:

Rompiendo el programa

Diseño propio. Christopher Díaz Riveros

He resaltado esos 0x44444444porque son la representación de nuestras Ds :) ahora hemos empezado a agregar input al programa, y como pueden apreciar, estamos a solo dos líneas de nuestra dirección deseada, vamos a llenarlo hasta estar justo antes de las direcciones que resaltamos en el paso anterior.

Cambiando la ruta de retorno

Ahora que hemos logrado entrar en esta sección del código donde indica el retorno de la función, vamos a ver qué sucede si cambiamos la direacción :) en lugar de ir a la ubicación del opcode que sigue al que teníamos hace un momento, ¿qué les parece si regreamos a return_input? Pero para esto, es necesario escribir en binario la direacción que deseamos, vamos a hacerlo con la función printf de bash :)

Diseño propio. Christopher Díaz Riveros

Ahora hemos recibido dos veces la información :D seguramente el programa no estaba hecho para eso, pero hemos conseguido romper el código y hacer que repita algo que no se suponía que haga.

Reflexiones

Este simple cambio puede considerarse un exploit muy básico :) ha logrado romper el programa y hacer algo que nosotros deseamos que haga.

Este es solo el primer paso en una casi infinita lista de cosas por ver y agregar, existen formas de agregar más cosas que simplemente repetir una orden, pero esta vez he escrito mucho y todo lo relacionado a shellcoding es un tema para escribir más que artículos, libros completos diría yo. Disculpen si no he podido ahondar un poco más en temas que me hubieran gustado, pero seguro ya habrá oportunidad :) Saludos y gracias por llegar hasta aquí.