ToC
开始
由于 <a>
设置了 pointer-events: none;
因此无法点击。复制链接进入第一题。
一只方熊猫
下载后发现图片无法打开:
使用 010Editor
打开后提示 CRC
错误:
随便找了个爆破图片宽高的脚本:
1# -*- coding: utf8 -*-2
3import os4import binascii5import struct6misc = open("panda.png","rb").read()7
8# 爆破宽9for i in range(1024):10 data = misc[12:16] + struct.pack('>i',i)+ misc[20:29] #IHDR数据11 crc32 = binascii.crc32(data) & 0xffffffff12 if crc32 == 0x4A920BA4: #IHDR块的crc32值13 print("w")14 print(i)15 print("hex:"+hex(i))16
17# 爆破高18for i in range(1024):19 data = misc[12:20] + struct.pack('>i',i)+ misc[24:29]20 crc32 = binascii.crc32(data) & 0xffffffff21 if crc32 == 0x4A920BA4:22 print("h")23 print(i)24 print("hex:"+hex(i))
拿脚本跑一下,得到图片的真正 height
:
修改原文件,得到 flag
:
根据 UUID
进入下一题。
🤝
给出的链接是 http://arealexistingdomain/flag.html,第一想法就是在 /etc/hosts
里加个域名(
然后 curl
一下就出来了(
根据 UUID
进入下一题(
粗心的小明
题中给出的代码如下:
1const fs = require("fs");2const uuid = require("uuid");3const crypto = require("crypto");4
5let red_envelope_2021 = uuid.v4();6
7let key = crypto.scryptSync("xiaomingSecureKey2021", "xiaomingSuperSalt", 32);8let cipher = crypto.createCipheriv("aes-256-cfb", key, crypto.randomBytes(16));9
10let json = JSON.stringify({11 red_envelope_2021,12});13
14fs.writeFile(15 "red_envelope_encrypted.hex",16 cipher.update(json, "utf8", "hex") + cipher.final("hex"),17 function (err) {18 if (err) {19 console.error(err);20 } else {21 console.info("Red envelope stored successfully!");22 }23 }24);
给出的 hex
如下:
1d365895fbcdbd3a29b1bf00307429fd07d53ba3c0553b8789867d4aee3b8c3bbb0e5a8fd582a9696aabbdc1e373f97efac2529d588320800449553f6
加密使用的是 aes-256-cfb
,以 IV
作为初始文本,加密(encrypt_block
)得到一个块(block_encrypted
),再将 block_encrypted
和明文异或(⊕)得到密文(prev_ciphertext
)。之后,再以 prev_ciphertext
为初始文本,加密得到下一个 block_encrypted
,再与下一段明文异或……以此类推,如下图所示[1]:
而文本的前半段是确定的,为 {"red_envelope_2021":
,这已经超过了一个块的大小(16
),因此可以通过第一个块得到 IV
。简单步骤如下:
- 将原文转换为
hex
:7b227265645f656e76656c6f70655f32
- 从密文中摘取第一块:
d365895fbcdbd3a29b1bf00307429fd0
- 异或得到
AES
加密后的内容:a847fb3ad884b6cced7e9c6c7727c0e2
- 从
Node
的运行结果中得到32
位密钥:03da3479601ff722a6472329347a161bf8a712ead1d04b5560414be1dead3566
- 解密即可
解密使用了 boppreh/aes
的 AES
实现[2],修改后的脚本如下:
1#!/usr/bin/env python32
3s_box = (4 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,5 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,6 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,7 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,8 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,9 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,10 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,11 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,12 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,13 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,14 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,15 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,16 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,17 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,18 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,19 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,20)21
22inv_s_box = (23 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,24 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,25 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,26 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,27 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,28 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,29 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,30 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,31 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,32 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,33 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,34 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,35 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,36 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,37 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,38 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D,39)40
41
42def sub_bytes(s):43 for i in range(4):44 for j in range(4):45 s[i][j] = s_box[s[i][j]]46
47
48def inv_sub_bytes(s):49 for i in range(4):50 for j in range(4):51 s[i][j] = inv_s_box[s[i][j]]52
53
54def shift_rows(s):55 s[0][1], s[1][1], s[2][1], s[3][1] = s[1][1], s[2][1], s[3][1], s[0][1]56 s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2]57 s[0][3], s[1][3], s[2][3], s[3][3] = s[3][3], s[0][3], s[1][3], s[2][3]58
59
60def inv_shift_rows(s):61 s[0][1], s[1][1], s[2][1], s[3][1] = s[3][1], s[0][1], s[1][1], s[2][1]62 s[0][2], s[1][2], s[2][2], s[3][2] = s[2][2], s[3][2], s[0][2], s[1][2]63 s[0][3], s[1][3], s[2][3], s[3][3] = s[1][3], s[2][3], s[3][3], s[0][3]64
65def add_round_key(s, k):66 for i in range(4):67 for j in range(4):68 s[i][j] ^= k[i][j]69
70
71# learned from http://cs.ucsb.edu/~koc/cs178/projects/JT/aes.c72xtime = lambda a: (((a << 1) ^ 0x1B) & 0xFF) if (a & 0x80) else (a << 1)73
74
75def mix_single_column(a):76 # see Sec 4.1.2 in The Design of Rijndael77 t = a[0] ^ a[1] ^ a[2] ^ a[3]78 u = a[0]79 a[0] ^= t ^ xtime(a[0] ^ a[1])80 a[1] ^= t ^ xtime(a[1] ^ a[2])81 a[2] ^= t ^ xtime(a[2] ^ a[3])82 a[3] ^= t ^ xtime(a[3] ^ u)83
84
85def mix_columns(s):86 for i in range(4):87 mix_single_column(s[i])88
89
90def inv_mix_columns(s):91 # see Sec 4.1.3 in The Design of Rijndael92 for i in range(4):93 u = xtime(xtime(s[i][0] ^ s[i][2]))94 v = xtime(xtime(s[i][1] ^ s[i][3]))95 s[i][0] ^= u96 s[i][1] ^= v97 s[i][2] ^= u98 s[i][3] ^= v99
100 mix_columns(s)101
102
103r_con = (104 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,105 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A,106 0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A,107 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39,108)109
110
111def bytes2matrix(text):112 """ Converts a 16-byte array into a 4x4 matrix. """113 return [list(text[i:i+4]) for i in range(0, len(text), 4)]114
115def matrix2bytes(matrix):116 """ Converts a 4x4 matrix into a 16-byte array. """117 return bytes(sum(matrix, []))118
119def xor_bytes(a, b):120 """ Returns a new byte array with the elements xor'ed. """121 return bytes(i^j for i, j in zip(a, b))122
123def inc_bytes(a):124 """ Returns a new byte array with the value increment by 1 """125 out = list(a)126 for i in reversed(range(len(out))):127 if out[i] == 0xFF:128 out[i] = 0129 else:130 out[i] += 1131 break132 return bytes(out)133
134def pad(plaintext):135 """136 Pads the given plaintext with PKCS#7 padding to a multiple of 16 bytes.137 Note that if the plaintext size is a multiple of 16,138 a whole block will be added.139 """140 padding_len = 16 - (len(plaintext) % 16)141 padding = bytes([padding_len] * padding_len)142 return plaintext + padding143
144def unpad(plaintext):145 """146 Removes a PKCS#7 padding, returning the unpadded text and ensuring the147 padding was correct.148 """149 padding_len = plaintext[-1]150 assert padding_len > 0151 message, padding = plaintext[:-padding_len], plaintext[-padding_len:]152 assert all(p == padding_len for p in padding)153 return message154
155def split_blocks(message, block_size=16, require_padding=True):156 assert len(message) % block_size == 0 or not require_padding157 return [message[i:i+16] for i in range(0, len(message), block_size)]158
159
160class AES:161 rounds_by_key_size = {16: 10, 24: 12, 32: 14}162 def __init__(self, master_key):163 """164 Initializes the object with a given key.165 """166 assert len(master_key) in AES.rounds_by_key_size167 self.n_rounds = AES.rounds_by_key_size[len(master_key)]168 self._key_matrices = self._expand_key(master_key)169
170 def _expand_key(self, master_key):171 """172 Expands and returns a list of key matrices for the given master_key.173 """174 # Initialize round keys with raw key material.175 key_columns = bytes2matrix(master_key)176 iteration_size = len(master_key) // 4177
178 # Each iteration has exactly as many columns as the key material.179 columns_per_iteration = len(key_columns)180 i = 1181 while len(key_columns) < (self.n_rounds + 1) * 4:182 # Copy previous word.183 word = list(key_columns[-1])184
185 # Perform schedule_core once every "row".186 if len(key_columns) % iteration_size == 0:187 # Circular shift.188 word.append(word.pop(0))189 # Map to S-BOX.190 word = [s_box[b] for b in word]191 # XOR with first byte of R-CON, since the others bytes of R-CON are 0.192 word[0] ^= r_con[i]193 i += 1194 elif len(master_key) == 32 and len(key_columns) % iteration_size == 4:195 # Run word through S-box in the fourth iteration when using a196 # 256-bit key.197 word = [s_box[b] for b in word]198
199 # XOR with equivalent word from previous iteration.200 word = xor_bytes(word, key_columns[-iteration_size])201 key_columns.append(word)202
203 # Group key words in 4x4 byte matrices.204 return [key_columns[4*i : 4*(i+1)] for i in range(len(key_columns) // 4)]205
206 def encrypt_block(self, plaintext):207 """208 Encrypts a single block of 16 byte long plaintext.209 """210 assert len(plaintext) == 16211
212 plain_state = bytes2matrix(plaintext)213
214 add_round_key(plain_state, self._key_matrices[0])215
216 for i in range(1, self.n_rounds):217 sub_bytes(plain_state)218 shift_rows(plain_state)219 mix_columns(plain_state)220 add_round_key(plain_state, self._key_matrices[i])221
222 sub_bytes(plain_state)223 shift_rows(plain_state)224 add_round_key(plain_state, self._key_matrices[-1])225
226 return matrix2bytes(plain_state)227
228 def decrypt_block(self, ciphertext):229 """230 Decrypts a single block of 16 byte long ciphertext.231 """232 assert len(ciphertext) == 16233
234 cipher_state = bytes2matrix(ciphertext)235
236 add_round_key(cipher_state, self._key_matrices[-1])237 inv_shift_rows(cipher_state)238 inv_sub_bytes(cipher_state)239
240 for i in range(self.n_rounds - 1, 0, -1):241 add_round_key(cipher_state, self._key_matrices[i])242 inv_mix_columns(cipher_state)243 inv_shift_rows(cipher_state)244 inv_sub_bytes(cipher_state)245
246 add_round_key(cipher_state, self._key_matrices[0])247
248 return matrix2bytes(cipher_state)249
250if __name__ == '__main__':251 from base64 import b64encode252 from binascii import unhexlify253 ret=AES(unhexlify('03da3479601ff722a6472329347a161bf8a712ead1d04b5560414be1dead3566')).decrypt_block(unhexlify('a847fb3ad884b6cced7e9c6c7727c0e2'))254 print(b64encode(ret))
得到 IV
的 base64
:Jle9TPezMnv770Y5b+Wc1g==
,最后使用 CyberChef
解密:
得到下一题的地址。
太 🆒 啦
找了好久,结果 Esolang Wiki
还真有全名就叫这个的语言[3](
题面如下:
1🆕6️⃣🍿🍔🌭🥪🌮🍆🥑💬🍔💬🌭💬🥪💬🌮💬🍆💬🥑🛑🆕🔟🤔🖇🙃🍖🍟🥔🌽🥩🥕🌶️🌯🥥❓🆓⚖️🆓✖️🆓➗🙃🔢1️⃣8️⃣4️⃣7️⃣🔢🛑4️⃣🛑🔢2️⃣4️⃣8️⃣🔢🛑🆓🍿🌶️🥥🌽🥕🌽🥔🍿🌽🍖🌽🥩🌯🍟🛑🆓💬🔤🚫📥️🔛🔤🛑🛑💬🔤💻🔑🔑🔑🔑🔑🔑🔤🤔🎛️🔤9️⃣❤️9️⃣4️⃣🔤🔤1️⃣9️⃣7️⃣4️⃣🔤🔤4️⃣💜💛7️⃣🔤🔤➖🔤🔤🧡7️⃣9️⃣9️⃣🔤🔤8️⃣❤️4️⃣💛🔤🔤💚6️⃣0️⃣💛🔤🔤2️⃣5️⃣💙1️⃣🔤🔤2️⃣3️⃣7️⃣2️⃣🔤
提示
做
1❤️ A 🧡 B 💛 C 💚 D 💙 E 💜 F
于是下面分析的内容就将这些红心全都替换成对应的文本了。
🍿
按 square-cool
仓库[4] 的描述,🆕 是定义函数,后面跟随一个数字 n
,表示参数的数量,然后是函数名 emoji
,最后的 n
个 emoji
表示参数名,函数以 🛑 结尾。于是开头的一一部分可以翻译成下面的形式:
1# 🍿: Print six arguments2🆕6️🍿3
4🍔🌭🥪🌮🍆🥑5
6💬🍔7💬🌭8💬🥪9💬🌮10💬🍆11💬🥑12
13🛑
其中 💬 是内置的函数,表示输出。这个函数的功能也就是连续输出对应的 6 个参数。
🤔
同样是一个函数,翻译如下:
1# 🤔:2🆕🔟🤔3🖇 # modifier4🙃 # input5🍖 # p16🍟 # p27🥔 # p38🌽 # p49🥩 # p510🥕 # p611🌶️ # p712🌯 # p813🥥 # p914❓ # Returns the second argument if the first is true, otherwise returns the third argument.15# input / 1847 * 4 == 2️4️8️16🆓17 ⚖️18 🆓19 ✖️20 🆓21 ➗🙃🔢1️8️4️7️🔢22 🛑23 4️24 🛑25 🔢2️4️8️🔢26🛑27
28🆓29 # 🍿(p7, p9, -, p6, -, p3)30 🍿🌶️🥥🌽🥕🌽🥔31 # 🍿(-, p1, -, p5, p8, p2)32 🍿🌽🍖🌽🥩🌯🍟33🛑34
35🆓36 # print('🚫📥️🔛')37 💬🔤🚫📥️🔛🔤38🛑39
40🛑
其中 🆓 的功能类似作用域开始,并以 🛑 结束。
最后
1# main2# print('💻🔑🔑🔑🔑🔑🔑')3💬🔤💻🔑🔑🔑🔑🔑🔑🔤4
5# 🤔(stdin, '9️A9️4️', '1️9️7️4️', '4️FC7️', '-', 'B7️9️9️', '8️A4️C', 'D6️0️C', '2️5️E1️', '2️3️7️2️')6# d60c2372-8a4c-4fc7-9a94-b79925e119747🤔🎛️8🔤9️A9️4️🔤9🔤1️9️7️4️🔤10🔤4️FC7️🔤11🔤➖🔤12🔤B7️9️9️🔤13🔤8️A4️C🔤14🔤D6️0️C🔤15🔤2️5️E1️🔤16🔤2️3️7️2️🔤
最后就是调用了,其中 🎛️ 表示从 stdin
读入一个整数,但在实际分析中并没有起到作用。
最后得到 UUID
:d60c2372-8a4c-4fc7-9a94-b79925e11974
,进入终章。
Happy 2021
游戏结束,新年快乐!あけましておめでとうございます!(笑)