Unha ollada á explotación de vulnerabilidades

Como estaba desexando seguir debatendo sobre este tema, déixeme contarvos un pouco de historia, teoría e práctica sobre vulnerabilidades. Todos xa escoitamos que os fallos de seguridade poden custar moito, todos sabemos que necesitamos manter o noso software actualizado, todos sabemos que moitas actualizacións son causadas por erros de seguridade. Pero hoxe vouvos contar un pouco sobre como se atopan e explotan estes erros 🙂 Pero antes disto aclararemos algúns detalles para ter unha mellor visión xeral.

Antes de comezar

Primeiro quero dicirlle que nos imos centrar na primeira vulnerabilidade que aprendín a explotar, a coñecida Desbordamentos de búfer, nesta vulnerabilidade aproveitamos a falta de verificación da memoria para facer cousas divertidas 🙂 Pero aclaremos un pouco máis sobre iso.

Este non vai ser un escenario do mundo real

Non me podo permitir o luxo de ensinarlles a romper calquera programa que vexan: primeiro porque é perigoso para os seus ordenadores, segundo porque iso tería máis que a miña cota de palabras habitual.

Imos de viaxe aos anos 80

O que vou amosar podo facer no meu portátil, pero iso non significa que se poida facer hoxe dun xeito sinxelo 🙂 moitos destes conceptos xa foron explotados tantas veces que novos métodos de protección e novos métodos para eludir xurdiron 😛 pero iso devólvenos ao mesmo lugar, non hai espazo para poder contar todo iso 🙂

É posible que non funcione no seu procesador

Aínda que vou empregar un exemplo moi sinxelo, quero que quede bastante claro dende o principio que os detalles disto son tantos e tan variados que ao igual que pode saír igual ca min, se queres probalo , é posible que o efecto desexado tamén non se acade 🙂 Pero podes imaxinar que non podo explicar que neste espazo, especialmente porque con esta introdución xa tomei máis de 300 palabras, polo que chegamos directamente ao noso punto.

Que é un Desbordamento do búfer

Para responder a isto primeiro temos que entender a primeira metade desta combinación.

Buffers

Dado que todo se trata de memoria nun ordenador, é lóxico que haxa algún tipo de contedor de información. Cando falamos de inputs outputs, chegamos directamente ao concepto de buffers. Para facelo curto, a amortecer É un espazo de memoria de tamaño definido no que imos almacenar unha cantidade de información, sinxela 🙂

Os desbordamentos prodúcense, como o nome indica, cando un búfer enche con máis información da que pode almacenar. Pero por que é importante isto?

Pila

Tamén coñecidos como pilas, son un tipo de datos abstracto no que podemos pila información, a súa principal característica é que teñen unha ordenación LIFO (Última entrada en primeiro lugar). Pensemos por un segundo nunha pila de placas, poñémolas encima unha a unha e logo sacámolas unha por unha da parte superior, isto fai o último prato que colocamos (o que está na parte superior ) é a primeira placa que imos sacar, obviamente se só podemos sacar unha placa á vez e decidimos facela por esa orde: P.

Agora que xa coñeces estes dous conceptos, temos que poñelos en orde. As pilas son importantes porque cada programa que executamos ten o seu pila de execución. Pero esta pila ten un característica particularmedra. O único que debes saber sobre isto é que, mentres se está executando un programa, cando se chama unha función, a pila pasa dun número X na memoria a un número (Xn). Pero para continuar debemos entender un concepto máis.

Punteiros

Este é un concepto que volve tolo a moitos programadores cando comezan no mundo de C, de feito o gran poder da programación en C débese en parte ao uso de punteiros. Para facelo sinxelo, un punteiro sinala un enderezo de memoria. Parece complexo, pero non é tan complexo, todos temos memoria RAM nas nosas máquinas non? Ben, isto pódese definir como disposición consecutiva de bloques, estas localizacións normalmente exprésanse en números hexadecimais (de 0 a 9 e logo de A a F, como 0x0, 0x1, 0x6, 0xA, 0xF, 0x10). Aquí como nota curiosa, 0x10 NON é igual a 10 😛 se o convertemos en orde decimal sería o mesmo que dicir 15. Isto é algo que tamén confunde máis de un ao principio, pero imos a iso.

