36

I have a binary on a Linux (Kernel 2.6) which I can execute, but can't read (chmod 0711). Therefore no static analysis is possible.

user1: $ ls -l bin 
-r-s--x--- user2 user1 bin
user1: $ file bin
setuid executable, regular file, no read permission

I'm looking for different dynamic techniques to gather as much information as possible.

For example strace works with this executable.


UPDATE : I was able to resolve the issue. See answer.

Thank you all <3 this new reverseengineering community rocks!

6
  • Are you looking only for a dynamic analysis technique, or are you looking for a solution in general?
    – JMcAfreak
    Mar 20, 2013 at 21:41
  • I guess only dynamical analysis techniques work here. But I am open to anything that can help to understand what the executable does. I have no "physical" access to filesystem and so forth.
    – samuirai
    Mar 20, 2013 at 22:01
  • If strace works, try attaching with GDB and dumping memory (see here)
    – Igor Skochinsky
    Mar 20, 2013 at 22:10
  • @IgorSkochinsky, I just tried it on my machine, no luck. GDB refuses to attach with: Could not attach to process. ... try again as the root user. It depends on ptrace (which gives the same error) so ltrace doesn't work either. Mar 20, 2013 at 22:13
  • 2
    @IgorSkochinsky If you run strace ./bin, it runs the program with no extra privileges. If you start the program normally, it runs with elevated privileges, and you then can't ptrace it without having elevated privileges yourself. Mar 20, 2013 at 22:17

5 Answers 5

27

I initially thought you wouldn't be able to dump the program, but it turns out that you can — see the second section of this answer.

Running the program

Most of the usual methods won't work because the executable is setuid. If you start the program normally, it runs with elevated privileges (euid ≠ ruid), and most debugging facilities are reserved to root. For example, anything that relies on attaching to the program with ptrace is reserved to root. This includes just about anything you could do in a debugger. Linux lets you dump the memory of a running process (with ptrace or through /proc/$pid/maps and /proc/$pid/mem), but that too is disabled for a setuid executable.

You can run strace ./bin, and see what system calls the program makes. However, this runs the program without any extra privileges. It may well stop early and complain that it can't read a file, or that it isn't running as the right user.

You can see statistics like CPU usage, IO usage, network usage, memory usage, etc. in /proc/$pid: files like status, sched and net/netstat are world-readable (whether the program is actually started with extra privileges or not, the setuid restrictions apply). The one interesting thing I see is the program's network connections. The really juicy stuff, like memory contents (mem) and even open files (fd) is disabled either way. Preloading a library with LD_PRELOAD or running the program with a different LD_LIBRARY_PATH is also disabled either way. Nor will you get a core dump.

So what works? You can see open network connections, maybe one of them will give you a clue or will be spoofable. You can try to figure out what files the program accesses by modifying them when you can, or by running the program from another directory (try making a symbolic link).

If you have access to a chroot jail on the machine, try running the program from there. If you can run a virtual machine which implements shared folders (e.g. VMware or VirtualBox), see if you can access the program from there. This is all about subverting the security of the system: the permissions are designed to hinder you in your task.

An advanced technique is to observe the program under heavy load and try to deduce what it does from the time it takes. Pollute the disk caches and see what files become quickly accessible. See how the swap usage varies depending on what the program does (so you get an idea of how many different pages are in its working set, not just how much total memory it's using). A lot of trial and error will be required. When you know the code and are trying to obtain the data, this is a well-known side channel attack. When you don't know the code, I expect it to be very difficult to obtain useful information this way.

Dumping the memory contents

I initially thought you couldn't dump the memory of the program because of the setuid bit (which usually wouldn't be a problem because you'd just copy the executable, but here this isn't possible because the executable is not readable). This turns out to be wrong. If you attach to the program with ptrace before calling execve (like running strace ./bin does), you do have access to ptrace in all its glory, including PTRACE_PEEKDATA and PTRACE_PEEKEXEC. That doesn't run the program setuid, so it may behave differently, but the code and static data loaded into memory are the same either way.

Kudos to samuirai for pointing this out.

Here's an overview of how you can dump the program's memory:

  • Fork, and call ptrace(PTRACE_TRACEME, ...) in the child then execute the program (execl("./bin", "./bin", NULL)).
  • In the parent, call waitpid to wait for the child to return from execve.
  • Figure out the address at which the code is loaded. The value of the IP register (obtained through PTRACE_GETREGS) should give a clue, and you can look for file format headers.
  • Read the child's memory word by word (PTRACE_PEEKTEXT, PTRACE_PEEKDATA).
  • Reconstruct the file format (e.g. make an ELF binary from text and data sections) if needed. Dump out the data into a file.
  • Kill the child — it won't even get to execute anything.

Dougall pointed out a working proof-of-concept tool for ELF executables: xocopy.

