Skip to content

Instantly share code, notes, and snippets.

@mniip
Last active August 31, 2016 18:23
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mniip/10599541 to your computer and use it in GitHub Desktop.
Save mniip/10599541 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",
NULL
};
static const char *whiteprefix[] = {
"/dev/",
"/usr/include/",
"/usr/lib/tcc/include/",
"/usr/lib/python2.7/",
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[] = {
NULL
};
static const char *whiteprefix[] = {
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;
}
}
@katlogic
Copy link

Don't forget to check actual opcode at rip and cs segment, otherwise people can play ABI personality tricks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment