diff options
Diffstat (limited to '')
| -rw-r--r-- | docs/writeups/2023/lactf/rev/ctfd-plus.txt | 125 | 
1 files changed, 125 insertions, 0 deletions
diff --git a/docs/writeups/2023/lactf/rev/ctfd-plus.txt b/docs/writeups/2023/lactf/rev/ctfd-plus.txt new file mode 100644 index 0000000..cf8df62 --- /dev/null +++ b/docs/writeups/2023/lactf/rev/ctfd-plus.txt @@ -0,0 +1,125 @@ +CTFd is too insufferably slow.  You know why?  Because they use an SQL database +that's bogged down by JOINs instead of a web scale database like MongoDB. +MongoDB is web scale.  You turn it on and it scales right up.  You know what's +more web scale though?  Nothing.  That's right, the throughput of /dev/null is +off the charts.  Behold, CTFd+, the first databaseless CTF platform.  Can you +get the flag for the only challenge? + + + + +RE +-- +We are given a binary that presents a welcome message along with a dummy PWN +challenge, then asks for the flag for said challenge.  As this is a mock CTF +platform, it is a flag checker RE challenge.  The content of the embedded PWN +is not relevant: the program just calls `puts` then exits. + +Ghidra shows that the flag we supply is validated character by character +against the result of some computation performed on a static array of data. +(Some markup provided by me). + +``` +undefined8 main(void) +{ +  int flag_check_char; +  size_t flag_in_len; +  long idx; +  int *static_data; +  char flag_in [256]; +   +  puts("Welcome to CTFd+!"); +  puts( +      "So far, we only have one challenge, which is one more than the number of databases we have.\n " +      ); +  puts("Very Doable Pwn - 500 points, 0 solves"); +  puts("Can you help me pwn this program?"); +  puts("#include <stdio.h>\nint main(void) {\n    puts(\"Bye!\");\n    return 0;\n}\n"); +  puts("Enter the flag:"); +  fgets(flag_in,0x100,stdin); +  flag_in_len = strcspn(flag_in,"\n"); +  idx = 0; +  static_data = INT_ARRAY_00104060; +  flag_in[flag_in_len] = '\0'; +  do { +    flag_check_char = flag_gen(static_data[idx]); +    if ((char)flag_check_char != flag_in[idx]) { +      puts("Incorrect flag."); +      return 0; +    } +    idx = idx + 1; +  } while (idx != 0x2f); +  puts("You got the flag! Unfortunately we don\'t exactly have a database to store the solve in...") +  ; +  return 0; +} +``` + +If at any point in the validation a character fails, the whole flag is +considered incorrect. + + + + +Solution +-------- +To extract the expected flag, I ported the static data and the `flag_gen` +computation logic to a standalone C program and simply print out the value of +each character.  We know the expected size of the flag string from the bounds +of `main`'s while loop (`while (idx != 0x2f)`). + +``` +#include <stdio.h> +#include <stdint.h> +#include <sys/types.h> + +typedef unsigned char byte; + +static const unsigned char DATA[] = { +    0x74, 0x92, 0x7f, 0x9c, 0xe9, 0x0c, 0xbb, 0xc2, 0x29, 0x19, 0x9b, 0x40, +    0xd7, 0x6e, 0x3d, 0xbe, 0x04, 0xe1, 0x83, 0x4f, 0x83, 0xd4, 0x85, 0x91, +    0xaf, 0x70, 0x5d, 0xfd, 0xf1, 0x7f, 0xc4, 0x88, 0x71, 0xfa, 0x78, 0xe6, +    0x0d, 0xdf, 0xcb, 0x72, 0xa7, 0x3d, 0xb6, 0xf4, 0x3d, 0x9e, 0x29, 0x54, +    0xf4, 0x7b, 0x05, 0xaa, 0xa3, 0x4d, 0x14, 0x14, 0x3c, 0x02, 0xc6, 0xe1, +    0x39, 0xb5, 0xb9, 0x74, 0x0f, 0xd8, 0x5f, 0x54, 0x29, 0x73, 0x7a, 0x04, +    0x3f, 0xd9, 0x41, 0xad, 0xd0, 0xbc, 0x16, 0x96, 0x50, 0x62, 0x59, 0x76, +    0x0f, 0xec, 0xa7, 0xaa, 0x2f, 0xf2, 0xb1, 0x21, 0x7e, 0xb3, 0x80, 0x87, +    0x15, 0x14, 0x8d, 0x76, 0x60, 0xad, 0xf3, 0x56, 0x4d, 0x6f, 0x84, 0x2c, +    0x3e, 0x57, 0x38, 0x15, 0x9e, 0x7b, 0x95, 0x6a, 0x70, 0x08, 0x03, 0xaa, +    0xbc, 0xbf, 0xc7, 0x27, 0x4d, 0x88, 0x2e, 0x47, 0x71, 0x09, 0x34, 0xbc, +    0x94, 0xc0, 0x70, 0x95, 0xea, 0x21, 0x55, 0xd6, 0xbe, 0x14, 0x84, 0x86, +    0x8d, 0xec, 0xf7, 0xff, 0xff, 0x65, 0x14, 0xaa, 0xa7, 0x6a, 0xd1, 0x21, +    0x0c, 0xc1, 0x97, 0x84, 0xf7, 0xd2, 0x3a, 0x51, 0xca, 0xbb, 0x11, 0x62, +    0xe5, 0xc8, 0x99, 0x87, 0xbd, 0xfc, 0x37, 0xb5, 0xed, 0x29, 0xcc, 0x44, +    0x5d, 0xd9, 0x8a, 0x40, 0xb1, 0x02, 0x09, 0x2d }; + +int flag_gen(uint param_1) +{ +  byte bVar1; +  uint uVar2; +  int iVar3; + +  uVar2 = 0; +  iVar3 = 0; +  do { +    bVar1 = (byte)iVar3 & 0x1f; +    iVar3 = iVar3 + 1; +    param_1 = (param_1 * param_1 >> bVar1 | param_1 * param_1 << 0x20 - bVar1) * 0x1337 + 0x4201337 +              ^ uVar2; +    uVar2 = uVar2 + 0x13371337; +  } while (iVar3 != 0x20); +  return (param_1 >> 8) + (param_1 >> 0x10) + param_1 + (param_1 >> 0x18); +} + +int main(void) { +    const int *arr = (void *)DATA; +    for (int i = 0; i < 47; i++) { +        int c = flag_gen(arr[i]); +        printf("%c", (char)c); +    } +    printf("\n"); +    return 0; +} +``` + +lactf{m4yb3_th3r3_1s_s0m3_m3r1t_t0_us1ng_4_db}  | 
