diff options
-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} |