Giter Site home page Giter Site logo

saruman's Issues

Saruman crashes host program on first bootstrap instruction

Hello there,

I am unable to run the Saruman correctly due to a really weird bug on my Debian 8:
Linux M 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux. Saruman crashes the host program right in call_fn(BOOTSTRAP_CODE, ...) at the very first instruction of program's entry point.

Host:

~/devel/infector/saruman2 $ ./launcher `pidof host` ../parasite ""

#include <stdio.h>
int main(void) {
	for(;;) {
		printf("I am a boring program\n");
		sleep(3);
	} 
}

Parasite:

#include <stdio.h>
int main(void){
	int i;
	for (i = 0; i < 10; i++)
		printf("I AM AN EVIL FUCKING PARASITE!!!\n");
}

Launching slightly modified Saruman's code:

[+] Target pid: 389
[+] map_elf_binary(ptr, ../parasite)
[+] Parasite entry point will be main(): 0x400506
[+] Found text segment
[+] Found data segment
[+] Found dynamic segment
[+] Found dynamic string table
[+] Found dynamic symbol table
[+] Found G.O.T
[+] PLT count: 72 entries
[DEBUG]-> get_sym_from_libc() addr of __libc_dl_*: 7f112c69cb00
[+] PT_ATTACHED -> 389
[+] calling bootstrap

-- [RIP - 0x00400000] Single step
rax: 0xfffffffffffffdfc (-516)
rbx: 0x7ffce59de1a0 (140724160815520)
rcx: 0xffffffffffffffff (-1)
rdx: 0x04000000 (67108864)
rsi: 0x000005c8 (1480)
rdi: 0x00c00000 (12582912)
rbp: 0xffffffff (4294967295)
rsp: 0x7ffce59de188 (140724160815496)
r8: 0x7ffce59de2a0 (140724160815776)
r9: 0x7ffce59de0e0 (140724160815328)
r10: 0x00000008 (8)
r11: 0x00000246 (582)
r12: 0x7ffce59de220 (140724160815648)
r13: 0x7ffce59de450 (140724160816208)
r14: 0x00000000 (0)
r15: 0x00000000 (0)
First 12 bytes from RIP:
55 48 89 e5 48 81 ec a0 48 81 ec a0

-- [RIP - 0x003ffffe] Single step
rax: 0x000000db (219)
rbx: 0x7ffce59de1a0 (140724160815520)
rcx: 0xffffffffffffffff (-1)
rdx: 0x04000000 (67108864)
rsi: 0x000005c8 (1480)
rdi: 0x00c00000 (12582912)
rbp: 0xffffffff (4294967295)
rsp: 0x7ffce59de188 (140724160815496)
r8: 0x7ffce59de2a0 (140724160815776)
r9: 0x7ffce59de0e0 (140724160815328)
r10: 0x00000008 (8)
r11: 0x00000246 (582)
r12: 0x7ffce59de220 (140724160815648)
r13: 0x7ffce59de450 (140724160816208)
r14: 0x00000000 (0)
r15: 0x00000000 (0)
First 12 bytes from RIP:
ff ff ff ff ff ff ff ff 89 e5 48 81

[!] Target process has been stopped, something went wrong. Signal: 11 (Segmentation fault)
call_fn(BOOTSTRAP_CODE, ...) failed: Success

One can notice that the second registers dump differs from first one in:

  • RAX being changed
  • RIP being shifted two bytes back
  • bytes at RIP being malformed with 0xff - not to mention, that following bytes: 89 e5 48 ... are shifted even further from RIP - four bytes off.

Here are all modifications made to Saruman (launcher.c):

  • added function display_regs for tracing purposes
  • slightly modified call_fn function to instead of PTRACE_CONT got into PTRACE_SINGLESTEP
  • added a do..while loop with more thorough status interpretation code
  • added failover code for run_bootstrap in main() as saruman should fail when bootstrap did not manage to run.
