DUCTF 2024
decrypt then eval
Challenge:
#!/usr/bin/env python3
from Crypto.Cipher import AES
import os
KEY = os.urandom(16)
IV = os.urandom(16)
FLAG = os.getenv('FLAG', 'DUCTF{testflag}')
def main():
while True:
ct = bytes.fromhex(input('ct: '))
aes = AES.new(KEY, AES.MODE_CFB, IV, segment_size=128)
try:
print(eval(aes.decrypt(ct)))
except Exception:
print('invalid ct!')
if __name__ == '__main__':
main()
Solve:
Immediately notice the weakness of a fixed IV.
Google for an image of aes cfb encryption diagram
Blocks are encrypted like so:
from Crypto.Cipher import AES
import os
from pwn import xor
KEY = os.urandom(16)
IV = os.urandom(16)
pt = os.urandom(1024)
ct = AES.new(KEY, AES.MODE_CFB, IV, segment_size=128).encrypt(pt)
assert ct[:16] == xor(pt[:16], AES.new(KEY, AES.MODE_ECB).encrypt(IV))
assert ct[16:32] == xor(pt[16:32], AES.new(KEY, AES.MODE_ECB).encrypt(ct[:16]))
assert ct[32:48] == xor(pt[32:48], AES.new(KEY, AES.MODE_ECB).encrypt(ct[16:32]))
...
Now our goal is for the plaintext to be b'FLAG'
All we have to do is solve for AES.new(KEY, AES.MODE_ECB).encrypt(IV)[:4]
using a different plaintext and we can construct our ciphertext!
Some plaintexts will throw an exception so we can start by finding a valid one of length one and append to it byte by byte. If the plaintext is just some number, then eval() won’t cause any problems for us.
from pwn import *
def send(ct):
io.recv()
io.sendline(bytes(ct).hex().encode())
return io.recvline()[:-1]
#io = process(["python", "decrypt-then-eval.py"])
io = remote('2024.ductf.dev', 30020)
ct = []
while len(ct) < 4:
tmp = ct + [randint(0, 255)]
pt = send(tmp)
if all(i in b'123456789' for i in pt) and len(pt) == len(tmp):
ct = tmp
print(pt)
IV_ENC = xor(pt, ct)
forged_ct = xor(IV_ENC, b'FLAG')[:4]
print(send(forged_ct))
# DUCTF{should_have_used_authenticated_encryption!}
V for Vieta
Challenge:
#!/usr/bin/env python3
import os
import random
import json
from enum import Enum
FLAG = os.getenv("FLAG", "DUCTF{dummy_flag}")
class State(Enum):
INITIAL = 1
TEST = 2
QUIT = 3
class Server:
def __init__(self):
self.level = 2048
self.target = 2048
self.finish = 8
self.state = State.INITIAL
def win(self):
return {
"flag": FLAG,
}
def generate_k(self):
self.k = random.getrandbits(self.level) ** 2
self.state = State.TEST
return {
"k": self.k,
"level": self.level,
}
def test(self, challenge):
a, b = challenge["a"], challenge["b"]
if a <= 0 or b <= 0:
self.state = State.QUIT
return {"error": "Your answer must be positive!"}
if a.bit_length() <= self.target or b.bit_length() <= self.target:
self.state = State.QUIT
return {"error": "Your answer is too small!"}
num = a**2 + a * b + b**2
denom = 2 * a * b + 1
if num % denom != 0 or num // denom != self.k:
self.state = State.QUIT
return {"error": "Your answer wasn't a solution!"}
self.level -= self.level // 5
if self.level <= self.finish:
self.state = State.QUIT
return self.win()
else:
return self.generate_k()
def main():
server = Server()
print("V equals negative V plus or minus the squareroot of V squared minus 4 V V all divided by 2 V!")
while True:
if server.state == State.INITIAL:
print(json.dumps(server.generate_k()))
elif server.state == State.TEST:
challenge = json.loads(input())
print(json.dumps(server.test(challenge)))
elif server.state == State.QUIT:
exit(0)
if __name__ == "__main__":
main()
Solve:
from math import isqrt
from json import loads, dumps
from pwn import remote
def solve(k):
x, y = isqrt(k), 0
while True:
x, y = (2*k-1)*x - y, x
if x.bit_length() > 2048 and y.bit_length() > 2048:
return x, y
io = remote('2024.ductf.dev', 30018)
io.recvline()
while True:
out = io.recvline()
if b'DUCTF{' in out:
print(out)
break
k = int(loads(out)['k'])
a, b = solve(k)
io.sendline(dumps({'a':a, 'b':b}).encode())
# DUCTF{jump1n6_4nd_fl1pp1n6_up_7h3_hyp3r8011c_14dd3r}
three line crypto
Challenge:
import os, sys
q, y = os.urandom(16), 0
for x in sys.stdin.buffer.read(): sys.stdout.buffer.write(bytes([q[y % 16] ^ x])); y = x
Solve:
The flag format DUCTF{
will be quite useful for us.
Let’s analyse on a random plaintext first.
import os, sys
key = os.urandom(16)
y = 0
p = b'Lorem dolor ipsum here is the flag DUCTF{fake}.'
c = b''
for x in p:
c += bytes([key[y % 16] ^ x])
y = x
assert c[0] == key[0] ^ p[0]
assert c[1] == key[p[0] % 16] ^ p[1]
assert c[2] == key[p[1] % 16] ^ p[2]
assert c[3] == key[p[2] % 16] ^ p[3]
...
flag_index = 35 # brute real one
assert c[flag_index] == key[p[flag_index-1] % 16] ^ ord('D')
assert c[flag_index+1] == key[p[flag_index] % 16] ^ ord('U')
assert c[flag_index+2] == key[p[flag_index+1] % 16] ^ ord('C')
assert c[flag_index+3] == key[p[flag_index+2] % 16] ^ ord('T')
assert c[flag_index+4] == key[p[flag_index+3] % 16] ^ ord('F')
assert c[flag_index+5] == key[p[flag_index+4] % 16] ^ ord('{')
assert c[flag_index] == key[ord(' ') % 16] ^ ord('D')
assert c[flag_index+1] == key[ord('D') % 16] ^ ord('U')
assert c[flag_index+2] == key[ord('U') % 16] ^ ord('C')
assert c[flag_index+3] == key[ord('C') % 16] ^ ord('T')
assert c[flag_index+4] == key[ord('T') % 16] ^ ord('F')
assert c[flag_index+5] == key[ord('F') % 16] ^ ord('{')
Let’s see if any are the same:
for i in 'DUCTF{':
print(i, ord(i)%16)
D 4
U 5
C 3
T 4
F 6
{ 11
We see ‘D’ and ‘T’ have the same.
This can help narrow down where the flag index is.
assert c[flag_index+1] == key[4] ^ ord('U')
assert c[flag_index+4] == key[4] ^ ord('F')
for find_flag_index in range(len(c)-4):
if c[find_flag_index+1]^ord('U') == c[find_flag_index+4]^ord('F'):
print(find_flag_index)
We can get 5/16 key values from the flag format:
print([i for i in key])
recover_key = [0 for _ in range(16)]
recover_key[ord(' ') % 16] = ord('D') ^ c[flag_index]
recover_key[ord('D') % 16] = ord('U') ^ c[flag_index+1]
recover_key[ord('U') % 16] = ord('C') ^ c[flag_index+2]
recover_key[ord('C') % 16] = ord('T') ^ c[flag_index+3]
recover_key[ord('T') % 16] = ord('F') ^ c[flag_index+4]
recover_key[ord('F') % 16] = ord('{') ^ c[flag_index+5]
print(recover_key)
For the rest we can try some common words:
c = bytes.fromhex('309c0fe5880a72faaa3d1a13c00b6e0455d0860de4c022f58814577ac9f475ef058d200e2fe5c04e109c0fe588144ec9e3e26172f42d5f200ae216b10ed1880ed61a0a762b3a8813c78213dd3c864b13c00b6e1455c6a263d0c80a72e39a91850222fc33109dd1c04e11e4cb0e75ef0f01d5d413cd200f8813c00b6e144ecd2b3cd3c57b9c0fe5881706f8cb181a01e2d0c8045be5dcc4f46378aa2b3e0ecb0cd8b108d0c8109c0fe588171587cdc48763c4ad1706e5c1c0201f8813da81c4fdb1145f3c9ee83d1a01e2d0c813c01c81c3f9d185058d2b3d01c57f4f2d524e06e38d6e0a6a8513c00b23763d1461266382122002d3e83c9b5bfdb10bf8c29c1adb30617cd14e880098fed081cad73d1bb11ec06e13c00fe5880bf42ff58813c00b6e0098f8c1c1cb0cd41ec02fe3e26dfdfecc0cd413c00b6e1451b689b12bf8c78d3cc806ff0f88245f3c8d3d1a0a7ac9f5849b65c3ad0591851ecad73cc80587d7201fd1850f01cee102204b022fe39cc04e08cc085f6e04520fff0c912aa26f520ffecc02c4ff4b06f255d0864b01e2d0c813c00b6e170be4236347109c0b2fe585762fe3c49b6dff0f880a7acb0c98f42a88109dd1c04e13c00b6e008689e10262b11ecad73cc8090e39d975e2d7200f88009dc3f9849b78c00b6e03da89e4299c1adb1a08c4ad26f2520b22fed73d01ef06ff0f881ec06e21ece420183078c782159d3d4ec1c6491a0238e83cc80ca6cb0f84b10455cf766e01e2cdd6880ed184b121ece42018306dff0f8823da91c4f5857e72f8c1db1a13c7c5913ac00b3cd3ef1ecad73cc8009dc3f9db1a2e851453cb0cda616dff0f8813c001d762b101e2d0c8109c01d15f6e03cd22f8c29c1a8813c00b6e1095e3c57b01d09b5f6e01e4d79b4ea27f4a1589ff0cd401ff87cf33022fe39cc049871a109bcf71c806e58813c0178500868d2fe58813da81c1cd201f8f871a144eda87c9aa629b620e3e13dd200e75ef06ff0f880f0fe4201fcd3cc808c4ad13c00b6e008687d4e83d16b101e2d0c8109c01cf1978c01c8d2b6e0f1b200fda8d2a88145404d5d9649c07d1cd6e0f0b27c3e83c9b1a059a87d5875f6e13c00b6e059a89faaa3d169b78c00b6e01e83c9cc1c9f46e059a89faaa3d1a08c4ad245f21d101ef06ff0f880456fed6c00b2a880ecb4b1708d5913cc49b78c01785090ae5c1d3e86e01e2d08d3d4e8806ff0f882be8c65f2fff4b0bf0e69a18169b7c06ff47b114520b3e0f0b3c8c85749bc684b101e2d09b5bfaa6cb0cd8b106e21a13c00b6e0bfed4e84463c4ad13c007cb0e6e08d59a4b2a72f4200afde43d1a0455cc184eda89f8cb181a13c00b2b62b10f0b2fe3e26dff0f880f0b22e14bb128820bfed08c8808c4ad33cd29912fb0b126ff0f8813c001d762b113c7cd8e9b617acb0e3c9eecbdb101ff87cf33109c01d15f6e0f0fff0f8813c00b6e08cef8d3e86e144a159d200ccfc56dff0f880587db8877c1d65955d4e83c8d3cc808c4ad13c00b6e044f3c9ee82a88170bfed7299c55c56dff0f84b1058d2fe381cb0cd406b11ecad7200cd40443d5158d3d491a1587cdd685663e13c7d086479b7f53c9e7ecff1e3d16b106ff0f88209bc6db1a06fdfdb106ff0f88209bc6cccd3d495f3d169b7b9c01820a72faaa6e13c00b6e01e4c022f5db1a1ecad73cc8045be38d62b10587d6c04e1ec06e109c0182091e3c9b5f4478c00b6e13cd200fcd3cc812201855d59a4b0ecb08488d2fe25f62b106ff0f8801ff87cf330f0b2fe7e820617f520b2a8808cc4b0a72ff4c871a1455d59dcb0cd413c00b6e1581c6520b3d1a08c4ad1ecad73cc81589f8cb512663c4ad109c07c6524e08cc0e6e0ed61a23fd0d6eeef6b89c0b2034cbcb34c6c7d72c9cf7da01d09cf7cbd1f7cdd73ad528c56dff0f8813c001d762b10238e8204b13c001d762b108c4ad109c01cf3310916e0ca104d5d40904d6881ec03aa27b9c0fe5880a72ff1853cacc4b08c4ad13c00b6e1451a6c03d1a14520ffdfdb10f01cef58813c00b2b6e1455cdcc479b7b9c0b3ac00b3cc813c7821095e5cb524e0885913cc80453d1c1c03d1a058d6e13c01785109dc9fdbd9b6b868d2fe588245bf43d5be3c4b106ff0f8813c78213c9faaa6e13c00b6e022fe39cc04e0ecb4b04520fe38f91629b78c00fe58814558213c00b6e0a7ac29c1ad185109bd084f5880a72e885109122f255cf766e13c00b2b4460fed08c8808c4ad0f0b3cc80ecb08488d2fe25f62b10a72e24ecd3cc808c4ad0f0b3cc813c1c8763d169b6e81cb0fc1cb0cd413c017850a7cd6c00b3ccf871a0a6ad79cc4f46e1587d7200f8813c01785059a87d5d89b63d0c806e21a13c00b6e0587d7200fc4f43d491a08c15f2fff4c871a209bc68813c001d76e0455cf76629b7f55cef46e03da8d2ff58808c4ad145f2ffc762047b113c1c9fdb101ece3c833c01b22f46e0587d5fe6e8d28e2d08d6e13c00b2b62b106ff0f8833cd3ac017d61a109dcb4b13c00b2b6e13c7820f0b3cc81455cc617b9dd1c04e06fdfdb10f0b3cc81095e7e83d1a01e2d0c803c7d5913cd3ef08d0c806e21a06b1144ec9e3e260f4200f8813c0178501ff8d3d524e058d2ffc601a08d73cc80bf0f6939dcb0cd40a7ccc1fc01d1a13c78204520b2b3cc49b7b9c0b3c8d6e4080df9ddd1a8813c00b6e2a72f8c18806ff0f8813c001d15f6e17123c9b4f27cb0cd42456f0e687306db1144a06f25f6e0ed61a08d2022002cb0ccfef145f2b6fb1158d2a88345955d0980eca85871a145f22f7876407d61a06e385601a03da89e6871a0ecb47b11ec02fbdb106ff0f880f0fe5c04e0bf428f98813c00b2b6e0a7cd08d4478c00fff4b13c0178501f822fdb10a762b2a8808c4ad0f0b2fe7e820510c058d6e109c0fe58813c001d76e109dc9e585196ae2d0c8090e27d1c00b3cc833c9e39cc9e39d3d1a0f01d2023d1a13c782045bfdfdb113c00b2b6e0ca6cb0cd89b6204d0c80a72e88514558203c1d78d6e06b10be43d4e8808c4ad1455d4e83c8d27c29a1fd1af6969913cc80bf8c29c1a88123e08cc4b13c00b2b62b10f01d587558220868d2b2d5f6e06f5c57ad78d446922e8d653d02334871a01e4c022f5db16b106ff0f88371587d15f3c980ecb0e6e0904d6880f0b2b2aa2640b3cc80a7cd6c00b3ccf871a11e2cbc65f6e02201fda8d2fe5c1cb0cd413c782158d3add3c8646197ae2d72d521d5bf7e86e06b1171587d14a023c87d73d1a11e2dbc4f69162b106ff0f8814577ac9f46e08cc4b13c007d630616a850587cef58802200fcd2fe7e2d73cc4b106ff0f88170ed1d1cccb0cd8b10238e8204b06e21a2e899b78c00b3d5f6e1708cdd0c81095e888649dc9f5cd3c8d2a88144d95f8cb1816b106e58808cc085f6e058d299dcb479b6b8687d5d413c1c87622e885123d5f2a8812201fc78213c00b6e11e2cbc65f6e08c4ad171589e8c03cc66165cb4b022fe384e885144a1581cb0cd967c1c1cd62b1109c0b204b13c00b6e0ec6438503da81d56d617622e5db1a01ff87cf3313c00b6e0a7cd7201fc9f8cb181a0f01c3e3c4b106ff0f883d493e0f17d7cf871a059a8d2fe5c06479200981cb0fdb1a13c00b6e04489d237184f8cb0cd40456fec684b10238e8204b13c00b204b4080c1d61a13c1c87675c57c158d3d491a03cd2b3e471ecad73cc8170bfed7299c4e058d2607cb0f8813c00b6e008687c3ff02cb0cd408da429b6dff0f8813cd2ff2524e13c00b6e01f83c9a87d5d9719d3c8602d6520b2a8814520fe38d6e13c782145207cb0e606178c00fe5880bf0ff0f8813c00b6e044889e7e4cb0cd401ece385763ccf871a171589e8c03cc801f822f7e4c9e2169b7b9c07c6524e13df9dc65f6e13c00b6e144f20185207cb0e62b113df9dc65f6e13c00b6e01ff87d14e880f0fe21a01e822e593c56de889b113c00fe58f871a13c00b6e0bf0ff0f88109c01d15f6e0587d7200fc4f43d491a0f0fe39ee83d4e85704887d214306e9d3c9b4e84b1145f2b6fb113c00b6e0589e386181461266e9d3a88023c8d6e08d73cc80a763ac9fdb10456f42fe7e8446dff4b122000a104d59a4b144f3c8eecf25f62b10f0b2b2a8810916e13c78201e2d08d22f42fe3866178c00b6e109dcb0fdb1a06ff0f8811ece391cccb0cd413cd2363023cc808c4ad13c00b6e1451b6899b78c00b6e0bf8cb0e2ffdb113c1c9e5c04e06ff0f880f0ff381d1db1a08c4ad13c00b6e144a08d6849b7b9c0fe5880238e83c9185158d299dcacc4b1eccc022f5db16b106ff0f88109c0fe58803cd2002c03d1461640b3c8d6e0584f8d1c00b22f8c03cc8144a1581cb0c871a13c00b6e0455d08647b106ff0f880f0b3c8d6e13c00b6e008689e102629b78c00b3c8d6e022fe39cc04e0ed61a00868d2b204b109dd1c04e13cd200fcd3cc8008687d580c04e08c4ad13da8d2b3d306dff0f88008689e2491a12200981c1cccd20454b345f2b6e0f01d5d401ff87cf3333c57ccee43d1a0455cf763d3078c00b6e145bf7ebff87cc4c871a01ff89f68689ff085f62b10ed3e2d0918501ff87cf332ecb0f849b6aff87cf33345bf389b6871a10912ffaa3f8cb0cd41455cc181a13c00b27d7c801ff89ff00a6cb085f20185f629b65d787cc4b01ff87cf3313c00b6e090afaaa2a8824520ffde8c79b16b1045be24ec7d0c81589ff00c56aff87cf333708cc1fdd3d16b101ff87cf33223e0ed79d3d1a13c00b6e171581df49636306fdfc60306385d413c00b6e0a72e38d3d1a08c4ad2222f8d614617f4f2d524e13c00b6e023acd3c860afdb10587cc0fa26dff0f88144f2d524e13c00b6e0bf0e6871a059185290ae5dd3c8d69871a0f0fff0f880ec86308d15f2aa263cc4b0456f8c8766e06ff0f880456f8c87662b10269913cc81453cb085f6e13c00b6e171581c872fdb103c9e69a617b9c0b204b08cef58823cd3b2d5bfdf8cacc4b08cc4b13c00b6e12201b0221d20bf42a88022fe39cc0646f5be24e88144ec7cc0e3d16b1109c0b20085f6e0a762047b106b101e1f8cb1fd1851589f25f62b110913c8d6e158d2fe38d2a8661793e4713c00b204ab10ec3ad01ece58813c00b6e1455cbc9bdb10bf43a88144edd3c8cd185059d22fde230793e13dd3c864b0ed18801ff87cf3313c00b6e1ec02fe3cf871a01e4d79b4e8808d2022002cb0cd40a7ccc1fc01d169b6dff0f880bf43a8813c00b6e0456fec6db1a0bf8c06e0589e38d6e13c1c9fdb10589faaa2a8813c78203dd3d4ea26e918513c00b6e1581d5026e144f20181a08c4ad144f237e763cd3ef059d3a880ec3ad13c00b6e022fe39cc06460f43d491a01ff9d27d1cef822b10d593d4e88023c8d6e26e38b4edd3c9d3d1a1581d65f447b9dd1c04e14520ffdfdfed5913cc813da8d2008524e123e13c1c9e5880ed185334080df9dc9fdb1144f28ebe4c65f75c578c00b3c8d62b10bf43d4e8810912b2adb1a045201c9aa6e13c00b6e044887d240871a0be4361b3c81c4ff085f62b10f0b3c8d629b60f43d4e8813c00b6e14595bff1f880a7ccbd64edd3c8d6e01ecf8c9b113c00b6e0589e39a8d204b145bff0f866178c00b204b13c001d76e14520ffde588144f28ebe83cc80ecb4b06fde5cd3c860ae5cd6e1ec02fe39b3078c00b6e090e39d9618d2fe1022a8801e4c022f5db1a13c782158d3d4e84b106ff0f8808cc4b13c00b6e170bf0f8cb616db104489d3d4e8808c4ad1456fed6c04e13c7820f0fe38ccd2050ef08d0c4b1109c0b204b144ec9e39b306de38d6e04520fff0c912a880ecb4b0f0b2fe7e82047b113c00b3c8d6e1455d5d413c00b6e009bcef5cd204b008689f8cb617b9c0b3c8d6e023c9b4e84b10be4361b3c81c4ff1f88109dd1c04e0ed1db1a16e427d3e83c81cb0cd41708c6849b7c1222e25f62b108d0c813c00b6e1456f4200fcd3cc811e83acb5243704887d24bb113c001d76e0f0fe24e880456f42fe38d2a849b6dff0f880be43e0ecb4b1455d73cc4b1109c01d15f6e059a81d1dcc4f46e144ec9fdfabc1a06e381d65f629b6db10f1b3c9cc4f8cb0cd401e2d08d3d4e864b21e2d0c813c00b6e170bf0f8cb4b0ed61a1706e38b520b2aa26e918501e1f0e943704887d24bb11706e38b520b2a8805918508c3e5db16b10591851708d2170ec03d1a1706e38b520b2aa265cb4b2bf43ac00b636056e423718d3cc803da8d2008520b2a864b290ae5c002f43d491a05918504520fff0c914478c00b6e13da89e7ecf8c9f8cb0cd4022fe39cc04e0ed61a0bf8c29c1acd200e2a84b1059d3a88144ec1cb1f880904d6a27b9dd1c04e158d28f83d5f6e1581c6524e13c7821455c3faef13c00b6e13c007d79b4ed1851455cbc9bd9b6dff0f88145201d5913cc801e2d722b106e2520b3d1a0885913cc813c00b6e0236060fe43d4ecd2a8801e4c022f5db146178c01b3d1a0591851587d6c9e5c1cacc4b0bf8ceaa6e158d3e08d15f6e0ed61a0095f8cb0e2a849b6204d0c8022fe39cc04e0a762fff1c9c07c9f46e12200e2fe38d2a8806ff0f8813c00fff00a3f43d491a0bf428f9866163c4f984b113c7cd8eb14080df9dc9fdb10587cdd68813c78201e4d78d6e13c00b6e090afaaa2a8801e4c022f5db169b6dff0f8813c00b6e0bf8c29c1a88144edd2c8a84f46e059d3c864b109dd1c04e044889f251a3f8cb0cd401e1f0fc763d01c57b9c0b3ac00b3cc813c00fe588022fe39cc04e13c00b3c8d28ff87cf331455cf766e0f07c1cccd204b144eda8d200c80c0646dff0f8801ece5dccd2002cb0cd401e2cdc68803cd3c81d3e83d16b108d0c813c00fe58813c00b6e01e4d78d446e89faaa3d1a0238e83c91850584f4237ad6524e08d73a84b106ff0f88144d912fe5db1a06e695e8af692ff2524e123d5f22f43d491a0f1b237cd73cc4b108d0c813c00fe58813c00b6e0f0b2fe588122007fec151bc30620e39d41706e2495bf6913d1a06ff0f88145f2d488d3a881708d08d3d16b1109c0b3c8d2c91af78c00b27d7c80bf8c3e863795927c65f6e13c78213c00b6e13cd200fcd3cc80584f0f5cd3d1a0a72e885109dcb50c563d0c813c00fe5880ed1880f0fe38ccd20181a0a7cd08d6e06ff0f880f0b22e1141a13c7820581cb0fa278c00b6e0095e10ecb0cd411e827cb1816b10bf43d4e881702200e3ada89e5c1cb0cd4145201d5913c9b169b63d0c801e4c03c8b5f6e144f204c871a1589e7e82002cb0cd40a7ac29c1a84b108d0c8145f2fe38b5207cb0cd40584f0e24ea263c4ad13c00b6e0caa2b204b0904d09cc04e145201d722f588145f2fe3c813c00b233d4b309122fdbdb12e85109bd6849b640b6e145f3c9ee83d1a13c00b6e01e4c022f5db1a109c0182109dd1c04e0f07d61a0f0fe39a87d5d4059a8d2ffabc3078c00b6e1456e429939dd6524e0456fec6db16b106ff0f880f1b3c8cc4f43d1a08d153c03cc567df9dcb0e2aa2640ffdf43d1a0885913cc813c00b2328ef01ff87cf3313c00b6e01ece3c828cee8c8630ec4ff4b0f0b27c29c1aa26407c833009bcef5cd204b245f3c8d3d1a0904d6880ecb4b11ecf8cb4b158d2995e38cdb01c56dff0f880f0b62b1109c01820f0fe7e4cb0cd4170bfed7299c0b2a8813c00b6e01ecfdfdfed5d4170bf0f8cb616dff0f880f0b2fe7e82a880ed1db1a01f83c9a87d58d851581c1cf913d16b113dd3c86181a08cc085f6e0a7cd08d446f4887d14917649dd65f6e0f07d61a14520fe5dccd3c81cb0cd414520fe38d62b1109dd1c04e144eda87c9aa6e08cc4b144eda87c9aa4478c00b6e022fe39cc04e06e2495bf8c9e216b106ff0f880a72faaa3d1a13c00b6e01e4c022f5880f07d61a13c01c89fdfdbf')
recover_key = [0 for _ in range(16)]
flag_index = 1333
recover_key[ord(' ') % 16] = ord('D') ^ c[flag_index]
recover_key[ord('D') % 16] = ord('U') ^ c[flag_index+1]
recover_key[ord('U') % 16] = ord('C') ^ c[flag_index+2]
recover_key[ord('C') % 16] = ord('T') ^ c[flag_index+3]
recover_key[ord('T') % 16] = ord('F') ^ c[flag_index+4]
recover_key[ord('F') % 16] = ord('{') ^ c[flag_index+5]
for word in [b' the ', b' of ', b' and ', b' to ', b' a ', b' is ', b' for ', b' in ', b' on ', b' that ', b' by ', b' this ', b' with ', b' you ', b' it ', b' not ', b' or ', b' be ', b' are ', b' from ', b' at ', b' as ', b' your ', b' all ', b' have ', b' an ', b' was ', b' we ', b' will ', b' can ', b' about ', b' if ', b' my ', b' has ', b' free ', b' but ', b' our ', b' one ', b' other ', b' do ', b' no ', b' time ', b' they ', b' he ', b' up ', b' may ', b' what ', b' which ', b' their ', b' out ', b' use ', b' any ', b' there ', b' see ', b' only ', b' so ', b' his ', b' when ', b' here ', b' who ', b' also ', b' now ', b' get ' b' first ', b' am ', b' been ', b' would ', b' how ', b' were ', b' some ', b' these ', b' like ', b' than ']:
for idx in range(len(c)):
valid = True
for i in range(len(word)-1):
if recover_key[word[i]%16] != 0:
if recover_key[word[i]%16] != word[i+1] ^ c[idx+i]:
valid = False
break
if valid:
for i in range(len(word)-1):
if recover_key[word[i]%16] == 0:
recover_key[word[i]%16] = word[i+1] ^ c[idx+i]
recover_key[10] = 44
# recover_key = [103, 145, 232, 58, 168, 78, 141, 244, 110, 165, 44, 207, 145, 19, 107, 162]
y = 0
p = b''
for x in c:
d = recover_key[y % 16] ^ x
p += bytes([d])
y = d
print(p.decode())
What makes the cornfield smile; beneath what star
Maecenas, it is meet to turn the sod
Or marry elm with vine; how tend the steer;
What pains for cattle-keeping, or what proof
Of patient trial serves for thrifty bees;
Such are my themes.
O universal lights
Most glorious! ye that lead the gliding year
Along the sky, Liber and Ceres mild,
If by your bounty holpen earth once changed
Chaonian acorn for the plump wheat-ear,
And mingled with the grape, your new-found gift,
The draughts of Achelous; and ye Fauns
To rustics ever kind, come foot it, Fauns
And Dryad-maids together; your gifts I sing.
And thou, for whose delight the war-horse first
Sprang from earth's womb at thy great trident's stroke,
Neptune; and haunter of the groves, for whom
Three hundred snow-white heifers browse the brakes,
The fertile brakes of Ceos; and clothed in power,
Thy native forest and Lycean lawns,
Pan, shepherd-god, forsaking, as the love
Of thine own Maenalus constrains thee, hear
And help, O lord of Tegea! And thou, too,
Minerva, from whose hand the olive sprung;
And boy-discoverer of the curved plough;
And, bearing a young cypress root-uptorn,
Silvanus, and Gods all and Goddesses,
Who make the fields your care, both ye who nurse
The tender unsown increase, and from heaven
Shed on man's sowing the riches of your rain:
Of which one is DUCTF{when_in_doubt_xort_it_out};
And thou, even thou, of whom we know not yet
What mansion of the skies shall hold thee soon,
Whether to watch o'er cities be thy will,
Great Caesar, and to take the earth in charge,
That so the mighty world may welcome thee
Lord of her increase, master of her times,
Binding thy mother's myrtle round thy brow,
Or as the boundless ocean's God thou come,
Sole dread of seamen, till far Thule bow
Before thee, and Tethys win thee to her son
With all her waves for dower; or as a star
Lend thy fresh beams our lagging months to cheer,
Where 'twixt the Maid and those pursuing Claws
A space is opening; see! red Scorpio's self
His arms draws in, yea, and hath left thee more
Than thy full meed of heaven: be what thou wilt-
For neither Tartarus hopes to call thee king,
Nor may so dire a lust of sovereignty
E'er light upon thee, howso Greece admire
Elysium's fields, and Proserpine not heed
Her mother's voice entreating to return-
Vouchsafe a prosperous voyage, and smile on this
My bold endeavour, and pitying, even as I,
These poor way-wildered swains, at once begin,
Grow timely used unto the voice of prayer.
In early spring-tide, when the icy drip
Melts from the mountains hoar, and Zephyr's breath
Unbinds the crumbling clod, even then 'tis time;
Press deep your plough behind the groaning ox,
And teach the furrow-burnished share to shine.
That land the craving farmer's prayer fulfils,
Which twice the sunshine, twice the frost has felt;
Ay, that's the land whose boundless harvest-crops
Burst, see! the barns.
But ere our metal cleave
An unknown surface, heed we to forelearn
The winds and varying temper of the sky,
The lineal tilth and habits of the spot,
What every region yields, and what denies.
Here blithelier springs the corn, and here the grape,
There earth is green with tender growth of trees
And grass unbidden. See how from Tmolus comes
The saffron's fragrance, ivory from Ind,
From Saba's weakling sons their frankincense,
Iron from the naked Chalybs, castor rank
From Pontus, from Epirus the prize-palms
O' the mares of Elis.
Such the eternal bond
And such the laws by Nature's hand imposed
On clime and clime, e'er since the primal dawn
When old Deucalion on the unpeopled earth
Cast stones, whence men, a flinty race, were reared.
Up then! if fat the soil, let sturdy bulls
Upturn it from the year's first opening months,
And let the clods lie bare till baked to dust
By the ripe suns of summer; but if the earth
Less fruitful just ere Arcturus rise
With shallower trench uptilt it- 'twill suffice;
There, lest weeds choke the crop's luxuriance, here,
Lest the scant moisture fail the barren sand.
Then thou shalt suffer in alternate years
The new-reaped fields to rest, and on the plain
A crust of sloth to harden; or, when stars
Are changed in heaven, there sow the golden grain
Where erst, luxuriant with its quivering pod,
Pulse, or the slender vetch-crop, thou hast cleared,
And lupin sour, whose brittle stalks arise,
A hurtling forest. For the plain is parched
By flax-crop, parched by oats, by poppies parched
In Lethe-slumber drenched. Nathless by change
The travailing earth is lightened, but stint not
With refuse rich to soak the thirsty soil,
And shower foul ashes o'er the exhausted fields.
Thus by rotation like repose is gained,
Nor earth meanwhile uneared and thankless left.
Oft, too, 'twill boot to fire the naked fields,
And the light stubble burn with crackling flames;
Whether that earth therefrom some hidden strength
And fattening food derives, or that the fire
Bakes every blemish out, and sweats away
Each useless humour, or that the heat unlocks
New passages and secret pores, whereby
Their life-juice to the tender blades may win;
Or that it hardens more and helps to bind
The gaping veins, lest penetrating showers,
Or fierce sun's ravening might, or searching blast
Of the keen north should sear them. Well, I wot,
He serves the fields who with his harrow breaks
The sluggish clods, and hurdles osier-twined
Hales o'er them; from the far Olympian height
Him golden Ceres not in vain regards;
And he, who having ploughed the fallow plain
And heaved its furrowy ridges, turns once more
Cross-wise his shattering share, with stroke on stroke
The earth assails, and makes the field his thrall.