Hacking the /proc filesystem memory!

Hacking the /proc filesystem memory!

The /proc filesystem


The /proc filesystem is a virtual filesystem in Linux that provides information about the system and processes running on it. It does not exist on the physical disk, but is created by the kernel in memory when the system boots.

No alt text provided for this image

The /proc/pid

The /proc/PID directory contains information about a specific process running on the system, where PID is the process ID of the process. This directory can be used to examine the status of a process and its resources.

Inside the /proc/PID directory, there are several files that provide information about the process, including:

  • /proc/PID/status: contains information about the status of the process, such as its process ID, parent process ID, user and group IDs, and the process state (e.g. running, sleeping, etc.).
  • /proc/PID/fd: contains a symbolic link for each open file descriptor of the process, allowing the user to see which files the process has open and their associated file descriptor numbers.
  • /proc/PID/cmdline: contains the command line that was used to start the process, with arguments separated by null characters.
  • /proc/PID/environ: contains the environment variables that were set when the process was started, separated by null characters.
  • /proc/PID/maps: contains a list of memory maps for the process, including the addresses and permissions for each mapped area.
  • /proc/PID/stat: contains a variety of statistics about the process, such as the number of voluntary and involuntary context switches, the number of page faults, and the amount of time the process has spent in various states.

How to read in memory

In the Linux (or any *nix) OS when a program is loaded into memory and executed, it receives a PID, a process ID. Today we will consider two special "files" associated with each such running process: /proc/PID/maps and /proc/PID/mem where PID is the actual integer process ID in question. Let's clarify this with an example, but before we do all the code and files discussed in this article can be found here

First it will be useful to create a little C program that will make testing all this very convenient for us.



No alt text provided for this image

The program allocates a string, "Holberton", in its heap memory and loops once a second indefinitely, each time printing the: iteration count, value of the string, and the strings' memory address (the hexadecimal value of its pointer). The program needs to loop in order to stay resident in memory so that we can hack it! And if we do so successfully, we should see the string value it prints change right before our eyes...

So if we quickly compile this program to the binary executable a.out and run it, we can use the pgrep command to find the PID corresponding to our program name. We'll need to do this in two separate terminal windows because the one that loads the program will get a bit spammy!

Window 1:

No alt text provided for this image

Window 2:

No alt text provided for this image

The pgrep command is just a convenient way to quickly isolate the PID matching our program name. We could of course use the classic ps aux or even ps aux | grep a.out

As we mentioned originally, there will now be at least two special files corresponding to our process in /proc/10223/maps and /proc/10223/mem

The first file we can just read with the cat command:

55908acd6000-55908acf7000 rw-p 00000000 00:00 0                         [heap]

No alt text provided for this image

That's a lot of data but only this one line which ends with "[heap]" is relevant to us right now:


55908acd6000-55908acf7000 rw-p 00000000 00:00 0                          [heap]        

This tells us that our a.out application's heap memory begins at address 55908acd6000 and ends at address 55908acf7000 (or 0x55908acd6000 to 0x55908acf7000). Also, the rw-p indicates that both read and write access to this memory is available.

Let's recall the string address printed before by our running C program:


[0] Holberton School (0x55908acd62a0)        

And note that indeed 0x55908acd6000 <= 0x55908acd62a0 <= 0x55908acf7000. So the address of our string as displayed by our C program itself is between the start and stop addresses of the heap as parsed in the maps file. We're on the right track!

But how do we actually access these memory addresses? Simply by reading and writing to the virtual /proc/10223/mem file! But to do this we'll need to write a script to open the file in binary mode. Let's use Python for its clarity and convenience.

NOW is time to expline my code

first, we need to define main function:

No alt text provided for this image

The main() function starts by initializing the exit_code variable to 1. This variable will be used to indicate if the script ran successfully. If the script runs without any errors, the exit_code will be set to 0 before the script exits.

Checking the input arguments:


    if len(argv) != 4
            raise ValueError('Usage: read_write_heap.py'
                             ' pid search_string replace_string')
        if len(argv[3]) > len(argv[2]):
            raise IndexError:        

It then checks if the number of input arguments is 4, if not it raises ValueError and prompts the user with the correct usage of the script. Also it checks if the replace string is bigger than the search string, if so it raises an IndexError

Calling the rw_file_mem function:


     rw_file_mem(int(argv[1]), argv[2], argv[3])        

It calls the rw_file_mem function, passing in the input arguments. The first argument, the PID of the process, is passed as an integer after converting it from a string using the int() function.

Handling Exceptions:

except Exception as a
        print(a):        

The script uses a try-except block to catch any exceptions that might be raised. If an exception is caught, it is printed to the console.

AND finally Printing out information and exit:

finally
        if (exit_code == 0):
            print("[*] maps: /proc/{}/maps".format(argv[1]))
            print("[*] mem: /proc/{}/mem".format(argv[1]))
            print("[*] Found \"{}\"".format(argv[2]))
            print("[*] Replaced it with \"{}\"".format(argv[3]))
        exit(exit_code):        

If the script ran successfully (exit_code is set to 0), it will print out information about the process using the /proc/pid/maps and /proc/pid/mem files, as well as the strings that were searched for and replaced.

Finally, the script exits with the exit_code variable. This is the same as before.

So we need to Define the rw_file_mem function:

No alt text provided for this image



This function takes three arguments: the PID of the running process, the string to search for in the heap, and the string to replace it with. It starts by creating the paths to the /proc/pid/maps and /proc/pid/mem files, and opening them in read and read/write mode respectively.

Finding the heap in the memory map:

No alt text provided for this image

The function reads the /proc/pid/maps file line by line, looking for the line that corresponds to the heap of the process. Once it finds the line, it extracts the start and end addresses of the heap, as well as the permissions of the heap. The start and end addresses of heap are in hexadecimal format, so we need to convert them to decimal using the int() function and the base 16.

Checking the permission of heap:

No alt text provided for this image

The function then checks the permission of heap if it's read and write. If it's not, the function raises a PermissionError.

Reading the heap memory:

No alt text provided for this image

The function seeks to the start address of the heap in the /proc/pid/mem file, and reads the memory from the start to the end of the heap, storing it in a buffe

Searching for the string in the heap memory:

No alt text provided for this image

The function then searches for the specified string in the buffer using the index() function. It converts the string to bytes using the bytes() function.


Replacing the string in the heap memory:

No alt text provided for this image

It then seeks to the position of the string in the /proc/pid/mem file and writes the new string there

SO NOW IT IS TIME TO TEST :

No alt text provided for this image
WINDOWS 1



No alt text provided for this image
WINDOWS 2
No alt text provided for this image
WINDOWS 1

To view or add a comment, sign in

More articles by Louay Chaaben

  • All about the Virtual Memory

    Initially, I would like to describe quick definitions/terms, then walk through on What is Virtual Memory and How does…

Others also viewed

Explore content categories