@@ -52,370 +52,6 @@ OMG_it_took_like_LITerally_s0oO00_long_2_MAK3_md5_werrk_you_have_no_id34
52
52
Putting that into the website's prompt gives a popup with the flag:
53
53
` easyctf{OUR_3nCRYpti0n_is_N0T_br0k3n_Ur_brok3n_6c5a390d} `
54
54
55
- ### Alternate Solution
56
- ###### Writeup by rofl0r
57
- Here's the site:
58
- ![ Screenshot 1] ( https://raw.githubusercontent.com/ValarDragon/CTF-Writeups/master/2017/EasyCTF/Genius/Site-1.png )
59
- ![ Screenshot 2] ( https://raw.githubusercontent.com/ValarDragon/CTF-Writeups/master/2017/EasyCTF/Genius/Site-2.png )
60
-
61
- the hash function was available as embedded javascript snippet in the page.
62
- unrolled version:
63
-
64
- ``` js
65
- var super_secret_hash = function (s ){
66
- function L (k ,d ){return (k<< d)| (k>>> (32 - d))}
67
- function K (G ,k ){
68
- var I ,d,F ,H ,x;
69
- F = (G & 2147483648 );
70
- H = (k& 2147483648 );
71
- I = (G & 1073741824 );
72
- d= (k& 1073741824 );
73
- x= (G & 1073741823 )+ (k& 1073741823 );
74
- if (I & d){
75
- return (x^ 2147483648 ^ F ^ H )
76
- }
77
- if (I | d){
78
- if (x& 1073741824 ){
79
- return (x^ 3221225472 ^ F ^ H )
80
- } else {
81
- return (x^ 1073741824 ^ F ^ H )
82
- }
83
- } else {
84
- return (x^ F ^ H )
85
- }
86
- }
87
- function r (d ,F ,k ){
88
- return (d& F )| ((~ d)& k)
89
- }
90
- function q (d ,F ,k ){
91
- return (d& k)| (F & (~ k))
92
- }
93
- function p (d ,F ,k ){
94
- return (d^ F ^ k)
95
- }
96
- function n (d ,F ,k ){
97
- return (F ^ (d| (~ k)))
98
- }
99
- function u (G ,F ,aa ,Z ,k ,H ,I ){
100
- G = K (G ,K (K (r (F ,aa,Z ),k),I ));
101
- return K (L (G ,H ),F )
102
- }
103
- function f (G ,F ,aa ,Z ,k ,H ,I ){
104
- G = K (G ,K (K (q (F ,aa,Z ),k),I ));
105
- return K (L (G ,H ),F )
106
- }
107
- function D (G ,F ,aa ,Z ,k ,H ,I ){
108
- G = K (G ,K (K (p (F ,aa,Z ),k),I ));
109
- return K (L (G ,H ),F )
110
- }
111
- function t (G ,F ,aa ,Z ,k ,H ,I ){
112
- G = K (G ,K (K (n (F ,aa,Z ),k),I ));
113
- return K (L (G ,H ),F )
114
- }
115
- function e (G ){
116
- var Z ;
117
- var F = G .length ;
118
- var x= F + 8 ;
119
- var k= (x- (x% 64 ))/ 64 ;
120
- var I = (k+ 1 )* 16 ;
121
- var aa= Array (I - 1 );
122
- var d= 0 ;
123
- var H = 0 ;
124
- while (H < F ){
125
- Z = (H - (H % 4 ))/ 4 ;
126
- d= (H % 4 )* 8 ;
127
- aa[Z ]= (aa[Z ]| (G .charCodeAt (H )<< d));
128
- H ++
129
- }
130
- Z = (H - (H % 4 ))/ 4 ;
131
- d= (H % 4 )* 8 ;
132
- aa[Z ]= aa[Z ]| (128 << d);
133
- aa[I - 2 ]= F << 3 ;
134
- aa[I - 1 ]= F >>> 29 ;
135
- return aa
136
- }
137
- function B (x ){
138
- var k= " " ,F = " " ,G ,d;
139
- for (d= 0 ;d<= 3 ;d++ ){
140
- G = (x>>> (d* 8 ))& 255 ;
141
- F = " 0" + G .toString (16 );
142
- k= k+ F .substr (F .length - 2 ,2 )
143
- }
144
- return k
145
- }
146
- function J (k ){
147
- k= k .replace (/ rn/ g ," n" );
148
- var d= " " ;
149
- for (var F = 0 ;F < k .length ;F ++ ){
150
- var x= k .charCodeAt (F );
151
- if (x< 128 ){
152
- d+= String .fromCharCode (x)
153
- } else {
154
- if ((x> 127 )&& (x< 2048 )){
155
- d+= String .fromCharCode ((x>> 6 )| 192 );
156
- d+= String .fromCharCode ((x& 63 )| 128 )
157
- } else {
158
- d+= String .fromCharCode ((x>> 12 )| 224 );
159
- d+= String .fromCharCode (((x>> 6 )& 63 )| 128 );
160
- d+= String .fromCharCode ((x& 63 )| 128 )
161
- }
162
- }
163
- }
164
- return d
165
- }
166
- var C = Array ();
167
- var P ,h,E ,v,g,Y ,X ,W ,V ;
168
- var S = 7 ,Q = 12 ,N = 17 ,M = 22 ;
169
- var A = 5 ,z= 9 ,y= 14 ,w= 20 ;
170
- var o= 4 ,m= 11 ,l= 16 ,j= 23 ;
171
- var U = 6 ,T = 10 ,R = 15 ,O = 21 ;
172
- s= J (s);
173
- C = e (s);
174
- Y = 1732584193 ;
175
- X = 4023233417 ;
176
- W = 2562383102 ;
177
- V = 271733878 ;
178
- for (P = 0 ;P < C .length ;P += 16 ){
179
- h= Y ;
180
- E = X ;
181
- v= W ;
182
- g= V ;
183
- Y = u (Y ,X ,W ,V ,C [P + 0 ],S ,3614090360 );
184
- V = u (V ,Y ,X ,W ,C [P + 1 ],Q ,3905402710 );
185
- W = u (W ,V ,Y ,X ,C [P + 2 ],N ,606105819 );
186
- X = u (X ,W ,V ,Y ,C [P + 3 ],M ,3250441966 );
187
- Y = u (Y ,X ,W ,V ,C [P + 4 ],S ,4118548399 );
188
- V = u (V ,Y ,X ,W ,C [P + 5 ],Q ,1200080426 );
189
- W = u (W ,V ,Y ,X ,C [P + 6 ],N ,2821735955 );
190
- X = u (X ,W ,V ,Y ,C [P + 7 ],M ,4249261313 );
191
- Y = u (Y ,X ,W ,V ,C [P + 8 ],S ,1770035416 );
192
- V = u (V ,Y ,X ,W ,C [P + 9 ],Q ,2336552879 );
193
- W = u (W ,V ,Y ,X ,C [P + 10 ],N ,4294925233 );
194
- X = u (X ,W ,V ,Y ,C [P + 11 ],M ,2304563134 );
195
- Y = u (Y ,X ,W ,V ,C [P + 12 ],S ,1804603682 );
196
- V = u (V ,Y ,X ,W ,C [P + 13 ],Q ,4254626195 );
197
- W = u (W ,V ,Y ,X ,C [P + 14 ],N ,2792965006 );
198
- X = u (X ,W ,V ,Y ,C [P + 15 ],M ,1236535329 );
199
- Y = f (Y ,X ,W ,V ,C [P + 1 ],A ,4129170786 );
200
- V = f (V ,Y ,X ,W ,C [P + 6 ],z,3225465664 );
201
- W = f (W ,V ,Y ,X ,C [P + 11 ],y,643717713 );
202
- X = f (X ,W ,V ,Y ,C [P + 0 ],w,3921069994 );
203
- Y = f (Y ,X ,W ,V ,C [P + 5 ],A ,3593408605 );
204
- V = f (V ,Y ,X ,W ,C [P + 10 ],z,38016083 );
205
- W = f (W ,V ,Y ,X ,C [P + 15 ],y,3634488961 );
206
- X = f (X ,W ,V ,Y ,C [P + 4 ],w,3889429448 );
207
- Y = f (Y ,X ,W ,V ,C [P + 9 ],A ,568446438 );
208
- V = f (V ,Y ,X ,W ,C [P + 14 ],z,3275163606 );
209
- W = f (W ,V ,Y ,X ,C [P + 3 ],y,4107603335 );
210
- X = f (X ,W ,V ,Y ,C [P + 8 ],w,1163531501 );
211
- Y = f (Y ,X ,W ,V ,C [P + 13 ],A ,2850285829 );
212
- V = f (V ,Y ,X ,W ,C [P + 2 ],z,4243563512 );
213
- W = f (W ,V ,Y ,X ,C [P + 7 ],y,1735328473 );
214
- X = f (X ,W ,V ,Y ,C [P + 12 ],w,2368359562 );
215
- Y = D (Y ,X ,W ,V ,C [P + 5 ],o,4294588738 );
216
- V = D (V ,Y ,X ,W ,C [P + 8 ],m,2272392833 );
217
- W = D (W ,V ,Y ,X ,C [P + 11 ],l,1839030562 );
218
- X = D (X ,W ,V ,Y ,C [P + 14 ],j,4259657740 );
219
- Y = D (Y ,X ,W ,V ,C [P + 1 ],o,2763975236 );
220
- V = D (V ,Y ,X ,W ,C [P + 4 ],m,1272893353 );
221
- W = D (W ,V ,Y ,X ,C [P + 7 ],l,4139469664 );
222
- X = D (X ,W ,V ,Y ,C [P + 10 ],j,3200236656 );
223
- Y = D (Y ,X ,W ,V ,C [P + 13 ],o,681279174 );
224
- V = D (V ,Y ,X ,W ,C [P + 0 ],m,3936430074 );
225
- W = D (W ,V ,Y ,X ,C [P + 3 ],l,3572445317 );
226
- X = D (X ,W ,V ,Y ,C [P + 6 ],j,76029189 );
227
- Y = D (Y ,X ,W ,V ,C [P + 9 ],o,3654602809 );
228
- V = D (V ,Y ,X ,W ,C [P + 12 ],m,3873151461 );
229
- W = D (W ,V ,Y ,X ,C [P + 15 ],l,530742520 );
230
- X = D (X ,W ,V ,Y ,C [P + 2 ],j,3299628645 );
231
- Y = t (Y ,X ,W ,V ,C [P + 0 ],U ,4096336452 );
232
- V = t (V ,Y ,X ,W ,C [P + 7 ],T ,1126891415 );
233
- W = t (W ,V ,Y ,X ,C [P + 14 ],R ,2878612391 );
234
- X = t (X ,W ,V ,Y ,C [P + 5 ],O ,4237533241 );
235
- Y = t (Y ,X ,W ,V ,C [P + 12 ],U ,1700485571 );
236
- V = t (V ,Y ,X ,W ,C [P + 3 ],T ,2399980690 );
237
- W = t (W ,V ,Y ,X ,C [P + 10 ],R ,4293915773 );
238
- X = t (X ,W ,V ,Y ,C [P + 1 ],O ,2240044497 );
239
- Y = t (Y ,X ,W ,V ,C [P + 8 ],U ,1873313359 );
240
- V = t (V ,Y ,X ,W ,C [P + 15 ],T ,4264355552 );
241
- W = t (W ,V ,Y ,X ,C [P + 6 ],R ,2734768916 );
242
- X = t (X ,W ,V ,Y ,C [P + 13 ],O ,1309151649 );
243
- Y = t (Y ,X ,W ,V ,C [P + 4 ],U ,4149444226 );
244
- V = t (V ,Y ,X ,W ,C [P + 11 ],T ,3174756917 );
245
- W = t (W ,V ,Y ,X ,C [P + 2 ],R ,718787259 );
246
- X = t (X ,W ,V ,Y ,C [P + 9 ],O ,3951481745 );
247
- Y = K (Y ,h);
248
- X = K (X ,E );
249
- W = K (W ,v);
250
- V = K (V ,g)
251
- }
252
- var i= B (Y )+ B (X )+ B (W )+ B (V );
253
- return i .toLowerCase ()
254
- }
255
- ;
256
-
257
- function hash (word ) {
258
- var out = " " ;
259
- for (var i = 0 ; i < word .length ; i += 4 ) {
260
- out += super_secret_hash (word .substring (i, i + 4 ));
261
- }
262
- return out;
263
- }
264
-
265
- ```
266
- what immediately hit my eye is this statement:
267
- ``` js
268
- for (var i = 0 ; i < word .length ; i += 4 ) {
269
- out += super_secret_hash (word .substring (i, i + 4 ));
270
- ` ` `
271
- that makes it clear that the final hash is just a concatenation of hashes from the input string in chunks of 4 bytes.
272
- so instead of analyzing the hash function, i just immediately started looking if it is feasible to bruteforce the hash.
273
-
274
- for testing, i added the following code to generate a hash from a word passed in as a commandline argument:
275
- ` ` ` js
276
- word = process .argv [2 ];
277
- console .log (hash (word));
278
- ` ` `
279
- and created the hash of the word "flag".
280
- ` ` `
281
- $ node genius- sum .js flag
282
- 327a6c4304ad5938eaf0efb6cc3e53dc
283
- ` ` `
284
- to generate the candidates for hashing, i used my battle-proofed bruteforce password generator (i call it brutus):
285
- ` ` ` c
286
- #include < stdio .h >
287
- #include < string .h >
288
- #include < stdlib .h >
289
-
290
- struct bruteforcer {
291
- unsigned long long step;
292
- char* out;
293
- size_t out_len;
294
- const char *alpha ;
295
- size_t alpha_len;
296
- unsigned * pows;
297
- };
298
-
299
- static unsigned long upow (unsigned base , unsigned exp ) {
300
- unsigned long res = 1 , i= 0 ;
301
- while (i++ < exp) res = res * base;
302
- return res;
303
- }
304
-
305
- static unsigned calc_pow (unsigned position , size_t maxlen , size_t alpha_len ) {
306
- unsigned r = (maxlen - position) - 1 ;
307
- return upow (alpha_len, r);
308
- }
309
-
310
- /* init bruteforcer state. out_len needs to be *not the buffersize*, but the size of characters in out!
311
- that means that you need to put the zero-terminator manually before initialising the state */
312
- static void bruteforcer_init (struct bruteforcer * bf , char * out , size_t out_len , const char * alpha , size_t alpha_len ) {
313
- *bf = (struct bruteforcer) {.step = - 1ULL , .out = out, .out_len = out_len, .alpha = alpha, .alpha_len = alpha_len};
314
- bf - > pows = malloc (sizeof * (bf- > pows) * out_len);
315
- unsigned i ;
316
- for (i= 0 ; i< out_len; i++ ) bf - > pows [i ] = calc_pow (i, out_len, alpha_len);
317
- }
318
-
319
- static unsigned powstep (unsigned long long step , unsigned long long pwr , size_t alpha_len , int * termflag ) {
320
- unsigned long long d = step / pwr;
321
- if (d < alpha_len) return d;
322
- else * termflag = 1 ;
323
- return 0 ;
324
- }
325
-
326
- static int bruteforcer_step (struct bruteforcer * b ) {
327
- b- > step++ ;
328
- unsigned long long mystep = b- > step;
329
- size_t i;
330
- int termflag = 0 ;
331
- for (i= 0 ;i < b- > out_len; i++ ) {
332
- unsigned pwr = b- > pows[i];
333
- unsigned x = powstep (mystep, pwr, b- > alpha_len, & termflag);
334
- if (i == 0 && termflag) return 0 ;
335
- if (x) mystep -= (pwr * x);
336
- b- > out[i] = b- > alpha[ x ];
337
- }
338
- return 1 ;
339
- }
340
-
341
- static int usage (char * argv0 ) {
342
- printf (
343
- " brutus 1.0 by rofl0r\n "
344
- " usage: %s alphabet length\n "
345
- " for example:\n "
346
- " %s abcdef 4\n " , argv0, argv0);
347
- return 1 ;
348
- }
349
-
350
- int main (int argc , char ** argv ) {
351
- if (argc != 3 ) return usage (argv[0 ]);
352
- const char *alpha = argv[1 ];
353
- struct bruteforcer b;
354
- int candidatelen = atoi (argv[2 ]);
355
- char* candidate = malloc (candidatelen+ 1 );
356
- candidate[candidatelen] = 0 ;
357
- bruteforcer_init (& b, candidate, candidatelen, alpha, strlen (alpha));
358
- while (bruteforcer_step (& b)) puts (candidate);
359
- return 0 ;
360
- }
361
- ` ` `
362
- Then i created a new js file with the hash function and the following snippet:
363
- the idea is to pass the hash as command line argument (` want ` ), and the candidates to hash and compare via stdin.
364
- ` ` ` js
365
- want = process .argv [2 ];
366
-
367
- // that's a generic codeblock to be able to read from stdin.
368
- var readline = require (' readline' );
369
- var rl = readline .createInterface ({
370
- input: process .stdin ,
371
- output: process .stdout ,
372
- terminal: false
373
- });
374
-
375
- rl .on (' line' , function (line ){
376
- if (hash (line) === want) {
377
- console .log (line);
378
- process .exit (0 );
379
- }
380
- })
381
- ` ` `
382
-
383
- For the first try i used just a lowercase alphabet against the hash of "flag" to see if the attack is feasible (i.e. fast enough):
384
-
385
- ` ` `
386
- time ./ brutus abcdefghijklmnopqrstuvwxyz 4 | node genius .js 327a6c4304ad5938eaf0efb6cc3e53dc
387
- flag
388
-
389
- real 0m1 .087s
390
- user 0m1 .095s
391
- sys 0m0 .008s
392
- ` ` `
393
- very promising!
394
-
395
- in order to use all 8 available cores, i resorted to my tool [jobflow](https://github.com/rofl0r/jobflow).
396
- it's similar to GNU parallel, but it's much slimmer and faster since it's written in C.
397
- and it uses much less memory. it's also by default installed on my distro [sabotage linux](http://github.com/sabotage-linux/sabotage).
398
- the concept is to evenly distribute the input from brutus to 8 node.js processes running the "password cracker".
399
- i then went to the guts and started cracking the actual hashes of the competition (in 32 byte chunks).
400
-
401
- ` ` `
402
- time ./ brutus _ - 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 4 | jobflow - j = 8 - exec node genius .js 8a7fca9234d2f19c8abfcd812971a26c
403
- OMG_
404
-
405
- real 0m24 .328s
406
- user 0m1 .145s
407
- sys 0m1 .535s
408
-
409
- ` ` `
410
- success! the first part of the input string is ` OMG_ ` .
411
- since the lowercase alphabet didn't cut it, i expanded the alphabet for brutus to include uppercase, lowercase, plus some special chars.
412
-
413
- i repeated the process with the other hash chunks, and got the final "password":
414
- ` OMG_it_took_like_LITerally_s0oO00_long_2_MAK3_md5_werrk_you_have_no_id34 `
415
-
416
- Putting that into the website's prompt gives a popup with the flag:
417
- ` easyctf {OUR_3nCRYpti0n_is_N0T_br0k3n_Ur_brok3n_6c5a390d}`
418
-
419
55
### External Writeups
420
56
421
57
* [ https://github.com/HackThisCode/CTF-Writeups/blob/master/2017/EasyCTF/Genius/README.md ] ( https://github.com/HackThisCode/CTF-Writeups/blob/master/2017/EasyCTF/Genius/README.md )
0 commit comments