We're given an ARM binary. Decompiling it with Ghidra (and adding some names) gives us the following:
undefined4 FUN_000109ec(void)
{
int iVar1;
int __c;
FILE *__stream;
char password [100];
char filename [100];
iVar1 = __stack_chk_guard;
setvbuf(stdout,(char *)0x0,2,0);
while( true ) {
while( true ) {
get_flag_name(filename);
puts("Hi there! I\'ve been waiting for your password!");
gets(password);
__c = check_password(password);
if (__c == 0) break;
report_error(1,0);
}
__stream = fopen(filename,"rb");
if (__stream != (FILE *)0x0) break;
report_error(2,filename);
}
while (__c = _IO_getc((_IO_FILE *)__stream), __c != -1) {
putchar(__c);
}
fclose(__stream);
if (iVar1 == __stack_chk_guard) {
return 0;
}
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
It looks like what we have to do is reverse the check_password
subroutine.
undefined4 check_password(byte *pbParm1)
{
size_t sVar1;
undefined4 uVar2;
sVar1 = strlen((char *)pbParm1);
if (sVar1 < 0x10) {
uVar2 = 1;
}
else {
if (((((*pbParm1 == 0x76) && ((pbParm1[1] ^ *pbParm1) == 0x4e)) &&
((pbParm1[2] ^ pbParm1[1]) == 0x1e)) &&
((((pbParm1[3] ^ pbParm1[2]) == 0x15 && ((pbParm1[4] ^ pbParm1[3]) == 0x5e)) &&
(((pbParm1[5] ^ pbParm1[4]) == 0x1c &&
(((pbParm1[6] ^ pbParm1[5]) == 0x21 && ((pbParm1[7] ^ pbParm1[6]) == 1)))))))) &&
(((pbParm1[8] ^ pbParm1[7]) == 0x34 &&
((((((pbParm1[9] ^ pbParm1[8]) == 7 && ((pbParm1[10] ^ pbParm1[9]) == 0x35)) &&
((pbParm1[0xb] ^ pbParm1[10]) == 0x11)) &&
(((pbParm1[0xc] ^ pbParm1[0xb]) == 0x37 && ((pbParm1[0xd] ^ pbParm1[0xc]) == 0x3c)))) &&
(((pbParm1[0xe] ^ pbParm1[0xd]) == 0x72 && ((pbParm1[0xf] ^ pbParm1[0xe]) == 0x47)))))))) {
uVar2 = 0;
}
else {
uVar2 = 2;
}
}
return uVar2;
}
Looks pretty straightforward:
>>> x = '\x76'
>>> x += chr(ord(x[-1]) ^ 0x4e)
>>> x += chr(ord(x[-1]) ^ 0x1e)
>>> x += chr(ord(x[-1]) ^ 0x15)
>>> x += chr(ord(x[-1]) ^ 0x5e)
>>> x += chr(ord(x[-1]) ^ 0x1c)
>>> x += chr(ord(x[-1]) ^ 0x21)
>>> x += chr(ord(x[-1]) ^ 0x1)
>>> x += chr(ord(x[-1]) ^ 0x34)
>>> x += chr(ord(x[-1]) ^ 0x7)
>>> x += chr(ord(x[-1]) ^ 0x35)
>>> x += chr(ord(x[-1]) ^ 0x11)
>>> x += chr(ord(x[-1]) ^ 0x37)
>>> x += chr(ord(x[-1]) ^ 0x3c)
>>> x += chr(ord(x[-1]) ^ 0x72)
>>> x += chr(ord(x[-1]) ^ 0x47)
>>> x
'v8&3mqPQebWFqM?x'
Let's try the password:
$ nc warm.q.2019.volgactf.ru 443
Hi there! I've been waiting for your password!
v8&3mqPQebWFqM?x
Seek file with something more sacred!
Hmm, the file doesn't actually contain a flag! Let's recall the first subroutine analyzed:
char password [100];
char filename [100];
It's easy to overflow the password
buffer and write arbitrary content into filename
. "Something sacred", you said?
$ nc warm.q.2019.volgactf.ru 443
Hi there! I've been waiting for your password!
v8&3mqPQebWFqM?xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaasacred
VolgaCTF{1_h0pe_ur_wARM_up_a_1ittle}