diff --git a/crypto/XMASSpirit/README.md b/crypto/XMASSpirit/README.md index b7fa7c2..148aa2e 100644 --- a/crypto/XMASSpirit/README.md +++ b/crypto/XMASSpirit/README.md @@ -9,4 +9,24 @@ letter? ## Flag +HTB{4ff1n3_c1ph3r_15_51mpl3_m47h5} + +- See letter.pdf + +## How to solve + +See `decrypt.py` + ## Progress + +### Code analysis: + - Choose a `randint` `a` from `1` to `mod`, that is not divisible by mod. + - Choose a `randint` `b` from `1` to `mod`. + - for each byte in `dt` + - calculate `(a * byte + b) % mod` + - append to output bytes + - return bytes when done + +### Notes + +- `s_next = (a * s_prev + b) mod p` is a linear congruential generator diff --git a/crypto/XMASSpirit/challenge.py b/crypto/XMASSpirit/challenge.py old mode 100644 new mode 100755 index a30b4ea..9502b0a --- a/crypto/XMASSpirit/challenge.py +++ b/crypto/XMASSpirit/challenge.py @@ -4,17 +4,17 @@ import random from math import gcd def encrypt(dt): - mod = 256 - while True: - a = random.randint(1,mod) - if gcd(a, mod) == 1: break - b = random.randint(1,mod) + mod = 256 + while True: + a = random.randint(1,mod) + if gcd(a, mod) == 1: break + b = random.randint(1,mod) - res = b'' - for byte in dt: - enc = (a*byte + b) % mod - res += bytes([enc]) - return res + res = b'' + for byte in dt: + enc = (a*byte + b) % mod + res += bytes([enc]) + return res dt = open('letter.pdf', 'rb').read() diff --git a/crypto/XMASSpirit/decrypt.py b/crypto/XMASSpirit/decrypt.py new file mode 100755 index 0000000..0748f01 --- /dev/null +++ b/crypto/XMASSpirit/decrypt.py @@ -0,0 +1,43 @@ +#!/bin/env python + +# the modulus for the lcg prng +n = 256 +# the header of the pdf v1.5 file +header = [ 0x25, 0x50, 0x44, 0x46, 0x2d, 0x31, 0x2e ] +# primes from 1 to 256 +primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251 ] +# the lookup table was previously generated, get_factors determines that a=169, b=160 +lookup = {160:0, 73:1, 242:2, 155:3, 68:4, 237:5, 150:6, 63:7, 232:8, 145:9, 58:10, 227:11, 140:12, 53:13, 222:14, 135:15, 48:16, 217:17, 130:18, 43:19, 212:20, 125:21, 38:22, 207:23, 120:24, 33:25, 202:26, 115:27, 28:28, 197:29, 110:30, 23:31, 192:32, 105:33, 18:34, 187:35, 100:36, 13:37, 182:38, 95:39, 8:40, 177:41, 90:42, 3:43, 172:44, 85:45, 254:46, 167:47, 80:48, 249:49, 162:50, 75:51, 244:52, 157:53, 70:54, 239:55, 152:56, 65:57, 234:58, 147:59, 60:60, 229:61, 142:62, 55:63, 224:64, 137:65, 50:66, 219:67, 132:68, 45:69, 214:70, 127:71, 40:72, 209:73, 122:74, 35:75, 204:76, 117:77, 30:78, 199:79, 112:80, 25:81, 194:82, 107:83, 20:84, 189:85, 102:86, 15:87, 184:88, 97:89, 10:90, 179:91, 92:92, 5:93, 174:94, 87:95, 0:96, 169:97, 82:98, 251:99, 164:100, 77:101, 246:102, 159:103, 72:104, 241:105, 154:106, 67:107, 236:108, 149:109, 62:110, 231:111, 144:112, 57:113, 226:114, 139:115, 52:116, 221:117, 134:118, 47:119, 216:120, 129:121, 42:122, 211:123, 124:124, 37:125, 206:126, 119:127, 32:128, 201:129, 114:130, 27:131, 196:132, 109:133, 22:134, 191:135, 104:136, 17:137, 186:138, 99:139, 12:140, 181:141, 94:142, 7:143, 176:144, 89:145, 2:146, 171:147, 84:148, 253:149, 166:150, 79:151, 248:152, 161:153, 74:154, 243:155, 156:156, 69:157, 238:158, 151:159, 64:160, 233:161, 146:162, 59:163, 228:164, 141:165, 54:166, 223:167, 136:168, 49:169, 218:170, 131:171, 44:172, 213:173, 126:174, 39:175, 208:176, 121:177, 34:178, 203:179, 116:180, 29:181, 198:182, 111:183, 24:184, 193:185, 106:186, 19:187, 188:188, 101:189, 14:190, 183:191, 96:192, 9:193, 178:194, 91:195, 4:196, 173:197, 86:198, 255:199, 168:200, 81:201, 250:202, 163:203, 76:204, 245:205, 158:206, 71:207, 240:208, 153:209, 66:210, 235:211, 148:212, 61:213, 230:214, 143:215, 56:216, 225:217, 138:218, 51:219, 220:220, 133:221, 46:222, 215:223, 128:224, 41:225, 210:226, 123:227, 36:228, 205:229, 118:230, 31:231, 200:232, 113:233, 26:234, 195:235, 108:236, 21:237, 190:238, 103:239, 16:240, 185:241, 98:242, 11:243, 180:244, 93:245, 6:246, 175:247, 88:248, 1:249, 170:250, 83:251, 252:252, 165:253, 78:254, 247:255} + +def get_factors(ct): + for b in range(1, n): + for prime in primes: + for i in range(1, len(header)): + if((header[i] * prime + b) % n != ct[i]): + break + if(i+1 == len(header)): + print(f'a:{prime} b:{b}') + return (prime, b) + return (0,0) + +def generate_lookuptable(a, b): + for i in range(0, 256): + lt = (i * a + b) % 256 + print(f'{lt}:{i},', end=" ") + +def decrypt(ct): + res = b'' + for byte in ct: + dec = lookup[byte] + res += bytes([dec]) + return res + +if __name__ == '__main__': + # open encrypted file + ct = open('encrypted.bin', 'rb').read() + #a,b = get_factors(ct) + #generate_lookuptable(169, 160) + dec = decrypt(ct) + # write decrypted file + with open('letter.pdf', 'wb') as f: + f.write(dec) diff --git a/crypto/XMASSpirit/letter.pdf b/crypto/XMASSpirit/letter.pdf new file mode 100644 index 0000000..858c36c Binary files /dev/null and b/crypto/XMASSpirit/letter.pdf differ