Rexistros

Os procesadores traballan cunha serie de rexistros, que funcionan para transmitir localizacións desde a memoria física ao procesador, para arquitecturas que usan 64 bits, o número de rexistros é grande e difícil de describir aquí, pero para facerse a idea, os rexistros son como punteiros, indican entre outras cousas , un espazo de memoria (localización).

Agora practica

Sei que ata agora foi moita información procesar, pero en realidade son cuestións un tanto complexas que tento explicar dun xeito moi sinxelo, imos ver un pequeno programa que usa buffers e imos a rompeo para entender isto sobre os desbordamentos, obviamente este non é un programa real e imos "eludir" moitas das contramedidas que se usan hoxe, só para amosar como se facían as cousas antes 🙂 e porque algunhas destas os principios son necesarios para poder aprender cousas máis complexas 😉

GDB

Un gran programa que sen dúbida é un dos máis empregados polos programadores de C. Entre as súas moitas virtudes temos o feito de que nos permite ver todo isto do que estivemos falando ata agora, os rexistros, a pila, os buffers, etc. 🙂 Vexamos o programa que imos empregar para o noso exemplo.

reinput.c

Propio. Christopher Díaz Riveros

Este é un programa bastante sinxelo, imos usar a biblioteca stdio.h para poder obter información e amosala nun terminal. Podemos ver unha función chamada return_input que xera un amortecer chamado orde, que ten unha lonxitude de 30 bytes (o tipo de datos char ten unha lonxitude de 1 byte).

A función gets(array); solicite información por consola e función printf() devolve o contido da matriz e amósao na pantalla.

Cada programa escrito en C comeza coa función main(), isto só será o encargado de chamar a return_input, agora imos recompilar o programa.

Propio. Christopher Díaz Riveros

Tomemos un pouco do que acabo de facer. A opción -ggdb dille a gcc que compile o programa con información para que gdb poida depurar correctamente. -fno-stack-protector É unha opción que, obviamente, non deberiamos usar, pero que imos usar porque doutro xeito sería posible xerar o desbordamento do búfer na pila. Ao final probei o resultado. ./a.out só executa o que acabo de compilar, pídeme información e devólvaa. Correndo 🙂

Advertencias

Outra nota aquí. Podes ver os avisos? claramente é algo a ter en conta cando traballamos con código ou compilamos, isto é algo obvio e hai poucos programas que hoxe teñan a función gets() No código. Unha das vantaxes de Gentoo é que, ao compilar cada programa, podo ver o que podería estar mal, un programa "ideal" non debería telas, pero sorprenderíache cantos programas grandes teñan estes avisos porque son moi grandes e son moi grandes. é difícil seguirlles funcións perigosas cando hai moitos avisos ao mesmo tempo. Agora si continuamos

Depuración do programa

Propio. Christopher Díaz Riveros

Agora esta parte pode ser un pouco confusa, pero como xa escribín bastante, non me podo permitir o luxo de explicalo todo, así que disculpa se ves que vou demasiado rápido 🙂

Desarmar o código

Comecemos mirando o noso programa de linguaxe máquina compilado.

Propio. Christopher Díaz Riveros

Este é o código da nosa función principal en montaxe, isto é o que o noso procesador entende, a liña á esquerda é a dirección física na memoria, a <+ n> é coñecido como compensar, basicamente a distancia desde o comezo da función (principal) ata esa afirmación (coñecida como código de operación). Despois vemos o tipo de instrución (push / mov / callq ...) e un ou máis rexistros. Resumido podemos dicir que é a indicación seguida da fonte / orixe e o destino. <return_input> refírese á nosa segunda función, botemos unha ollada.

Retorno_entrada

Propio. Christopher Díaz Riveros

Isto é un pouco máis complexo, pero só quero que comprobes un par de cousas, hai unha etiqueta chamada <gets@plt> e un último opcode chamado retq indicando o final da función. Imos poñer un par de puntos de interrupción, un na función gets e outro no retq.

