2023 Cyber Apocalypse: CSHells

Challenge Information

AttributeDetails
Event2023 Cyber Apocalypse
CategoryReverse
ChallengeCSHells

Summary

This challenge requires reverse engineering a custom shell binary to understand its command structure and password validation mechanism. The binary uses XOR encryption to protect a password, and recovering it requires analyzing the encryption/decryption logic and extracting the encrypted values from the binary.


Analysis

The binary implements a custom shell with limited commands:

  • ls [directory] - list files
  • whoami - print current user
  • cat [files...] - print file contents
  • getflag - requires password (admin only)
  • help - show help

Key function analysis:

The func_flag() function implements password verification:

// Read password input
fgets((char *)&local_118, 0x100, stdin);
// XOR with m1 array
for (local_c = 0; local_c < 0x4d; local_c = local_c + 1) {
*(byte *)((long)&local_118 + (long)(int)local_c) =
*(byte *)((long)&local_118 + (long)(int)local_c) ^ m1[(int)local_c];
}
// Compare with t array
local_14 = memcmp(&local_118, t, 0x4d);
if (local_14 == 0) {
// XOR with m2 to get flag
for (local_10 = 0; local_10 < 0x4d; local_10 = local_10 + 1) {
*(byte *)((long)&local_118 + (long)(int)local_10) =
*(byte *)((long)&local_118 + (long)(int)local_10) ^ m2[(int)local_10];
}
printf("Flag: %s\n", &local_118);
}

Vulnerability: The password verification logic uses XOR with known arrays. By XORing the t array with m1 array, we can recover the password.


Solution

Extract the binary data using objdump:

Terminal window
objdump -M intel -d -j .rodata ./shell > shell_disassembly.txt

Find the m1, m2, and t arrays in the binary. Then apply the XOR operation:

m1 = bytearray.fromhex("6e3fc3b9d78d1558e50ffbac224d57dbdfcfedfc1c846ad81ca617c4c1bfa08587a143d4584f8da8b2f27ca3b98637dabf070a7e73df5c60aecacfb9e0deff0070b9e45fc89ab351f5aea87e8d00000000")
m2 = bytearray.fromhex("641ef5e2c097441bf85ff9be185d488e91e4f6f15c8d269e2ba102f7c6f7e4b398fe57ed4a4bd1f6a1eb09c699f258facb6f6f5e1fbe2b138ea5a99993ab8f701cc0c43ea6fe933590c3c910e950617373776f72643a2000466c61673a2025730a0025733a2025730a00")
t = bytearray.fromhex("2c4ab799a3e57078936e97d9476d38bdffbb85996fe14aab74c37ba8b29fd7ecebcd63b23923e184929609c699f258facb6f6f5e1fbe2b138ea5a99993ab8f701cc0c43ea6fe933590c3c910e950617373776f72643a2000466c61673a2025730a0025733a2025730a00")
# Recover password: XOR t with m1
password = bytearray(0x4d)
for i in range(len(password)):
password[i] = t[i] ^ m1[i]
print(password.decode('utf-8'))

This reveals the password which can then be used to execute the getflag command and obtain the flag.


Key Takeaways

  • XOR is a symmetric operation: a ⊕ b ⊕ b = a
  • Static data in binaries can be extracted and analyzed
  • Password verification logic must be cryptographically sound
  • Known ciphertext + known key = trivial plaintext recovery
  • Binary analysis tools (Ghidra, objdump) are essential
  • Understanding encryption/decryption is crucial for reverse engineering