[...]
void display_regs(pid_t pid, struct user_regs_struct *pt_reg ) {

    long word[3] = {0};
    char *buf = (char*)word;

    word[0] = ptrace(PTRACE_PEEKTEXT, pid, pt_reg->rip, NULL);
    word[1] = ptrace(PTRACE_PEEKTEXT, pid, pt_reg->rip+4, NULL);
    word[2] = ptrace(PTRACE_PEEKTEXT, pid, pt_reg->rip+8, NULL);
    
    printf("\n-- [RIP - 0x%08lx] Single step \n"
            "rax: 0x%08lx (%ld)\nrbx: 0x%08lx (%ld)\nrcx: 0x%08lx (%ld)\n"
            "rdx: 0x%08lx (%ld)\nrsi: 0x%08lx (%ld)\nrdi: 0x%08lx (%ld)\n"
            "rbp: 0x%08lx (%ld)\nrsp: 0x%08lx (%ld)\nr8: 0x%08lx (%ld)\n"
            "r9: 0x%08lx (%ld)\nr10: 0x%08lx (%ld)\nr11: 0x%08lx (%ld)\n"
            "r12: 0x%08lx (%ld)\nr13: 0x%08lx (%ld)\nr14: 0x%08lx (%ld)\n"
            "r15: 0x%08lx (%ld)\n"
            "First 12 bytes from RIP:\n"
            "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
            pt_reg->rip,
            pt_reg->rax, pt_reg->rax, pt_reg->rbx, pt_reg->rbx, pt_reg->rcx, pt_reg->rcx,
            pt_reg->rdx, pt_reg->rdx, pt_reg->rsi, pt_reg->rsi, pt_reg->rdi, pt_reg->rdi, 
            pt_reg->rbp, pt_reg->rbp, pt_reg->rsp, pt_reg->rsp, pt_reg->r8, pt_reg->r8,
            pt_reg->r9, pt_reg->r9, pt_reg->r10, pt_reg->r10, pt_reg->r11, pt_reg->r11,
            pt_reg->r12, pt_reg->r12, pt_reg->r13, pt_reg->r13, pt_reg->r14, pt_reg->r14,
            pt_reg->r15, pt_reg->r15, 
            buf[0] & 0xff, buf[1] & 0xff, buf[2] & 0xff, buf[3] & 0xff, buf[4] & 0xff, 
            buf[5] & 0xff, buf[6] & 0xff, buf[7] & 0xff, buf[8] & 0xff, buf[9] & 0xff, buf[10] & 0xff, buf[11] & 0xff);
}

/*
 * call_fn() allows one to inject a function
 * (select by functionPayloads_t) into the remote
 * process, and execute it. The return value for
 * the function is stored in payloads.function[func].retval
 */
#define SLACK_SIZE 32
int call_fn(functionPayloads_t func, handle_t *h, uint64_t ip)
{

[...]

case 6:
            pt_reg->rdi = (uintptr_t)h->payloads.function[func].args[0];
                        pt_reg->rsi = (uintptr_t)h->payloads.function[func].args[1];
                        pt_reg->rdx = (uintptr_t)h->payloads.function[func].args[2];
                        pt_reg->rcx = (uintptr_t)h->payloads.function[func].args[3];
                        pt_reg->r8 =  (uintptr_t)h->payloads.function[func].args[4];
            pt_reg->r9 =  (uintptr_t)h->payloads.function[func].args[5];
            break;
    }

    display_regs(h->tasks.pid, pt_reg);
    
    if (ptrace(PTRACE_SETREGS, h->tasks.pid, NULL, pt_reg) < 0)
        return -1;
    
    //if (ptrace(PTRACE_CONT, h->tasks.pid, NULL, NULL) < 0)
    //    return -1;

    do {
        ptrace(PTRACE_SINGLESTEP, h->tasks.pid, 0, 0);
		if (waitpid(h->tasks.pid, &status, 0) == -1) {
			perror("waitpid");
            return -1;
		}

        if (ptrace(PTRACE_GETREGS, h->tasks.pid, NULL, pt_reg) < 0) {
            perror("PTRACE_GETREGS");
            return -1;
        }
        
        display_regs(h->tasks.pid, pt_reg);

		if (WIFEXITED(status)) {
			fprintf(stderr, "\n[!] Target process has exited, something went wrong. Status: %d\n", WEXITSTATUS(status));
			return -1;
		} else if (WIFSIGNALED(status)) {
			fprintf(stderr, "\n[!] Target process has been killed, something went wrong. Signal: %d (%s)\n", WTERMSIG(status), strsignal(WTERMSIG(status)));
			if (WCOREDUMP(status)) {
				fprintf(stderr, "\tChild process has produced a core dump. Damn it.\n");
			}
			return -1;
		} else if (WIFSTOPPED(status)) {
			fprintf(stderr, "\n[!] Target process has been stopped, something went wrong. Signal: %d (%s)\n", WSTOPSIG(status), strsignal(WSTOPSIG(status)));
			return -1;
		} else if (WIFCONTINUED(status)) {
		}

	} while (!WIFEXITED(status) && !WIFSIGNALED(status));
    
    /* Get return value */
    if (ptrace(PTRACE_GETREGS, h->tasks.pid, NULL, pt_reg) < 0) {
        perror("PTRACE_GETREGS");
        return -1;
    }

    h->payloads.function[func].retval = (pt_reg_t)pt_reg->rax;
    
    return 0;

}

[...]

int main(int argc, char **argv)
{
[...]
if(run_bootstrap(&parasite) < 0) {
        goto done;
    }

As can be seen, there is a really strange behaviour that I don't understand going on in host's body:

  1. First, host get's saruman's bootstrap code injected, which gets injected fine - as it can be seen from registers (RIP, and so on) also from first 12 bytes dumped from RIP:
55 push $rsp
48 89 mov $rsp, $rbp
...
  1. Then, after a single step, where push $rsp should occur, out of the sudden we land at 2 bytes before entry point (0x3ffffe), RAX modified, and bytes at RIP malformed.

  2. Host gets it's SIGSEGV and it's life is over.

I don't understand that course of action, neither how it can be traced or fixed.
Any ideas?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.