RED TEAM
  • Presentación
  • Apuntes Linux
  • Apuntes Blue Team
  • Apuntes Python
  • Ricardev github
  • RECON
    • OSINT
    • DESCUBRIENDO LA RED
      • NMAP NSE
    • SNIFFING
      • TCPDUMP
  • TECHNIQUES
    • FUERZA BRUTA
      • HYDRA
      • MEDUSA
      • JOHN THE RIPPER
      • NCRACK
      • RAINBOW TABLES
      • CHEATSHEET
    • CLAVES RSA DÉBILES
  • WEB HACKING
    • FUZZING
      • GOBUSTER
      • WFUZZ
      • OTRAS HERRAMIENTAS DE RECONOCIMIENTO WEB
    • OWASP TOP 10
      • A1-2017. SQL INJECTION
        • LOGIN FORM BYPASS
        • EXTRACCIÓN DE INFORMACIÓN.
        • SQLI MODIFIED HEADERS
        • BOOLEAN BLIND SQLI
        • TIME-BASED BLIND SQLI
        • AUTOMATIC INJECTION
      • A2-2017. ATAQUES A SISTEMAS DE AUTENTICACIÓN
      • A3-2017 - EXPOSICIÓN DE DATOS SENSIBLES
      • A4-2017. XXE
      • A5-2017. CONTROL DE ACCESO VULNERABLE
      • A6-2017. SEGURIDAD MAL CONFIGURADA
      • A7-2017. XSS
      • A8-2017. DESERIALIZACIÓN INSEGURA
      • A9-2017. USO DE COMPONENTES CON VULNERABILIDADES CONOCIDAS
      • A10-2017. REGISTRO Y MONITOREO INSUFICIENTE
    • SERVICIOS WEB
      • APACHE TOMCAT (RCE)
      • PRTG NETWORK MONITOR (RCE)
  • SERVICES HACKING (BOTH)
    • 20,21 - FTP
      • FTP BOUNCE ATTACK - ESCANEO
      • FTP BOUNCE ATTACK- DESCARGA DE OTRA FTP
    • 23 - TELNET
    • 25, 465 587 - SMTP
    • 111, 2049 - RPCBIND Y NFS
    • 161,162,10161,10162/udp - SNMP
      • SNMP (RCE Linux)
    • 445 - SMB
      • ETERNALBLUE
    • 3306 - MYSQL
  • SERVICES HACKING (LINUX)
    • 3632 - DISTCCD
  • SERVICES HACKING (WINDOWS)
    • 135, 539 - MSRPC
    • 389, 636 - LDAP / LDAPS
    • 1443 - MSSQL
  • ACTIVE DIRECTORY HACKING
    • CREANDO UN LABORATORIO DE AD
      • 1. Instalación de Windows Server 2016
      • 2. ROL DE ACTIVE DIRECTORY
      • 3. MALAS PRÁCTICAS NECESARIAS
    • CONCEPTOS
      • SPN Y KERBEROS
    • ENUMERACIÓN
      • BLOODHOUND
    • ATAQUES
      • SMB RELAY
      • NTLM RELAY
      • KERBEROASTING
      • AS_REP ROASTING
  • PRIVESC
    • WINDOWS
    • LINUX
      • LXD/LXC GROUP
  • EXFILTRACIÓN
    • EXFILTRANDO INFORMACIÓN
  • SHELL AND POWERSHELL TRICKS
    • Transfiriendo datos (traducir)
    • MEJORANDO SHELL A TTY INTERACTIVA (Traducir)
  • PWN LINUX
    • CREANDO UN LABORATORIO SIN MITIGACIONES
    • TEORÍA
      • ESTRUCTURA DE UN BINARIO DE LINUX
        • HERRAMIENTAS
      • ENSAMBLADOR
      • CONVENCIÓN DE LLAMADAS
      • MITIGACIONES
      • SYSCALL Y SHELLCODE
      • FORMAT STRING
      • RETURN-ORIENTED PROGRAMMING
        • GADGETS
    • ESTRATEGIAS DE EXPLOIT
      • STACK EXPLOITS
        • ATAQUE “SMASH THE STACK” SENCILLO
        • ATAQUE RET2WIN
        • ATAQUE RET2SHELLCODE
        • ATAQUE FORMAT STRING RET2SHELLCODE 2 BYTES
        • ATAQUE FORMAT STRING RET2SHELLCODE 4 BYTES
        • CANARY BYPASS
        • ATAQUE RET2LIBC
    • PRÁCTICA
      • PHOENIX
        • SETUP
        • STACK-ZERO amd64
        • STACK-ONE amd64
        • STACK-TWO amd64
        • STACK-THREE amd64
        • STACK-FOUR amd64
        • STACK-FIVE amd64
        • STACK-SIX amd64
        • FORMAT-ZERO amd64
