¿Hay alguna razón por la que no pueda simplemente estudiar el código? 30 líneas de ASM deben ser fáciles de entender. Solo agregue comentarios que expliquen lo que hace cada línea, y lo entenderá de inmediato. Esto será aún más fácil dado lo rápido que puede buscar exactamente lo que hace una instrucción individual.
Aunque estoy de acuerdo en que es poco probable que sea capaz de hacer algo peligroso por sí solo con un solo syscall, nunca debe subestimar el ingenio que pueden tener algunos desarrolladores. Es perfectamente posible diseñar shellcode para engañar a un desensamblador para que le muestre un código que no está realmente allí, como lo señaló DEF CON talk hace algunos años. 30 líneas de ensamblaje x86 son suficientes para una carga útil de la etapa 1.
Es probable que sea completamente inofensivo. Sin embargo, no apostaría mi vida en ello.
Puedes usar seccomp en Linux para aislarlo. Seccomp es una característica de Linux que filtra las llamadas al sistema. Hay dos modos para seccomp, modo 1 y modo 2. El modo 1 es el modo más estricto. Cuando un programa habilita este modo, está limitado a usar solo cuatro syscalls codificados : read()
, write()
, exit()
y rt_sigreturn()
. El modo 2 es más complejo y utiliza un filtro eBPF generado por el espacio de usuario para especificar una lista blanca personalizada de syscalls y argumentos de syscall. Para un código de shell muy simple, solo necesitarías el modo 1.
El modo de activación 1 seccomp es simple. Tomado de otra respuesta que escribí en otro sitio de StackExchange, un programa de ejemplo que ejecuta de forma segura una función que devuelve 42 en el código de bytes:
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <linux/seccomp.h>
/* "mov al,42; ret" aka "return 42" */
static const unsigned char code[] = "\xb0\x2a\xc3";
void main(void)
{
int fd[2], ret;
/* spawn child process, connected by a pipe */
pipe(fd);
if (fork() == 0) {
close(fd[0]);
/* enter mode 1 seccomp and execute untrusted bytecode */
prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
ret = (*(uint8_t(*)())code)();
/* send result over pipe, and exit */
write(fd[1], &ret, sizeof(ret));
syscall(SYS_exit, 0);
} else {
close(fd[1]);
/* read the result from the pipe, and print it */
read(fd[0], &ret, sizeof(ret));
printf("untrusted bytecode returned %d\n", ret);
}
}
Desearía reemplazar code[]
con el código de shell que desea probar.
Usted mencionó que hay un syscall para time()
. En la mayoría de los sistemas Linux, esto no es un verdadero syscall, sino un vDSO . Si en realidad es un syscall directo, deberá usar el modo 2 seccomp y explícitamente incluirlo en la lista blanca. Para esto, desearía utilizar libseccomp para abstraer la complejidad.