12
  • Actually I think I can use ptrace not as root. gdb works fine with other executables. The problem is that the executable is not readable, and therefore gdb won't load the file. I thought because I can execute it, I could run it under my privileges and debug it that way (and accept that privileges required running with setuid wont work). But again. gdb won't work because it's not readable.
    – samuirai
    Mar 21, 2013 at 0:54
  • @samuirai: you can't attach to the process once it's running?
    – Igor Skochinsky
    Mar 21, 2013 at 0:59
  • @IgorSkochinsky: I tried it, but I am too slow. The executable finishes very fast.
    – samuirai
    Mar 21, 2013 at 1:05
  • 1
    Working PoC gist.github.com/Samuirai/5217680 and a small update in my question
    – samuirai
    Mar 21, 2013 at 23:21
  • 2
    @samuirai Good idea deducing interesting addresses from registers. I'll try to upgrade my code to something that works this week-end. Mar 21, 2013 at 23:32
10

I found a small tool which uses ptrace to single step instructions from a forked child which executed another program:

http://pastebin.com/yHbkc0Je

This worked perfectly. I got a dump of all the instructions used. I know that it didn't run with the setuid privileges, but it will probably help me anyway. Now I only need something to disassemble the instruction trace.


PoC (https://gist.github.com/Samuirai/5217680) based on this.

/*
This tool was developed through the awesome community effort on reverseengineering.stackexchange.com

The Thread: http://reverseengineering.stackexchange.com/questions/98/how-can-i-analyse-an-executable-with-no-read-permission

Special thanks to:
 + igor-skochinsky who pointed at this Phrack article and gave the initial idea: http://www.phrack.com/issues.html?issue=63&id=12&mode=txt
 + gilles for his engagement and enthisiasm about this question and his code attempt
 + and all others who were involved

This new stackexchange reverseengineering community is F**KING AWESOME!!! <3

*/
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/syscall.h>

int main()
{
    pid_t pid;

    pid = fork();
    if(pid == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("./bin", "./bin", NULL);
    }
    else {
        int status;
        struct user_regs_struct regs;

        // single stepping until it reached the real code segment
        while(1) {

            wait(&status);
            if(WIFEXITED(status))
                break;

            // get registers for the instruction pointer
            ptrace(PTRACE_GETREGS, pid, NULL, &regs);

            // strace ./bin
            // brk(0) = 0x804a000
            if(regs.eip>0x804a000)
            {
                printf("in code section at EIP=%lx\n",regs.eip);
                unsigned int i;

                // Dump code
                for(i=0; i<0x1000; i+=4) {
                    long data = ptrace(PTRACE_PEEKTEXT, pid, regs.eip+i, 0);
                    printf("%lx",data);
                }
            }

            // single step in child process
            ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);
        }
    }
    return 0;
}

As Gilles pointed out, you can't read /proc/<pid>/maps to get the adresses of the code area, but I recognized that strace has this nice single line brk(0) = 0x804a000.

I single step through the child process, until EIP is around this value; then, I dump 0x1000 bytes.

3
  • 5
    Why not just modify it to dump memory instead of tracing? There is already a read using PTRACE_PEEKTEXT, you just need to modify it to read all process memory. See some sample code here
    – Igor Skochinsky
    Mar 21, 2013 at 3:03
  • On SE, we usually like to see (not too long) codes just in the answer, not behind a link. This is because pastebin doesn't guarantee to store the codes. Could you add the code to your answer please?
    – user187
    Mar 22, 2013 at 18:45
  • It's the same code from my question update. I will add the reference
    – samuirai
    Mar 23, 2013 at 21:14
8

Using ptrace-based dynamic analysis tools on suid binaries makes them run without privileges. Because of this, a copy of the file running as your user is probably sufficient for analysis purposes.

When I have had to do this, I used the xocopy tool, which uses ptrace to reconstruct ELF files when the header is mapped into memory (most compilers do this, possibly for use by the dynamic linker). I haven't tested the tool with ASLR, but you may be able to combine it with some of the techniques covered in other answers. Once the file has been dumped, it can be analysed statically, or run with any dynamic analysis tool.

1
  • 1
    Is there a 64bit version of this tool? It doesn't seem to work on 64 bit elf files. :/
    – Skylion
    Feb 29, 2016 at 1:46
5

Here are some tools that might or might not work:

I'll be interested to hear if you had any success with any of them.

3
  • 3
    None of these will work on a setuid executable. Mar 20, 2013 at 22:00
  • @Guiles it isn't setuid nor did he ever say it was.
    – cb88
    Mar 21, 2013 at 4:18
  • 1
    @cb88 to be fair the quoted permissions do show the s flag. Though I guess it should have been mentioned explicitly.
    – Igor Skochinsky
    Mar 21, 2013 at 11:10
1
  1. Gain root read the file
  2. Copy the filesystem to a different sytem and read it if you have permissions to do so (copy the fs to a system where you have root access).
  3. examine the memory of the process as it executes. won't work if it is setuid and you aren't that user.
  4. I'm just being silly but you could execute it over and over untill it fills up swap and then copy it from there :P Or create a swap file in your home folder with priority higher than system swap. And then do the above... and when the system swap starts filling up copy your swapfile to save it. I would suggest doing that in a script otherwise your system might end up locked up. You could monitor swap useage with free I believe or failing that parse the useage out of /proc. Of course kill all the created tasks after you copy your swapfile.

Chances are you don't have permission to do any of that :P. that said you probably need to provide more specifics about what you can/can't do on this system.

1
  • 1
    That's right. I am just a normal user and can't access /proc. Though strace works and I thought that there exist other stuff I don't know about.
    – samuirai
    Mar 20, 2013 at 22:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.