Cryptohorrific
3 minutes to read
We are given an iOS app. Inside the app, there is a file called challenge.plist that is unreadable.
Solution
Using a tool called plistutil, it is possible to extract the information in XML format:
$ plistutil -i hackthebox.app/challenge.plist -o challenge.plist.xml
$ cat challenge.plist.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>flag</key>
<string>Tq+CWzQS0wYzs2rJ+GNrPLP6qekDbwze6fIeRRwBK2WXHOhba7WR2OGNUFKoAvyW7njTCMlQzlwIRdJvaP2iYQ==</string>
<key>id</key>
<string>123</string>
<key>title</key>
<string>HackTheBoxIsCool</string>
</dict>
</array>
</plist>
The flag is not just encoded in Base64, but also encrypted:
$ echo Tq+CWzQS0wYzs2rJ+GNrPLP6qekDbwze6fIeRRwBK2WXHOhba7WR2OGNUFKoAvyW7njTCMlQzlwIRdJvaP2iYQ== | base64 -d | xxd
00000000: 4eaf 825b 3412 d306 33b3 6ac9 f863 6b3c N..[4...3.j..ck<
00000010: b3fa a9e9 036f 0cde e9f2 1e45 1c01 2b65 .....o.....E..+e
00000020: 971c e85b 6bb5 91d8 e18d 5052 a802 fc96 ...[k.....PR....
00000030: ee78 d308 c950 ce5c 0845 d26f 68fd a261 .x...P.\.E.oh..a
Using Ghidra we can analyze the content of the app (called hackthebox, which is a MacOS X Mach-O binary). There is a class called ViewController that has a function SecretManager:key:iv:data::
ID ViewController::SecretManager:key:iv:data:(ID param_1, SEL param_2, unsigned int param_3, ID param_4, ID param_5, ID param_6) {
CCOperation op;
CCCryptorStatus CVar1;
undefined8 uVar2;
void *dataIn;
ID IVar3;
size_t local_b0;
void *local_a8;
size_t local_a0;
size_t local_98;
undefined8 local_90;
long local_88;
undefined8 local_80;
unsigned int local_74;
SEL local_70;
ID local_68;
undefined8 local_60;
undefined local_58[32];
undefined local_38[24];
long local_20;
local_20 = *(long *) __got::___stack_chk_guard;
local_80 = 0;
local_74 = param_3;
local_70 = param_2;
local_68 = param_1;
__stubs::_objc_storeStrong(&local_80, param_4);
local_88 = 0;
__stubs::_objc_storeStrong(&local_88, param_5);
local_90 = 0;
__stubs::_objc_storeStrong(&local_90);
__stubs::_memset(local_38, 0, 0x11);
__stubs::_objc_msgSend(local_80, "getCString:maxLength:encoding:", local_38, 0x11, 4);
__stubs::_memset(local_58, 0, 0x11);
if (local_88 != 0) {
__stubs::_objc_msgSend(local_88, "getCString:maxLength:encoding:", local_58, 0x11, 4);
}
local_98 = __stubs::_objc_msgSend(local_90, "length");
local_a0 = local_98 + 0x10;
local_a8 = __stubs::_malloc(local_a0);
op = local_74;
local_b0 = 0;
uVar2 = __stubs::_objc_retainAutorelease(local_90);
dataIn = (void *) __stubs::_objc_msgSend(uVar2);
CVar1 = __stubs::_CCCrypt(op, 0, 3, local_38, 0x10, local_58, dataIn, local_98, local_a8, local_a0, &local_b0);
if (CVar1 == 0) {
uVar2 = __stubs::_objc_msgSend(&_OBJC_CLASS_$_NSData, "dataWithBytesNoCopy:length:", local_a8, local_b0);
local_60 = __stubs::_objc_retainAutoreleasedReturnValue(uVar2);
} else {
__stubs::_free(local_a8);
local_60 = 0;
}
__stubs::_objc_storeStrong(&local_90, 0);
__stubs::_objc_storeStrong(&local_88, 0);
__stubs::_objc_storeStrong(&local_80, 0);
IVar3 = __stubs::_objc_autoreleaseReturnValue(local_60);
if (*(long *) __got::___stack_chk_guard == local_20) {
return IVar3;
}
/* WARNING: Subroutine does not return */
__stubs::___stack_chk_fail();
}
It uses _CCCrypt to decrypt some ciphertext. According to developer.apple.com, this function is employed for symmetric ciphers such as AES.
The above function is called by viewDidLoad:
void ViewController::viewDidLoad(ID param_1, SEL param_2) {
undefined8 uVar1;
undefined8 uVar2;
undefined8 uVar3;
undefined8 uVar4;
undefined8 uVar5;
undefined8 uVar6;
undefined8 uVar7;
undefined8 uVar8;
ID IVar9;
ID IVar10;
ID local_28;
class_t *local_20;
SEL local_18;
ID local_10;
local_20 = &objc::class_t::ViewController;
local_28 = param_1;
local_18 = param_2;
local_10 = param_1;
__stubs::_objc_msgSendSuper2(&local_28, "viewDidLoad");
uVar1 = *(undefined8 *) (local_10 + l);
uVar2 = __stubs::_objc_msgSend(&_OBJC_CLASS_$_NSString, "alloc");
IVar10 = local_10;
uVar3 = __stubs::_objc_msgSend(&_OBJC_CLASS_$_NSData, "alloc");
uVar4 = __stubs::_objc_msgSend(&_OBJC_CLASS_$_NSArray, "alloc");
uVar5 = __stubs::_objc_msgSend(&_OBJC_CLASS_$_NSBundle, "mainBundle");
uVar5 = __stubs::_objc_retainAutoreleasedReturnValue(uVar5);
uVar6 = __stubs::_objc_msgSend(uVar5, "pathForResource:ofType:", &cf_challenge, &cf_plist);
uVar6 = __stubs::_objc_retainAutoreleasedReturnValue(uVar6);
uVar4 = __stubs::_objc_msgSend(uVar4, "initWithContentsOfFile:", uVar6);
uVar7 = __stubs::_objc_msgSend(uVar4, "objectAtIndex:", 0);
uVar7 = __stubs::_objc_retainAutoreleasedReturnValue(uVar7);
uVar8 = __stubs::_objc_msgSend(uVar7, "objectForKey:", &cf_flag);
uVar8 = __stubs::_objc_retainAutoreleasedReturnValue(uVar8);
IVar9 = __stubs::_objc_msgSend(uVar3, "initWithBase64EncodedString:options:", uVar8, 0);
IVar10 = SecretManager:key:iv:data:(IVar10, (SEL) "SecretManager:key:iv:data:", 1, (ID) "!A%D*G-KaPdSgVkY", (ID) "QfTjWnZq4t7w!z%C", IVar9);
uVar3 = __stubs::_objc_retainAutoreleasedReturnValue(IVar10);
uVar2 = __stubs::_objc_msgSend(uVar2, "initWithData:encoding:", uVar3, 4);
__stubs::_objc_msgSend(uVar1, "setText:", uVar2);
__stubs::_objc_release(uVar2);
__stubs::_objc_release(uVar3);
__stubs::_objc_release(IVar9);
__stubs::_objc_release(uVar8);
__stubs::_objc_release(uVar7);
__stubs::_objc_release(uVar4);
__stubs::_objc_release(uVar6);
__stubs::_objc_release(uVar5);
return;
}
Here we see that the function takes the encrypted flag from challenge.plist and tries to decrypt the content using !A%D*G-KaPdSgVkY as key and QfTjWnZq4t7w!z%C as IV.
Flag
Despite having an IV, the decryption is successful using AES ECB, which does not use IV. The flag can be captured as follows:
$ python3 -q
>>> from Crypto.Cipher import AES
>>> from Crypto.Util.Padding import unpad
>>> from base64 import b64decode
>>> key = b'!A%D*G-KaPdSgVkY'
>>> ct = b64decode('Tq+CWzQS0wYzs2rJ+GNrPLP6qekDbwze6fIeRRwBK2WXHOhba7WR2OGNUFKoAvyW7njTCMlQzlwIRdJvaP2iYQ==')
>>> cipher = AES.new(key, AES.MODE_ECB)
>>> unpad(cipher.decrypt(ct), 16)
b'HTB{%SoC00l_H4ckTh3b0xbyBs3cur31stCh4ll3ng3!!Cr4zY%}'