Artemis wants a copy of Windows, but she doesn’t feel like paying for it. She decided to hack Microsoft’s servers to generate a product key, and found their verification software, which runs on Linux for some reason. Can you get her a working product key (form ABCD-EFGH-IIJK-LMNO-PQRS-TUVW, each uppercase letter is a digit) using the email [email protected] and name Artemis Tosini?
Let’s start with analyzing the file.
1 2 |
$ file activate activate: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=bc88e8a6a08c19cde1069b1c70954502d2487c00, not stripped |
It is an 64-bit ELF exeuctable file which is dynamically linked and not stripped. Let’s decompile it using IDA Pro.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
int __cdecl main(int argc, const char **argv, const char **envp) { char s[48]; // [sp+10h] [bp-90h]@1 char v6[48]; // [sp+40h] [bp-60h]@1 char v7[40]; // [sp+70h] [bp-30h]@1 __int64 v8; // [sp+98h] [bp-8h]@1 v8 = *MK_FP(__FS__, 40LL); printf("Name: ", argv, argv); fflush(_bss_start); fgets(s, 33, stdin); s[strcspn(s, "\n")] = 0; printf("Email: ", "\n"); fflush(_bss_start); fgets(v6, 33, stdin); v6[strcspn(v6, "\n")] = 0; printf("Product key: ", "\n"); fflush(_bss_start); fgets(v7, 33, stdin); v7[strcspn(v7, "\n")] = 0; if ( (unsigned __int8)verify_key((__int64)s, (__int64)v6, v7) ) { puts("Windows has been activated"); result = 0; } else { puts("Invalid product key"); result = -1; } v4 = *MK_FP(__FS__, 40LL) ^ v8; return result; } |
It reads name, email, and product key from stdin respectively. Then, it calls verify_key function to check the serial key. Let’s decompile that function as well.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
__int64 __fastcall verify_key(__int64 a1, __int64 a2, char *a3) { char v9; // [sp+27h] [bp-C9h]@29 int v10; // [sp+28h] [bp-C8h]@1 int v11; // [sp+2Ch] [bp-C4h]@6 signed int i; // [sp+30h] [bp-C0h]@6 int j; // [sp+34h] [bp-BCh]@8 signed int k; // [sp+38h] [bp-B8h]@14 signed int l; // [sp+3Ch] [bp-B4h]@17 signed int m; // [sp+40h] [bp-B0h]@20 signed int n; // [sp+44h] [bp-ACh]@23 signed int ii; // [sp+48h] [bp-A8h]@26 signed int jj; // [sp+4Ch] [bp-A4h]@29 signed int v20; // [sp+50h] [bp-A0h]@7 const char *nptr; // [sp+60h] [bp-90h]@1 char *v22; // [sp+68h] [bp-88h]@7 __int64 v23; // [sp+70h] [bp-80h]@6 __int64 v24; // [sp+78h] [bp-78h]@6 int v25[8]; // [sp+80h] [bp-70h]@2 int v26; // [sp+A0h] [bp-50h]@26 int v27; // [sp+A4h] [bp-4Ch]@26 int v28; // [sp+A8h] [bp-48h]@26 int v29; // [sp+ACh] [bp-44h]@26 int v30; // [sp+B0h] [bp-40h]@26 int v31; // [sp+B4h] [bp-3Ch]@26 int v32; // [sp+C0h] [bp-30h]@26 int v33; // [sp+C4h] [bp-2Ch]@26 int v34; // [sp+C8h] [bp-28h]@26 int v35; // [sp+CCh] [bp-24h]@26 int v36; // [sp+D0h] [bp-20h]@26 int v37; // [sp+D4h] [bp-1Ch]@26 __int64 v38; // [sp+D8h] [bp-18h]@1 v38 = *MK_FP(__FS__, 40LL); v10 = 0; for ( nptr = strtok(a3, "-"); nptr; nptr = strtok(0LL, "-") ) { v3 = v10++; v25[v3] = atoi(nptr); } if ( v10 == 6 ) { v11 = 0; v23 = a2; v24 = a1; for ( i = 0; i <= 1; ++i ) { v22 = (char *)*(&v23 + i); v20 = strlen((const char *)*(&v23 + i)); if ( v20 <= 31 ) { for ( j = 0; j < 32 - v20; ++j ) *(&v22[v20] + j) = pad[(signed __int64)(v11 + j)]; v11 = 32 - v20; v22[32] = 0; } } for ( k = 0; k <= 31; ++k ) { *(_BYTE *)(k + a2) ^= 5u; *(_BYTE *)(k + a1) ^= 0xFu; } for ( l = 0; l <= 5; ++l ) { v5 = sumChars(a2, 0, 32, l + 2); v25[l] -= (signed int)((unsigned __int64)sumChars(a2, l + 1, 32, l + 2) * v5) % 10000; v6 = sumChars(a1, 0, 32, 2); v25[l] += 4 * (v6 - (unsigned __int64)sumChars(a1, 1, 32, 2)); } swapArr((__int64)v25, 3, 4); swapArr((__int64)v25, 2, 5); swapArr((__int64)v25, 1, 5); swapArr((__int64)v25, 2, 3); swapArr((__int64)v25, 0, 5); swapArr((__int64)v25, 4, 5); for ( m = 0; m <= 5; ++m ) { v25[m] += sumChars(a1, 0, 32, 1); v25[m] += sumChars(a2, 0, 32, 1); } for ( n = 0; n <= 5; ++n ) { v7 = sumChars(a2, 4 * n, 4 * n + 1, 1); v25[n] += v7 % (signed int)sumChars(a1, 4 * n + 2, 4 * n + 3, 1); } v26 = 2; v27 = 4; v28 = 6; v29 = 8; v30 = 7; v31 = 5; v32 = sumChars(a2, 0, 10, 1); v33 = sumChars(a2, 10, 25, 1); v34 = sumChars(a2, 25, 32, 1); v35 = sumChars(a1, 0, 13, 1); v36 = sumChars(a1, 13, 20, 1); v37 = sumChars(a1, 20, 32, 1); for ( ii = 0; ii <= 5; ++ii ) *(&v32 + ii) = *(&v32 + ii) * *(&v26 + ii) % 10000; v9 = 1; for ( jj = 0; jj <= 5; ++jj ) { if ( v25[jj] != *(&v32 + jj) ) v9 = 0; } result = (unsigned __int8)v9; } else { result = 0LL; } v8 = *MK_FP(__FS__, 40LL) ^ v38; return result; } |
First, it splits the serial key using ‘-‘ as delimeter and it converts each part to integer. Then, it iterates through the email and adds padding from the pad array until its length is 32. Then, it does the same thing to the given name. However, instead of padding from the start of the pad array, it continues from where it left off.
After padding, it xors each byte of the name with 15 and it xors each byte of the email with 5.
Next, it calculates 6 numbers making summations of email and name arrays. Then, it multiplies the first value with 2, second value with 4, third value with 6, forth value with 8, fifth value with 7, and sixth value with 5. It also takes modulus 10000 after each multiplication and replaces the result with the original value.
Now, it has 6 numbers that are derived from email and name. Then, it starts doing some operations on the 6 numbers it derived from the serial key. These operations include value swapping and summation of arrays using the name and the email. After all these operations are completed, it compares the final 6 numbers to the 6 numbers that it found before. If they are all equal, then the serial key is valid.
Now, in order to find the serial we will first calculate the 6 numbers that it derived from email and name using the same algorithm. Then, we will apply the reverse operations the verification code applies to the serial key to these 6 values we calculated. As a result, we will have the serial key’s 6 parts as integers. Then, we can convert it to a string using ‘-‘ as the delimeter.
Here is the python script I created for the task.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#!/usr/bin/env python def swapArr(L, i, j): L[i], L[j] = L[j], L[i] pad = [0x77, 0x3C, 0x1E, 0x6B, 0x39, 0x13, 0x22, 0x0F, 0x24, 0x2, 0x73, 0x59, 0x67, 0x64, 0x21, 0x73, 0x17, 0x1E, 0x6D, 0x5B, 0x4, 0x66, 0x65, 0x51, 0x5B, 0x43, 0x57, 0x27, 0x0E, 0x6A, 0x0F, 0x6D, 0x2F, 0x1, 0x48, 0x44, 0x3B, 0x48, 0x5E, 0x80, 0x4E, 0x1F, 0x27, 0x11, 0x33, 0x46, 0x33, 0x4A, 0x25, 0x5E, 0x33, 0x32, 0x28, 0x60, 0x6E, 0x0, 0x6, 0x1F, 0x28, 0x67, 0x43, 0x7D, 0x57, 0x32] name = [ord(x) for x in "Artemis Tosini"] emailPadding = 32 - len(email) namePadding = 32 - len(name) name += pad[emailPadding:emailPadding + namePadding] email += pad[:emailPadding] for i in xrange(32): name[i] ^= 0xF; email[i] ^= 0x5; values = [0, 0, 0, 0, 0, 0] values[0] = sum(email[0:10]) values[1] = sum(email[10:25]) values[2] = sum(email[25:32]) values[3] = sum(name[0:13]) values[4] = sum(name[13:20]) values[5] = sum(name[20:32]) for i, val in enumerate([2, 4, 6, 8, 7, 5]): values[i] = values[i] * val % 10000 for i in xrange(6): values[i] -= email[4 * i] % name[4 * i + 2] for i in xrange(6): values[i] -= sum(email) values[i] -= sum(name) swapArr(values, 4, 5) swapArr(values, 0, 5) swapArr(values, 2, 3) swapArr(values, 1, 5) swapArr(values, 2, 5) swapArr(values, 3, 4) for i in xrange(6): values[i] -= 4 * (sum(name[0:32:2]) - sum(name[1:32:2])) values[i] += sum(email[i+1:32:i+2]) * sum(email[0:32:i+2]) % 10000 serial = '-'.join('{:0>4}'.format(x) for x in values) print serial |
Let’s execute the script and get the serial for Artemis.
1 2 |
$ python solve.py 3914-6104-4611-1711-1243-4699 |
Let’s check if it is the correct serial.
1 2 3 4 5 |
$ ./activate Name: Artemis Tosini Email: artemis.tosini@example.com Product key: 3914-6104-4611-1711-1243-4699 Windows has been activated |
Yes! It is the correct serial. This time the flag is not in the flag format and it is the serial itself.
So, our flag is 3914-6104-4611-1711-1243-4699.