Link to challenge: https://academy.hacking-lab.com
Date Completed: 19 December 2019
Challenge
HV19.18 Dance with me
1 2 3 4 5 6 7 8 |
Introduction Santa had some fun and created todays present with a special dance. this is what he made up for you: 096CD446EBC8E04D2FDE299BE44F322863F7A37C18763554EEE4C99C3FAD15 Dance with him to recover the flag. Resources HV19-dance.zip |
Resource mirror: HV19-dance.zip
Solution
In our zip file we get a dance binary that we discover is an arm binary. After some digging around we find out that it is in fact a DEB and written for iOS. We attempt to run the code in an emulator like QEMU but unfortunately don’t have much luck with getting the emulator to work. Instead we rely on static analysis.
We use IDA’s ARM decompiler to give us the Objective C code belonging to the binary. As I didn’t have the required IDA plugin I go this from BlindHero (thanks!).
This was the code:
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
#include <cstring> #include <cstdio> typedef long long int __int64; typedef long int _DWORD; typedef unsigned long int uint32; typedef unsigned int uint; typedef unsigned char _BYTE; typedef unsigned char __int8; template<class T> T __ROL__(T value, int count) { const uint nbits = sizeof(T) * 8; if ( count > 0 ) { count %= nbits; T high = value >> (nbits - count); if ( T(-1) < 0 ) // signed value high &= ~((T(-1) << count)); value <<= count; value |= high; } else { count = -count % nbits; T low = value << (nbits - count); value >>= count; value |= low; } return value; } inline uint32 __ROR4__(uint32 value, int count) { return __ROL__((uint32)value, -count); } __int64 dance_words(__int64 result, __int64 a2) { __int64 v2; // x8 int v3; // w6 int v4; // w7 int v5; // w8 int v6; // w14 int v7; // w16 int v8; // w17 int v9; // w3 int v10; // w5 int v11; // w12 int v12; // w13 int v13; // w2 int v14; // w15 int v15; // w4 int v16; // w19 signed int v17; // w11 int v18; // w9 int v19; // w10 int v20; // w17 int v21; // w5 int v22; // w7 int v23; // w14 int v24; // w3 int v25; // w6 int v26; // w8 int v27; // w16 int v28; // w19 int v29; // w10 int v30; // w13 int v31; // w2 int v32; // w9 int v33; // w12 int v34; // w15 int v35; // w4 __int64 v36; // x11 int v37; // [xsp+8h] [xbp-58h] int v38; // [xsp+Ch] [xbp-54h] int v39; // [xsp+10h] [xbp-50h] int v40; // [xsp+14h] [xbp-4Ch] int v41; // [xsp+18h] [xbp-48h] int v42; // [xsp+1Ch] [xbp-44h] int v43; // [xsp+20h] [xbp-40h] int v44; // [xsp+24h] [xbp-3Ch] int v45; // [xsp+28h] [xbp-38h] int v46; // [xsp+2Ch] [xbp-34h] int v47; // [xsp+30h] [xbp-30h] int v48; // [xsp+34h] [xbp-2Ch] int v49; // [xsp+38h] [xbp-28h] int v50; // [xsp+3Ch] [xbp-24h] int v51; // [xsp+40h] [xbp-20h] int v52; // [xsp+44h] [xbp-1Ch] v2 = 0LL; do { *(&v37 + 4 * ((unsigned int)v2 >> 2) + (v2 & 3)) = *(_DWORD *)(a2 + 4 * v2); ++v2; } while ( v2 != 16 ); v4 = v49; v3 = v50; v6 = v37; v5 = v38; v8 = v41; v7 = v42; v10 = v45; v9 = v46; v12 = v43; v11 = v44; v13 = v47; v14 = v48; v16 = v51; v15 = v52; v17 = 10; v19 = v39; v18 = v40; do { v20 = v8 ^ __ROR4__(v6 + v4, 25); v21 = v10 ^ __ROR4__(v20 + v6, 23); v22 = v4 ^ __ROR4__(v21 + v20, 19); v23 = v6 ^ __ROR4__(v22 + v21, 14); v24 = v9 ^ __ROR4__(v7 + v5, 25); v25 = v3 ^ __ROR4__(v24 + v7, 23); v26 = v5 ^ __ROR4__(v25 + v24, 19); v27 = v7 ^ __ROR4__(v26 + v25, 14); v28 = v16 ^ __ROR4__(v13 + v12, 25); v29 = v19 ^ __ROR4__(v28 + v13, 23); v30 = v12 ^ __ROR4__(v29 + v28, 19); v31 = v13 ^ __ROR4__(v30 + v29, 14); v32 = v18 ^ __ROR4__(v15 + v14, 25); v33 = v11 ^ __ROR4__(v32 + v15, 23); v34 = v14 ^ __ROR4__(v33 + v32, 19); v35 = v15 ^ __ROR4__(v34 + v33, 14); v5 = v26 ^ __ROR4__(v32 + v23, 25); v19 = v29 ^ __ROR4__(v5 + v23, 23); v18 = v32 ^ __ROR4__(v19 + v5, 19); v6 = v23 ^ __ROR4__(v18 + v19, 14); v12 = v30 ^ __ROR4__(v27 + v20, 25); v11 = v33 ^ __ROR4__(v12 + v27, 23); v8 = v20 ^ __ROR4__(v11 + v12, 19); v7 = v27 ^ __ROR4__(v8 + v11, 14); v14 = v34 ^ __ROR4__(v31 + v24, 25); v10 = v21 ^ __ROR4__(v14 + v31, 23); v9 = v24 ^ __ROR4__(v10 + v14, 19); v13 = v31 ^ __ROR4__(v9 + v10, 14); v4 = v22 ^ __ROR4__(v35 + v28, 25); v3 = v25 ^ __ROR4__(v4 + v35, 23); v16 = v28 ^ __ROR4__(v3 + v4, 19); v15 = v35 ^ __ROR4__(v16 + v3, 14); --v17; } while ( v17 ); v36 = 0LL; v49 = v4; v50 = v3; v37 = v6; v38 = v5; v41 = v8; v42 = v7; v45 = v10; v46 = v9; v43 = v12; v44 = v11; v47 = v13; v48 = v14; v51 = v16; v52 = v15; v39 = v19; v40 = v18; do { *(_DWORD *)(result + 4 * v36) = *(_DWORD *)(a2 + 4 * v36) + *(&v37 + 4 * ((unsigned int)v36 >> 2) + (v36 & 3)); ++v36; } while ( v36 != 16 ); return result; } __int64 dance_block(__int64 a1, int *a2, __int64 a3, __int64 a4) { __int64 v4; // x19 __int64 result; // x0 int v6; // w8 __int64 v7; // x9 char v8[64]; // [xsp+8h] [xbp-98h] int v9; // [xsp+48h] [xbp-58h] int v10; // [xsp+4Ch] [xbp-54h] int v11; // [xsp+50h] [xbp-50h] int v12; // [xsp+54h] [xbp-4Ch] int v13; // [xsp+58h] [xbp-48h] int v14; // [xsp+5Ch] [xbp-44h] __int64 v15; // [xsp+60h] [xbp-40h] __int64 v16; // [xsp+68h] [xbp-38h] int v17; // [xsp+70h] [xbp-30h] int v18; // [xsp+74h] [xbp-2Ch] int v19; // [xsp+78h] [xbp-28h] int v20; // [xsp+7Ch] [xbp-24h] int v21; // [xsp+80h] [xbp-20h] int v22; // [xsp+84h] [xbp-1Ch] v4 = a1; v9 = 1634760805; v10 = *a2; v11 = a2[1]; v12 = a2[2]; v13 = a2[3]; v14 = 857760878; v15 = a3; v16 = a4; v17 = 2036477234; v18 = a2[4]; v19 = a2[5]; v20 = a2[6]; v21 = a2[7]; v22 = 1797285236; result = dance_words((__int64)v8, (__int64)&v9); v6 = 0; v7 = 0LL; do { *(_BYTE *)(v4 + v7) = *(_DWORD *)&v8[4 * ((unsigned int)v7 >> 2)] >> (v6 & 0x18); ++v7; v6 += 8; } while ( v7 != 64 ); return result; } __int64 dance(__int64 result, __int64 length, __int64 a3, __int64 a4) { __int64 v4; // x19 int *v5; // x20 __int64 v6; // x21 __int64 v7; // x22 __int64 v8; // x23 char v9[64]; // [xsp+8h] [xbp-88h] if ( length ) { v4 = a4; v5 = (int *)a3; v6 = length; v7 = result; v8 = 0LL; do { if ( !(v8 & 0x3F) ) result = dance_block((__int64)v9, v5, v4, (unsigned int)v8 >> 6); *(_BYTE *)(v7 + v8) ^= v9[v8 & 0x3F]; ++v8; } while ( v6 != v8 ); } return result; } unsigned char unk_100007F50[] = { 0x03, 0x20, 0x63, 0x46, 0x61, 0xB6, 0x3C, 0xAF, 0xAA, 0x76, 0xC2, 0x7E, 0xEA, 0x00, 0xB5, 0x98 }; unsigned char unk_100007F80[] = { 0xAD, 0xF1, 0x6C, 0x63, 0x45, 0x6A, 0x6E, 0xF1, 0xED, 0x90, 0x79, 0x46, 0x9D, 0xA2, 0xA0, 0xB5 }; unsigned char unk_100007F60[] = { 0xFB, 0x2F, 0x70, 0x97, 0x21, 0x4F, 0xD0, 0x4C, 0xB2, 0x57, 0xAC, 0x29, 0x04, 0xEF, 0xEE, 0x46 }; unsigned char unk_100007F70[] = { 0x79, 0x73, 0x0F, 0xF4, 0xEC, 0x0C, 0x40, 0x6B, 0xFD, 0x91, 0xC9, 0x1F, 0xE7, 0x04, 0x00, 0xA8 }; int main(int argc, const char **argv, const char **envp) { size_t input_length; // x19 size_t v4; // x21 int result; // w0 char v6; // [xsp+10h] [xbp-D0h] unsigned char v7[32]; // [xsp+30h] [xbp-B0h] __int128 v8; // [xsp+40h] [xbp-A0h] __int128 v9; // [xsp+50h] [xbp-90h] __int128 v10; // [xsp+60h] [xbp-80h] unsigned char* v11; // [xsp+70h] [xbp-70h] unsigned char* v12; // [xsp+80h] [xbp-60h] unsigned char* v13; // [xsp+90h] [xbp-50h] unsigned char* v14; // [xsp+A0h] [xbp-40h] __int64 v15; // [xsp+B8h] [xbp-28h] v13 = unk_100007F70; v14 = unk_100007F80; v11 = unk_100007F50; v12 = unk_100007F60; v9 = 0u; v10 = 0u; // v7 = 0u; v8 = 0u; printf("Input your flag: ", argv, envp); fgets(&v6, 32, stdin); input_length = strlen(&v6); if ( input_length ) memcpy(&v7, &v6, input_length); dance((__int64)&v7, input_length, (__int64)&v11, -5678246756302764783LL); if ( strlen(&v6) ) { v4 = 0LL; do printf("%02X", *((unsigned __int8 *)&v7 + v4++)); while ( strlen(&v6) > v4 ); } result = putchar(10); return result; } |
It takes a while to analyse this code to learn what cipher is actually being used.
Through many clues we learn its a stream cipher and the appropriately named Salsa20 cipher.
At this point its reasonable to assume we need to decrypt our encrypted flag
096CD446EBC8E04D2FDE299BE44F322863F7A37C18763554EEE4C99C3FAD15 which is just hex data using the Salsa20 algorithm and some key and nonce. We are looking for a 128 bit or 256 bit key and 64 bit nonce. We can see four arguments passed to the dance method. The first is the result and the second is simply the input length used to check against loop guards. The last two arguments are our key and nonce. We take the longer length argument
&v11 as our nonce.
We use IDA to easily fetch the key as raw data:
1 |
\x03\x20\x63\x46\x61\xB6\x3C\xAF\xAA\x76\xC2\x7E\xEA\x00\xB5\x9B\xFB\x2F\x70\x97\x21\x4F\xD0\x4C\xB2\x57\xAC\x29\x04\xEF\xEE\x46\x79\x73\x0F\xF4\xEC\x0C\x40\x6B\xFD\x91\xC9\x1F\xE7\x04\x00\xA8\xAD\xF1\x6C\x63\x45\x6A\x5E\xF1\xED\x9D\x79\x46\x9D\xA2\xA0\xB5 |
As it turns out only the upper (first) 32 bytes of this key are used.
Our nonce is the number
-5678246756302764783 which we convert to hex as
\xB1\x32\xD0\xA8\xE7\x8F\x45\x11. However, we are using little endian ordering so we reverse the order of bytes to
\x11\x45\x8F\xE7\xA8\xD0\x32\xB1.
Finally we can write a python script to decode our Salsa20 encoded string. We simply use a template from online:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Hackvent 2019 - Day 18 # Mo Beigi (https://mobeigi.com) from Crypto.Cipher import Salsa20 key = b'\x03\x20\x63\x46\x61\xB6\x3C\xAF\xAA\x76\xC2\x7E\xEA\x00\xB5\x9B\xFB\x2F\x70\x97\x21\x4F\xD0\x4C\xB2\x57\xAC\x29\x04\xEF\xEE\x46' nonce = b'\x11\x45\x8F\xE7\xA8\xD0\x32\xB1' ciphertext = b'\x09\x6C\xD4\x46\xEB\xC8\xE0\x4D\x2F\xDE\x29\x9B\xE4\x4F\x32\x28\x63\xF7\xA3\x7C\x18\x76\x35\x54\xEE\xE4\xC9\x9C\x3F\xAD\x15' cipher = Salsa20.new(key=key, nonce=nonce) plaintext = cipher.decrypt(ciphertext) print(plaintext) |
Running this prints out our daily flag!
Flag: HV19{Danc1ng_Salsa_in_ass3mbly}