Is there a reason you can't just study the code? 30 lines of ASM should be easy to understand. Just add comments explaining what each line does, and you'll understand it in no time. This will be even easier given how quickly you can look up exactly what an individual instruction does.
Although I would agree that it is unlikely to be capable of doing anything dangerous on its own with only a single syscall, you should never underestimate the ingenuity some developers can have. It is perfectly possible to design shellcode to trick a disassembler into showing you code which is not actually there, as DEF CON talk a few years ago pointed out. 30 lines of x86 assembly is plenty for a stage 1 payload.
Chances are it's completely harmless. I wouldn't bet my life on it, though.
You can use seccomp on Linux to isolate it. Seccomp is a Linux feature which filters system calls. There are two modes for seccomp, mode 1 and mode 2. Seccomp mode 1 is the most strict mode. When a program enables this mode, it is limited to using only four hardcoded syscalls: read()
, write()
, exit()
, and rt_sigreturn()
. Mode 2 is more complex, and uses a userspace-generated BPF filter to specify a custom whitelist of syscalls and syscall arguments. For a very simple piece of shellcode, you would only need mode 1.
Activating mode 1 seccomp is simple. Taken from another answer I wrote on another StackExchange site, a sample program that securely executes a function that returns 42 in bytecode:
#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";
int 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);
return 0;
}
}
You would want to replace code[]
with the shellcode you want to test.
You mentioned that there is one syscall to time()
. On most Linux systems, this is not a true syscall, but a vDSO. If it is actually a direct syscall, you would need to use mode 2 seccomp and explicitly whitelist it. For this, you would want to use libseccomp to abstract away the complexity.