# CTF Writeup - Move or not (BambooFox CTF) View the book with "<i class="fa fa-book fa-fw"></i> Book Mode". Resources --- - [Position Independent Executables (PIE)](https://access.redhat.com/blogs/766093/posts/1975793) - [ghidra](https://www.ghidra-sre.org) - [other writeup](https://github.com/SPRITZ-Research-Group/ctf-writeups) - [ltrace](http://man7.org/linux/man-pages/man1/ltrace.1.html) First step --- * `file` ![](https://i.imgur.com/3mm4d2A.png) we see `PIE` executable, this means that ###### tags: `reverse engineering` `obscure function` `PIE` Execute --- ```shell= parallels@parallels-vm:~/Desktop/MoveOrNot$ ./pro.dms First give me your password: 1 You don't know static analysis ! ``` Firstly, it asks you to enter the password, if a wrong password is given, it mocks you and end. ```shell= parallels@parallels-vm:~/Desktop/MoveOrNot$ ./pro.dms First give me your password: 98416 Second give me your key: 50 Then Verify your flag: ? You don't know dynamic analysis ! ``` Through, reverse engineering, we can easily figure out that the first password is `98416`. After this, it will ask you to enter the second key, and ask you to verify your flag at the end. Reverse --- ```cmake= undefined8 main(void) { int iVar1; long in_FS_OFFSET; int option; int count; char try_flag [40]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); count = 0; printf("First give me your password: "); /* scanf("%d", an integer) */ scanf(&DAT_00100a06,&option); if (option != 98416) { puts("You don\'t know static analysis !"); /* WARNING: Subroutine does not return */ exit(0); } printf("Second give me your key: "); /* scanf("%d", an integer) */ scanf(&DAT_00100a06,&option); option = option + -49; count = 0; while (count < 0xc) { *(char *)(FUN_00301020 + count) = (char)FUN_00301020[count] + (char)option; count = count + 1; } FUN_00301020(&magicstring); printf("Then Verify your flag: "); scanf(&DAT_00100a63,try_flag); iVar1 = strcmp((char *)&magicstring,try_flag); if (iVar1 == 0) { puts("You are right. Congratulations !!"); } else { puts("You don\'t know dynamic analysis !"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; } ``` The code above has been sorted, so it should be easier to read. Firstly, `line 16` shows the first password. Secondly, `line 23` scans the second key with data type `int`. The key will be updated by substracting 49. Here comes the funniest part. ```clike= while (count < 0xc) { *(char *)(FUN_00301020 + count) = (char)FUN_00301020[count] + (char)option; count = count + 1; } ``` it modifies machine code somehow by add a certain value. We left this behind for a second, and find that it call the modified function `FUN_00301020(&magicstring);` ok ... so what we got there? `FUN_00301020()` ```clike /* WARNING: Control flow encountered bad instruction data */ void FUN_00301020(int param_1,undefined8 param_2,undefined8 param_3,undefined4 *param_4) { int *piVar1; int in_EAX; undefined4 in_register_00000004; char *magic_string; char unaff_R14B; bool in_ZF; char in_SF; char in_OF; if (!in_ZF && in_OF == in_SF) { piVar1 = (int *)(CONCAT44(in_register_00000004,in_EAX) + -0x7cb7c3f9); *piVar1 = *piVar1 + in_EAX; *param_4 = 0x48480780; magic_string = (char *)(ulong)(param_1 + 1); *magic_string = *magic_string + 'o'; magic_string[1] = magic_string[1] + ';'; magic_string[2] = magic_string[2] + 'Y'; magic_string[3] = magic_string[3] + 'F'; magic_string[4] = magic_string[4] + -0x14; magic_string[5] = magic_string[5] + '\b'; magic_string[6] = magic_string[6] + '\x01'; magic_string[7] = magic_string[7] + '1'; magic_string[8] = magic_string[8] + '\x18'; magic_string[9] = magic_string[9] + -9; magic_string[10] = magic_string[10] + '\x11'; magic_string[0xb] = magic_string[0xb] + -5; magic_string[0xc] = magic_string[0xc] + -0x45; magic_string[0xd] = magic_string[0xd] + '\x1a'; magic_string[0xe] = magic_string[0xe] + '-'; magic_string[0xf] = magic_string[0xf] + -0xb; magic_string[0x10] = magic_string[0x10] + 'U'; magic_string[0x11] = magic_string[0x11] + '\x18'; magic_string[0x12] = magic_string[0x12] + '\x19'; magic_string[0x13] = magic_string[0x13] + -0x3b; magic_string[0x14] = magic_string[0x14] + -9; magic_string[0x15] = magic_string[0x15] + '\b'; magic_string[0x16] = magic_string[0x16] + 'X'; return; } if (unaff_R14B == '\0' || SCARRY1(unaff_R14B,'\0') != unaff_R14B < '\0') { /* WARNING: Bad instruction - Truncating control flow here */ halt_baddata(); } /* WARNING: Bad instruction - Truncating control flow here */ halt_baddata(); } ``` I was confused by this segment in the first place ```clike= piVar1 = (int *)(CONCAT44(in_register_00000004,in_EAX) + -0x7cb7c3f9); *piVar1 = *piVar1 + in_EAX; *param_4 = 0x48480780; ``` it turns out that we don't and should not read this code, I'll explain this later. The rest codes are quite easy to understand though ```clike= magic_string = (char *)(ulong)(param_1 + 1); *magic_string = *magic_string + 'o'; magic_string[1] = magic_string[1] + ';'; magic_string[2] = magic_string[2] + 'Y'; magic_string[3] = magic_string[3] + 'F'; ``` modify the `magic_string` According to `Ghidra` the `magic_string` is at address `00301100` ```shell= magicstring XREF[2]: main:001008d4(*), main:00100912(*) 00301100 69 db 69h 00301101 41 ?? 41h A 00301102 78 ?? 78h x 00301103 55 ?? 55h U 00301104 2e ?? 2Eh . 00301105 7c ?? 7Ch | 00301106 26 ?? 26h & 00301107 33 ?? 33h 3 00301108 30 ?? 30h 0 00301109 0c ?? 0Ch 0030110a 29 ?? 29h ) ... ``` OK. Let's pause and think we got - `magic_string`: a raw string - `FUN_00301020`: modify `magic_string` somehow Let's go back to `FUN_00301020` and microscope it's assembly ```clike= 00301020 7f 2e JG LAB_00301050 00301022 26 47 82 ADD R14B,0x0 c6 00 00301027 7f 06 JG LAB_0030102f 00301029 1f ?? 1Fh 0030102a 47 ?? 47h G 0030102b 82 ?? 82h 0030102c c7 ?? C7h 0030102d 01 ?? 01h 0030102e 80 ?? 80h LAB_0030102f XREF[1]: 00301027(j) 0030102f 2f ?? 2Fh / 00301030 0b ?? 0Bh 00301031 48 ?? 48h H 00301032 83 ?? 83h 00301033 c7 ?? C7h 00301034 01 ?? 01h 00301035 80 ?? 80h 00301036 07 ?? 07h 00301037 0d ?? 0Dh 00301038 48 ?? 48h H 00301039 83 ?? 83h 0030103a c7 ?? C7h 0030103b 01 ?? 01h 0030103c 80 ?? 80h 0030103d 07 ?? 07h 0030103e 41 ?? 41h A 0030103f 48 ?? 48h H 00301040 83 ?? 83h 00301041 c7 ?? C7h 00301042 01 ?? 01h 00301043 80 ?? 80h 00301044 2f ?? 2Fh / 00301045 0d ?? 0Dh 00301046 48 ?? 48h H 00301047 83 ?? 83h 00301048 c7 ?? C7h 00301049 01 ?? 01h 0030104a 80 ?? 80h 0030104b 07 ?? 07h 0030104c 20 ?? 20h 0030104d 48 ?? 48h H 0030104e 83 ?? 83h 0030104f c7 ?? C7h LAB_00301050 XREF[1]: 00301020(j) 00301050 01 80 07 ADD dword ptr [RAX + -0x7cb7c3f9],EAX 3c 48 83 00301056 c7 01 80 MOV dword ptr [RCX],0x48480780 07 48 48 0030105c 83 c7 01 ADD magic_string,0x1 0030105f 80 07 6f ADD byte ptr [magic_string],0x6f 00301062 48 83 c7 01 ADD magic_string,0x1 00301066 80 07 3b ADD byte ptr [magic_string],0x3b 00301069 48 83 c7 01 ADD magic_string,0x1 0030106d 80 07 59 ADD byte ptr [magic_string],0x59 00301070 48 83 c7 01 ADD magic_string,0x1 00301074 80 07 46 ADD byte ptr [magic_string],0x46 00301077 48 83 c7 01 ADD magic_string,0x1 0030107b 80 2f 14 SUB byte ptr [magic_string],0x14 0030107e 48 83 c7 01 ADD magic_string,0x1 00301082 80 07 08 ADD byte ptr [magic_string],0x8 00301085 48 83 c7 01 ADD magic_string,0x1 ... ``` Note that the first 49 lines are non-sense ... so it must be related to the obscure function mentioned above. By analyzing the machine code below, get ``` examples: 83 c7 01 ADD magic_string,0x1 80 07 6f ADD byte ptr [magic_string],0x6f 48 83 c7 01 ADD magic_string,0x1 80 2f 14 SUB byte ptr [magic_string],0x14 features: 83 c7 01 80 07 () 48 83 c7 01 80 2f 14 ``` Does the non-sense codes meet this features after some operations? The answer is: Yes, `plus 1` ``` from 00301020: (all plus 1) 80 2f 27 SUB 0x27 48 83 c7 01 ADD, magic_string, 0x1 80 07 20 ADD 0x20 from 0030102e: 80 2f 0b SUB 0xb 48 83 c7 01 ADD, magic_string, 0x1 80 07 0d ADD 0xd 48 83 c7 01 ADD, magic_string, 0x1 80 07 41 ADD 0x41 48 83 c7 01 ADD, magic_string, 0x1 80 2f 0d SUB 0xd 48 83 c7 01 ADD, magic_string, 0x1 80 07 20 ADD 0x20 48 83 c7 01 ADD, magic_string, 0x1 80 07 3c ADD 0x3c 48 83 c7 01 ADD, magic_string, 0x1 80 07 48 ADD 0x48 ``` It's at this point that we realize that the ```clike= while (count < 0xc) { *(char *)(FUN_00301020 + count) = (char)FUN_00301020[count] + (char)option; count = count + 1; } ``` actually expect the option be `1`(so we should enter `50`). Now we can come up with `exploit.py` ```python= magic_string = [ 0x69, 0x41, 0x78, 0x55, 0x2E, 0x7C, 0x26, 0x33, 0x30, 0x0C, 0x29, 0x20, 0x28, 0x48, 0x65, 0x68, 0x32, 0x47, 0x3A, 0x62, 0x64, 0x79, 0x52, 0x46, 0x3B, 0x0A, 0x4F, 0x59, 0x6E, 0x3D, 0x6C, 0x25, 0x00, ] seasoning = [ -0x27, 0x20, -0xb, 0xd, 0x41, -0xd, 0x20, 0x3c, 0x48, 0x6f, 0x3b, 0x59, 0x46, -0x14, 0x8, 0x1, 0x31, 0x18, -0x9, 0x11, -0x5, -0x45, 0x1a, 0x2d, -0xb, 0x55, 0x18, 0x19, -0x3b, -0x9, 0x8, 0x58, ] """ 0030105f 80 07 6f ADD byte ptr [magic_string],0x6f 00301062 48 83 c7 01 ADD magic_string,0x1 00301066 80 07 3b ADD byte ptr [magic_string],0x3b 00301069 48 83 c7 01 ADD magic_string,0x1 0030106d 80 07 59 ADD byte ptr [magic_string],0x59 00301070 48 83 c7 01 ADD magic_string,0x1 00301074 80 07 46 ADD byte ptr [magic_string],0x46 00301077 48 83 c7 01 ADD magic_string,0x1 0030107b 80 2f 14 SUB byte ptr [magic_string],0x14 0030107e 48 83 c7 01 ADD magic_string,0x1 00301082 80 07 08 ADD byte ptr [magic_string],0x8 00301085 48 83 c7 01 ADD magic_string,0x1 00301089 80 07 01 ADD byte ptr [magic_string],0x1 0030108c 48 83 c7 01 ADD magic_string,0x1 00301090 80 07 31 ADD byte ptr [magic_string],0x31 00301093 48 83 c7 01 ADD magic_string,0x1 00301097 80 07 18 ADD byte ptr [magic_string],0x18 0030109a 48 83 c7 01 ADD magic_string,0x1 0030109e 80 2f 09 SUB byte ptr [magic_string],0x9 003010a1 48 83 c7 01 ADD magic_string,0x1 003010a5 80 07 11 ADD byte ptr [magic_string],0x11 003010a8 48 83 c7 01 ADD magic_string,0x1 003010ac 80 2f 05 SUB byte ptr [magic_string],0x5 003010af 48 83 c7 01 ADD magic_string,0x1 003010b3 80 2f 45 SUB byte ptr [magic_string],0x45 003010b6 48 83 c7 01 ADD magic_string,0x1 003010ba 80 07 1a ADD byte ptr [magic_string],0x1a 003010bd 48 83 c7 01 ADD magic_string,0x1 003010c1 80 07 2d ADD byte ptr [magic_string],0x2d 003010c4 48 83 c7 01 ADD magic_string,0x1 003010c8 80 2f 0b SUB byte ptr [magic_string],0xb 003010cb 48 83 c7 01 ADD magic_string,0x1 003010cf 80 07 55 ADD byte ptr [magic_string],0x55 003010d2 48 83 c7 01 ADD magic_string,0x1 003010d6 80 07 18 ADD byte ptr [magic_string],0x18 003010d9 48 83 c7 01 ADD magic_string,0x1 003010dd 80 07 19 ADD byte ptr [magic_string],0x19 003010e0 48 83 c7 01 ADD magic_string,0x1 003010e4 80 2f 3b SUB byte ptr [magic_string],0x3b 003010e7 48 83 c7 01 ADD magic_string,0x1 003010eb 80 2f 09 SUB byte ptr [magic_string],0x9 003010ee 48 83 c7 01 ADD magic_string,0x1 003010f2 80 07 08 ADD byte ptr [magic_string],0x8 003010f5 48 83 c7 01 ADD magic_string,0x1 003010f9 80 07 58 ADD byte ptr [magic_string],0x58 """ flags = [] for m, s in zip(magic_string, seasoning): flags.append(chr(m+s)) print(''.join(flags)) ``` ```shell= ztex@Ztexde-MBP  ~/Desktop/MoveOrNot  python exploit.py BambooFox{dyn4mic_1s_4ls0_gr34t} ``` **Flag**: `BambooFox{dyn4mic_1s_4ls0_gr34t}` Discusion === We can also solve this by `ltrace`. ```shell= ltrace is a program that simply runs the specified command until it exits. It intercepts and records the dynamic library calls which are called by the executed process and the signals which are received by that process. It can also intercept and print the system calls executed by the program. Its use is very similar to strace(1). ltrace shows parameters of invoked functions and system calls. To determine what arguments each function has, it needs external declaration of function prototypes. Those are stored in files called prototype libraries--see ltrace.conf(5) for details on the syntax of these files. See the section PROTOTYPE LIBRARY DISCOVERY to learn how ltrace finds prototype libraries. ``` * answer ```shell= parallels@parallels-vm:~/Desktop/MoveOrNot$ ltrace ./pro.dms printf("First give me your password: ") = 29 __isoc99_scanf(0x5578e53a3a06, 0x7fffd2555a38, 0x7f787d131780, 29First give me your password: 98416 ) = 1 printf("Second give me your key: ") = 25 __isoc99_scanf(0x5578e53a3a06, 0x7fffd2555a38, 0x7f787d131780, 25Second give me your key: 50 ) = 1 printf("Then Verify your flag: ") = 23 __isoc99_scanf(0x5578e53a3a63, 0x7fffd2555a40, 0x7f787d131780, 23Then Verify your flag: a ) = 1 strcmp("BambooFox{dyn4mic_1s_4ls0_gr34t}"..., "a") = -31 puts("You don't know dynamic analysis "...You don't know dynamic analysis ! ) = 34 +++ exited (status 0) +++ ``` if the second key is wrong, crash ```shell= parallels@parallels-vm:~/Desktop/MoveOrNot$ ltrace ./pro.dms printf("First give me your password: ") = 29 __isoc99_scanf(0x55f18e10ba06, 0x7ffd7d515de8, 0x7fd811ecf780, 29First give me your password: 98416 ) = 1 printf("Second give me your key: ") = 25 __isoc99_scanf(0x55f18e10ba06, 0x7ffd7d515de8, 0x7fd811ecf780, 25Second give me your key: 1 ) = 1 --- SIGILL (Illegal instruction) --- +++ killed by SIGILL +++ ```