77 views
# Fast Calculator - rev One binary file is given. 64bit ELF file, compiled with golang. c= while ( 1 ) { do { while ( 1 ) { printf("Enter your operation: ", v8, v28, v29, v30, v31, v63, v64, v65, v66); while ( 1 ) { v8 = &v74; if ( _isoc99_scanf(" %lf %c %lf", &v74, &v67, v75, v32, v33, v63) == 3 ) break; while ( getchar() != 10 ) ; printf("Enter your operation: ", &v74, v34, v35, v36, v37); } if ( v67 == '+' || v67 == '-' || v67 == '*' || v67 == '/' || v67 == '%' || v67 == '^' ) break; puts("Invalid operation!", &v74, v38, v39); } LODWORD(v79) = v67; v80 = v74; v81 = v75; v76 = calculate(" %lf %c %lf", &v74, v38, v39, v40, v41, *v75, v3, v4, v5, v42, v43, v6, v7, v67, v74, *v75); printf("Result: %lf\n", &v74, v44, v45, v46, v47, v76); v48 = v76; } while ( v76 != 8573.8567 );  Throwing binary into ida and checking the pseudo code, you can see that the calculation formula is input first, calculated with the calculate function, and then repeated until the result is 8573.8567. c= enc_data = &v63; j_memcpy(&v63, v83, v70); flip_count = 0; for ( i = 0; i < operations_length; ++i ) { v48 = calculate(...v82[3 * i],*&v82[3 * i + 1],*&v82[3 * i + 2]); if ( gauntlet(v48) ) { i_div = i / 8; v52 = (1 << (7 - i % 8)) ^ enc_data[i / 8]; enc_data[i_div] ^= 1 << (7 - i % 8); ++flip_count; } } v8 = operations_length; printf("I calculated %d operations, tested each result in the gauntlet, and flipped %d bits in the encrypted flag!\n",operations_length,flip_count); puts("Here is your decrypted flag:\n", v8, v57, v58); printf("%s\n\n", enc_data, v59, v60, v61, v62);  enc_data -> encrypted flag v82 -> 0x00000000004B8240 If the result is 8573.8567, the loop is end, and an operator symbol and two double-type numbers are taken from v82 and called as arguments of the calculate function. gauntlet function just checks if the number is negative. so, each bit of encrypted flag is xored with 1 when the return value of calculate is negative. That's all. But the final decrypted data is uiuctf{This is a fake flag. You are too fast!}. User input has no effect on the decryption. With some guessing, I checked somethings + init_array -> just call set_fast_math + another array -> nop + swap the positions of each numbers -> nop So I started debugging and I have encountered some peculiar operations in the calculate function. In one specific calculation, when -218.05xx is divided by 218.05xx using %, the result is -0.0 (0x8000000000000000). However, the gauntlet function does not interpret this value as negative. Similarly, in certain square operations, the result is -nan (0xfff8000000000000), but the gauntlet function does not consider it negative. As far as I know, the result of the modulo operation is always negative if the dividend is negative. The same is true for squares. I implemented the operation back in Python to handle these exceptions. solve script python= import struct ops = open("./ops", "rb").read() enc = open("./calc_out", "rb").read() enc = list(enc) c = 0 for i in range(0, len(ops), 24): op = chr(ops[i]) n1, n2 = struct.unpack("<dd", ops[i + 8:i + 24]) if op in ["+", "-", "*", "/"]: res = eval(f"n1 {op} n2") elif op == "%": res = -1 if n1 < 0.0 else 1 elif op == "^": res = -1 if n1 < 0.0 else 1 if res < 0.0: enc[c // 8] ^= 1 << (7 - c % 8) c += 1 print(bytes(enc))  ops -> dumped 0x00000000004B8240 ~ 0x00000000004B8240+0x170 * 0x24 enc -> dumped v83 ~ v83+0x40 > uiuctf{n0t_So_f45t_w1th_0bscur3_b1ts_of_MaThs} second blood 🩸🩸