Propio. Christopher Díaz Riveros

Correr

Agora imos executar o programa para ver como comeza a acción.

Propio. Christopher Díaz Riveros

Podemos ver que aparece unha pequena frecha que indica o código operativo onde estamos, quero que teñan en conta a dirección 0x000055555555469b, este é o enderezo despois da chamada a return_input en función main , isto é importante xa que é aquí onde o programa debería volver cando remate de recibir o entrada, imos entrar na función. Agora imos comprobar a memoria antes de entrar na función gets.

Propio. Christopher Díaz Riveros

Puxen de novo a función principal e resaltei o código ao que me refería, como podes ver, debido a endia separouse en dous segmentos, quero que teñan en conta a dirección 0x7fffffffdbf0 (o primeiro pola esquerda despois do comando x/20x $rsp) dado que esta é a situación que temos que empregar para comprobar os resultados de get, continuemos:

Romper o programa

Propio. Christopher Díaz Riveros

Destáqueos 0x44444444porque son a representación dos nosos D 🙂 agora comezamos a engadir entrada ao programa e, como podes ver, estamos a só dúas liñas do noso enderezo desexado, imos enchelo ata que esteamos xusto antes das direccións que destacamos no paso anterior.

Cambiando o camiño de volta

Agora que conseguimos introducir esta sección do código onde indica a devolución da función, vexamos que pasa se cambiamos a dirección 🙂 en lugar de ir á localización do código opcional que segue a que tiñamos hai un momento, que pensas se volvemos return_input? Pero para iso, é necesario escribir o enderezo que queremos en binario, imos facelo coa función printf de bash 🙂

Propio. Christopher Díaz Riveros

Agora recibimos a información dúas veces, seguramente o programa non foi creado para iso, pero conseguimos romper o código e facer que repita algo que non se supuña que debía facer.

Reflexións

Este simple cambio pódese considerar un explotar moi básico managed conseguiu romper o programa e facer algo que queremos que faga.

Este é só o primeiro paso dunha lista case infinita de cousas para ver e engadir, hai xeitos de engadir máis cousas que simplemente repetir un pedido, pero esta vez escribín moito e todo o relacionado con codificación de shell é un tema para escribir máis que artigos, libros completos diría. Desculpe se non puiden afondar un pouco máis en temas que me gustaría, pero seguramente haberá unha oportunidade 🙂 Un saúdo e grazas por chegar aquí.


O contido do artigo adhírese aos nosos principios de ética editorial. Para informar dun erro faga clic en aquí.

14 comentarios, deixa os teus

Deixa o teu comentario

Enderezo de correo electrónico non será publicado. Os campos obrigatorios están marcados con *

*

