Created
March 27, 2018 13:10
-
-
Save selenologist/ac55e46494a6b8075591c75af25c9841 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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