Challenge Description
Recon / Initial Analysis
int main(int argc, char **argv)
{
FILE *f = fopen(argv[1], "rb");
fseek(f, 0, SEEK_END);
int size = ftell(f);
rewind(f);
unsigned char *buf = malloc(size);
fread(buf, 1, size, f);
fclose(f);
for (int i = 0; i < size; i += 16)
{
unsigned char chunk[16] = {0};
char ipv6[64];
int remain = size - i;
memcpy(chunk, buf + i, remain >= 16 ? 16 : remain);
inet_ntop(AF_INET6, chunk, ipv6, sizeof(ipv6));
printf("\"%s\",\n", ipv6);
}
}
We can see here that they used a different type of obfuscation, transforming the bytes in IPV6 addresses.
The function that they used is getting bytes (16) then tranforming it to an IPV6 address which is printed out. Documentation
char *ipv6_data[] = {
"4d5a:9000:300:0:400:0:ffff:0",
"b800::4000:0:0:0",
"::",
"::128.0.0.0",
"e1f:ba0e:b4:9cd:21b8:14c:cd21:5468",
"6973:2070:726f:6772:616d:2063:616e:6e6f",
"7420:6265:2072:756e:2069:6e20:444f:5320",
"6d6f:6465:2e0d:d0a:2400::",
"5045:0:6486:a00:b69f:ac69::",
"::f000:2e02:b02:226:6c:0",
"96:0:c:0:f014:0:10:0",
"0:40:100:0:10:0:2:0",
"400::500:200:0:0",
....
"::",
"::",
"::",
"::",
"::",
"::",
"::",
"::",
};
int ipv6_count = 2464;
Solution
The solve is using the reverse process of it to transform those IP addresses to raw network structures which for us are bytes to an exe.
I found the same function and the reverse of it in this site where I got the script from.
{
"list": [
"4d5a:9000:300:0:400:0:ffff:0",
"b800::4000:0:0:0",
"::",
"::128.0.0.0",
"e1f:ba0e:b4:9cd:21b8:14c:cd21:5468",
"6973:2070:726f:6772:616d:2063:616e:6e6f",
....
"::",
"::",
"::",
"::",
"::",
"::"
],
"ipv6_count": 2464
}
import socket
import json
f = open("data.json")
DATA = json.loads(f.read())
f.close()
FAMILY = socket.AF_INET6
BinaryData = b""
for IPV6 in DATA["list"]:
IP_Binary = socket.inet_pton(FAMILY, IPV6)
BinaryData = BinaryData + IP_Binary
with open("file.exe", "wb") as f:
f.write(BinaryData)
This resulted an .exe file that I uploaded to dogbolt.org to decompile it. I first tried with ghidra which transformed the string to char vector but then saw that the binary ninja's version is cleaner.
int64_t sub_1400079e0()
{
sub_140001650();
int64_t var_78;
__builtin_strncpy(&var_78, "Alpaca{ipv6_obfuscation_can_evade_signature}", 0x2d);
sub_140001550("Input flag: ", 0x646176655f6e6163);
char _Buffer[0x40];
fgets(&_Buffer, 0x40, data_140008090(0));
int64_t rax_1 = 0;
while (true)
{
uint64_t rdx_1 = _Buffer[rax_1];
rax_1 += 1;
void var_79;
if (rdx_1 != *(&var_79 + rax_1))
{
sub_140001550("Wrong\n", rdx_1);
break;
}
if (rax_1 == 0x2c)
{
sub_140001550("Correct!\n", rdx_1);
break;
}
}
return 0;
}
Flag
Alpaca{ipv6_obfuscation_can_evade_signature}