In this post I am going to demonstrate how to easily find out what an evil ELF is doing to your system. This can be useful if you have one that is making secure network connections and you want to have a closer look… Or just for fun.
Linked library dependencies and ldd
The easiest to start with are linked library dependencies. In our example:
The rest of the output is stripped; the important thing is that our app seems
to use libcurl
to communicate with the evil servers.
LD_PRELOAD
and debug libraries
To have some more info on what is going on behind the scenes, we can grab a
copy of libcurl
and build a debug version that has verbose logging enabled by
default.
Now we can use the debug version of libcurl.so
to get a lot of debugging
output about the network connections made:
The debug build automatically enables the [CURLOPT_VERBOSE
] param, which logs
all connection information, except the transferred payload. To also log the
payload, have a look at the sample code in debug.c
(part of the
libcurl
project).
Static (built-in) libs and objdump
Now that we can inspect the traffic, we can use curl
to impersonate the app.
But what if the requests are signed, and the signature is verified on the
server? We want to be able to generate those fingerprints ourselves.
Let’s assume that we’ve noticed a 40-char digit hex string in every request. 40 characters? It is most likely SHA1. But we didn’t see any linked library that could be used to generate such hashes… Perhaps they are not dynamically linked (that happens often with distributed binaries).
To have a closer look at the evil app, let’s take it apart with objdump
:
Bingo! It seems the Boost library is used to generate the SHA1 hashes. A quick
look at the source reveals that the routines live inside boost/uuid/sha1.hpp
.
Runtime inspection with gdb
Instead of preloading a debug version of this, we’ll use gdb
to break
execution of the app when it feeds the string to be hashed:
Now when execution stops at process_bytes
, we know that the string (char *
to be precise) we need is somewhere at hand. Probably near the top of the
stack, or maybe in a register. We know it is the first parameter when calling
the function, but the compiler may have mangled that away, plus we have to
consider the hidden argument (this
) implied when calling a C++ method.
We can try printing these addresses as characters to see if we find our char *
.
There it is, in the RCX
register! Let’s print it as a string!
Awesome. Now that we see how the signature is being generated, we can do the
same when faking the requests with curl
.
Automate it all with .gdbinit
One easy way to automate printing the data being hashed is by creating a
.gdbinit
file like this:
Now, to start the monitored version of evil-elf
, just run gdb
, and the rest
will be taken care of.
A few more hints
When running commands from gdb
, you can call functions as well, using the
call
command. However, it is easy to run into recursions, and gdb
will stop
there, without completing the call
ed function correctly. An easy fix is to
disable
the breakpoint at the beginning of the commands
block, and
re-enable them just before the end
.
Sources
- gdb — the GNU debugger:
man gdb
- objdump — display information from object files:
man objdump
- libcurl — client-side URL transfers:
man libcurl
- lsof — list open files:
man lsof
.gdbinit
commands: this answer on stack exchange