From 3eb8d20f0367730ac354d4385ae859e71817f558 Mon Sep 17 00:00:00 2001 From: Nicholas Assaderaghi Date: Sat, 11 Apr 2026 03:47:50 -0400 Subject: [PATCH 1/4] UNG chall files --- .../crypto/Unfinished Ninjago Game/PRNG.h | 1170 +++++++++++++++++ .../crypto/Unfinished Ninjago Game/main | Bin 0 -> 54656 bytes 2 files changed, 1170 insertions(+) create mode 100644 umassctf-2026/crypto/Unfinished Ninjago Game/PRNG.h create mode 100644 umassctf-2026/crypto/Unfinished Ninjago Game/main diff --git a/umassctf-2026/crypto/Unfinished Ninjago Game/PRNG.h b/umassctf-2026/crypto/Unfinished Ninjago Game/PRNG.h new file mode 100644 index 00000000..e4ee1d1f --- /dev/null +++ b/umassctf-2026/crypto/Unfinished Ninjago Game/PRNG.h @@ -0,0 +1,1170 @@ +#pragma once +// Mostly taken from here: +// https://prng.di.unimi.it/xoshiro512starstar.c +// Added more jumps functions so that most jumps can be made quickly, the arrays were computed exactly the same way as the original jumps for 2^256 and 2^384 +// I only care about outputting a number between a fixed modulus, so the return value of next was slightly modified +// TODO: write short code to compute pow(x, n, char_poly(next)) for arbitrary n instead of relying on multiple fixed jumps, would prolly only need basic polynomial multiplication instead of full FFT nonsense since small number of bits +unsigned long long int s[8]; + +static void next() { + const unsigned long long int s1 = s[1] << 11; + s[2] ^= s[0]; + s[5] ^= s[1]; + s[1] ^= s[2]; + s[7] ^= s[3]; + s[3] ^= s[4]; + s[4] ^= s[5]; + s[0] ^= s[6]; + s[6] ^= s[7]; + s[6] ^= s1; + s[7] = s[7] << 21 | s[7] >> 43; +} + +static void jump0() { next(); } + +static void jump16() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x3d96a5f67b544b01 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xed1329c6a4070e53 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x12990bf72e92851e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x6d09b79c36d62b2f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4190b88dd2af5806 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xde92a62cf9b6e481 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xf07d188da9aded5b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1f45f1c244710ce1 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump32() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x18587e0ed4e7026e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xcfb2c59a17a592d9 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x937d6ff4e373df9f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4cd76c9dcf183c6c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x371b5582ae2acff1 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xf265c755b171fd8e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x5c01eaa035e14907 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4bc4a81d8546c20d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump48() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x8958658958ca33f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xe4c5a8880cf02295 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x55b14f2578556fe5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9890b3a1c8935134 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x96c270272cf41807 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x2ead9e6150f7d960 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x97b44ef421417311 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa48cf07b146bca55 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump64() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xe7b4e73e78fb8117 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x8391bfebd93542fe & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x60a90a164c9a4cbb & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x7bf9956ec44fed53 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x90ac3bf9614acfab & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd7c0431b301e1f7a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x6a330fe287306857 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x87550d2f87fcf1fd & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump80() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xd05c71bf5ffd1534 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd22fe5a42481b62e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xf7d3bf2ba2f22c79 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xc807b587123fb841 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x32de84354ad6c9bc & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x236f99122db9c8de & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x2fd0caf4f1a62820 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xbed6922c2773dfda & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump96() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xe0824d90a5490508 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x723b4f9544153207 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x75ea598ff8973cf9 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xf0cb913d52eeb61a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x420368c354e7cac8 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x336db69730b9a17e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x7fbe82d4470462c4 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xbf45cf4053864168 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump112() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x169b24003f7292c3 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x414f738ae68a9f4a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x7bdf255c4f34f1b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x94d5f5f6fa264104 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd1a4374e988d117f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4be623490982080c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4798f17a45140ecb & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x36fa2b6ec1cacd12 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump128() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xc7ae12ce65328237 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9d283542f60f82d5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x970289e59c77718c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xc8fc2c850773a5df & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x18a3f30ca204b590 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xbedc44bec76af8b5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x557983fb4641ed85 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa60ed942c519640 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump144() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x3cea15cb43aefa36 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x22020191a316000a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x18c4d44a8797cb39 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x6122bfd9a0a785cb & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4c7f05b716e5b0c1 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x3e9fad53d0530ac0 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1079ac5f07dff5f0 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xb95cfb1e2a52d674 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump160() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x8629d3c5d6551e50 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x5d03e2f0a6b10792 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4f4a3215c5aefcb0 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xfb7d2409cf4c3a40 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x791a8c2764c9e386 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xbebc0990a0b7460a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xcb5f001623de86d6 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x8a48ac97c38bf985 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump176() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xac3ef4491db9b702 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xab8304c0e985d771 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x671fb1fc4fd3da81 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xafa612907027c7c0 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd3411a1b316b572d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa4d797a39551776b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x67e4ba767df771e3 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x611bceea68b8cd1e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump192() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xdb496e81422d1c3 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xe1370b805b2009a0 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x742c230c72b776e9 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x97c12094ff9a5f45 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x17ca9d94fb0e001b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x185f30cc44e113ef & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xebd44028e8b9e999 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x37d3e61834a52ea2 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump208() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xe0b829713cf23deb & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xc4356949a2cfd83b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xfdf11664f39bd49a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xc2b5421e6b3ebd2e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4e4797b898a3d3b0 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x626ccca2199edab & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x2a861861a55c63ce & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x96e59ebb7acda1ae & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump224() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x5af41fbd515ade8f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd9638bfa3fe0691f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xefb9f85dfc143a3f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x6779d155151f533a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1f96d479dc122662 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x97a611049af72d13 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x744a6fff3a3b4820 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa184aa3b79af5993 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump240() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xed13bed2fb3eddac & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9b9b44cbf76614a0 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xba230e58290f59b2 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x6e430bfc7350d66a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4ee0045da1560e11 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa5b53c715478ed5e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x58a10b29be1c2449 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x2d6c7aa0c0a99366 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump256() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x33ed89b6e7a353f9 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x760083d7955323be & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x2837f2fbb5f22fae & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4b8c5674d309511c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xb11ac47a7ba28c25 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xf1be7667092bcc1c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x53851efdb6df0aaf & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1ebbc8b23eaf25db & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump272() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xb50f1629425328b5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xb3e28f1df09693a1 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd8248055372c51de & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xad730eb31a31a609 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1afa753e0d0413a7 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x20dbaa97688f9ca9 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd3017240492a427c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xe9f9a468de9c2f89 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump288() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x7d1daa6ee2ebadb6 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x287fb383602eb70d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x66905c40cc44b83 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x216494af139e9904 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x8d6d7ed8082eb7d4 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x5779be6822895d90 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa6743c01521e3d40 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x619cd7d6c5269f5f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump304() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xeceda37c8801b9d5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x55d7a5bb4bb5a08d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9c801a619e7ce402 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x250722190a2a6eea & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd224b134d69a1f64 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x648b5535dee537e1 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x2e79d2f16cd1640b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x630c49df3e41db4e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump320() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x7cd739a43307753f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xef985d8085e83f79 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x5d40b3da06d8c43d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1c05fad8cda42c6 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9d3d80a518fb1c00 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x77d6aa13954aef87 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x51ea18c1c8429006 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x48d0cea9bbce0f6d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump336() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x5caa48245a2f43f5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x538532a2f6902501 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xc45c12d07f79de13 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xfffb3d171015e50c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9139536c243747d5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1db8779bb59d26be & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x14f6d7673eb70d4e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x56375088c8287602 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump352() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x59eda6cec05f85d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4c2c027d49680335 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xcab77fc791b25528 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x43bd22ea733667d5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd50ec2170882e367 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x3c1643a8607a9567 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xdbb8d29627c4ff68 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xb3a6cf4b19a16195 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump368() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x209f92f82aae2b2b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x38f7682590d78c42 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x3f0f7ca6aa72d16e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x95837ce91ff5d5ee & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x30342a74677609bc & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x2c1920d76bf5dc2e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd619493e79da5e4c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xc73662c92bf90be7 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump384() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x11467fef8f921d28 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa2a819f2e79c8ea8 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa8299fc284b3959a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xb4d347340ca63ee1 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1cb0940bedbff6ce & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd956c5c4fa1f8e17 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x915e38fd4eda93bc & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x5b3ccdfa5d7daca5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump400() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xcbcb1ce2f9ed900f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9a19b30d6c86b4 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x8ed114c552d7443f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xfb8c922a34ef014f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x74ff372663da5327 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xe504f7171144b4b3 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa207f51dac168f8b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x61e110fdf65bc9cd & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump416() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x1d881515c655b44b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa67e5a484a024e04 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x62a28b07467aea38 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xe73f7257426fdbf6 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x6313a7f540a3c5ba & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x63beeb8fef52d756 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xafe9c85b5337451b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xc376c206f6369913 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump432() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x3d44f46071dcde9f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x26bbbc2cb5ad3397 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x48eb53308836e739 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9f0afb1f101d583d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xe933b386338e6c81 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4def7d8adedea1a7 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xc2f46d6879ee8e11 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xfa0054daa05bd531 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump448() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x6ef6eee71fa0848e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x542c1a94fc3d12b6 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x8aefb26285b6a7ce & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x55048c79feda7401 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x5115d5e1a57feb1e & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xb2a0f741a34abc8f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x69e6815f5520641c & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xf45915de2f2b79ec & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump464() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x5e622ae6f1aaebd & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x1f6bbe4ce1b5e32b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x4958654680afe816 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x2569693ddab27cf4 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xdd12b9a77bf8ef33 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x26b56e1beb2a90ad & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x9e99817fa900580d & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x425acbe1ac2256f & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump480() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0x57f6ca29db34f1f4 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd8fca164df0c1e9b & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x6f84294e00b66d35 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xeea4145662c7876a & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa10fc918dcf5ee09 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xd02e3e36e2460115 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xaab23c13d43fb4e5 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x7f506cf199417579 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} + +static void jump496() { + unsigned long long int t[8] = {0}; + for (int b = 0; b < 64; b++) { + if (0xa8ea9a4e883df5c6 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x7426a74e6e34fb0 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xe3372d102776d037 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x416e824af0f05487 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x8550f93157d450af & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xa997a5bda82d8272 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0x8d92ef37e45bffa6 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int b = 0; b < 64; b++) { + if (0xb706ea9ac253f320 & 1ull << b) for (int w = 0; w < 8; w++) t[w] ^= s[w]; + next(); + } + for (int w = 0; w < 8; w++) s[w] = t[w]; +} diff --git a/umassctf-2026/crypto/Unfinished Ninjago Game/main b/umassctf-2026/crypto/Unfinished Ninjago Game/main new file mode 100644 index 0000000000000000000000000000000000000000..3c99b2bafe41adb9d4cb52ce12d1efeef191b4b7 GIT binary patch literal 54656 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 F{{V%JPkjIY literal 0 HcmV?d00001 From 67d449d9164bdda4928fe65acb822c18524718fd Mon Sep 17 00:00:00 2001 From: Nicholas Assaderaghi Date: Sun, 12 Apr 2026 13:59:10 -0400 Subject: [PATCH 2/4] Isaac's analysis.md Co-authored-by: Isaac Trost --- .../Unfinished Ninjago Game/analysis.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 umassctf-2026/crypto/Unfinished Ninjago Game/analysis.md diff --git a/umassctf-2026/crypto/Unfinished Ninjago Game/analysis.md b/umassctf-2026/crypto/Unfinished Ninjago Game/analysis.md new file mode 100644 index 00000000..a19bd688 --- /dev/null +++ b/umassctf-2026/crypto/Unfinished Ninjago Game/analysis.md @@ -0,0 +1,20 @@ +### Overall Structure + +The challenge first reads the flag, XORd with getrandom(&s,0x40,0). +It then gives you a menu option. You can to r to do a right thing, l to do a left. Each of these is basically the same, picks a random jump from PRNG.h and applying it to the output (after you send an A to apply) Then we get a leak of +s % 0x65 + DAT_0010d048 % 0x65 + DAT_0010d050 % 0x65 + DAT_0010d058 % 0x65 + +DAT_0010d060 % 0x65 + DAT_0010d068 % 0x65 + DAT_0010d070 % 0x65 + DAT_0010d078 % 0x65; +  putc((int)uVar1 + (int)(uVar1 / 0x65) * -0x65,stdout); + +If we send an m + +With these we should be able to fully constrain the initial state. + +There is this other weird thing with the other inputs, you can read and wr + +### Solution Idea +We can leak some state, then try to get the initial prng state out, which we can xor with the flag. + +This should be doable since all the modifications are linear and thus simple to reverse in theory + +We may also be able to do it with the poly arithmetic. From 978e1f181996698f81aa5ccf790e15d43009f519 Mon Sep 17 00:00:00 2001 From: Nicholas Assaderaghi Date: Wed, 15 Apr 2026 04:56:32 -0400 Subject: [PATCH 3/4] challenge solved! --- .../crypto/Unfinished Ninjago Game/README.md | 228 ++++++++++++++++++ .../Unfinished Ninjago Game/analysis.md | 20 -- .../crypto/Unfinished Ninjago Game/exploit.py | 151 ++++++++++++ 3 files changed, 379 insertions(+), 20 deletions(-) create mode 100644 umassctf-2026/crypto/Unfinished Ninjago Game/README.md delete mode 100644 umassctf-2026/crypto/Unfinished Ninjago Game/analysis.md create mode 100644 umassctf-2026/crypto/Unfinished Ninjago Game/exploit.py diff --git a/umassctf-2026/crypto/Unfinished Ninjago Game/README.md b/umassctf-2026/crypto/Unfinished Ninjago Game/README.md new file mode 100644 index 00000000..dd862ffb --- /dev/null +++ b/umassctf-2026/crypto/Unfinished Ninjago Game/README.md @@ -0,0 +1,228 @@ +# Unfinished Ninjago Game + +## Summary + +I'm writing a ninjago game, but apparently there's some crazy cheat codes someone has already found!? And apparently that's supposed to help decrypt hidden secret stuff by the Overlord? + +**Artifacts:** + +- `main`: ELF 64-bit PIE x86-64 binary; the challenge server +- `PRNG.h`: source for the modified xoshiro512\*\* PRNG embedded in `main` +- `exploit.py`: Python script that implements the attack + +## Context + +### PRNG + +`PRNG.h` implements a modified [xoshiro512\*\*](https://prng.di.unimi.it/) generator. The state is eight 64-bit words `s[0..7]` (a total of 512 bits), seeded by `getrandom` before anything else happens. The file also defines thirty-two precomputed **jump functions** (`jump0`, `jump16`, …, `jump496`) that advance the state by fixed powers of two without calling `next()` iteratively. The comment in the file hints at a planned-but-unimplemented feature: + +```c +// TODO: write short code to compute pow(x, n, char_poly(next)) for arbitrary n +``` + +This is a red herring; the intended exploit does not require computing arbitrary jumps. + +### Program Flow + +On each TCP connection, `main` runs the following sequence: + +1. **Seed** — `getrandom(s, 64, 0)` fills all eight state words with fresh random bytes. +2. **`load()`** — Opens `flag.txt`, reads up to 64 bytes into a local buffer, and prints the ciphertext: + + ```c + for (int i = 0; i < 8; i++) + printf("%llu ", local_buf[i] ^ s[i]); + putc('\n', stdout); + remove("flag.txt"); + ``` + + The flag is treated as eight little-endian `uint64` words. The PRNG state is **never advanced** before or after this step, so `s[i]` at this point is exactly the seeded value. +3. **`explore_right()`** — An interactive command loop that reads single characters from stdin: + + | Input | Action | + | ------------- | ------------------------------------------------------------------------------------------ | + | `e` | exit | + | `m` | call `explore_middle()` — the oracle | + | `a` | advance PRNG with a**randomly chosen** jump (via `getrandom`; not user-controlled) | + | `s` | set a global pointer `d` to a stack address | + | `w` | write one `getc` byte via `d` (out-of-bounds write, but cannot reach the canary) | + | `r` / `l` | recurse into `explore_right` / `explore_left` | + +### The Oracle + +`explore_middle()` computes and outputs a single byte: + +```c +uint64_t sum = s[0] + s[1] + ... + s[7]; // wrapping uint64 addition +putc((int)(sum % 101), stdout); +``` + +The magic-number division constant `0x446f86562d9faee5` is the compiler's fast unsigned divide-by-101. If `'m'` is sent **before** any `'a'` commands, the oracle reflects the original seeded state — the same state used to encrypt the flag. + +## Vulnerability + +Because the attacker knows `ct[i] = s[i] XOR flag_word[i]` and the oracle $O = \left(\sum_{i=0}^{7} s_i\right) \bmod 101$, all the information needed to recover the flag is present in each connection — it just needs to be extracted algebraically. + +### Bit-level decomposition + +Index the 512 flag bits as $j = 0, 1, \ldots, 511$, where bit $j$ belongs to word $\lfloor j / 64 \rfloor$ at bit position $j \bmod 64$. Let: + +- $b_j = \text{bit}_j(ct)$ — the corresponding ciphertext bit (known) +- $x_j = \text{bit}_j(flag)$ — the flag bit (unknown) + +Since XOR is bitwise addition mod 2, $\text{bit}_j(s) = b_j \oplus x_j$. As integers, this is: + +$$ +b_j \oplus x_j = b_j + x_j - 2 b_j x_j +$$ + +Substituting into the oracle (where bit $j$ contributes weight $2^{j \bmod 64}$ to its containing word): + +$$ +O = \sum_{j=0}^{511} 2^{j \bmod 64} \cdot (b_j + x_j - 2 b_j x_j) \pmod{101} +$$ + +$$ +O - \underbrace{\sum_{j=0}^{511} 2^{j \bmod 64} \cdot b_j}_{\displaystyle=\;\sum_{i=0}^{7} ct_i \pmod{101}} \;\equiv\; \sum_{j=0}^{511} \underbrace{(1 - 2b_j) \cdot 2^{j \bmod 64}}_{\displaystyle H_j} \cdot x_j \pmod{101} +$$ + +### Linear system + +Defining: + +$$ +h \;=\; \left(O - \sum_{i=0}^{7} ct_i\right) \bmod 101 \qquad H_j \;=\; (1 - 2b_j) \cdot 2^{j \bmod 64} \bmod 101 +$$ + +every connection yields one linear equation $\sum_j H_j x_j \equiv h \pmod{101}$, with known coefficients determined by that connection's ciphertext and oracle reading. Because $101$ is prime, $\mathbb{Z}/101\mathbb{Z}$ is a field. With 512 fresh connections producing linearly independent equations (guaranteed with overwhelming probability because the ciphertexts are independently random), the $512 \times 512$ system has a **unique solution** over $\mathbb{Z}/101\mathbb{Z}$. Since the unknowns $x_j$ are flag bits, they are in $\{0, 1\} \subset \mathbb{Z}/101\mathbb{Z}$, so the unique field solution equals the true bit values. + +## Exploitation + +The below steps are implemented in [exploit.py](./exploit.py). + +### 1. Collect (ciphertext, oracle) pairs + +Open 20 parallel TCP connections. For each connection, read the eight ciphertext words and send `'m'` immediately (before any `'a'` commands so the state is still at its initial seeded value), then read the oracle byte. + +```python +def query(): + """Open one connection, return (ct, oracle) or raise on error.""" + s = socket.socket() + s.settimeout(15) + try: + s.connect((HOST, PORT)) + data = b"" + while b"\n" not in data: + data += s.recv(4096) + ct = [int(x) for x in data.split(b"\n")[0].split()] + s.sendall(b"m") + oracle = s.recv(1)[0] + s.sendall(b"e") + return ct, oracle + finally: + s.close() +``` + +Worker threads run `query()` in a loop and push results onto a shared queue. The main thread reads from the queue until 560 samples are collected (48 extra beyond 512 as a safety margin). + +### 2. Build the linear system + +Translate each `(ct, oracle)` pair into a row of the matrix $H$ and the right-hand-side vector $h$ over $\mathbb{Z}/101\mathbb{Z}$. + +The ciphertext values can exceed $2^{63}$, so Python's arbitrary-precision integers are used for the modular arithmetic in $h$ rather than NumPy (which would silently overflow `int64`). + +```python +def build_system(samples): + """ + Build H (n×512) and h (n,) over Z/101Z from (ct, oracle) pairs. + + For flag bit j spanning word index j//64 at bit position j%64: + H[i][j] = (1 - 2 * bit_j(ct[i])) * 2^(j%64) mod 101 + h[i] = (oracle[i] - Σ_k ct[i][k]) mod 101 + """ + n = len(samples) + H = np.zeros((n, N_BITS), dtype=np.int64) + h = np.zeros(n, dtype=np.int64) + for i, (ct, oracle) in enumerate(samples): + h[i] = (oracle - sum(c % MOD for c in ct)) % MOD # Python int: safe for uint64 + for j in range(N_BITS): + ct_bit = (ct[j // 64] >> (j % 64)) & 1 + H[i, j] = (1 - 2 * ct_bit) * POW2[j] % MOD + return H, h +``` + +`POW2[j] = pow(2, j % 64, 101)` is precomputed once at module load. + +### 3. Solve via RREF over $\mathbb{Z}/101\mathbb{Z}$ + +Gaussian elimination (reduced row echelon form) is performed on the augmented matrix $[H \mid h]$. For each pivot column, the pivot row is scaled so the leading entry becomes 1 (using Fermat's little theorem to compute the modular inverse: $a^{-1} \equiv a^{99} \pmod{101}$), and then that column is eliminated from every other row simultaneously using NumPy vectorised operations. + +```python +def rref_mod_p(A, b, p): + """ + Reduce augmented matrix [A|b] to RREF over Z/pZ (p prime). + Returns (reduced matrix, list of pivot column indices). + """ + M = np.hstack([A % p, (b % p).reshape(-1, 1)]).astype(np.int64) + pivots, r = [], 0 + + for col in range(A.shape[1]): + # Find a non-zero entry in this column at or below row r + nz = np.nonzero(M[r:, col] % p)[0] + if not len(nz): + continue + + M[[r, r + nz[0]]] = M[[r + nz[0], r]] # swap into pivot position + M[r] = M[r] * pow(int(M[r, col]), p - 2, p) % p # scale so pivot = 1 + + # Eliminate this column in every other row + mask = (M[:, col] % p != 0) + mask[r] = False + M[mask] = (M[mask] - M[mask, col:col+1] * M[r]) % p + + pivots.append(col) + r += 1 + if r >= M.shape[0]: + break + + return M, pivots +``` + +After RREF the solution vector is read directly from the last column of the reduced matrix: `x[col] = M[i, 512]` for each pivot `(i, col)`. + +### 4. Reconstruct the flag + +The 512 recovered bits are packed back into 64 bytes. Bit $j$ lands at byte $\lfloor j/8 \rfloor$ at bit position $j \bmod 8$, which follows directly from the little-endian layout of the eight `uint64` flag words. + +```python +def reconstruct_flag(x): + """ + Pack the 512 recovered flag bits back into 64 bytes. + Bit j maps to byte j//8 (little-endian within each 64-bit word), bit position j%8. + """ + flag = bytearray(64) + for j in range(N_BITS): + flag[j // 8] |= x[j] << (j % 8) + return bytes(flag) +``` + +Running the exploit against the live server: + +``` +[*] Collecting 560 samples (20 threads)... + 100/560 (221 q/s) + 200/560 (225 q/s) + ... +[*] Collected 560 samples in 2.5s +[*] Building 560×512 matrix over Z/101Z... +[*] Solving via RREF... + +[+] UMASS{sparse_fourier_transforms_are_so_much_fun!fhwtftw!yayayay} +``` + +## Remediation + +The root cause is that the oracle `(Σ s[i]) mod 101` leaks a linear combination of the PRNG state, and because each connection re-seeds the PRNG independently while broadcasting the ciphertext, an attacker accumulates enough linear equations to fully determine the secret state. Two concrete fixes: + +1. **Use a larger modulus.** A modulus of 101 means coefficients lie in a tiny field, making the linear system easy to solve. If the modulus were a 64-bit prime (or the output were a full 64-bit word), the attacker would need far more information per query and the system would be much harder to solve in practice. +2. **Do not expose a linear oracle over freshly seeded states with known ciphertext.** The combination of a known linear function of the state *and* a known linear function of `state XOR flag` is what enables the attack. Either hide one of the two (don't print the ciphertext, or don't offer the oracle), or introduce non-linearity (e.g., hash the state before outputting it). diff --git a/umassctf-2026/crypto/Unfinished Ninjago Game/analysis.md b/umassctf-2026/crypto/Unfinished Ninjago Game/analysis.md deleted file mode 100644 index a19bd688..00000000 --- a/umassctf-2026/crypto/Unfinished Ninjago Game/analysis.md +++ /dev/null @@ -1,20 +0,0 @@ -### Overall Structure - -The challenge first reads the flag, XORd with getrandom(&s,0x40,0). -It then gives you a menu option. You can to r to do a right thing, l to do a left. Each of these is basically the same, picks a random jump from PRNG.h and applying it to the output (after you send an A to apply) Then we get a leak of -s % 0x65 + DAT_0010d048 % 0x65 + DAT_0010d050 % 0x65 + DAT_0010d058 % 0x65 + -DAT_0010d060 % 0x65 + DAT_0010d068 % 0x65 + DAT_0010d070 % 0x65 + DAT_0010d078 % 0x65; -  putc((int)uVar1 + (int)(uVar1 / 0x65) * -0x65,stdout); - -If we send an m - -With these we should be able to fully constrain the initial state. - -There is this other weird thing with the other inputs, you can read and wr - -### Solution Idea -We can leak some state, then try to get the initial prng state out, which we can xor with the flag. - -This should be doable since all the modifications are linear and thus simple to reverse in theory - -We may also be able to do it with the poly arithmetic. diff --git a/umassctf-2026/crypto/Unfinished Ninjago Game/exploit.py b/umassctf-2026/crypto/Unfinished Ninjago Game/exploit.py new file mode 100644 index 00000000..1fa314a8 --- /dev/null +++ b/umassctf-2026/crypto/Unfinished Ninjago Game/exploit.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +"""Exploit for "Unfinished Ninjago Game" — UMassCTF 2026 (hard, pwn/crypto) + +Per-connection protocol: + Server → "ct0 ct1 … ct7\n" (ct[i] = s[i] XOR flag_word[i], printed by load()) + Client → "m" (call explore_middle()) + Server → <1 byte 0–100> (oracle = (Σ s[i]) mod 101) + Client → "e" (exit) + +Attack: each connection gives one linear equation over Z/101Z in the 512 flag bits. +Collect 560 equations, solve H·x ≡ h (mod 101) via RREF, reconstruct the flag. +""" + +import socket, sys, threading, time +import numpy as np +from queue import Queue, Empty + +HOST, PORT = "34.26.193.62", 32769 +MOD = 101 +N_BITS = 512 # 8 words × 64 bits +N_COLLECT = 560 # extra equations for safety margin +N_THREADS = 20 + +# Precomputed: pow(2, j % 64, 101) for j in 0..511 +POW2 = [pow(2, j % 64, MOD) for j in range(N_BITS)] + +queue = Queue() + + +def query(): + """Open one connection, return (ct, oracle) or raise on error.""" + s = socket.socket() + s.settimeout(15) + try: + s.connect((HOST, PORT)) + data = b"" + while b"\n" not in data: + data += s.recv(4096) + ct = [int(x) for x in data.split(b"\n")[0].split()] + s.sendall(b"m") + oracle = s.recv(1)[0] + s.sendall(b"e") + return ct, oracle + finally: + s.close() + + +def worker(): + while True: + try: + queue.put(query()) + except Exception: + pass + + +def build_system(samples): + """ + Build H (n×512) and h (n,) over Z/101Z from (ct, oracle) pairs. + + For flag bit j spanning word index j//64 at bit position j%64: + H[i][j] = (1 - 2 * bit_j(ct[i])) * 2^(j%64) mod 101 + h[i] = (oracle[i] - Σ_k ct[i][k]) mod 101 + """ + n = len(samples) + H = np.zeros((n, N_BITS), dtype=np.int64) + h = np.zeros(n, dtype=np.int64) + for i, (ct, oracle) in enumerate(samples): + h[i] = (oracle - sum(c % MOD for c in ct)) % MOD # Python int: safe for uint64 + for j in range(N_BITS): + ct_bit = (ct[j // 64] >> (j % 64)) & 1 + H[i, j] = (1 - 2 * ct_bit) * POW2[j] % MOD + return H, h + + +def rref_mod_p(A, b, p): + """ + Reduce augmented matrix [A|b] to RREF over Z/pZ (p prime). + Returns (reduced matrix, list of pivot column indices). + """ + M = np.hstack([A % p, (b % p).reshape(-1, 1)]).astype(np.int64) + pivots, r = [], 0 + + for col in range(A.shape[1]): + # Find a non-zero entry in this column at or below row r + nz = np.nonzero(M[r:, col] % p)[0] + if not len(nz): + continue + + M[[r, r + nz[0]]] = M[[r + nz[0], r]] # swap into pivot position + M[r] = M[r] * pow(int(M[r, col]), p - 2, p) % p # scale so pivot = 1 + + # Eliminate this column in every other row + mask = (M[:, col] % p != 0) + mask[r] = False + M[mask] = (M[mask] - M[mask, col:col+1] * M[r]) % p + + pivots.append(col) + r += 1 + if r >= M.shape[0]: + break + + return M, pivots + + +def reconstruct_flag(x): + """ + Pack the 512 recovered flag bits back into 64 bytes. + Bit j maps to byte j//8 (little-endian within each 64-bit word), bit position j%8. + """ + flag = bytearray(64) + for j in range(N_BITS): + flag[j // 8] |= x[j] << (j % 8) + return bytes(flag) + + +def main(): + print(f"Collecting {N_COLLECT} samples ({N_THREADS} threads)...") + for _ in range(N_THREADS): + threading.Thread(target=worker, daemon=True).start() + + samples, t0 = [], time.time() + while len(samples) < N_COLLECT: + try: + samples.append(queue.get(timeout=30)) + n = len(samples) + if n % 100 == 0: + print(f" {n}/{N_COLLECT} ({n / (time.time() - t0):.0f} q/s)") + except Empty: + sys.exit("[!] Timed out waiting for samples") + + print(f"Collected {len(samples)} samples in {time.time()-t0:.1f}s") + print(f"Building {len(samples)}×{N_BITS} matrix over Z/{MOD}Z...") + H, h = build_system(samples) + + print("Solving via RREF...") + M, pivots = rref_mod_p(H, h, MOD) + + if len(pivots) < N_BITS: + sys.exit(f"[!] Rank {len(pivots)} < {N_BITS} — collect more samples") + + x = [0] * N_BITS + for i, col in enumerate(pivots): + x[col] = int(M[i, N_BITS]) + + flag = reconstruct_flag(x) + flag_str = flag.rstrip(b"\x00").decode() + print(f"\nFlag: {flag_str}") + + +if __name__ == "__main__": + main() From eb3b342ed66b04cc6780d5d8ecf5f84b17a357f9 Mon Sep 17 00:00:00 2001 From: Nicholas Assaderaghi Date: Fri, 15 May 2026 15:04:55 +0000 Subject: [PATCH 4/4] feat: polish up and finish writeup Covers the xoshiro512** linear oracle attack: RE'd binary with Ghidra, derived the Z/101Z linear system, and verified the exploit locally against the Docker image from the now publicly available source files. --- .../crypto/Unfinished Ninjago Game/README.md | 171 +++++++++--------- .../crypto/Unfinished Ninjago Game/exploit.py | 2 +- 2 files changed, 90 insertions(+), 83 deletions(-) diff --git a/umassctf-2026/crypto/Unfinished Ninjago Game/README.md b/umassctf-2026/crypto/Unfinished Ninjago Game/README.md index dd862ffb..c14e9c83 100644 --- a/umassctf-2026/crypto/Unfinished Ninjago Game/README.md +++ b/umassctf-2026/crypto/Unfinished Ninjago Game/README.md @@ -2,7 +2,9 @@ ## Summary -I'm writing a ninjago game, but apparently there's some crazy cheat codes someone has already found!? And apparently that's supposed to help decrypt hidden secret stuff by the Overlord? +Challenge description: "I'm writing a ninjago game, but apparently there's some crazy cheat codes someone has already found!? And apparently that's supposed to help decrypt hidden secret stuff by the Overlord?" + +A 64-bit ELF binary runs as a TCP server. On each connection it seeds a 512-bit xoshiro512\*\* PRNG state with `getrandom`, XORs that state against the flag to produce a public ciphertext, then accepts commands — including an oracle that outputs a single byte `(Σ s[i]) mod 101`. Because the oracle is a linear function of the PRNG state and the ciphertext reveals `state XOR flag`, every connection yields one linear equation over $\mathbb{Z}/101\mathbb{Z}$ in the 512 flag bits. Collecting 512 such equations and solving via Gaussian elimination recovers the flag. **Artifacts:** @@ -14,7 +16,7 @@ I'm writing a ninjago game, but apparently there's some crazy cheat codes someon ### PRNG -`PRNG.h` implements a modified [xoshiro512\*\*](https://prng.di.unimi.it/) generator. The state is eight 64-bit words `s[0..7]` (a total of 512 bits), seeded by `getrandom` before anything else happens. The file also defines thirty-two precomputed **jump functions** (`jump0`, `jump16`, …, `jump496`) that advance the state by fixed powers of two without calling `next()` iteratively. The comment in the file hints at a planned-but-unimplemented feature: +`PRNG.h` implements a modified [xoshiro512\*\*](https://prng.di.unimi.it/) generator. The state is eight 64-bit words `s[0..7]` (512 bits total), seeded fresh by `getrandom` on each TCP connection. The file defines 32 precomputed **jump functions** (`jump0`, `jump16`, …, `jump496`) that advance the state by $2^n$ steps for $n = 0, 16, 32, \ldots, 496$ without iterating through all intermediate `next()` calls. A comment in the file hints at a planned-but-unimplemented feature: ```c // TODO: write short code to compute pow(x, n, char_poly(next)) for arbitrary n @@ -22,68 +24,94 @@ I'm writing a ninjago game, but apparently there's some crazy cheat codes someon This is a red herring; the intended exploit does not require computing arbitrary jumps. -### Program Flow +### Binary Analysis -On each TCP connection, `main` runs the following sequence: +Reversing `main` with Ghidra reveals five key functions (`main` at `0x0010a440`, `load` at `0x0010a0f8`, `explore_middle` at `0x0010a235`, `explore_right` at `0x0010a711`, `explore_left` at `0x0010a4c3`). -1. **Seed** — `getrandom(s, 64, 0)` fills all eight state words with fresh random bytes. -2. **`load()`** — Opens `flag.txt`, reads up to 64 bytes into a local buffer, and prints the ciphertext: +**`main`** seeds the global PRNG state `s[0..7]` with `getrandom`, calls `load()`, then gates `explore_right()` on `errno`. The `remove("flag.txt")` call inside `load()` always fails with `EACCES` — the server runs as an unprivileged `ctf` user and cannot write to the root-owned `/ctf` directory. This non-zero `errno` is what makes `explore_right()` reachable: - ```c - for (int i = 0; i < 8; i++) - printf("%llu ", local_buf[i] ^ s[i]); - putc('\n', stdout); - remove("flag.txt"); - ``` +```c +int main(void) { + errno = 0; + if (getrandom(s, 64, 0) != 64) return -1; // seed 512-bit state + load(); + if (errno == 0) return -1; // remove() always fails → errno always set → explore_right reachable + explore_right(); + return 0; +} +``` - The flag is treated as eight little-endian `uint64` words. The PRNG state is **never advanced** before or after this step, so `s[i]` at this point is exactly the seeded value. -3. **`explore_right()`** — An interactive command loop that reads single characters from stdin: +**`load()`** reads `flag.txt` into a stack buffer, XORs each 64-bit word with the seeded PRNG state, and prints the ciphertext. The buffer is pre-filled by a `getrandom` call before being overwritten by the flag contents — the pre-fill is immediately discarded: - | Input | Action | - | ------------- | ------------------------------------------------------------------------------------------ | - | `e` | exit | - | `m` | call `explore_middle()` — the oracle | - | `a` | advance PRNG with a**randomly chosen** jump (via `getrandom`; not user-controlled) | - | `s` | set a global pointer `d` to a stack address | - | `w` | write one `getc` byte via `d` (out-of-bounds write, but cannot reach the canary) | - | `r` / `l` | recurse into `explore_right` / `explore_left` | +```c +void load(void) { + setbuf(stdout, NULL); + uint64_t buf[8] = {0}; + if (getrandom(buf, 64, 0) != 64) return; // pre-fill discarded; only checks syscall success + int fd = open("flag.txt", O_CREAT|O_RDONLY, 0644); // 0x40 = O_CREAT; opens existing file read-only + read(fd, buf, 64); // overwrites buf with flag bytes + close(fd); + for (int i = 0; i < 8; i++) { + printf("%llu ", buf[i] ^ s[i]); // ct[i] = flag_word[i] XOR s[i] + buf[i] = 0; // wipe from stack + } + putc('\n', stdout); + remove("flag.txt"); // always fails (EACCES) — sets errno, keeps flag.txt for the next connection +} +``` -### The Oracle +The flag is treated as eight little-endian `uint64` words. The PRNG state is **never advanced** between the `getrandom` seed and the XOR — `s[i]` in the XOR is exactly the freshly seeded value. -`explore_middle()` computes and outputs a single byte: +**`explore_middle()`** — the oracle — outputs a single byte. Ghidra shows the compiler emits per-element reductions mod 101 before summing, rather than summing all eight words first; the two forms are algebraically identical: ```c -uint64_t sum = s[0] + s[1] + ... + s[7]; // wrapping uint64 addition -putc((int)(sum % 101), stdout); +void explore_middle(void) { + uint64_t sum = s[0]%101 + s[1]%101 + s[2]%101 + s[3]%101 + + s[4]%101 + s[5]%101 + s[6]%101 + s[7]%101; + putc((int)(sum % 101), stdout); +} ``` -The magic-number division constant `0x446f86562d9faee5` is the compiler's fast unsigned divide-by-101. If `'m'` is sent **before** any `'a'` commands, the oracle reflects the original seeded state — the same state used to encrypt the flag. +**`explore_right()`** and **`explore_left()`** are a pair of mutually recursive command loops sharing the same command set: + +| Input | Action | +| --------- | ---------------------------------------------------------------------------------------------- | +| `e` | exit | +| `m` | call `explore_middle()` — the oracle | +| `a` | advance PRNG with a randomly chosen jump (random nibble from `getrandom`; not user-controlled) | +| `s` | set global pointer `d` to a local stack address | +| `w` | write one `getc` byte via `d` (out-of-bounds write, but cannot reach the stack canary) | +| `r` / `l` | recurse into `explore_right` / `explore_left` | + +The two functions differ only in which jump functions their `'a'` command dispatches. For `'a'`, both functions call `getrandom(&byte, 1, 0)`, mask to the lower nibble, and index into a fixed table: `explore_right` selects from `jump0`–`jump240` (nibble × 16) while `explore_left` selects from `jump256`–`jump496` ((nibble × 16) + 256). Together they cover all 32 jump functions. Nibble 0xf in either function executes the highest jump in its range and loops back to re-read, effectively retrying until a nibble below 0xf is drawn. The jump choice is entirely server-side and uncontrollable by the attacker. + +The critical observation: if `'m'` is sent **before** any `'a'` commands, `explore_middle()` sees the original seeded state — the same state used to XOR the flag in `load()`. ## Vulnerability -Because the attacker knows `ct[i] = s[i] XOR flag_word[i]` and the oracle $O = \left(\sum_{i=0}^{7} s_i\right) \bmod 101$, all the information needed to recover the flag is present in each connection — it just needs to be extracted algebraically. +Because the attacker knows $\text{ct}[i] = s[i] \oplus \text{flag}[i]$ and the oracle $O = \left(\sum_{i=0}^{7} s_i\right) \bmod 101$, all the information needed to recover the flag is present in each connection — it just needs to be extracted algebraically. ### Bit-level decomposition -Index the 512 flag bits as $j = 0, 1, \ldots, 511$, where bit $j$ belongs to word $\lfloor j / 64 \rfloor$ at bit position $j \bmod 64$. Let: +Index the 512 flag bits as $j = 0, 1, \ldots, 511$, where bit $j$ belongs to word $\lfloor j/64 \rfloor$ at bit position $j \bmod 64$. Let: -- $b_j = \text{bit}_j(ct)$ — the corresponding ciphertext bit (known) -- $x_j = \text{bit}_j(flag)$ — the flag bit (unknown) +- $b_j = \text{bit}_j(\text{ct})$ — the corresponding ciphertext bit (known) +- $x_j = \text{bit}_j(\text{flag})$ — the flag bit (unknown) -Since XOR is bitwise addition mod 2, $\text{bit}_j(s) = b_j \oplus x_j$. As integers, this is: +Since XOR is bitwise addition mod 2, $\text{bit}_j(s) = b_j \oplus x_j$. As integers: $$ b_j \oplus x_j = b_j + x_j - 2 b_j x_j $$ -Substituting into the oracle (where bit $j$ contributes weight $2^{j \bmod 64}$ to its containing word): +Substituting into the oracle (where bit $j$ contributes weight $2^{j \bmod 64}$ to its word): $$ O = \sum_{j=0}^{511} 2^{j \bmod 64} \cdot (b_j + x_j - 2 b_j x_j) \pmod{101} $$ $$ -O - \underbrace{\sum_{j=0}^{511} 2^{j \bmod 64} \cdot b_j}_{\displaystyle=\;\sum_{i=0}^{7} ct_i \pmod{101}} \;\equiv\; \sum_{j=0}^{511} \underbrace{(1 - 2b_j) \cdot 2^{j \bmod 64}}_{\displaystyle H_j} \cdot x_j \pmod{101} +O - \underbrace{\sum_{j=0}^{511} 2^{j \bmod 64} \cdot b_j}_{\displaystyle=\;\sum_{i=0}^{7} \text{ct}_i \pmod{101}} \;\equiv\; \sum_{j=0}^{511} \underbrace{(1 - 2b_j) \cdot 2^{j \bmod 64}}_{\displaystyle H_j} \cdot x_j \pmod{101} $$ ### Linear system @@ -91,10 +119,12 @@ $$ Defining: $$ -h \;=\; \left(O - \sum_{i=0}^{7} ct_i\right) \bmod 101 \qquad H_j \;=\; (1 - 2b_j) \cdot 2^{j \bmod 64} \bmod 101 +h \;=\; \left(O - \sum_{i=0}^{7} \text{ct}_i\right) \bmod 101 \qquad H_j \;=\; (1 - 2b_j) \cdot 2^{j \bmod 64} \bmod 101 $$ -every connection yields one linear equation $\sum_j H_j x_j \equiv h \pmod{101}$, with known coefficients determined by that connection's ciphertext and oracle reading. Because $101$ is prime, $\mathbb{Z}/101\mathbb{Z}$ is a field. With 512 fresh connections producing linearly independent equations (guaranteed with overwhelming probability because the ciphertexts are independently random), the $512 \times 512$ system has a **unique solution** over $\mathbb{Z}/101\mathbb{Z}$. Since the unknowns $x_j$ are flag bits, they are in $\{0, 1\} \subset \mathbb{Z}/101\mathbb{Z}$, so the unique field solution equals the true bit values. +every connection yields one linear equation $\sum_j H_j x_j \equiv h \pmod{101}$, with known coefficients determined by that connection's ciphertext and oracle reading. Because 101 is prime, $\mathbb{Z}/101\mathbb{Z}$ is a field. With 512 fresh connections producing linearly independent equations (guaranteed with overwhelming probability because the ciphertexts are independently random), the $512 \times 512$ system has a **unique solution** over $\mathbb{Z}/101\mathbb{Z}$. Since the unknowns $x_j$ are flag bits in $\{0, 1\} \subset \mathbb{Z}/101\mathbb{Z}$, the unique field solution equals the true bit values. + +This vulnerability falls under [CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)](https://cwe.mitre.org/data/definitions/338.html). xoshiro512\*\* is a high-quality statistical PRNG, but it is not a CSPRNG — its output is not computationally indistinguishable from random, and the linear structure of its state update is precisely what makes the oracle attack tractable. ## Exploitation @@ -102,11 +132,10 @@ The below steps are implemented in [exploit.py](./exploit.py). ### 1. Collect (ciphertext, oracle) pairs -Open 20 parallel TCP connections. For each connection, read the eight ciphertext words and send `'m'` immediately (before any `'a'` commands so the state is still at its initial seeded value), then read the oracle byte. +Open 20 parallel TCP connections. For each connection, read the eight ciphertext words and send `'m'` immediately (before any `'a'` commands, so the PRNG state is still the freshly seeded value), then read the oracle byte. ```python def query(): - """Open one connection, return (ct, oracle) or raise on error.""" s = socket.socket() s.settimeout(15) try: @@ -123,23 +152,16 @@ def query(): s.close() ``` -Worker threads run `query()` in a loop and push results onto a shared queue. The main thread reads from the queue until 560 samples are collected (48 extra beyond 512 as a safety margin). +Worker threads run `query()` in a loop and push results onto a shared queue. The main thread reads from the queue until 560 samples are collected (48 extra beyond 512 as a safety margin for rank deficiency). ### 2. Build the linear system -Translate each `(ct, oracle)` pair into a row of the matrix $H$ and the right-hand-side vector $h$ over $\mathbb{Z}/101\mathbb{Z}$. +Translate each `(ct, oracle)` pair into a row of the matrix $H$ and the right-hand side vector $h$ over $\mathbb{Z}/101\mathbb{Z}$. -The ciphertext values can exceed $2^{63}$, so Python's arbitrary-precision integers are used for the modular arithmetic in $h$ rather than NumPy (which would silently overflow `int64`). +The ciphertext words are unsigned 64-bit integers and can exceed `int64` range, so Python's arbitrary-precision integers handle the modular arithmetic in $h$ rather than NumPy (which would silently overflow): ```python def build_system(samples): - """ - Build H (n×512) and h (n,) over Z/101Z from (ct, oracle) pairs. - - For flag bit j spanning word index j//64 at bit position j%64: - H[i][j] = (1 - 2 * bit_j(ct[i])) * 2^(j%64) mod 101 - h[i] = (oracle[i] - Σ_k ct[i][k]) mod 101 - """ n = len(samples) H = np.zeros((n, N_BITS), dtype=np.int64) h = np.zeros(n, dtype=np.int64) @@ -155,74 +177,59 @@ def build_system(samples): ### 3. Solve via RREF over $\mathbb{Z}/101\mathbb{Z}$ -Gaussian elimination (reduced row echelon form) is performed on the augmented matrix $[H \mid h]$. For each pivot column, the pivot row is scaled so the leading entry becomes 1 (using Fermat's little theorem to compute the modular inverse: $a^{-1} \equiv a^{99} \pmod{101}$), and then that column is eliminated from every other row simultaneously using NumPy vectorised operations. +Gaussian elimination (reduced row echelon form) is performed on the augmented matrix $[H \mid h]$. For each pivot column the pivot row is scaled so the leading entry becomes 1 (using Fermat's little theorem: $a^{-1} \equiv a^{99} \pmod{101}$), then that column is eliminated from every other row simultaneously using NumPy vectorised operations. ```python def rref_mod_p(A, b, p): - """ - Reduce augmented matrix [A|b] to RREF over Z/pZ (p prime). - Returns (reduced matrix, list of pivot column indices). - """ M = np.hstack([A % p, (b % p).reshape(-1, 1)]).astype(np.int64) pivots, r = [], 0 - for col in range(A.shape[1]): - # Find a non-zero entry in this column at or below row r nz = np.nonzero(M[r:, col] % p)[0] if not len(nz): continue - - M[[r, r + nz[0]]] = M[[r + nz[0], r]] # swap into pivot position - M[r] = M[r] * pow(int(M[r, col]), p - 2, p) % p # scale so pivot = 1 - - # Eliminate this column in every other row + M[[r, r + nz[0]]] = M[[r + nz[0], r]] + M[r] = M[r] * pow(int(M[r, col]), p - 2, p) % p mask = (M[:, col] % p != 0) mask[r] = False M[mask] = (M[mask] - M[mask, col:col+1] * M[r]) % p - pivots.append(col) r += 1 if r >= M.shape[0]: break - return M, pivots ``` -After RREF the solution vector is read directly from the last column of the reduced matrix: `x[col] = M[i, 512]` for each pivot `(i, col)`. +After RREF, the solution vector is read from the last column: `x[col] = M[i, 512]` for each pivot `(i, col)`. ### 4. Reconstruct the flag -The 512 recovered bits are packed back into 64 bytes. Bit $j$ lands at byte $\lfloor j/8 \rfloor$ at bit position $j \bmod 8$, which follows directly from the little-endian layout of the eight `uint64` flag words. +The 512 recovered bits are packed back into 64 bytes. Bit $j$ lands at byte $\lfloor j/8 \rfloor$ at bit position $j \bmod 8$, following directly from the little-endian layout of the eight `uint64` flag words. ```python def reconstruct_flag(x): - """ - Pack the 512 recovered flag bits back into 64 bytes. - Bit j maps to byte j//8 (little-endian within each 64-bit word), bit position j%8. - """ flag = bytearray(64) for j in range(N_BITS): flag[j // 8] |= x[j] << (j % 8) return bytes(flag) ``` -Running the exploit against the live server: - ``` -[*] Collecting 560 samples (20 threads)... - 100/560 (221 q/s) - 200/560 (225 q/s) - ... -[*] Collected 560 samples in 2.5s -[*] Building 560×512 matrix over Z/101Z... -[*] Solving via RREF... - -[+] UMASS{sparse_fourier_transforms_are_so_much_fun!fhwtftw!yayayay} +Collecting 560 samples (20 threads)... + 100/560 (616 q/s) + 200/560 (547 q/s) + 300/560 (611 q/s) + 400/560 (638 q/s) + 500/560 (669 q/s) +Collected 560 samples in 0.8s +Building 560×512 matrix over Z/101Z... +Solving via RREF... + +Flag: UMASS{sparse_fourier_transforms_are_so_much_fun!fhwtftw!yayayay} ``` ## Remediation -The root cause is that the oracle `(Σ s[i]) mod 101` leaks a linear combination of the PRNG state, and because each connection re-seeds the PRNG independently while broadcasting the ciphertext, an attacker accumulates enough linear equations to fully determine the secret state. Two concrete fixes: +The root cause is that the oracle `(Σ s[i]) mod 101` leaks a linear combination of the PRNG state, and because each connection re-seeds the PRNG independently while broadcasting `state XOR flag` as the ciphertext, an attacker accumulates enough linear equations to fully determine the secret state. Two concrete fixes: -1. **Use a larger modulus.** A modulus of 101 means coefficients lie in a tiny field, making the linear system easy to solve. If the modulus were a 64-bit prime (or the output were a full 64-bit word), the attacker would need far more information per query and the system would be much harder to solve in practice. -2. **Do not expose a linear oracle over freshly seeded states with known ciphertext.** The combination of a known linear function of the state *and* a known linear function of `state XOR flag` is what enables the attack. Either hide one of the two (don't print the ciphertext, or don't offer the oracle), or introduce non-linearity (e.g., hash the state before outputting it). +1. **Use a larger modulus.** A modulus of 101 means the coefficient field has only 101 elements, keeping the linear system small and tractable. A 64-bit prime modulus (or the full 64-bit sum output) would require far more oracle queries per flag bit. +2. **Do not expose a linear oracle over freshly seeded states alongside a known ciphertext.** The attack requires both a known linear function of the state (`ct[i] = s[i] XOR flag[i]`) and a second independent linear function of the same state (the oracle). Removing either — suppressing the ciphertext, hiding the oracle, or introducing non-linearity such as hashing the state before output — breaks the attack entirely. diff --git a/umassctf-2026/crypto/Unfinished Ninjago Game/exploit.py b/umassctf-2026/crypto/Unfinished Ninjago Game/exploit.py index 1fa314a8..f3347ed3 100644 --- a/umassctf-2026/crypto/Unfinished Ninjago Game/exploit.py +++ b/umassctf-2026/crypto/Unfinished Ninjago Game/exploit.py @@ -15,7 +15,7 @@ import numpy as np from queue import Queue, Empty -HOST, PORT = "34.26.193.62", 32769 +HOST, PORT = "localhost", 1337 MOD = 101 N_BITS = 512 # 8 words × 64 bits N_COLLECT = 560 # extra equations for safety margin