Skip to content

Instantly share code, notes, and snippets.

@ntavish
Forked from mniip/tracer.c
Last active August 29, 2015 14:19
Show Gist options
  • Save ntavish/cff0d5d447565c8b0851 to your computer and use it in GitHub Desktop.
Save ntavish/cff0d5d447565c8b0851 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <signal.h>
#include <limits.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <asm/unistd.h>
pthread_mutex_t mutex;
void *timeout(void *p)
{
sleep(3);
pthread_mutex_lock(&mutex);
kill((pid_t)(intptr_t)p, SIGKILL);
printf("%s", "[Timed out]");
waitpid((pid_t)(intptr_t)p, NULL, 0);
exit(EXIT_SUCCESS);
}
#define ptraceif(...) if(0 > ptrace(__VA_ARGS__)){ if(errno == ESRCH) exit(EXIT_SUCCESS); else { perror("ptrace"); exit(EXIT_FAILURE); } }
void waitif(int pid)
{
int status = 0;
waitpid(pid, &status, 0);
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
if(WIFSIGNALED(status))
{
printf("[%s]\n", strsignal(WTERMSIG(status)));
kill(pid, SIGKILL);
exit(EXIT_SUCCESS);
}
if(WIFSTOPPED(status))
{
if(WSTOPSIG(status) != SIGTRAP)
{
ptraceif(PTRACE_SYSCALL, pid, NULL, WSTOPSIG(status));
waitif(pid);
}
}
if(WIFEXITED(status))
{
kill(pid, SIGKILL);
exit(EXIT_SUCCESS);
}
}
char *getstr(pid_t pid, long unsigned int addr)
{
int size = 256;
char *str = malloc(size);
int i = 0;
while(1)
{
long v;
int j;
errno = 0;
v = ptrace(PTRACE_PEEKTEXT, pid, addr + i, NULL);
if(errno)
{
free(str);
return NULL;
}
*(void **)(str + i) = (void *)v;
for(j = 0; j < sizeof(void *); j++)
if(!str[i + j])
return str;
i += sizeof(void *);
if(i > size)
{
size += 256;
str = realloc(str, size);
}
}
return str;
}
int allowed_file(const char *name)
{
static const char *whitelist[] = {
"/etc/ld.so.nohwcap",
"/etc/ld.so.preload",
"/etc/ld.so.cache",
"/lib/x86_64-linux-gnu/libc.so.6",
"/usr/lib/x86_64-linux-gnu/libstdc++.so.6",
"/usr/local/lib/libluajit-5.1.so.2",
"/usr/lib/locale/locale-archive",
"/lib/x86_64-linux-gnu/libm.so.6",
"/lib/x86_64-linux-gnu/libdl.so.2",
"/lib/x86_64-linux-gnu/libreadline.so.6",
"/lib/x86_64-linux-gnu/libc.so.6",
"/lib/x86_64-linux-gnu/libtinfo.so.5",
"/lib/x86_64-linux-gnu/libhistory.so.6",
"/lib/x86_64-linux-gnu/libncurses.so.5",
"/lib/x86_64-linux-gnu/libgcc_s.so.1",
"/usr/lib/x86_64-linux-gnu/../tcc/libtcc1.a",
"/lib/x86_64-linux-gnu/libpthread.so.0",
"/lib/x86_64-linux-gnu/libutil.so.1",
"/lib/x86_64-linux-gnu/libz.so.1",
"/usr/lib/x86_64-linux-gnu/liblua5.1.so",
"/usr/lib/python2.7/",
"/usr/lib/python2.7",
"/var/lib/xsbot/sandbox/fsbox",
NULL
};
static const char *whiteprefix[] = {
"/dev/",
"/usr/include/",
"/usr/lib/tcc/include/",
"/usr/lib/python2.7/",
"/var/lib/xsbot/sandbox/fsbox/",
NULL
};
int i;
for(i = 0; whitelist[i]; i++)
if(!strcmp(whitelist[i], name))
return 1;
char realname[PATH_MAX];
realpath(name, realname);
for(i = 0; whitelist[i]; i++)
if(!strcmp(whitelist[i], realname))
return 1;
for(i = 0; whiteprefix[i]; i++)
if(!strncmp(whiteprefix[i], realname, strlen(whiteprefix[i])))
return 1;
return 0;
}
int allowed_write_file(const char *name)
{
static const char *whitelist[] = {
"/var/lib/xsbot/sandbox/fsbox",
NULL
};
static const char *whiteprefix[] = {
"/var/lib/xsbot/sandbox/fsbox/",
NULL
};
int i;
for(i = 0; whitelist[i]; i++)
if(!strcmp(whitelist[i], name))
return 1;
char realname[PATH_MAX];
realpath(name, realname);
for(i = 0; whitelist[i]; i++)
if(!strcmp(whitelist[i], realname))
return 1;
for(i = 0; whiteprefix[i]; i++)
if(!strncmp(whiteprefix[i], realname, strlen(whiteprefix[i])))
return 1;
return 0;
}
#define ARCH_SYSNUM(x) ((x).orig_rax)
#define ARCH_SYSRET(x, y) ((x).rax = (y))
#define ARCH_SYSARG1(x) ((x).rdi)
#define ARCH_SYSARG2(x) ((x).rsi)
#define ARCH_SYSDENY(x) ((x).orig_rax = -1)
int main(int argc, const char *argv[])
{
char realname[PATH_MAX];
realpath(argv[1], realname);
chdir("fsbox");
int pid;
if(pid = fork())
{
pthread_t timer;
pthread_create(&timer, NULL, timeout, (void *)(intptr_t)pid);
pthread_mutex_init(&mutex, NULL);
if(pid < 0)
{
perror("fork");
return EXIT_FAILURE;
}
waitif(pid);
while(1)
{
int override = 0;
long long v;
intptr_t oreturn;
struct user_regs_struct regs;
ptraceif(PTRACE_SYSCALL, pid, NULL, NULL);
waitif(pid);
ptraceif(PTRACE_GETREGS, pid, NULL, &regs);
#if 1
v = ptrace(PTRACE_PEEKTEXT, pid, regs.rip - 2, NULL) & 0xFFFF;
if(v != 0x050F)
{
printf("STUB(CS=%04x): Expected 0F05, found %02X%02X\n", regs.cs, v >> 8, v & 0xFF);
override = 1;
oreturn = -EFAULT;
}
#endif
switch(ARCH_SYSNUM(regs))
{
case __NR_exit:
case __NR_exit_group:
case __NR_brk:
case __NR_getuid:
case __NR_getgid:
case __NR_geteuid:
case __NR_getegid:
case __NR_getpid:
case __NR_gettid:
case __NR_getppid:
case __NR_getpgrp:
case __NR_getcwd:
case __NR_pipe:
case __NR_pipe2:
case __NR_dup:
case __NR_dup2:
case __NR_dup3:
case __NR_uname:
case __NR_time:
case __NR_nanosleep:
case __NR_lseek:
case __NR_ioctl:
case __NR_fcntl:
case __NR_times:
case __NR_rt_sigaction:
case __NR_rt_sigprocmask:
case __NR_close:
case __NR_read:
case __NR_write:
case __NR_readv:
case __NR_writev:
case __NR_splice:
case __NR_tee:
case __NR_fstat:
case __NR_getdents:
case __NR_mmap:
case __NR_munmap:
case __NR_mremap:
case __NR_mprotect:
case __NR_select:
case __NR_arch_prctl:
case __NR_clock_gettime:
break;
case __NR_open:
{
char *f = getstr(pid, ARCH_SYSARG1(regs));
if(f)
{
if((ARCH_SYSARG2(regs) & ~O_CLOEXEC) == O_RDONLY)
{
if(!allowed_file(f))
{
override = 1;
oreturn = -ENOENT;
}
}
else
{
if(!allowed_write_file(f))
{
override = 1;
oreturn = -ENOENT;
}
}
free(f);
break;
}
override = 1;
oreturn = -EFAULT;
break;
}
case __NR_mkdir:
case __NR_chmod:
case __NR_chown:
case __NR_unlink:
case __NR_creat:
{
char *f = getstr(pid, ARCH_SYSARG1(regs));
if(f)
{
if(!allowed_write_file(f))
{
override = 1;
oreturn = -ENOENT;
}
free(f);
break;
}
override = 1;
oreturn = -EFAULT;
break;
}
case __NR_stat:
case __NR_lstat:
case __NR_readlink:
case __NR_access:
{
char *f = getstr(pid, ARCH_SYSARG1(regs));
if(f)
{
if(!allowed_file(f))
{
override = 1;
oreturn = -EPERM;
}
free(f);
break;
}
override = 1;
oreturn = -EFAULT;
break;
}
case __NR_openat:
{
if(ARCH_SYSARG1(regs) == AT_FDCWD)
{
char *f = getstr(pid, ARCH_SYSARG2(regs));
if(f)
{
if(!allowed_write_file(f))
{
override = 1;
oreturn = -EPERM;
}
free(f);
break;
}
override = 1;
oreturn = -EFAULT;
}
else
{
override = 1;
oreturn = -EPERM;
}
break;
}
default:
printf("Unknown syscall %d\n", ARCH_SYSNUM(regs));
case __NR_tgkill:
case __NR_tkill:
case __NR_kill:
case __NR_execve:
case __NR_fork:
case __NR_pause:
case __NR_waitid:
case __NR_wait4:
case __NR_vfork:
case __NR_clone:
case __NR_rename:
case __NR_sendto:
case __NR_recvfrom:
case __NR_sendmsg:
case __NR_recvmsg:
case __NR_set_tid_address:
case __NR_set_robust_list:
case __NR_getrlimit:
case __NR_link:
case __NR_symlink:
case __NR_umask:
override = 1;
oreturn = -EPERM;
break;
case __NR_futex:
override = 1;
oreturn = -ENFILE;
break;
}
if(override)
{
ARCH_SYSDENY(regs);
ptraceif(PTRACE_SETREGS, pid, NULL, &regs);
}
ptraceif(PTRACE_SYSCALL, pid, NULL, NULL);
waitif(pid);
if(override)
{
if(v != 0x050F)
{
printf("[Not continuing]\n");
exit(EXIT_SUCCESS);
}
ptraceif(PTRACE_GETREGS, pid, NULL, &regs);
ARCH_SYSRET(regs, oreturn);
ptraceif(PTRACE_SETREGS, pid, NULL, &regs);
}
}
}
else
{
ptrace(PTRACE_TRACEME);
char **arg = calloc(argc, sizeof(char *));
int i;
for(i = 1; i < argc - 1; i++)
{
arg[i] = malloc(strlen(argv[i]) + 1);
strcpy(arg[i], argv[i + 1]);
}
arg[0] = "dongs";
arg[argc - 1] = NULL;
execvp(realname, arg);
perror("execvp");
return EXIT_FAILURE;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment