Skip to content

Instantly share code, notes, and snippets.

@selenologist
Created March 27, 2018 13:10
Show Gist options
  • Save selenologist/ac55e46494a6b8075591c75af25c9841 to your computer and use it in GitHub Desktop.
Save selenologist/ac55e46494a6b8075591c75af25c9841 to your computer and use it in GitHub Desktop.
// play with ptrace
// was trying to make a thing that messed with a parent shell,
// but only for fun because of course you can't ptrace your own
// processes on modern hardened kernels without overriding those
// features.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/ptrace.h>
// idk actually, these are approximates from /proc/$pid/maps
const uintptr_t TEXT_SECTION_MIN = 0x550000000000;
const uintptr_t TEXT_SECTION_MAX = 0x5f0000000000;
void printstatus(int wstatus){
if(WIFSTOPPED(wstatus)){
printf("stopped with %u\n", WSTOPSIG(wstatus));
}
else if(WIFEXITED(wstatus)){
printf("exited with %u\n", WEXITSTATUS(wstatus));
}
else if(WIFSIGNALED(wstatus)){
printf("terminated with %u\n", WTERMSIG(wstatus));
}
}
int step(pid_t pid, int* wstatus){
if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0){
printf("failed to singlestep\n");
return 1;
}
waitpid(pid, wstatus, 0);
return 0;
}
int main(){
pid_t ppid = getppid();
printf("parent pid is %u\n", ppid);
struct user_regs_struct old_regs; // backup old regs and also get rip
if(ptrace(PTRACE_ATTACH, ppid, NULL, NULL) < 0){
printf("caught! ptrace attach failed\n");
return 1;
}
int wstatus;
printf("wait for attach\n");
waitpid(ppid, &wstatus, 0); // wait for attach
printstatus(wstatus);
// ensure RIP is in text section, take register backup
printf("probe rip\n");
ptrace(PTRACE_GETREGS, ppid, NULL, &old_regs);
while(0){/* actually it's fine if it's in a library afterall, so don't ensure text section
old_regs.rip < TEXT_SECTION_MIN ||
old_regs.rip > TEXT_SECTION_MAX){*/
printf("rip 0x%016llX not in text section beginning at\n"
" 0x%016lX\n", old_regs.rip, TEXT_SECTION_MIN);
step(ppid, &wstatus);
ptrace(PTRACE_GETREGS, ppid, NULL, &old_regs);
}
printf("rip is 0x%016llX, is %saligned\n",
old_regs.rip,
(old_regs.rip & 0b111)? "un": "");
// just make the parent exit
// nulls are fine
char shellcode[16] __attribute__((aligned(8))) =
"\xb8\x3c\x00\x00\x00" // mov rax, 0x3c
"\xbf\xff\x00\x00\x00" // mov rdi, 255
"\x0f\x05" // syscall
"\xcc" // int3 (trap)
"\xcc\xcc\xcc"; // padding from 13 to 16 bytes (2 words)
// read into backup of area around rip a word at a time
char rip_area_backup[sizeof shellcode] __attribute__((aligned(8)));
printf("backup rip area\n");
for(uintptr_t *iaddr = (uintptr_t*)old_regs.rip,//rip itself, not addr on stack
*oaddr = (uintptr_t*)rip_area_backup;
oaddr < (uintptr_t*)((char*)rip_area_backup + sizeof rip_area_backup);
iaddr++, oaddr++){
*oaddr = ptrace(PTRACE_PEEKTEXT, ppid, iaddr, NULL);
printf("word 0x%016lX from traced %p to tracer %p\n",
*oaddr, iaddr, oaddr);
}
// write the shellcode in place of it
printf("write shellcode\n");
for(uintptr_t *iaddr = (uintptr_t*)shellcode,
*oaddr = (uintptr_t*)old_regs.rip;
iaddr < (uintptr_t*)((char*)shellcode + sizeof shellcode);
iaddr++, oaddr++){
printf("word 0x%016lX from tracer %p to traced %p\n",
*iaddr, iaddr, oaddr);
ptrace(PTRACE_POKETEXT, ppid, oaddr, *iaddr);
*iaddr = ptrace(PTRACE_PEEKTEXT, ppid, oaddr, 0);
printf("chk 0x%016lX at traced %p\n",
*iaddr, oaddr);
}
// continue the traced process, now hopefully running our shellcode
printf("continue, to execute shellcode\n");
ptrace(PTRACE_CONT, ppid, NULL, NULL);
// wait for trap
printf("wait trap\n");
waitpid(ppid, &wstatus, 0);
printstatus(wstatus);
// probe RIP for debug
struct user_regs_struct new_regs;
ptrace(PTRACE_GETREGS, getppid(), NULL, &new_regs);
printf("rip is 0x%016llX, 0x%llX bytes after old rip\n",
new_regs.rip, new_regs.rip - old_regs.rip);
// restore old area around rip
printf("restore RIP area\n");
for(uintptr_t *iaddr = (uintptr_t*)rip_area_backup,
*oaddr = (uintptr_t*)old_regs.rip;
iaddr < (uintptr_t*)((char*)rip_area_backup + sizeof shellcode); //safe
iaddr++, oaddr++){
printf("word 0x%016lX from tracer %p to traced %p\n",
*iaddr, iaddr, oaddr);
ptrace(PTRACE_POKETEXT, ppid, oaddr, *iaddr);
}
// restore old regs
printf("restore regs\n");
ptrace(PTRACE_SETREGS, ppid, NULL, &old_regs);
// continue the traced process again, now hopefully clean
printf("continuing\n");
ptrace(PTRACE_CONT, ppid, NULL, NULL);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment