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
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
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
libcurl and build a debug version that has verbose logging enabled by
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
Static (built-in) libs and
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
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
Runtime inspection with
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 (
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
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
Automate it all with
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
called function correctly. An easy fix is to
disable the breakpoint at the beginning of the
commands block, and
re-enable them just before the
- gdb — the GNU debugger:
- objdump — display information from object files:
- libcurl — client-side URL transfers:
- lsof — list open files:
.gdbinitcommands: this answer on stack exchange