Con tecnología de GitBook
En esta página
  • UTILIZANDO EL DEPURADOR
  • PREPARANDO EL EXPLOIT
  • EXPLOTANDO LA VULNERABILIDAD
  • CÓDIGO FUENTE

¿Te fue útil?

  1. PWN LINUX
  2. PRÁCTICA
  3. PHOENIX

STACK-FOUR amd64

AnteriorSTACK-THREE amd64SiguienteSTACK-FIVE amd64

Última actualización hace 3 años

¿Te fue útil?

Este nivel explica lo que puede pasar si tenemos la capacidad de sobrescribir el puntero de instrucción rip. Esto es lo que se considera un Buffer Overflow estandar.

El puntero de instrucción no siempre se encuentra justo después de las variables en el STACK. Algunas cosas como el padding del propio compilador pueden afectar a su posición.

Algunas arquitecturas ni siquiera almacenan el puntero de instrucción en el stack.

UTILIZANDO EL DEPURADOR

Como en todos los anteriores, lo primero es utilizar rabin2 para obtener más información del binario:

Después lo podemos abrir en nuestro depurador, analizar todo y comprobar las funciones para ver qué estamos buscando:

Vemos que el binario utiliza las funciones printf, gets, puts y exit. Todas ellas las conocemos de los ejemplos anteriores así que ya sabemos que gets es explotable.

Además de eso, vemos que hay llamadas a tres funciones interesantes:

  • sym.complete_level

// COMPLETE LEVEL

// PROLOGO
push rbp
mov rbp, rsp

// EJECUCION PRINCIPAL (Puts normal)
mov edi, str.Congratulations__you_ve_finished_phoenix_stack_four...
call sym.imp.puts 

// EXIT
mov edi, 0
call sym.imp.exit

Como vemos, la función complete_level no tiene más que una llamada a puts() que no encierra ningún problema a estas alturas.

  • sym.start_level

// START LEVEL

// PROLOGO
push rbp
mov rbp, rsp

// DECLARACIÓN DE VARIABLES
sub rsp, 0x50                            // resta 0x50 (80d) al rsp 

// EJECUCION PRINCIPAL
lea rax, qword [rbp - local_50h]         // carga en rax un puntero a [rbp - 0x50]
mov rdi, rax                             // mueve el puntero a rdi
call sym.imp.gets                        // llamada a gets en [rbp - 0x50]
mov rax, qword [rbp + arg_8h]            // mueve el contenido de [rbp + 0x8] a rax
mov qword [rbp - local_8h], rax          // mueve lo anterior a [rbp - 0x8]
mov rax, qword [rbp - local_8h]          // mueve a rax el valor de [rbp - 0x8]
mov rsi, rax                             // mueve dicho valor al rsi
mov edi, str.and_will_be_returning_to... // mueve la string al edi
mov eax, 0                               // mueve a eax el valor 0x0
call sym.imp.printf                      // llamada a printf

// EPILOGO
nop
leave
ret

Esta función ya tiene un funcionamiento más complejo. Cuando analicemos main, veremos start_level en profundidad.

Como vemos, en radare2 los offset se pueden nombrar de dos maneras:

  • qword [rbp - local_50h] -> equivale a [rbp - 0x50]

  • qword [rbp - arg_8h] -> equivale a [rbp + 0x8]

Esto se debe a que las variables locales se almacenan posteriormente al rbp y por tanto en direcciones de memoria más bajas mientras que los argumentos se almacenan previamente al rbp y por tanto en direcciones de memoria más altas.

No todo lo que se almacena en direcciones de memoria más altas son argumentos, como ahora veremos.

  • sym.main

// MAIN

//PROLOGO
push rbp
mov rbp, rsp

// DECLARACION DE VARIABLES
sub rsp, 0x10
mov dword [rbp - local_4h], edi
mov qword [rbp - local_10h], rsi

