Never fear, the flag shall not appear (in memory)! I've switched it out for a more secure system! Note: The actual flag consists purely of non-whitespace printable characters (aka ascii values 0x21 through 0x7e). RE -- The switcheroo binary is compiled from assembly language, rather than C or anything else higher-level. However, it is still very useful to use Ghidra to analyze strings and add additional comments and markup in the code listing view. Given the above, the file is actually quite small and simple. We have just a couple boilerplate sections in the ELF, .data contains 3 strings and an array of 64-bit integers, and all of the logic lies sequentially under .text. The program is a flag checker. When run, it first asks you to input the flag, then asserts that the size of input == 64, so we know the length of the expected flag. To validate the content of the proposed flag, we enter a loop to iterate over each input character (this is denoted by the `loop_start` label below). For each input character, we start by using the value of that character (ascii as an int) as an index into the 64-bit integer array found in the program. The value we lookup will encode what position(s) the character index may correctly occupy in the real flag. We enter an inner loop to decode the 64-bit value. At each step, an 8-bit left bit rotation is performed on the value, we then look at the resulting lowest-order byte in the 64-bit value. The first value we extract is a count value. The code asserts that the value of the current flag character position (as a byte) exists in the looked-up value within the next `count` byte rotations. This area is denoted by the `inner_loop_start` label below. If we hit a matching position value, the outer loop advances. However, if we exhaust `count` many rotations before that occurs, we increment another register that records how many failed characters were given, and the outer loop continues regardless. If the outer loop finishes without incrementing the failure register, the correct flag was given. See the full .text logic here, with some markup from me: ``` undefined __stdcall entry(void) undefined AL:1 undefined1 Stack[0x0]:1 local_res0 XREF[1]: 00401040(*) entry XREF[4]: Entry Point(*), 00400018(*), 00400088(*), _elfSectionHeaders::00000090(*) 00401000 48 8d 34 LEA RSI,[s_Give_me_the_flag:_00402000] = "Give me the flag: " 25 00 20 40 00 00401008 bf 01 00 MOV EDI,0x1 00 00 0040100d ba 12 00 MOV EDX,0x12 00 00 00401012 b8 01 00 MOV EAX,0x1 00 00 write(stdout, "Give me the flag", size) 00401017 0f 05 SYSCALL 00401019 31 ff XOR EDI,EDI 0040101b 48 89 e6 MOV RSI,RSP 0040101e ba 64 00 MOV EDX,0x64 00 00 00401023 31 c0 XOR EAX,EAX read(stdin, &stack, 100) 00401025 0f 05 SYSCALL 00401027 48 83 f8 40 CMP RAX,0x40 if read() != 64: fail 0040102b 75 5e JNZ fail 0040102d 4d 31 e4 XOR R12,R12 00401030 4c 8d 1c LEA R11,[static_data] 25 3c 20 40 00 00401038 4d 31 d2 XOR R10,R10 loop_start XREF[1]: 00401071(j) 0040103b 49 39 c2 CMP R10,RAX 0040103e 7d 33 JGE loop_break R9 = stack[R10] // get current char 00401040 4e 0f b6 MOVZX R9,byte ptr [RSP + R10*0x1]=>local_res0 0c 14 R8 = static_data[R9] // lookup long by current char 00401045 4e 8b 04 MOV R8,qword ptr [static_data + R9*0x8] cd 3c 20 40 00 rotate left by 8-bits (1-byte) 0040104d 49 c1 c0 08 ROL R8,0x8 R13 = (char)(static_data[stack[i]] >> 56) 00401051 4d 0f b6 e8 MOVZX R13,R8B 00401055 4d 31 f6 XOR R14,R14 while R14 < R13 inner_loop_start XREF[1]: 00401069(j) 00401058 4d 39 ee CMP R14,R13 0040105b 7d 0e JGE inner_loop_break_bad 0040105d 49 c1 c0 08 ROL R8,0x8 00401061 45 38 d0 CMP R8B,R10B 00401064 74 08 JZ inner_loop_break_good 00401066 49 ff c6 INC R14 00401069 eb ed JMP inner_loop_start inner_loop_break_bad XREF[1]: 0040105b(j) 0040106b 49 ff c4 INC R12 inner_loop_break_good XREF[1]: 00401064(j) 0040106e 49 ff c2 INC R10 00401071 eb c8 JMP loop_start loop_break XREF[1]: 0040103e(j) 00401073 4d 85 e4 TEST R12,R12 00401076 75 13 JNZ fail 00401078 eb 02 JMP success 0040107a eb ?? EBh 0040107b 00 ?? 00h success XREF[1]: 00401078(j) 0040107c 48 8d 34 LEA RSI,[s_That_was_the_flag!_0040202a] = "That was the flag!" 25 2a 20 40 00 00401084 ba 12 00 MOV EDX,0x12 00 00 00401089 eb 0f JMP exit fail XREF[2]: 0040102b(j), 00401076(j) 0040108b 48 8d 34 LEA RSI,[s_That_was_not_the_flag_:(_00402012] = "That was not the flag :(" 25 12 20 40 00 00401093 ba 18 00 MOV EDX,0x18 00 00 00401098 eb 00 JMP exit exit XREF[2]: 00401089(j), 00401098(j) 0040109a bf 01 00 MOV EDI,0x1 00 00 0040109f b8 01 00 MOV EAX,0x1 00 00 write(stdout, "That was [not] the flag", size) 004010a4 0f 05 SYSCALL 004010a6 6a 0a PUSH 0xa 004010a8 48 89 e6 MOV RSI,RSP 004010ab ba 01 00 MOV EDX,0x1 00 00 004010b0 b8 01 00 MOV EAX,0x1 00 00 write(stdout, "\n", 1) 004010b5 0f 05 SYSCALL 004010b7 31 ff XOR EDI,EDI 004010b9 48 c7 c0 MOV RAX,0x3c 3c 00 00 00 exit(0) 004010c0 0f 05 SYSCALL ``` Solution -------- To extract the flag, I extracted the 64-bit integer array to attempt to fully decode it. With the rules we learned during RE, we know that each entry can be used to validate whether or not it resides at any given position in the flag string. I wrote a C program to iterate over a flag string (aka position from 0 -> 64) and print any character value (aka array entry at an ascii index) that _would_ pass validation. This initially gave some ambiguous results, however when my search was restricted per the problem description (0x21 -> 0x7e), a unique flag appears. ``` #include static const unsigned char A[] = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x07, 0x7e, 0xfc, 0x3c, 0x78, 0xfb, 0xf4, 0x04, 0x3e, 0x03, 0x87, 0x30, 0x04, 0x3d, 0x81, 0x70, 0x9d, 0x4d, 0xa7, 0x3d, 0x09, 0xf8, 0xa1, 0x51, 0x4a, 0xfc, 0x02, 0x8b, 0xa8, 0xb2, 0x50, 0x49, 0x0c, 0x4b, 0xfd, 0xf0, 0x62, 0x92, 0x1d, 0xd2, 0x02, 0xa9, 0x46, 0xbe, 0xaa, 0xbe, 0x81, 0xe7, 0x88, 0x94, 0x19, 0xbb, 0x85, 0x0e, 0x0f, 0x09, 0x77, 0xfb, 0x49, 0x3f, 0x35, 0x24, 0xb8, 0x4e, 0x56, 0x47, 0x9b, 0x62, 0xeb, 0x9d, 0x7a, 0x0b, 0x7f, 0xd1, 0x18, 0x6e, 0x14, 0x30, 0xb1, 0x3f, 0x01, 0xed, 0x65, 0xd4, 0x4d, 0x7e, 0xe1, 0x50, 0xe6, 0xe7, 0xab, 0x3d, 0x80, 0x04, 0x67, 0xd3, 0x81, 0x7a, 0x8c, 0x71, 0x73, 0xa3, 0x80, 0xce, 0x02, 0x73, 0x48, 0x25, 0x30, 0x09, 0x51, 0x2c, 0x82, 0x4e, 0x68, 0x70, 0x50, 0x47, 0x37, 0x08, 0x57, 0xb5, 0x14, 0x10, 0x32, 0xec, 0x34, 0xd7, 0x3b, 0xb3, 0x98, 0xdd, 0x7e, 0xe3, 0x1e, 0x5e, 0x97, 0xfd, 0x4c, 0x0d, 0xb6, 0xfe, 0x44, 0x81, 0x15, 0x91, 0x42, 0x15, 0xac, 0x6c, 0x7c, 0x40, 0x88, 0x1b, 0xfc, 0x0d, 0x92, 0x2b, 0x19, 0xae, 0x0f, 0x80, 0x26, 0x76, 0x7c, 0x6e, 0x67, 0x0c, 0xfc, 0x27, 0xef, 0x94, 0x6d, 0xd3, 0xb8, 0x50, 0x48, 0xa2, 0x4d, 0x88, 0x0d, 0x72, 0x66, 0x64, 0x08, 0xdc, 0x48, 0x4f, 0xc1, 0x53, 0x04, 0x7e, 0xac, 0xd8, 0xdf, 0x61, 0x24, 0xad, 0x68, 0x25, 0x7c, 0x08, 0x1f, 0x91, 0xde, 0x5d, 0x06, 0x3b, 0x47, 0x71, 0x58, 0xe6, 0x42, 0xd7, 0x1f, 0xed, 0x1e, 0x48, 0x25, 0xb8, 0xc1, 0xc3, 0xed, 0x81, 0x6c, 0xff, 0x02, 0x18, 0xb6, 0x71, 0x9f, 0x16, 0x03, 0xe0, 0x3f, 0xae, 0x92, 0x67, 0x39, 0xa7, 0xe4, 0xca, 0xb7, 0x10, 0x6b, 0xac, 0x08, 0xcf, 0x90, 0xa0, 0x81, 0xf0, 0x31, 0x09, 0xa1, 0x4c, 0x00, 0x3b, 0x68, 0x89, 0x8f, 0xf1, 0x3d, 0xf5, 0x00, 0x49, 0x8f, 0xde, 0xb1, 0xbc, 0x42, 0x42, 0x00, 0xca, 0x84, 0xac, 0xce, 0x00, 0x59, 0x7e, 0x00, 0xa4, 0x0e, 0x11, 0xe8, 0x0f, 0x6f, 0x70, 0x00, 0x4e, 0x50, 0x62, 0x20, 0xb4, 0x78, 0x22, 0x00, 0x9c, 0x2b, 0xa5, 0x5d, 0xa9, 0x05, 0x2a, 0x00, 0x49, 0x0e, 0xdf, 0xb9, 0xd0, 0xc5, 0x4d, 0x00, 0xc4, 0xf0, 0x6c, 0xe0, 0x41, 0x36, 0x6c, 0x00, 0x6e, 0x4a, 0x37, 0x8e, 0xa6, 0x47, 0x79, 0x00, 0x02, 0x68, 0x33, 0x2b, 0x63, 0xd2, 0xd8, 0x00, 0x2e, 0x02, 0xb5, 0x2f, 0xc4, 0x56, 0x38, 0x01, 0x18, 0x39, 0x08, 0x1a, 0xdb, 0x14, 0x3b, 0x00, 0xbb, 0xe1, 0x2f, 0xb1, 0x3a, 0x67, 0xf8, 0x00, 0x24, 0x12, 0xed, 0x14, 0xb0, 0x08, 0xa1, 0x00, 0x04, 0x3b, 0xef, 0x6a, 0x2e, 0x89, 0xb2, 0x00, 0xd6, 0x24, 0x00, 0x18, 0xd3, 0x3c, 0x2c, 0x02, 0x34, 0x5f, 0xfc, 0xd0, 0xaa, 0x11, 0x39, 0x02, 0xe6, 0x4e, 0x4b, 0x71, 0xc9, 0x07, 0x08, 0x02, 0x27, 0x23, 0x30, 0x09, 0x1c, 0x33, 0x1a, 0x07, 0x45, 0xd2, 0xa9, 0x21, 0x29, 0x06, 0x18, 0x04, 0x53, 0x92, 0x0f, 0x2b, 0x34, 0x16, 0x1f, 0x05, 0x7c, 0xe9, 0x0a, 0xda, 0xbf, 0x19, 0x3b, 0x01, 0x1e, 0x2f, 0x19, 0x35, 0x25, 0x17, 0x12, 0x07, 0x95, 0xd2, 0x10, 0xc9, 0x0b, 0x2a, 0x32, 0x03, 0x94, 0x63, 0x19, 0xfa, 0xff, 0x70, 0xc0, 0x00, 0xdd, 0x99, 0x49, 0xa8, 0x72, 0xb0, 0x9a, 0x00, 0xac, 0xaa, 0xa1, 0x74, 0xe1, 0x82, 0x4e, 0x00, 0xb9, 0x84, 0x95, 0x41, 0x05, 0x52, 0xaa, 0x00, 0xd5, 0xe3, 0x1e, 0x8c, 0x2b, 0x8a, 0x67, 0x00, 0xd0, 0xe7, 0xd8, 0xc9, 0xac, 0xbc, 0x45, 0x00, 0xf9, 0xd4, 0xb0, 0x84, 0xc3, 0x19, 0x3a, 0x00, 0x1b, 0xc5, 0x82, 0xa5, 0x60, 0x19, 0x8b, 0x00, 0xcd, 0x2a, 0x2a, 0x6c, 0x50, 0xc4, 0x4e, 0x00, 0xe1, 0x86, 0x57, 0x60, 0xc8, 0x73, 0x74, 0x00, 0x7f, 0xb3, 0xba, 0x6b, 0x53, 0x16, 0x13, 0x01, 0xef, 0xde, 0xdb, 0x7f, 0x24, 0x0d, 0x0f, 0x00, 0x76, 0x1e, 0xde, 0xa9, 0x08, 0xf6, 0xad, 0x00, 0xe3, 0x82, 0xcf, 0x68, 0x08, 0x54, 0x9f, 0x00, 0x1d, 0x80, 0x2b, 0x31, 0xfc, 0xf5, 0x14, 0x00, 0xfe, 0x24, 0x32, 0xc4, 0x14, 0x6f, 0x10, 0x00, 0xc5, 0x37, 0x8b, 0x5d, 0xad, 0x5b, 0xb8, 0x00, 0x0d, 0x99, 0x8c, 0x84, 0x17, 0xce, 0xed, 0x00, 0xc4, 0xbc, 0x11, 0xdf, 0x13, 0xdc, 0xcc, 0x00, 0x9b, 0x55, 0xa0, 0x76, 0x67, 0x2d, 0x0c, 0x02, 0xf7, 0x21, 0x75, 0xea, 0x64, 0x1b, 0x0a, 0x02, 0x82, 0xbe, 0x90, 0xd9, 0x24, 0x1d, 0x39, 0x00, 0xa8, 0x3c, 0xd7, 0x9d, 0x71, 0x00, 0x75, 0x00, 0x37, 0x2a, 0xa8, 0xb2, 0xad, 0x2e, 0xef, 0x00, 0x60, 0x14, 0x96, 0x40, 0x0a, 0xf1, 0x8e, 0x00, 0xa5, 0x67, 0x3f, 0xfe, 0x2c, 0x9a, 0x33, 0x00, 0x9d, 0x87, 0x65, 0x63, 0x30, 0x71, 0xe0, 0x00, 0xf0, 0xdc, 0x75, 0x2a, 0x3d, 0x6e, 0x6e, 0x00, 0xf0, 0x73, 0x82, 0xc1, 0x54, 0x72, 0x2e, 0x01, 0x7f, 0x66, 0x9b, 0xcd, 0xad, 0x08, 0x48, 0x00, 0x95, 0x63, 0xea, 0x30, 0x12, 0x80, 0x10, 0x01, 0x38, 0xfc, 0xf1, 0xf1, 0xc7, 0x98, 0xfa, 0x00, 0xd9, 0x7b, 0xfe, 0xe6, 0x0f, 0x40, 0x0d, 0x01, 0x4d, 0xb9, 0x08, 0x00, 0x15, 0x9e, 0x47, 0x00, 0x95, 0x48, 0x13, 0x38, 0xcf, 0x80, 0xf9, 0x00, 0xac, 0x5d, 0xcb, 0x7b, 0xfe, 0x0f, 0x2a, 0x00, 0x99, 0x9b, 0xce, 0x0f, 0x4b, 0x0b, 0x75, 0x00, 0x58, 0x77, 0xb3, 0x79, 0xfe, 0x6e, 0xbd, 0x00, 0x28, 0x36, 0x15, 0x31, 0x24, 0x0e, 0x20, 0x07, 0x02, 0xbc, 0x32, 0x40, 0xf1, 0xaa, 0x7c, 0x00, 0xbb, 0x95, 0x83, 0x61, 0x1f, 0x01, 0x3d, 0x02, 0x9e, 0x6f, 0xfe, 0x34, 0x78, 0x80, 0x53, 0x00, 0xc8, 0x70, 0x86, 0xe8, 0x3d, 0x30, 0x02, 0x01, 0xf8, 0xd8, 0x08, 0xc7, 0x3a, 0x0c, 0xf5, 0x00, 0xbc, 0x75, 0x9d, 0xa9, 0xf8, 0x5e, 0xff, 0x00, 0x7e, 0x51, 0x0e, 0x10, 0x12, 0x3a, 0x04, 0x02, 0x06, 0xd7, 0x49, 0xd0, 0x19, 0x2d, 0xe8, 0x00, 0x14, 0x42, 0x62, 0xdb, 0x31, 0x26, 0x14, 0x02, 0x0b, 0xa6, 0xff, 0xb0, 0x63, 0x13, 0xa9, 0x00, 0xf9, 0xb2, 0x7d, 0x13, 0xa1, 0x8e, 0x85, 0x00, 0xec, 0x18, 0xb0, 0x06, 0xfa, 0x4f, 0x6f, 0x00, 0x34, 0xf0, 0x80, 0x76, 0xe7, 0x57, 0x00, 0x01, 0x72, 0x72, 0x8c, 0x8b, 0xb8, 0x5a, 0x5a, 0x00, 0x82, 0x1d, 0x08, 0x34, 0x68, 0x19, 0x1d, 0x01, 0xac, 0xea, 0xfa, 0x56, 0x07, 0xee, 0x6d, 0x00, 0x78, 0x00, 0x16, 0x0c, 0xe8, 0x61, 0x16, 0x00, 0x13, 0xc3, 0x58, 0x1a, 0x7a, 0xa6, 0xcc, 0x00, 0xe8, 0x13, 0x1b, 0xb8, 0x83, 0x36, 0x22, 0x01, 0x8d, 0xd6, 0x45, 0x03, 0xc2, 0xca, 0x65, 0x00, 0xdc, 0xec, 0x50, 0x60, 0x1a, 0x1c, 0x03, 0x01, 0x05, 0x19, 0x2c, 0x97, 0xec, 0x04, 0x37, 0x01, 0x25, 0x0b, 0x6c, 0x8b, 0xe4, 0xc9, 0x4e, 0x00, 0xdd, 0xe1, 0xa5, 0x12, 0x0d, 0xc4, 0x55, 0x00, 0x0e, 0xe4, 0x20, 0xc4, 0xfc, 0xd4, 0xf0, 0x00, 0xf3, 0x27, 0x8b, 0x99, 0xc1, 0xb8, 0x9b, 0x00, 0xf6, 0x1a, 0x04, 0x04, 0x5d, 0xc8, 0xca, 0x00, 0x05, 0xba, 0x8e, 0x89, 0xaf, 0x41, 0x05, 0x02, 0x95, 0x10, 0x67, 0x3d, 0x26, 0xb0, 0x2c, 0x00, 0x43, 0x36, 0xac, 0xe7, 0xff, 0x42, 0x3e, 0x02, 0x87, 0x60, 0x09, 0x4e, 0x62, 0xf3, 0x19, 0x00, 0xec, 0x88, 0xa7, 0x5a, 0x99, 0xd3, 0xf8, 0x26 }; static const unsigned long *B = (unsigned long *)A; static void rol(long *v) { long tmp = *v << 8; tmp |= *v >> 56; *v = tmp; } int check_char_in_pos(int c, int pos) { long value = B[c]; rol(&value); for (int count = value & 0xff; count > 0; count--) { rol(&value); if ((value & 0xff) == pos) return 1; } return 0; } int main(void) { for (int pos = 0; pos < 0x40; pos++) { for (int c = 0x21; c <= 0x7e; c++) { if (check_char_in_pos(c, pos)) { printf("%c", (char)c); break; } } } printf("\n"); return 0; } ``` lactf{4223M8LY_5W17Ch_57473M3n75_4r3_7h3_4850LU73_8357_u+1f60a} I still don't know where the switch statement was...