Recovering a hacked website – Recovery THM Writeup

Khalid AlnajjarHacking, Security Leave a Comment

Alex is in a big trouble, she has opened a malicious binary that was emailed to her and she ran it on the production web server. As she is our friend, we need to step in and help her. This is the story of a new box at There is a tl;dr section at the end of the post if you are in a hurry.

Alex has given us the SSH password, so let’s jump to it and recover the server! To facilitate accessing the server, I have added recovery.thm to /etc/hosts and copied my local SSH keys there with ssh-copy-id [email protected].

Fixing SSH

Once logging in to SSH, we’d keep getting “YOU DIDN’T SAY THE MAGIC WORD!” repeatedly. Let’s exit and fix it by disabling .bashrc that is automatically loaded when SShing:

ssh [email protected] -f 'mv .bashrc .bashrc_bk; exit'

I noticed that ever few minutes, the SSH connection gets killed automatically. This means that there is a cronjob that monitors our SSH session. Looking in the typical locations for cronjobs, we spot a cronjob named evil in /etc/cron.d/. Looking in it, we can see that it is executing/opt/ as root, and indeed that script is what keeps killing our connection. The script is editable by anyone, so let’s overwrite it’s content and escalate our user.

printf '#!/bin/bash\n' > /opt/
echo 'echo "ssh-rsa AAAAB3....XE= kali@kali" > /root/.ssh/authorized_keys' >> /opt/

Recovering the Server

Alex has mentioned that the binary was called fixutil, which was in the her home directory. Running strings /home/alex/fixutil reveals that the binary has a string of a command echo pwned | /bin/admin > /dev/null, which appears to exist on the server.

Analyzing /bin/admin with r2, strings and ldd, we see that it is calling functions that are not defined in the code which suggests that they are loaded from a shared library. Let’s search for all the files that have been modified after Jun 12 08:09, the date when fixutil was added:

find / -newer /home/alex/fixutil 2> /dev/null

We can see that /lib/x86_64-linux-gnu/ has been modified and it is used in /bin/admin. Downloading it and decompiling it using ghidra shows us all the changes it performs to infect and break the server.

Decompilation result of

Now, let’s fix them once by one and clean the server.

It appears that we have already cleaned the attacker’s SSH keys with ours, so that’s solved. Moving to the next issue, a new user with root permissions has been created. Let’s delete it with:

sed -i '$ d' /etc/passwd

Now that we have root, we don’t need the and evil cronjob, delete them with rm /opt/ /etc/cron.d/evil.

The next step is to restore the but let’s keep the evil library as we will use it for decrypting the website in step:

mv /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/
cp /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/

Decrypting the Website

We can see that the website has been encrypted using XOR encryption, and the path of the website is /usr/local/apache2/htdocs/ and the encryption key is stored in /opt/.fixutil/backup.txt. I will describe two methods, one that relies on downloading the files along with the key and decrypting them on our local machine using Python. The other method reuses the encryption function implemented in the evil logging library to decrypt them.

Decryption using Python

The Wikipedia article regarding XOR encryption had some Python code. I extended it to cover encrypting using a repeated key:

if __name__ == '__main__':
    import sys, os

    # Usage: python ./backup.txt ./htdocs/
    key_f = sys.argv[1]
    key_fp = open(key_f, 'r')
    key = key_fp.readline().strip()

    enc_dir = sys.argv[2]
    for file in os.listdir(enc_dir):
        fpath = os.path.join(enc_dir, file)
        data = open(fpath, 'rb')
        result = ""
        i = 0
        while 1:
            byte_s =
            if not byte_s:
            result += chr(ord(byte_s) ^ ord(key[i]))
            i = (i + 1) % len(key)
        out_f = open(fpath, 'w')

Then, all we’d need to do is retrieve the files, decrypt them and then reupload them:

# on local machine
mkdir htdocs
scp [email protected]:/usr/local/apache2/htdocs/* ./htdocs/
scp [email protected]:/opt/.fixutil/backup.txt .
python ./backup.txt ./htdocs/
scp ./htdocs/* [email protected]:/usr/local/apache2/htdocs/

Decrypting using

This is the coolest part of this post, we’ll use the evil’s code to recover the encrypted files! From analyzing the library, I saw that function XOREncryptWebFiles is called which then generates a random key, retrieves files in the web directory and calls XORFile to encrypt them. The header for XORFile function was:

void XORFile(char *f_path,char *encryption_key);

We can now create our program that uses that function like this:

extern void XORFile(char *f_path,char *encryption_key);
void main(int argc, char** argv){ XORFile(argv[1],argv[2]);}

We can compile it with:

gcc -c xor.c
gcc -o xor xor.o -L/lib/x86_64-linux-gnu/ -lloggingevil

Then, all we need to do is retrieve the encryption key and execute our program for every file in the htdocs directory:

key=$(head -n 1 /opt/.fixutil/backup.txt); for f in  /usr/local/apache2/htdocs/* ; do /tmp/xor "$f" "$key"; done

Tada! The files are now decrypted! Hope you have enjoyed this post 🙂


# 0
ssh-copy-id [email protected] # madeline
ssh [email protected] -f 'mv .bashrc .bashrc_bk; exit'

# 1 and 3
ssh [email protected]
printf '#!/bin/bash\n' > /opt/
echo 'echo "ssh-rsa AAAAB3....XE= kali@kali" > /root/.ssh/authorized_keys' >> /opt/

# wait for around a minute
ssh [email protected]

# 2
mv /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/
cp /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/

# 4
sed -i '$ d' /etc/passwd

# 5
cd /tmp/
(echo 'extern void  XORFile(char *f_path,char *encryption_key);'; echo 'void main(int argc, char** argv){XORFile(argv[1], argv[2]);}') > xor.c
gcc -c xor.c
gcc -o xor xor.o -L/lib/x86_64-linux-gnu/ -lloggingevil
key=$(head -n 1 /opt/.fixutil/backup.txt); for f in  /usr/local/apache2/htdocs/* ; do /tmp/xor "$f" "$key"; done

# extra
rm /opt/ /etc/cron.d/evil
rm /lib/x86_64-linux-gnu/