*

  1. Responsable dos datos: Miguel Ángel Gatón
  2. Finalidade dos datos: controlar SPAM, xestión de comentarios.
  3. Lexitimación: o seu consentimento
  4. Comunicación dos datos: os datos non serán comunicados a terceiros salvo obrigación legal.
  5. Almacenamento de datos: base de datos aloxada por Occentus Networks (UE)
  6. Dereitos: en calquera momento pode limitar, recuperar e eliminar a súa información.

  1.   2p2 dixo

    Sexa máis directo. Escribe menos e céntrate no que importa

    1.    ChrisADR dixo

      Ola, grazas polo comentario.

      A dicir verdade, cortei boa parte das ideas, pero aínda así pareceume que deixei o mínimo para que alguén que non teña coñecemento de programación poida ter unha idea.

      lembranzas

      1.    Anónimo dixo

        O problema é que aqueles que non teñen coñecemento de programación non se enterarán de nada porque é moi complexo para comezar, pero os que saben programar agradecen ser máis directos.

        Supoño que non podes chegar a todos, tes que escoller e neste caso pecaches de querer cubrir moito.

        Por certo, dígoche como crítica construtiva, encántanme estes temas e gustaríame que continuases escribindo artigos, parabéns!

    2.    Anónimo dixo

      Eu creo que o mesmo.

      1.    ChrisADR dixo

        Moitas grazas aos dous !! certamente é difícil entender como chegar ao público obxectivo cando o certo é que o número de persoas cun nivel avanzado de programación que le estes artigos é escaso (polo menos iso pódese inferir en función dos comentarios)

        Sen dúbida pechei por querer simplificar algo que require unha ampla base de coñecemento para ser entendida. Espero que entendades que, desde que estou a comezar no blogueo, aínda non descubrín o punto exacto no que os meus lectores saben e entenden o que digo. Iso faría moito máis doado dicir a verdade 🙂

        Intentarei ser máis curto cando se merece sen despersonalizar o formato, xa que separar a forma de escribir do contido é un pouco máis complicado do que se podería imaxinar, polo menos téñoos bastante ligados, pero supoño que finalmente poderei para engadir liñas no canto de cortar contido.

        lembranzas

  2.   Mario dixo

    Onde podería saber máis sobre o tema? Algún libro recomendado?

    1.    ChrisADR dixo

      O exemplo foi tomado do manual de The Shellcoder's por Chris Anley, John Heasman, Felix Linder e Gerardo Richarte, pero para facer a tradución de 64 bits tiven que aprender sobre a miña arquitectura, o manual de desenvolvedores de intel, os volumes 2 e 3 son un fonte bastante fiable para iso. Tamén é bo ler a documentación do GDB, que vén co comando 'info gdb', Para aprender Assembly e C hai moitos libros moi bos, agás que os libros de Assembly son un pouco antigos polo que hai un oco para cubrir con outro documentación tipo.

      O código shell xa non é tan eficaz nestes días por varias razóns, pero aínda é interesante aprender novas técnicas.

      Espero que axude un pouco 🙂 Saúdos

  3.   Franz dixo

    Bo artigo, o vello blog desdelinux renaceu =)
    Cando di que o shell remoto non é tan eficaz, refírese a contramedidas deseñadas para mitigar ataques, chámanlle seguridade ofensiva.
    Un saúdo e seguide así

    1.    ChrisADR dixo

      Moitas grazas Franz, palabras moi amables, de feito quería dicir que Shellcoding hoxe é moito máis complexo do que vemos aquí. Temos o ASLR (xerador de localización aleatoria de memoria) o protector de pila, as distintas medidas e contramedidas que limitan o número de códigos de opcións que se poden inxectar nun programa, e é só o comezo.

      Saúdos,

  4.   Software Libre dixo

    Ola, farás outra parte ampliando o tema? É interesante

    1.    ChrisADR dixo

      Ola, o tema é certamente bastante interesante, pero o nivel de complexidade que tomaríamos chegaría a ser moi elevado, probablemente incluíndo un gran número de publicacións para explicar os diversos requisitos previos para comprender o outro. Probablemente escribirei sobre iso, pero non serán as seguintes publicacións, quero escribir algúns temas antes de continuar con este.

      Un saúdo e grazas por compartir

  5.   cacto dixo

    Moi bo che! Estás a contribuír con estupendas publicacións. Unha das preguntas: estou empezando esta cuestión de seguridade das TI lendo un libro chamado "Garantir a seguridade mediante probas de bolígrafos". Recoméndase este libro? Como me suxires que comece a indagar sobre estes problemas?

    1.    ChrisADR dixo

      Ola, cactus, é todo un universo sobre vulnerabilidades e outros, para dicir a verdade, depende moito do que chame a atención e das necesidades que teñas, un xestor de TI non precisa saber o mesmo que un probador de bolígrafos, Ou un investigador da vulnerabilidade ou un analista forense, un equipo de recuperación de desastres ten un conxunto de habilidades moi diferente. Obviamente, cada un deles require un nivel de coñecemento técnico diferente, recoméndolle que comece a descubrir exactamente o que lle gusta e que comece a devorar libros, artigos e outros e, o máis importante, que practique todo o que le, aínda que estea desactualizado, iso marcará a diferenza ao final.
      Saúdos,

  6.   Eizen dixo

    Olá
    Moitas grazas por explicar este tema, así como por comentar que para obter información adicional temos "The Shellcoder's Handbook". Xa teño unha lectura pendente 😉