Using ltrace and software shims to bypass a credential-checking algorithm

Overview

In this blog post, I will explain how I have used ltrace to analyze dynamic library calls from an application requesting user credentials and managed to hack a credential-checking algorithm:

hacking a credential-checking algorithm

Table of Contents

Introduction

The setup for demonstrating ltrace and software shims is described in GDB setup for debugging embedded applications blog post. As always, special thanks to Bootlin for providing the source code.

ltrace is a tool to trace shared library calls used by a program and all the signals it receives. This is especially useful when we don’t have access to the source files of the binaries we are analyzing. A software shim is a piece of code (a library to be more precise) that lives in between a program and some other library that the program uses. A shim intercepts calls from the program to the library allowing us to monitor and change the behavior of those calls.

These tools are extremely useful to analyze and change the behavior of an existing program for which we don’t have source code and that we cannot recompile.

My mind is unironically blown.

Running an authentication-constrained application

Since our application uses a local dynamic shared library which is not in the default paths expected by the linker, we provided that path using LD_LIBRARY_PATH by running the following on the target:

# export LD_LIBRARY_PATH=$PWD

And when we now run the application:

# ./authent

we can see that our application is failing to correctly authenticate the user:

attempting to run an authentication-constrained application

Bypassing credential-checking algorithm

By using ltrace on the target, we can trace the application on the target in order to understand what is going on and based on that trace which function fails:

# ltrace ./authent

ltrace overview

As you can see, the function that fails is al_authent_user() and this is the function which we will override in a new file, overload.c which will print the user, password and simply return 0:

overload.c source file overview

In this case, I looked at the authent_library.h inside the ltrace\ directory to conclude what the function prototype should be.

Now, to use the overload we just created, compile your source file on the development host with the following command:

$ ${CROSS_COMPILE}gcc -fPIC -shared overload.c -o overload.so

This command generates a position-independent shared library overload.so which can be loaded at any valid memory address without requiring modification.

Finally, run the application and preload the new library using the following command on the target:

# LD_PRELOAD=./overload.so ./authent

This command sets the LD_PRELOAD environment variable which tells the loader to first check our newly compiled overload.so library when running the authent binary. When al_authent_user function is called in the authent binary, our function with the same name will be called and credentials will be printed on the console:

authentication algorithm bypassed

If you would like to support the work I do, consider donating here.