Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
server {
# тут всякие настройки ssl и указание порта где поднимать
server_name dev.{{{HOSTNAME}}};
location / {
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-XSS-Protection "1; mode=block" always;
proxy_pass http://localhost:{{{DEVPORT}}};
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# тут всякий бойлерплейт под вебсокеты
location /ws {
proxy_read_timeout 50s;
proxy_pass http://localhost:{{{DEVPORT}}};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o BatchMode=yes -gnNT -R <PORT>:localhost:<PORT> -i <PEM сертификат> root@<HOSTNAME>
Попробуем перенаправить вызовы (*stream).Write в функцию-прокси:
Примерно так
При попытке вызвать ngrok с данным хуком получаем краш следующего вида:
специально для этого написал ptracer
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char** argv)
{
int payload_fd = open(argv[1], O_RDONLY);
if(payload_fd < 0)
{
perror("open");
return 1;
}
struct stat st;
if(fstat(payload_fd, &st))
{
perror("stat");
return 1;
}
pid_t pid = fork();
if(!pid)
{
if(ptrace(PTRACE_TRACEME, 0, 0, 0))
{
perror("ptrace");
exit(1);
}
execvp(argv[2], argv+2);
perror("execvp");
exit(1);
}
int status;
waitpid(pid, &status, WUNTRACED);
if(WIFEXITED(status))
{
fprintf(stderr, "Child has exited, wtf?\n");
exit(1);
}
for(int i = 0; i < 2; i++)
{
ptrace(PTRACE_SYSCALL, pid, 0, 0);
waitpid(pid, &status, WUNTRACED);
}
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, pid, 0, ®s);
if(regs.rax != 0)
{
fprintf(stderr, "execvp() failed\n");
ptrace(PTRACE_CONT, pid, 0, 0);
wait(&status);
exit(1);
}
char path_buf[64];
sprintf(path_buf, "/proc/%d/mem", (int)pid);
int fd = open(path_buf, O_RDWR);
if(fd < 0)
{
perror("open");
goto kill_it;
}
lseek(fd, regs.rip, SEEK_SET);
char buf[2];
if(read(fd, buf, 2) != 2)
{
perror("read");
goto kill_it;
}
lseek(fd, regs.rip, SEEK_SET);
char buf2[2] = {0x0f, 0x05};
if(write(fd, buf2, 2) != 2)
{
perror("write");
goto kill_it;
}
struct user_regs_struct regs2 = regs;
regs2.rax = regs2.orig_rax = __NR_mmap;
regs2.rdi = 0;
regs2.rsi = st.st_size;
regs2.rdx = PROT_READ | PROT_WRITE | PROT_EXEC;
regs2.r10 = MAP_PRIVATE;
regs2.r8 = payload_fd;
regs2.r9 = 0;
ptrace(PTRACE_SETREGS, pid, 0, ®s2);
for(int i = 0; i < 2; i++)
{
ptrace(PTRACE_SYSCALL, pid, 0, 0);
waitpid(pid, &status, WUNTRACED);
}
ptrace(PTRACE_GETREGS, pid, 0, ®s2);
if(regs2.rax & 0xffff000000000000ll)
{
errno = -regs2.rax;
perror("child mmap()");
goto kill_it;
}
printf("mapped at %p\n", (void*)regs2.rax);
lseek(fd, regs.rip, SEEK_SET);
if(write(fd, buf, 2) != 2)
{
perror("write");
goto kill_it;
}
regs2.rip = regs2.rax;
regs2.rsp -= 8;
char buf3[8] = {0};
lseek(fd, regs.rsp, SEEK_SET);
if(write(fd, buf3, 8) != 8)
{
perror("write");
goto kill_it;
}
ptrace(PTRACE_SETREGS, pid, 0, ®s2);
if(getenv("DEBUG_HOOKS"))
{
ptrace(PTRACE_DETACH, pid, 0, (void*)SIGSTOP);
char buf[64];
if(getenv("STRACE_HOOKS"))
sprintf(buf, "strace -p %d", (int)pid);
else
sprintf(buf, "gdb --pid %d", (int)pid);
system(buf);
return 0;
}
ptrace(PTRACE_CONT, pid, 0, 0);
waitpid(pid, &status, WUNTRACED);
ptrace(PTRACE_GETREGS, pid, 0, ®s2);
if(regs2.rip)
{
fprintf(stderr, "FATAL: shellcode crashed somewhere else\n");
goto kill_it;
}
ptrace(PTRACE_SETREGS, pid, 0, ®s);
if(getenv("DEBUG_MAIN"))
{
ptrace(PTRACE_DETACH, pid, 0, (void*)SIGSTOP);
char buf[64];
sprintf(buf, "gdb --pid %d", (int)pid);
system(buf);
return 0;
}
ptrace(PTRACE_DETACH, pid, 0, 0);
wait(&status);
return 0;
kill_it:
ptrace(PTRACE_KILL, pid, 0, 0);
wait(&status);
return 1;
}
#include <sys/syscall.h>
#include <sys/mman.h>
#include <stdarg.h>
#include "symbols.h"
#undef runtime_data
struct RUNTIME_DATA
{
unsigned long long hook_runtime_write[4];
} runtime_data;
asm("get_image_base:\n.byte 0xe8,0,0,0,0\npop %rax\nsub $5, %rax\nret");
unsigned long long get_image_base();
#define translate_addr(x) (get_image_base()+((unsigned long long)(x)))
#define runtime_data (*(struct RUNTIME_DATA*)translate_addr(&runtime_data))
long syscall(long nr, ...)
{
va_list args;
va_start(args, nr);
long data[7];
data[0] = nr;
for(int i = 1; i < 7; i++)
data[i] = va_arg(args, long);
va_end(args);
long* data_p = data;
long ans = 0;
asm volatile("push %%rbp\nmov %%rsp, %%rbp\nmov %1, %%rsp\npop %%rax\npop %%rdi\npop %%rsi\npop %%rdx\npop %%r10\npop %%r8\npop %%r9\nleave\nsyscall\nmov %%rax, %0":"=m"(ans):"r"(data_p));
return ans;
}
void mprotect_rw(unsigned long long addr)
{
unsigned long long end_addr = addr + 16;
addr &= ~4095ull;
syscall(__NR_mprotect, addr, end_addr - addr, PROT_READ | PROT_WRITE | PROT_EXEC);
}
void read_longlongs(volatile unsigned long long* p, unsigned long long arr[2])
{
arr[0] = p[0];
arr[1] = p[1];
}
void write_longlongs(volatile unsigned long long* p, unsigned long long arr[2])
{
*p = 0xfeebll;
p[1] = arr[1];
p[0] = arr[0];
}
void setup_hook(unsigned long long tgt, unsigned long long arr[2])
{
arr[0] = tgt << 24 | 0xb84850ll;
arr[1] = tgt >> 40 | 0xc324048748000000ll;
}
#define RBP() ({volatile long long* rbp; asm volatile("mov %%rbp, %0":"=r"(rbp)); rbp;})
#define RSP() (RBP()+1)
#define PUSH(x) asm volatile("pushq %0"::"m"(x))
#define CALL(x) asm volatile("call *%0"::"r"(x))
#define POP(n, res) asm volatile("add $"#n", %%rsp\nmov %%rax, %0":"=m"(res));
unsigned long long runtime_write_hook()
{
volatile long long* rsp = RSP();
char* buf = (char*)rsp[2];
long long len = rsp[3];
for(int i = 0; i < len; i += 2)
buf[i] = '$';
write_longlongs((volatile unsigned long long*)syscall_write, runtime_data.hook_runtime_write);
unsigned long long ans;
PUSH(rsp[3]);
PUSH(rsp[2]);
PUSH(rsp[1]);
CALL(syscall_write);
POP(24, ans);
write_longlongs((volatile unsigned long long*)syscall_write, runtime_data.hook_runtime_write+2);
return ans;
}
void _start()
{
mprotect_rw((unsigned long long)syscall_write);
read_longlongs((volatile unsigned long long*)syscall_write, runtime_data.hook_runtime_write);
setup_hook(translate_addr(runtime_write_hook), runtime_data.hook_runtime_write+2);
write_longlongs((volatile unsigned long long*)syscall_write, runtime_data.hook_runtime_write+2);
}
Реверс-инжиниринг протокола ngrok v2