summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/writeups/2023/lactf/rev/ctfd-plus.txt125
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}