// EJECUCION PRINCIPAL
mov edi, str.Welcome_to_phoenix_stack_four
call sym.imp.puts
mov eax, 0
call sym.start_level

// EPILOGO
|           0x0040068d      b800000000     mov eax, 0
|           0x00400692      c9             leave
\           0x00400693      c3             ret

La función main no encierra ninguna complejidad. Una llamada a puts para el BANNER y después llama directamente a la función start_level. Por este motivo, se debe analizar en profundidad la función start_level.

PREPARANDO EL EXPLOIT

Si nos fijamos en la ejecución principal de la función, podemos entender un poco mejor lo que hace:

// EJECUCION PRINCIPAL
lea rax, qword [rbp - local_50h]         // carga en rax un puntero a [rbp - 0x50]
mov rdi, rax                             // mueve el puntero a rdi
call sym.imp.gets                        // llamada a gets en [rbp - 0x50]
mov rax, qword [rbp + arg_8h]            // mueve el contenido de [rbp + 0x8] a rax
mov qword [rbp - local_8h], rax          // mueve lo anterior a [rbp - 0x8]
mov rax, qword [rbp - local_8h]          // mueve a rax el valor de [rbp - 0x8]
mov rsi, rax                             // mueve dicho valor al rsi
mov edi, str.and_will_be_returning_to... // mueve la string al edi
mov eax, 0                               // mueve a eax el valor 0x0
call sym.imp.printf                      // llamada a printf

Como vemos, la función hace varias cosas:

  • Llamada a gets para llenar el buffer en [rbp - 0x50].

  • Justo después coge el valor de [rbp + 0x8] y lo mete en [rbp - 0x8]

  • Por ultimo utiliza el valor de [rbp - 0x8] en un printf junto con una string que dice algo referente a la dirección de retorno.

si analizamos el stack justo después de la llamada a gets, podremos averiguar que es lo que se almacena en [rbp - 0x8]:

  • Vamos a preparar una prueba de concepto:

  • La introducimos en el debugger con rarun2:

  • Comprobamos el stack justo después de gets():

Al intentar utilizar "B" * 8, la dirección de retorno se veía afectada, por lo que para la explicación decidí utilizar "B" * 4 y dejar otros 4 espacios libres antes de la dirección de retorno.

Como podemos ver, al llenar el stack con datos, hemos alcanzado la dirección con offset [rbp + 0x4]. Si analizamos la información de [rbp + 0x8] podemos ver que tiene almacenada una dirección de memoria (Little Endian) que es 0x0040068d Si vemos el desensamblado de la función main, vemos que esta dirección de memoria es la dirección siguiente a la de call start_level. Esto significa que hemos encontrado la dirección de retorno.

Ahora, lo único que nos queda es sobrescribir la dirección de retorno con una dirección que nos lleve a la función completelevel. En este caso, la primera dirección de complete_level es: 0x0040061d.

EXPLOTANDO LA VULNERABILIDAD

A continuación podemos retocar nuestro exploit para que cumpla con nuestros requisitos:

Hemos conseguido sobrescribir la dirección de retorno y modificar el flujo del binario.

CÓDIGO FUENTE

Después de comprobar que el exploit funciona podemos analizar el código fuente para comprobar qué hacía el binario exactamente.

/*
 * phoenix/stack-four, by https://exploit.education
 *
 * The aim is to execute the function complete_level by modifying the
 * saved return address, and pointing it to the complete_level() function.
 *
 * Why were the apple and orange all alone? Because the bananna split.
 */

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BANNER \
  "Welcome to " LEVELNAME ", brought to you by https://exploit.education"

char *gets(char *);

void complete_level() {
  printf("Congratulations, you've finished " LEVELNAME " :-) Well done!\n");
  exit(0);
}

void start_level() {
  char buffer[64];
  void *ret;

  gets(buffer);

  ret = __builtin_return_address(0);
  printf("and will be returning to %p\n", ret);
}

int main(int argc, char **argv) {
  printf("%s\n", BANNER);
  start_level();
}

Después de vencer el reto es importante tener en cuenta que cuando se hace referencia a [rbp + arg_8h] en la función start_level, no se está haciendo referencia a un argumento sino a una información que se encuentra en direcciones mayores que el rbp. En este caso, lo que se referencia es la dirección de retorno.

Link register
stack-four. rabin2
stack-four. afl
stack-four. complete_level
stack-four. start_level
stack-four. main