ISCTF2025 部分题WP

debu8ger Lv3

SIGNIN

Ez_Caesar


题目描述:小蓝鲨看你们牢底坐穿决定送你们一点分。


下载附件:

def variant_caesar_encrypt(text):
encrypted = ""
shift = 2
for char in text:
if char.isalpha():
if char.isupper():
base = ord('A')
new_char = chr((ord(char) - base + shift) % 26 + base)
else:
base = ord('a')
new_char = chr((ord(char) - base + shift) % 26 + base)
encrypted += new_char
shift += 3
else:
encrypted += char
return encrypted

# KXKET{Tubsdx_re_hg_zytc_hxq_vnjma}

是简单的凯撒编码,直接在原来基础上编写decrypt函数:

def variant_caesar_decrypt(ciphertext):
decrypted = ""
shift = 2
for char in ciphertext:
if char.isalpha():
if char.isupper():
base = ord('A')
original_char = chr((ord(char) - base - shift) % 26 + base)
else:
base = ord('a')
original_char = chr((ord(char) - base - shift) % 26 + base)
decrypted += original_char
shift += 3
else:
decrypted += char
return decrypted

ciphertext = "KXKET{Tubsdx_re_hg_zytc_hxq_vnjma}"
print(variant_caesar_decrypt(ciphertext))

#flag:ISCTF{Caesar_is_so_easy_and_funny}

小蓝鲨的RC4系统


题目描述:送都送了,再给一个。


下载附件:

import hashlib

class StreamCipher:
def __init__(self, key):

self.S = list(range(256))
self.i = 0
self.j = 0

j = 0
key_bytes = self._key_to_bytes(key)
for i in range(256):
j = (j + self.S[i] + key_bytes[i % len(key_bytes)]) % 256
self.S[i], self.S[j] = self.S[j], self.S[i]

def _key_to_bytes(self, key):

if isinstance(key, str):
return hashlib.sha256(key.encode()).digest()
elif isinstance(key, bytes):
return hashlib.sha256(key).digest()

def _prga(self):

self.i = (self.i + 1) % 256
self.j = (self.j + self.S[self.i]) % 256
self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i]
K = self.S[(self.S[self.i] + self.S[self.j]) % 256]
return K

def crypt(self, data):

if isinstance(data, str):
data = data.encode('utf-8')

result = bytearray()
for byte in data:
key_byte = self._prga()
result.append(byte ^ key_byte)

return bytes(result)

def encrypt_string(text, key):

cipher = StreamCipher(key)
encrypted = cipher.crypt(text)
return encrypted.hex()

#ISCTF2025
#ba19a7116763ba8ba1c236c6bdc30187dcc8afb28c8fa5f266763880b74f5fff915613718f4d19c3baf4bbe24bd57303ce103d

得到是RC4流密码加密,

RC4的加密和解密用的为同一个函数(异或操作为自反的),所以解密只需把密文从十六进制转成字节,再调用crypt

exp:

import hashlib

class StreamCipher:
def __init__(self, key):
self.S = list(range(256))
self.i = 0
self.j = 0
j = 0
key_bytes = self._key_to_bytes(key)
for i in range(256):
j = (j + self.S[i] + key_bytes[i % len(key_bytes)]) % 256
self.S[i], self.S[j] = self.S[j], self.S[i]

def _key_to_bytes(self, key):
if isinstance(key, str):
return hashlib.sha256(key.encode()).digest()
elif isinstance(key, bytes):
return hashlib.sha256(key).digest()

def _prga(self):
self.i = (self.i + 1) % 256
self.j = (self.j + self.S[self.i]) % 256
self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i]
K = self.S[(self.S[self.i] + self.S[self.j]) % 256]
return K

def crypt(self, data):
if isinstance(data, str):
data = data.encode('utf-8')
result = bytearray()
for byte in data:
key_byte = self._prga()
result.append(byte ^ key_byte)
return bytes(result)

def decrypt_hex(hex_data, key):
ciphertext_bytes = bytes.fromhex(hex_data)
cipher = StreamCipher(key)
decrypted_bytes = cipher.crypt(ciphertext_bytes)
return decrypted_bytes.decode('utf-8')

if __name__ == "__main__":
hex_data = "ba19a7116763ba8ba1c236c6bdc30187dcc8afb28c8fa5f266763880b74f5fff915613718f4d19c3baf4bbe24bd57303ce103d"
key = "ISCTF2025"
print(decrypt_hex(hex_data, key))

#flag: ISCTF{Welcome_to_ISCTF_&_this_is_a_secret_with_RC4}

MISC

Guess!


题目描述:这是一个经典的猜数字,开始你的数字解密之旅吧!


下载附件,是pyinstaller处理后的MiscNumberChallenge.exe,用unpack.py解包后得到extracted目录

找到misc_challenge.pyc文件,在 https://pylingual.io/ 反编译.pyc文件:

import random
import time
import sys
import base64

def decode_flag(encoded_flag):
"""解码隐藏的Flag"""
try:
decoded = base64.b64decode(encoded_flag).decode('utf-8')
decoded = base64.b64decode(decoded).decode('utf-8')
return decoded
except:
return 'Flag解码错误'

def main():
secret_number = random.randint(1, 100)
max_attempts = 10
encoded_flag = 'SVNDVEZ7OXVlU3NfdGhFX0BuJHdlUn0='
encoded_flag = base64.b64encode(encoded_flag.encode()).decode()
print('==================================================')
print('欢迎参加Misc猜数字挑战!')
print(f'系统已生成1-100之间的秘密数字,你有{max_attempts}次机会')
print('猜对数字即可获得Flag!')
print('==================================================')
for attempt in range(max_attempts):
remaining = max_attempts - attempt
print(f'\n[剩余机会: {remaining}]')
while True:
try:
guess = int(input('请输入你的猜测 (1-100): '))
if 1 <= guess <= 100:
break
print('请输入1-100之间的整数!')
except:
print('无效输入!请输入整数。')
if guess < secret_number:
print(f'{guess} → 太小了,继续努力!')
elif guess > secret_number:
print(f'{guess} → 太大了,往小试试!')
else:
print('==================================================')
print(f'★ 恭喜!你猜对了!数字就是 {secret_number}')
print('★ 恭喜获得Flag:')
flag = decode_flag(encoded_flag)
print(f'★ {flag}')
print('==================================================')
print('程序将在10秒后关闭...')
time.sleep(10)
return
else:
print('\n==================================================')
print('⚠️ 挑战失败!')
print(f'⚠️ 正确答案是: {secret_number}')
print('⚠️ 请重新运行程序再次尝试!')
print('==================================================')
print('程序将在10秒后关闭...')
time.sleep(10)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('\n\n程序已中断。')
sys.exit()

容易得到,flag编码为base64,解码即得flag:ISCTF{9ueSs_thE_@n$weR}

REVERSE

ezzz_math


题目描述:来解方程吧


IDA中打开,可疑点_main函数:

{
int i; // [esp+4h] [ebp-6Ch]
char Str[100]; // [esp+8h] [ebp-68h] BYREF

sub_403B50(aPleaseInputYou);
sub_403B50(aTryToUseZ3Solv);
sub_403B90("%80s", Str);
if ( strlen(Str) == 23 )
{
for ( i = 0; i < 23; ++i )
Str[i] ^= 0xCu;
if ( sub_401000(Str) )
puts(aRight);
else
puts(aWrong);
}
else
{
puts(Buffer);
}
return 0;
}

逻辑就是输入23字节字符串,用0xC(12)异或,最后将异或完的数组传给sub_401000验证。

所以,我们看向sub_401000函数:

BOOL __cdecl sub_401000(char *a1)
{
return 94 * a1[22]
+ 74 * a1[21]
+ 70 * a1[19]
+ 12 * a1[18]
+ 20 * a1[16]
+ 62 * a1[12]
+ 82 * a1[10]
+ 7 * a1[7]
+ 63 * a1[6]
+ 18 * a1[5]
+ 58 * a1[4]
+ 94 * a1[2]
+ 77 * *a1
- 43 * a1[1]
- 37 * a1[3]
- 97 * a1[8]
- 23 * a1[9]
- 86 * a1[11]
- 6 * a1[13]
- 5 * a1[14]
- 79 * a1[15]
- 63 * a1[17]
- 93 * a1[20] == 20156
&& 87 * a1[22]
+ 75 * a1[21]
+ 73 * a1[15]
+ 67 * a1[14]
+ 30 * a1[13]
+ (a1[11] << 6)
+ 35 * a1[9]
+ 91 * a1[7]
+ 91 * a1[5]
+ 34 * a1[3]
+ 74 * *a1
- 89 * a1[1]
- 72 * a1[2]
- 76 * a1[4]
- 32 * a1[6]
- 97 * a1[8]
- 39 * a1[10]
- 23 * a1[12]
+ 8 * a1[16]
- 98 * a1[17]
- 4 * a1[18]
- 80 * a1[19]
- 83 * a1[20] == 7183
&& 51 * a1[21]
+ 22 * a1[20]
+ 15 * a1[19]
+ 51 * a1[17]
+ 96 * a1[12]
+ 34 * a1[7]
+ 77 * a1[5]
+ 59 * a1[2]
+ 89 * a1[1]
+ 92 * *a1
- 85 * a1[3]
- 50 * a1[4]
- 51 * a1[6]
- 75 * a1[8]
- 40 * a1[10]
- 4 * a1[11]
- 74 * a1[13]
- 98 * a1[14]
- 23 * a1[15]
- 14 * a1[16]
- 92 * a1[18]
- 7 * a1[22] == -7388
&& 61 * a1[22]
+ 72 * a1[21]
+ 28 * a1[20]
+ 55 * a1[18]
+ 20 * a1[17]
+ 13 * a1[14]
+ 51 * a1[13]
+ 69 * a1[12]
+ 10 * a1[11]
+ 95 * a1[10]
+ 43 * a1[9]
+ 53 * a1[8]
+ 76 * a1[7]
+ 25 * a1[6]
+ 9 * a1[5]
+ 10 * a1[4]
+ 98 * a1[1]
+ 70 * *a1
- 22 * a1[2]
+ 2 * a1[3]
- 49 * a1[15]
+ 4 * a1[16]
- 77 * a1[19] == 69057
&& 7 * a1[22]
+ 21 * a1[16]
+ 22 * a1[13]
+ 55 * a1[9]
+ 66 * a1[8]
+ 78 * a1[5]
+ 10 * a1[3]
+ 80 * a1[1]
+ 65 * *a1
- 20 * a1[2]
- 53 * a1[4]
- 98 * a1[6]
+ 8 * a1[7]
- 78 * a1[10]
- 94 * a1[11]
- 93 * a1[12]
- 18 * a1[14]
- 48 * a1[15]
- 9 * a1[17]
- 73 * a1[18]
- 59 * a1[19]
- 68 * a1[20]
- 74 * a1[21] == -31438
&& 33 * a1[19]
+ 78 * a1[15]
+ 66 * a1[10]
+ 3 * a1[9]
+ 43 * a1[4]
+ 24 * a1[3]
+ 3 * a1[2]
+ 27 * *a1
- 18 * a1[1]
- 46 * a1[5]
- 18 * a1[6]
- a1[7]
- 33 * a1[8]
- 50 * a1[11]
- 23 * a1[12]
- 37 * a1[13]
- 45 * a1[14]
+ 2 * a1[16]
- a1[17]
- 60 * a1[18]
- 87 * a1[20]
- 72 * a1[21]
- 6 * a1[22] == -26121
&& 31 * a1[20]
+ 80 * a1[18]
+ 34 * a1[17]
+ 34 * a1[15]
+ 38 * a1[14]
+ 53 * a1[13]
+ 35 * a1[12]
+ 82 * a1[9]
+ 27 * a1[8]
+ 80 * a1[7]
+ 46 * a1[6]
+ 18 * a1[4]
+ 5 * a1[1]
+ 98 * *a1
- 12 * a1[2]
- 9 * a1[3]
- 57 * a1[5]
- 46 * a1[10]
- 31 * a1[11]
- 68 * a1[16]
- 94 * a1[19]
- 93 * a1[21]
- 15 * a1[22] == 26005
&& 81 * a1[21]
+ 40 * a1[20]
+ 34 * a1[19]
+ 94 * a1[18]
+ 98 * a1[17]
+ 11 * a1[14]
+ 63 * a1[13]
+ 95 * a1[12]
+ 43 * a1[11]
+ 99 * a1[10]
+ 29 * a1[9]
+ 81 * a1[6]
+ 72 * a1[5]
+ 54 * a1[3]
+ 21 * *a1
- 26 * a1[1]
- 90 * a1[2]
- 15 * a1[4]
- 54 * a1[7]
- 12 * a1[8]
- 38 * a1[15]
- 15 * a1[16]
- 56 * a1[22] == 57169
&& 71 * a1[18]
+ 39 * a1[17]
+ 73 * a1[15]
+ 14 * a1[14]
+ 56 * a1[12]
+ 56 * a1[10]
+ 27 * a1[9]
+ 68 * a1[7]
+ 39 * a1[6]
+ 26 * a1[5]
+ 40 * a1[4]
+ 24 * a1[3]
+ 11 * a1[2]
+ 14 * a1[1]
+ 94 * *a1
- 10 * a1[8]
- 11 * a1[11]
- 63 * a1[13]
- 39 * a1[16]
- 14 * a1[19]
- 17 * a1[20]
- 23 * a1[21]
- 7 * a1[22] == 40024
&& (a1[22] << 6)
+ 80 * a1[21]
+ 89 * a1[20]
+ 70 * a1[19]
+ 66 * a1[18]
+ 55 * a1[17]
+ 16 * a1[16]
+ 84 * a1[13]
+ 48 * a1[12]
+ 11 * a1[7]
+ 32 * a1[5]
+ 99 * *a1
- 26 * a1[1]
- 91 * a1[2]
- 96 * a1[3]
- 63 * a1[4]
- 67 * a1[6]
- 72 * a1[8]
+ 4 * a1[9]
- 84 * a1[10]
- 81 * a1[11]
- 80 * a1[14]
- 98 * a1[15] == 432
&& a1[21]
+ 41 * a1[17]
+ 46 * a1[12]
+ 44 * a1[9]
+ 63 * *a1
- 73 * a1[1]
- 43 * a1[2]
+ 4 * a1[3]
- 37 * a1[4]
- 54 * a1[5]
- 58 * a1[6]
- 95 * a1[7]
- 2 * a1[8]
- 37 * a1[10]
- 5 * a1[11]
+ 2 * a1[13]
- 46 * a1[14]
- 27 * a1[15]
- 19 * a1[16]
- 78 * a1[18]
- 51 * a1[19]
- 82 * a1[20]
- 59 * a1[22] == -57338
&& 10 * a1[22]
+ 58 * a1[18]
+ 16 * a1[17]
+ 69 * a1[16]
+ 6 * a1[15]
+ 5 * a1[12]
+ 87 * a1[7]
+ 47 * a1[5]
+ 91 * a1[4]
+ 54 * a1[2]
+ 21 * a1[1]
+ 52 * *a1
- 76 * a1[3]
- 96 * a1[6]
- 27 * a1[8]
- 43 * a1[9]
- 15 * a1[10]
- 35 * a1[11]
- 53 * a1[13]
+ 4 * a1[14]
- 83 * a1[19]
- 68 * a1[20]
- 18 * a1[21] == 1777
&& 66 * a1[22]
+ 92 * a1[21]
+ 29 * a1[20]
+ 42 * a1[19]
+ 55 * a1[14]
+ 72 * a1[13]
+ 40 * a1[12]
+ 31 * a1[10]
+ 88 * a1[9]
+ 61 * a1[8]
+ 59 * a1[7]
+ 35 * a1[6]
+ 16 * a1[3]
+ 24 * a1[1]
+ 60 * *a1
- 55 * a1[2]
- 8 * a1[4]
- 7 * a1[5]
- 17 * a1[11]
- 25 * a1[15]
- 22 * a1[16]
- 10 * a1[17]
- 59 * a1[18] == 47727
&& 3 * a1[21]
+ 54 * a1[18]
+ 6 * a1[15]
+ 93 * a1[14]
+ 74 * a1[10]
+ 6 * a1[7]
+ 98 * a1[4]
+ 65 * a1[3]
+ 84 * a1[2]
+ 18 * a1[1]
+ 35 * *a1
- 29 * a1[5]
- 40 * a1[6]
- 35 * a1[8]
+ 8 * a1[9]
- 15 * a1[11]
- 4 * a1[12]
- 83 * a1[16]
- 74 * a1[17]
- 72 * a1[19]
- 53 * a1[20]
- 31 * a1[22] == 6695
&& 45 * a1[20]
+ 14 * a1[19]
+ 76 * a1[18]
+ 17 * a1[16]
+ 86 * a1[14]
+ 28 * a1[11]
+ 19 * a1[5]
+ 46 * a1[1]
+ 75 * *a1
- 12 * a1[2]
- 27 * a1[3]
- 66 * a1[4]
- 27 * a1[6]
- 32 * a1[7]
- 69 * a1[8]
- 31 * a1[9]
- 65 * a1[10]
- 54 * a1[12]
- 6 * a1[13]
+ 2 * a1[15]
- 10 * a1[17]
- 89 * a1[21]
- 16 * a1[22] == -3780
&& 62 * a1[21]
+ 74 * a1[20]
+ 28 * a1[18]
+ 7 * a1[17]
+ 74 * a1[16]
+ 45 * a1[15]
+ 57 * a1[14]
+ 34 * a1[11]
+ 85 * a1[10]
+ 98 * a1[6]
+ 29 * a1[4]
+ 94 * a1[3]
+ 51 * a1[2]
+ 85 * a1[1]
- 36 * a1[5]
- a1[7]
- 3 * a1[8]
- 74 * a1[9]
- 70 * a1[12]
- 68 * a1[13]
- 3 * a1[19]
+ 8 * a1[22] == 47300
&& 22 * a1[22]
+ 45 * a1[21]
+ 14 * a1[19]
+ 32 * a1[18]
+ 77 * a1[17]
+ 70 * a1[12]
+ 7 * a1[10]
+ 99 * a1[4]
+ 82 * *a1
- 48 * a1[1]
- 40 * a1[2]
- 81 * a1[3]
- 27 * a1[5]
- 75 * a1[6]
- 79 * a1[7]
- 26 * a1[8]
- 68 * a1[9]
- 57 * a1[11]
- 77 * a1[13]
- 32 * a1[14]
- a1[15]
- 91 * a1[16]
- 14 * a1[20] == -34153
&& 65 * a1[21]
+ 13 * a1[20]
+ 61 * a1[17]
+ 97 * a1[13]
+ 24 * a1[10]
+ 40 * a1[5]
+ 20 * *a1
- 81 * a1[1]
- 17 * a1[2]
- 77 * a1[3]
- 79 * a1[4]
- 45 * a1[6]
- 61 * a1[7]
- 48 * a1[8]
- 97 * a1[9]
- 49 * a1[11]
- 14 * a1[12]
- 81 * a1[14]
- 20 * a1[15]
- 27 * a1[16]
- 89 * a1[18]
- 93 * a1[19]
- 46 * a1[22] == -55479
&& 60 * a1[21]
+ 70 * a1[20]
+ 13 * a1[15]
+ 87 * a1[13]
+ 76 * a1[11]
+ 88 * a1[9]
+ 87 * a1[3]
+ 87 * *a1
- 97 * a1[1]
- 40 * a1[2]
- 49 * a1[4]
- 23 * a1[5]
- 30 * a1[6]
- 50 * a1[7]
- 98 * a1[8]
- 21 * a1[10]
- 54 * a1[12]
- 65 * a1[14]
- 80 * a1[17]
- 28 * a1[18]
- 57 * a1[19]
- 70 * a1[22] == -20651
&& 54 * a1[20]
+ 86 * a1[17]
+ 92 * a1[16]
+ 41 * a1[15]
+ 70 * a1[10]
+ 9 * a1[9]
+ a1[8]
+ 96 * a1[7]
+ 45 * a1[6]
+ 78 * a1[5]
+ 3 * a1[4]
+ 90 * a1[3]
+ 71 * a1[2]
+ 96 * *a1
- 8 * a1[1]
+ 4 * a1[11]
- 55 * a1[12]
- 73 * a1[13]
- 54 * a1[14]
- 89 * a1[18]
- (a1[19] << 6)
- 67 * a1[21]
+ 4 * a1[22] == 35926
&& 5 * a1[22]
+ 88 * a1[20]
+ 52 * a1[19]
+ 21 * a1[17]
+ 25 * a1[16]
+ 3 * a1[13]
+ 88 * a1[10]
+ 39 * a1[8]
+ 48 * a1[7]
+ 74 * a1[6]
+ 86 * a1[4]
+ 46 * a1[2]
+ 17 * *a1
- 98 * a1[1]
- 50 * a1[3]
- 28 * a1[5]
- 73 * a1[9]
- 33 * a1[11]
- 75 * a1[12]
- 14 * a1[14]
- 31 * a1[15]
- 26 * a1[18]
- 52 * a1[21] == 8283
&& 96 * a1[22]
+ 85 * a1[20]
+ 55 * a1[19]
+ 99 * a1[13]
+ 19 * a1[11]
+ 77 * a1[10]
+ 52 * a1[9]
+ 66 * a1[8]
+ 96 * a1[6]
+ 72 * a1[4]
+ 90 * a1[3]
+ 60 * a1[1]
+ 94 * *a1
- 99 * a1[2]
- 26 * a1[5]
- 94 * a1[7]
- 49 * a1[12]
- 32 * a1[14]
- 54 * a1[15]
- 92 * a1[16]
- 71 * a1[17]
- 63 * a1[18]
- 23 * a1[21] == 33789
&& 15 * a1[22]
+ a1[19]
+ 26 * a1[17]
+ 65 * a1[16]
+ 80 * a1[11]
+ 92 * a1[8]
+ 28 * a1[5]
+ 79 * a1[4]
+ 73 * *a1
- 98 * a1[1]
- 2 * a1[2]
- 70 * a1[3]
- 10 * a1[6]
- 30 * a1[7]
- 51 * a1[9]
- 77 * a1[10]
- 32 * a1[12]
- 32 * a1[13]
+ 8 * a1[14]
+ 4 * a1[15]
- 11 * a1[18]
- 83 * a1[20]
- 85 * a1[21] == -10455;
}

可以看到,这一大长串包含了22个等式,对a1[0]a1[22]共23个变量进行了约束,

值得注意的是:

  • *a1其实就是a1[0],第一个变量
  • a1[i] << 6 = 64 * a1[i] (左移6位实际上就是*64)

根据_main函数的汇编代码,得到是z3算法,

编写最终的EXP:

from z3 import *

x = [Int(f'x{i}') for i in range(23)]
s = Solver()
for i in range(23):
s.add(x[i] >= 32, x[i] <= 126)

s.add(
94*x[22] + 74*x[21] + 70*x[19] + 12*x[18] + 20*x[16] + 62*x[12] + 82*x[10] +
7*x[7] + 63*x[6] + 18*x[5] + 58*x[4] + 94*x[2] + 77*x[0] -
43*x[1] - 37*x[3] - 97*x[8] - 23*x[9] - 86*x[11] - 6*x[13] - 5*x[14] -
79*x[15] - 63*x[17] - 93*x[20] == 20156
)

s.add(
87*x[22] + 75*x[21] + 73*x[15] + 67*x[14] + 30*x[13] + 64*x[11] + 35*x[9] +
91*x[7] + 91*x[5] + 34*x[3] + 74*x[0] -
89*x[1] - 72*x[2] - 76*x[4] - 32*x[6] - 97*x[8] - 39*x[10] - 23*x[12] +
8*x[16] - 98*x[17] - 4*x[18] - 80*x[19] - 83*x[20] == 7183
)

s.add(
51*x[21] + 22*x[20] + 15*x[19] + 51*x[17] + 96*x[12] + 34*x[7] + 77*x[5] +
59*x[2] + 89*x[1] + 92*x[0] -
85*x[3] - 50*x[4] - 51*x[6] - 75*x[8] - 40*x[10] - 4*x[11] - 74*x[13] -
98*x[14] - 23*x[15] - 14*x[16] - 92*x[18] - 7*x[22] == -7388
)

s.add(
61*x[22] + 72*x[21] + 28*x[20] + 55*x[18] + 20*x[17] + 13*x[14] + 51*x[13] +
69*x[12] + 10*x[11] + 95*x[10] + 43*x[9] + 53*x[8] + 76*x[7] + 25*x[6] +
9*x[5] + 10*x[4] + 98*x[1] + 70*x[0] -
22*x[2] + 2*x[3] - 49*x[15] + 4*x[16] - 77*x[19] == 69057
)

s.add(
7*x[22] + 21*x[16] + 22*x[13] + 55*x[9] + 66*x[8] + 78*x[5] + 10*x[3] +
80*x[1] + 65*x[0] -
20*x[2] - 53*x[4] - 98*x[6] + 8*x[7] - 78*x[10] - 94*x[11] - 93*x[12] -
18*x[14] - 48*x[15] - 9*x[17] - 73*x[18] - 59*x[19] - 68*x[20] - 74*x[21] == -31438
)

s.add(
33*x[19] + 78*x[15] + 66*x[10] + 3*x[9] + 43*x[4] + 24*x[3] + 3*x[2] +
27*x[0] -
18*x[1] - 46*x[5] - 18*x[6] - x[7] - 33*x[8] - 50*x[11] - 23*x[12] -
37*x[13] - 45*x[14] + 2*x[16] - x[17] - 60*x[18] - 87*x[20] - 72*x[21] - 6*x[22] == -26121
)

s.add(
31*x[20] + 80*x[18] + 34*x[17] + 34*x[15] + 38*x[14] + 53*x[13] + 35*x[12] +
82*x[9] + 27*x[8] + 80*x[7] + 46*x[6] + 18*x[4] + 5*x[1] + 98*x[0] -
12*x[2] - 9*x[3] - 57*x[5] - 46*x[10] - 31*x[11] - 68*x[16] - 94*x[19] -
93*x[21] - 15*x[22] == 26005
)

s.add(
81*x[21] + 40*x[20] + 34*x[19] + 94*x[18] + 98*x[17] + 11*x[14] + 63*x[13] +
95*x[12] + 43*x[11] + 99*x[10] + 29*x[9] + 81*x[6] + 72*x[5] + 54*x[3] +
21*x[0] -
26*x[1] - 90*x[2] - 15*x[4] - 54*x[7] - 12*x[8] - 38*x[15] - 15*x[16] - 56*x[22] == 57169
)

s.add(
71*x[18] + 39*x[17] + 73*x[15] + 14*x[14] + 56*x[12] + 56*x[10] + 27*x[9] +
68*x[7] + 39*x[6] + 26*x[5] + 40*x[4] + 24*x[3] + 11*x[2] + 14*x[1] + 94*x[0] -
10*x[8] - 11*x[11] - 63*x[13] - 39*x[16] - 14*x[19] - 17*x[20] - 23*x[21] - 7*x[22] == 40024
)

s.add(
64*x[22] + 80*x[21] + 89*x[20] + 70*x[19] + 66*x[18] + 55*x[17] + 16*x[16] +
84*x[13] + 48*x[12] + 11*x[7] + 32*x[5] + 99*x[0] -
26*x[1] - 91*x[2] - 96*x[3] - 63*x[4] - 67*x[6] - 72*x[8] + 4*x[9] -
84*x[10] - 81*x[11] - 80*x[14] - 98*x[15] == 432
)

s.add(
x[21] + 41*x[17] + 46*x[12] + 44*x[9] + 63*x[0] -
73*x[1] - 43*x[2] + 4*x[3] - 37*x[4] - 54*x[5] - 58*x[6] - 95*x[7] -
2*x[8] - 37*x[10] - 5*x[11] + 2*x[13] - 46*x[14] - 27*x[15] - 19*x[16] -
78*x[18] - 51*x[19] - 82*x[20] - 59*x[22] == -57338
)

s.add(
10*x[22] + 58*x[18] + 16*x[17] + 69*x[16] + 6*x[15] + 5*x[12] + 87*x[7] +
47*x[5] + 91*x[4] + 54*x[2] + 21*x[1] + 52*x[0] -
76*x[3] - 96*x[6] - 27*x[8] - 43*x[9] - 15*x[10] - 35*x[11] - 53*x[13] +
4*x[14] - 83*x[19] - 68*x[20] - 18*x[21] == 1777
)

s.add(
66*x[22] + 92*x[21] + 29*x[20] + 42*x[19] + 55*x[14] + 72*x[13] + 40*x[12] +
31*x[10] + 88*x[9] + 61*x[8] + 59*x[7] + 35*x[6] + 16*x[3] + 24*x[1] + 60*x[0] -
55*x[2] - 8*x[4] - 7*x[5] - 17*x[11] - 25*x[15] - 22*x[16] - 10*x[17] - 59*x[18] == 47727
)

s.add(
3*x[21] + 54*x[18] + 6*x[15] + 93*x[14] + 74*x[10] + 6*x[7] + 98*x[4] + 65*x[3] +
84*x[2] + 18*x[1] + 35*x[0] -
29*x[5] - 40*x[6] - 35*x[8] + 8*x[9] - 15*x[11] - 4*x[12] - 83*x[16] -
74*x[17] - 72*x[19] - 53*x[20] - 31*x[22] == 6695
)

s.add(
45*x[20] + 14*x[19] + 76*x[18] + 17*x[16] + 86*x[14] + 28*x[11] + 19*x[5] +
46*x[1] + 75*x[0] -
12*x[2] - 27*x[3] - 66*x[4] - 27*x[6] - 32*x[7] - 69*x[8] - 31*x[9] -
65*x[10] - 54*x[12] - 6*x[13] + 2*x[15] - 10*x[17] - 89*x[21] - 16*x[22] == -3780
)

s.add(
62*x[21] + 74*x[20] + 28*x[18] + 7*x[17] + 74*x[16] + 45*x[15] + 57*x[14] +
34*x[11] + 85*x[10] + 98*x[6] + 29*x[4] + 94*x[3] + 51*x[2] + 85*x[1] -
36*x[5] - x[7] - 3*x[8] - 74*x[9] - 70*x[12] - 68*x[13] - 3*x[19] + 8*x[22] == 47300
)

s.add(
22*x[22] + 45*x[21] + 14*x[19] + 32*x[18] + 77*x[17] + 70*x[12] + 7*x[10] +
99*x[4] + 82*x[0] -
48*x[1] - 40*x[2] - 81*x[3] - 27*x[5] - 75*x[6] - 79*x[7] - 26*x[8] -
68*x[9] - 57*x[11] - 77*x[13] - 32*x[14] - x[15] - 91*x[16] - 14*x[20] == -34153
)

s.add(
65*x[21] + 13*x[20] + 61*x[17] + 97*x[13] + 24*x[10] + 40*x[5] + 20*x[0] -
81*x[1] - 17*x[2] - 77*x[3] - 79*x[4] - 45*x[6] - 61*x[7] - 48*x[8] -
97*x[9] - 49*x[11] - 14*x[12] - 81*x[14] - 20*x[15] - 27*x[16] - 89*x[18] -
93*x[19] - 46*x[22] == -55479
)

s.add(
60*x[21] + 70*x[20] + 13*x[15] + 87*x[13] + 76*x[11] + 88*x[9] + 87*x[3] + 87*x[0] -
97*x[1] - 40*x[2] - 49*x[4] - 23*x[5] - 30*x[6] - 50*x[7] - 98*x[8] -
21*x[10] - 54*x[12] - 65*x[14] - 80*x[17] - 28*x[18] - 57*x[19] - 70*x[22] == -20651
)

s.add(
54*x[20] + 86*x[17] + 92*x[16] + 41*x[15] + 70*x[10] + 9*x[9] + x[8] +
96*x[7] + 45*x[6] + 78*x[5] + 3*x[4] + 90*x[3] + 71*x[2] + 96*x[0] -
8*x[1] + 4*x[11] - 55*x[12] - 73*x[13] - 54*x[14] - 89*x[18] - 64*x[19] -
67*x[21] + 4*x[22] == 35926
)

s.add(
5*x[22] + 88*x[20] + 52*x[19] + 21*x[17] + 25*x[16] + 3*x[13] + 88*x[10] +
39*x[8] + 48*x[7] + 74*x[6] + 86*x[4] + 46*x[2] + 17*x[0] -
98*x[1] - 50*x[3] - 28*x[5] - 73*x[9] - 33*x[11] - 75*x[12] - 14*x[14] -
31*x[15] - 26*x[18] - 52*x[21] == 8283
)

s.add(
96*x[22] + 85*x[20] + 55*x[19] + 99*x[13] + 19*x[11] + 77*x[10] + 52*x[9] +
66*x[8] + 96*x[6] + 72*x[4] + 90*x[3] + 60*x[1] + 94*x[0] -
99*x[2] - 26*x[5] - 94*x[7] - 49*x[12] - 32*x[14] - 54*x[15] - 92*x[16] -
71*x[17] - 63*x[18] - 23*x[21] == 33789
)

s.add(
15*x[22] + x[19] + 26*x[17] + 65*x[16] + 80*x[11] + 92*x[8] + 28*x[5] +
79*x[4] + 73*x[0] -
98*x[1] - 2*x[2] - 70*x[3] - 10*x[6] - 30*x[7] - 51*x[9] - 77*x[10] -
32*x[12] - 32*x[13] + 8*x[14] + 4*x[15] - 11*x[18] - 83*x[20] - 85*x[21] == -10455
)

if s.check() == sat:
m = s.model()
x_vals = [m[x[i]].as_long() for i in range(23)]
flag_bytes = bytes([xi ^ 0xC for xi in x_vals])
print("Flag:", flag_bytes.decode('ascii'))
else:
print("No solution!")

# Flag: ISCTF{yR_A_Zzz_Ma5t3R!}

ezpy


题目描述:这是什么库?没见过呢


下载附件,是pyinstaller处理的ezpy.exe,按照之前做法,得到extracted文件夹,

反编译ezpy.pyc得到:

try:
from mypy import check

def main():
user_input = input('Please input your flag: ').strip()
if check(user_input):
print('Correct!')
return None
if __name__ == '__main__':
main()
except ImportError:
print('Error: Cannot import mypy module')
exit(1)

没什么用,继续找。在目录里看到了mypy.cp313-win_amd64.pyd,想着用IDA反编译试试…

在IDA中,Shift+F12看到RC4 flag checker module,得到是RC4加密

通过一个一个函数查看的艰难寻找,在sub_36F4D1519函数中得到RC4加密的密钥ISCTF2025

{
char *v2; // rsi
__m128i *v3; // rbx
__m128i *v5; // rax
unsigned int v6; // eax
__int64 v7; // rax
char v8[274]; // [rsp+26h] [rbp-132h] BYREF
char *Str; // [rsp+138h] [rbp-20h] BYREF

strcpy(v8, "ISCTF2025");
if ( !(unsigned int)PyArg_ParseTuple(a2, &unk_36F4D4000, &Str) )
return 0LL;
v2 = Str;
v3 = (__m128i *)Py_FalseStruct;
if ( (unsigned int)strlen(Str) == 25 )
{
v5 = (__m128i *)malloc(0x19uLL);
v3 = v5;
if ( v5 )
{
*v5 = _mm_loadu_si128((const __m128i *)v2);
*(__m128i *)((char *)v5 + 9) = _mm_loadu_si128((const __m128i *)(v2 + 9));
v6 = strlen(v8);
sub_36F4D1430(&v8[10], v8, v6);
sub_36F4D149C(&v8[10], v3, 25LL);
v7 = 0LL;
while ( v3->m128i_i8[v7] == byte_36F4D4050[v7] )
{
if ( ++v7 == 25 )
{
free(v3);
return (__m128i *)Py_TrueStruct;
}
}
free(v3);
return (__m128i *)Py_FalseStruct;
}
else
{
PyErr_NoMemory();
}
}
return v3;
}

生成RC4的S-box,写入v8[i],然后执行cipher[i] = plain[i] XOR RC4_next()

查看汇编代码,得到byte_36F4D4050的数据:

re1

最终的EXP:

cipher = bytes([
0x1D,0xD5,0x38,0x33,0xAF,0xB5,0x51,0xF3,0x2C,0x6B,
0x6E,0xFE,0x41,0x24,0x43,0xD2,0x71,0xCF,0xA4,0x4C,
0xE3,0x9A,0x9A,0xB5,0x31
])

key = b"ISCTF2025"

def rc4_ksa(key):
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) & 0xFF
S[i], S[j] = S[j], S[i]
return S

def rc4_prga(S, n):
i = j = 0
out = []
for _ in range(n):
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) & 0xFF]
out.append(K)
return bytes(out)

S = rc4_ksa(key)
keystream = rc4_prga(S, len(cipher))

plain = bytes([c ^ k for c, k in zip(cipher, keystream)])
print(plain)

# flag: ISCTF{Y0U_GE7_7HE_PYD!!!}

MysteriousStream


题目描述:小曲冒着生命风险,读入了一个神秘的 payload.dat文件,为了不枉费小曲的艰辛……,你能逆向出被加密的秘密吗?


下载附件,包含challengepayload.dat文件,定位至main函数:

{
FILE *v3; // rax
FILE *v4; // r12
signed __int64 v5; // r14
char *v6; // rax
char *v7; // rbp
size_t v8; // r13
__int64 i; // rcx
_BYTE v11[17]; // [rsp+7h] [rbp-41h] BYREF
unsigned __int64 v12; // [rsp+18h] [rbp-30h]

v12 = __readfsqword(0x28u);
v3 = fopen("payload.dat", "rb");
if ( v3 )
{
v4 = v3;
fseek(v3, 0LL, 2);
v5 = ftell(v4);
if ( v5 < 0 )
{
puts("Get file size failed");
fclose(v4);
return 1;
}
else
{
fseek(v4, 0LL, 0);
v6 = (char *)malloc(v5);
v7 = v6;
if ( v6 )
{
v8 = fread(v6, 1uLL, v5, v4);
fclose(v4);
if ( v5 == v8 )
{
qmemcpy(v11, "P4ssXORSecr3tK3y!", sizeof(v11));
rc4_variant(v7, v8, &v11[7], 10LL);
if ( v8 )
{
for ( i = 0LL; i != v8; ++i )
v7[i] ^= v11[i % 7];
}
__printf_chk(1LL, "Result: %s\n", v7);
free(v7);
return 0;
}
else
{
__printf_chk(1LL, "Read failed! Expected %ld bytes, got %zu bytes\n", v5, v8);
free(v7);
return 1;
}
}
else
{
puts("Malloc memory failed");
fclose(v4);
return 1;
}
}
}
else
{
puts("payload.dat not found");
return 1;
}
}

使用key = v11[7 : 7 + 10] = "Secr3tK3y!"加密,对payload (v7) 做RC4加密,

然后循环异或key_xor = v11[0:7] = b"P4ssXOR"v7[i] = v7[i] XOR key_xor[i % 7]

再看xor_cycle函数:

{
unsigned __int64 i; // r8
__int64 result; // rax

if ( a2 )
{
for ( i = 0LL; i != a2; ++i )
{
result = *(unsigned __int8 *)(a3 + i % a4);
*(_BYTE *)(a1 + i) ^= result;
}
}
return result;
}

rc4_variant函数:

{
_BYTE *v4; // r8
__int64 i; // rax
unsigned __int64 v7; // rcx
int v8; // ebx
char v9; // r11
_BYTE *v10; // r9
char v11; // al
_BYTE v13[264]; // [rsp+0h] [rbp-118h]
unsigned __int64 v14; // [rsp+108h] [rbp-10h]

v4 = a1;
v14 = __readfsqword(0x28u);
for ( i = 0LL; i != 256; ++i )
v13[i] = i;
v7 = 0LL;
LOBYTE(v8) = 0;
do
{
v9 = v13[v7];
v8 = (unsigned __int8)((v7 & 0xAA) + v8 + v9 + *(_BYTE *)(a3 + v7 % a4));
v13[v7++] = v13[v8];
v13[v8] = v9;
}
while ( v7 != 256 );
if ( a2 )
{
v10 = &a1[a2];
LOBYTE(a1) = 0;
LOBYTE(a2) = 0;
do
{
LODWORD(a1) = (unsigned __int8)((_BYTE)a1 + 1);
v11 = v13[(unsigned int)a1];
LODWORD(a2) = (unsigned __int8)(v11 + a2);
v13[(unsigned int)a1] = v13[(unsigned int)a2];
v13[(unsigned int)a2] = v11;
*v4++ ^= v13[(unsigned __int8)(v13[(unsigned int)a1] + v11)];
}
while ( v10 != v4 );
}
return v14 - __readfsqword(0x28u);
}

变种的RC4加密逻辑为j = (j + S[i] + key[i % keylen] + (i & 0xAA)) mod 256,解密只需逆着来即可。

所以,最终的加密逻辑为cipher = XOR( RC4_variant(plaintext, key="Secr3tK3y!") , key="P4ssXOR")

EXP:

def rc4_variant_crypt(data: bytes, key: bytes) -> bytes:

S = list(range(256))
j = 0
key_len = len(key)
for i in range(256):
j = ( (i & 0xAA) + j + S[i] + key[i % key_len] ) & 0xFF
S[i], S[j] = S[j], S[i]

out = bytearray()
i = 0
j = 0
for byte in data:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) & 0xFF]
out.append(byte ^ K)

return bytes(out)


def decrypt(cipher: bytes) -> bytes:
xor_key = b"P4ssXOR"
rc4_key = b"Secr3tK3y!"

tmp = bytearray(len(cipher))
for i in range(len(cipher)):
tmp[i] = cipher[i] ^ xor_key[i % len(xor_key)]

plain = rc4_variant_crypt(tmp, rc4_key)
return plain

if __name__ == "__main__":
with open("D:\\MysteriousStream\\payload.dat", "rb") as f:
cipher = f.read()
plaintext = decrypt(cipher)
print(plaintext.decode(errors="ignore"))

# flag: ISCTF{Y0u_a2e_2ea11y_a_1aby2inth_master}

CRYPTO

easy_RSA


题目描述:我们的爱情像欧拉函数φ(n)——无限趋近却永远达不到n的完美互质,最终只剩周期性的怀念在模n的世界里循环证明


下载加密代码:

from Crypto.Util.number import *

p = getPrime(1024)

q = getPrime(1024)

N = p*q

e = 65537

msg = bytes_to_long(b"ISCTF{dummy_flag}")

ct1 = pow(msg, e, N)

ct2 = pow(msg, p+q, N)

print(f"{N = }")

print(f"{ct1 = }")

print(f"{ct2 = }")

"""
N = 17630258257080557797062320474423515967705950026415012912087655679315479168903980901728425140787005046038000068414269936806478828260848859753400786557270120330760791255046985114127285672634413513991988895166115794242018674042563788348381567565190146278040811257757119090296478610798393944581870309373529884950663990485525646200034220648901490835962964029936321155200390798215987316069871958913773199197073860062515329879288106446016695204426001393566351524023857332978260894409698596465474214898402707157933326431896629025197964209580991821222557663589475589423032130993456522178540455360695933336455068507071827928617
ct1 = 5961639119243884817956362325106436035547108981120248145301572089585639543543496627985540773185452108709958107818159430835510386993354596106366458898765597405461225798615020342640056386757104855709899089816838805631480329264128349465229327090721088394549641366346516133008681155817222994359616737681983784274513555455340301061302815102944083173679173923728968671113926376296481298323500774419099682647601977970777260084799036306508597807029122276595080580483336115458713338522372181732208078117809553781889555191883178157241590455408910096212697893247529197116309329028589569527960811338838624831855672463438531266455
ct2 = 11792054298654397865983651507912282632831471680334312509918945120797862876661899077559686851237832931501121869814783150387308320349940383857026679141830402807715397332316601439614741315278033853646418275632174160816784618982743834204997402866931295619202826633629690164429512723957241072421663170829944076753483616865208617479794763412611604625495201470161813033934476868949612651276104339747165276204945125001274777134529491152840672010010940034503257315555511274325831684793040209224816879778725612468542758777428888563266233284958660088175139114166433501743740034567850893745466521144371670962121062992082312948789
"""

是RSA中的共模攻击,m = c1 ^ s1 * c2 ^ s2 mod N

得到EXP:

from Crypto.Util.number import long_to_bytes, inverse

N = 17630258257080557797062320474423515967705950026415012912087655679315479168903980901728425140787005046038000068414269936806478828260848859753400786557270120330760791255046985114127285672634413513991988895166115794242018674042563788348381567565190146278040811257757119090296478610798393944581870309373529884950663990485525646200034220648901490835962964029936321155200390798215987316069871958913773199197073860062515329879288106446016695204426001393566351524023857332978260894409698596465474214898402707157933326431896629025197964209580991821222557663589475589423032130993456522178540455360695933336455068507071827928617
ct1 = 5961639119243884817956362325106436035547108981120248145301572089585639543543496627985540773185452108709958107818159430835510386993354596106366458898765597405461225798615020342640056386757104855709899089816838805631480329264128349465229327090721088394549641366346516133008681155817222994359616737681983784274513555455340301061302815102944083173679173923728968671113926376296481298323500774419099682647601977970777260084799036306508597807029122276595080580483336115458713338522372181732208078117809553781889555191883178157241590455408910096212697893247529197116309329028589569527960811338838624831855672463438531266455
ct2 = 11792054298654397865983651507912282632831471680334312509918945120797862876661899077559686851237832931501121869814783150387308320349940383857026679141830402807715397332316601439614741315278033853646418275632174160816784618982743834204997402866931295619202826633629690164429512723957241072421663170829944076753483616865208617479794763412611604625495201470161813033934476868949612651276104339747165276204945125001274777134529491152840672010010940034503257315555511274325831684793040209224816879778725612468542758777428888563266233284958660088175139114166433501743740034567850893745466521144371670962121062992082312948789

e1 = 65537
e2 = N + 1

def extended_gcd(a, b):
if a == 0:
return b, 0, 1
else:
g, y, x = extended_gcd(b % a, a)
return g, x - (b // a) * y, y

g, s1, s2 = extended_gcd(e1, e2)

print(f"[*] gcd(e1, e2) = {g}")
print(f"[*] s1 = {s1}")
print(f"[*] s2 = {s2}")

if s1 > 0:
part1 = pow(ct1, s1, N)
else:
part1 = pow(inverse(ct1, N), -s1, N)

if s2 > 0:
part2 = pow(ct2, s2, N)
else:
part2 = pow(inverse(ct2, N), -s2, N)

msg_int = (part1 * part2) % N
flag = long_to_bytes(msg_int)

print("-" * 30)
print(f"[*] Decrypted Flag: {flag}")
try:
print(f"[*] Decoded: {flag.decode()}")
except:
pass
print("-" * 30)

# flag: ISCTF{Congratulations_you_master_Mathematical_ability}

小蓝鲨的LFSR系统


题目描述:”小蓝鲨是海洋情报局的新晋密码专家,它设计了一个基于LFSR的流密码系统来加密机密信息。这个系统看起来简单高效,但小蓝鲨不知道的是,LFSR在某些情况下可能存在安全隐患。 一天,小蓝鲨的加密系统被神秘的黑客组织””深海幽灵””入侵,他们截获了一段加密信息。作为海洋安全部门的成员,你需要分析这个加密系统,找出潜在的弱点,并解密被截获的信息。”


打开附件:

import secrets
import binascii

def simple_lfsr_encrypt(plaintext, init_state):
mask = [random.randint(0,1) for _ in range(128)]

state = init_state.copy()
for _ in range(256):
feedback = sum(state[i] & mask[i] for i in range(128)) % 2
state.append(feedback)

key = bytes(int(''.join(str(bit) for bit in mask[i*8:(i+1)*8]), 2)
for i in range(16))

keystream = (key * (len(plaintext)//16 + 1))[:len(plaintext)]
return bytes(p ^ k for p, k in zip(plaintext, keystream)), mask

和对应的输出文件:

initState = [0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0]
outputState = [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1]
ciphertext = '4b3be165a0a0edd67ca8f143884826725107fd42d6a6'

构造线性方程组A * X = B,在GF(2)域上使用高斯消元法解出mask

EXP:

import binascii

initState = [0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0]

outputState = [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1]

ciphertext_hex = '4b3be165a0a0edd67ca8f143884826725107fd42d6a6'

full_stream = initState + outputState
N = 128

def gaussian_elimination_gf2(A, B):
rows = len(A)
cols = len(A[0])
aug_matrix = [row + [B[i]] for i, row in enumerate(A)]

pivot_row = 0
for col in range(cols):
if pivot_row >= rows: break
if aug_matrix[pivot_row][col] == 0:
for i in range(pivot_row + 1, rows):
if aug_matrix[i][col] == 1:
aug_matrix[pivot_row], aug_matrix[i] = aug_matrix[i], aug_matrix[pivot_row]
break
else: continue
for i in range(rows):
if i != pivot_row and aug_matrix[i][col] == 1:
aug_matrix[i] = [x ^ y for x, y in zip(aug_matrix[i], aug_matrix[pivot_row])]
pivot_row += 1

solution = [0] * cols
for i in range(rows):
try:
pivot_col = aug_matrix[i][:-1].index(1)
solution[pivot_col] = aug_matrix[i][-1]
except ValueError: pass
return solution

matrix = []
results = []
num_equations = len(outputState)
print(f"[*] Building {num_equations} equations from stream...")

for i in range(min(num_equations, N + 20)):
row = full_stream[i : i + N]
res = full_stream[i + N]
matrix.append(row)
results.append(res)

print("[*] Solving for Mask via Gaussian Elimination...")
mask = gaussian_elimination_gf2(matrix, results)

print("[*] Verifying Mask...", end=" ")
state_test = initState.copy()
generated_test = []
for _ in range(len(outputState)):
feedback = sum(state_test[k] & mask[k] for k in range(128)) % 2
state_test.append(feedback)

check_pass = True
for i in range(len(outputState)):
prediction = sum(full_stream[i+k] & mask[k] for k in range(128)) % 2
if prediction != outputState[i]:
check_pass = False
break

if check_pass:
print("SUCCESS! Mask perfectly regenerates the output.")
else:
print("WARNING: Mask verification failed. The LFSR logic might differ.")

def decrypt_with_mode(mask_bits, ct_hex, mode_name):
key_bytes = []
for i in range(16):
bits = mask_bits[i*8 : (i+1)*8]
if "Little" in mode_name:
bits = bits[::-1]

val = int(''.join(str(b) for b in bits), 2)
key_bytes.append(val)

key = bytes(key_bytes)
ct = binascii.unhexlify(ct_hex)
keystream = (key * (len(ct)//16 + 1))[:len(ct)]
pt = bytes(c ^ k for c, k in zip(ct, keystream))

try:
pt_text = pt.decode('utf-8')
if "ISCTF{" in pt_text or "uuid" in pt_text or "{" in pt_text:
print(f"\n[+] SUCCESS ({mode_name}): {pt_text}")
else:
if all(32 <= c <= 126 for c in pt):
print(f"\n[?] Possible Candidate ({mode_name}): {pt_text}")
except:
pass
return pt

print("-" * 30)
print("[*] Attempting Decryption...")

res1 = decrypt_with_mode(mask, ciphertext_hex, "Standard Big-Endian")
res2 = decrypt_with_mode(mask, ciphertext_hex, "Little-Endian Bits")

print("-" * 30)
print(f"Key (Hex): {bytes(int(''.join(str(b) for b in mask[i*8:(i+1)*8]), 2) for i in range(16)).hex()}")

# flag: ISCTF{lf5R_jUst_So_s0}

小蓝鲨的RSA密文


题目描述:”小蓝鲨是海洋数学天才,它最近正在深耕RSA领域。你嗤之以鼻,心想RSA不是最基础的密码学知识吗?于是你信誓旦旦的跑到小蓝鲨面前告诉它你已经完全掌握了RSA,并宣称所有的RSA题目你都能做出来。 小蓝鲨意味深长的看了你一眼,并出了一道RSA来考考你。现在,该你向它证明你的实力了。”


打开.py文件,

import json, secrets
from Crypto.Util.number import getPrime, bytes_to_long
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

e = 3
N = getPrime(512) * getPrime(512)

a2_high = a2 >> LOW_BITS

aes_key = secrets.token_bytes(16)
m = bytes_to_long(aes_key)

f = a2 * (m * m) + a1 * m + a0

c = (pow(m, e) + f) % N

iv = secrets.token_bytes(16)
cipher = AES.new(aes_key, AES.MODE_CBC, iv=iv)
ct = cipher.encrypt(pad(FLAG, 16))

"""
N = 121288600621198389662246479277632294800423697823363188896668775456771641807233781416525282234787873435904747571468452950479817935684848143651716343606633656969395065588423982440884464542428742861388200306417822228591316703916504170245990423925894477848679490979364923848426643149659758241239900845544537886777

c = 3756824985347508967549776773725045773059311839370527149219720084008312247164501688241698562854942756369420003479117

a2_high = 9012778

LOW_BITS = 16

a1 = 621315

a0 = 452775142

iv = bf38e64bb5c1b069a07b7d1d046a9010

ct = 8966006c4724faf53883b56a1a8a08ee17b1535e1657c16b3b129ee2d2e389744c943014eb774cd24a5d0f7ad140276fdec72eb985b6de67b8e4674b0bcdc4a5
"""

模数N(两512位素数乘积)过大,而m(AES-key)只有128位

我们可以采用爆破low_bits,遍历0 - 65535的所有可能值,构造完整的a2;再求解三次方程m^3 + a2*m^2 + a1*m + (a0 - c) = 0;用二分查找找到m;最后,用找到的密钥m解密ct即可得到flag。

EXP:

import sys
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

c = 3756824985347508967549776773725045773059311839370527149219720084008312247164501688241698562854942756369420003479117
a2_high = 9012778
LOW_BITS = 16
a1 = 621315
a0 = 452775142
iv_hex = "bf38e64bb5c1b069a07b7d1d046a9010"
ct_hex = "8966006c4724faf53883b56a1a8a08ee17b1535e1657c16b3b129ee2d2e389744c943014eb774cd24a5d0f7ad140276fdec72eb985b6de67b8e4674b0bcdc4a5"

iv = bytes.fromhex(iv_hex)
ct = bytes.fromhex(ct_hex)

def solve_cubic_for_m(target_a2):
low = 0
high = 1 << 130

while low <= high:
mid = (low + high) // 2
val = mid**3 + target_a2 * (mid**2) + a1 * mid + a0
if val == c:
return mid
elif val < c:
low = mid + 1
else:
high = mid - 1
return None

print("[*] 开始爆破 a2 的低 16 位...")

found_m = None

for low_part in range(2**LOW_BITS):

current_a2 = (a2_high << LOW_BITS) + low_part
res = solve_cubic_for_m(current_a2)

if res is not None:
found_m = res
print(f"[+] 找到唯一解 m: {found_m}")
print(f"[+] 对应的 a2 低位为: {low_part}")
break

if found_m:
try:
aes_key = long_to_bytes(found_m)

if len(aes_key) < 16:
aes_key = aes_key.rjust(16, b'\0')

cipher = AES.new(aes_key, AES.MODE_CBC, iv=iv)
decrypted = cipher.decrypt(ct)

flag = unpad(decrypted, 16)
print(f"\n[SUCCESS] Flag: {flag.decode()}")

except Exception as e:
print(f"[ERROR] 解密失败: {e}")
else:
print("[-] 未找到符合条件的 m")

# flag: ISCTF{i7_533M5_Lik3_You_R34lLy_UNd3R574nd_Polinomials_4nD_RSA}

baby_math


题目描述:死去的记忆突然被唤醒了


打开:

from Crypto.Util.number import bytes_to_long

print(len(flag))
R = RealField(1000)
a,b = bytes_to_long(flag[:len(flag)//2]),bytes_to_long(flag[len(flag)//2:])
x = R(0.75872961153339387563860550178464795474547887323678173252494265684893323654606628651427151866818730100357590296863274236719073684620030717141521941211167282170567424114270941542016135979438271439047194028943997508126389603529160316379547558098144713802870753946485296790294770557302303874143106908193100)

enc = a*cos(x)+b*sin(x)


#1.24839978408728580181183027675785982784764821592156892598136000363397267152291738689909414790691435938223032351375697399608345468567445269769342300325192248438038963977207296241971217955178443170598629648414706345216797043374408541203167719396818925953801387623884200901703606288664141375049626635852e52

给出a * cos(x) + b * sin(x) = enc,此为背包问题的变种

可以利用```LLL算法``,构造格,构造矩阵使得目标向量为格的一个短向量,利用格基约减求解。

EXP:

from Crypto.Util.number import long_to_bytes
from sage.all import *

x_val_str = "0.75872961153339387563860550178464795474547887323678173252494265684893323654606628651427151866818730100357590296863274236719073684620030717141521941211167282170567424114270941542016135979438271439047194028943997508126389603529160316379547558098144713802870753946485296790294770557302303874143106908193100"
enc_val_str = "1.24839978408728580181183027675785982784764821592156892598136000363397267152291738689909414790691435938223032351375697399608345468567445269769342300325192248438038963977207296241971217955178443170598629648414706345216797043374408541203167719396818925953801387623884200901703606288664141375049626635852e52"

R = RealField(1000)
x = R(x_val_str)
enc = R(enc_val_str)

K = 10**300

M = Matrix(ZZ, [
[1, 0, int(K * cos(x))],
[0, 1, int(K * sin(x))],
[0, 0, int(K * enc)]
])

M_reduced = M.LLL()
vec = M_reduced[0]

a = abs(vec[0])
b = abs(vec[1])

print(f"[+] Found a: {a}")
print(f"[+] Found b: {b}")

try:
part1 = long_to_bytes(a)
part2 = long_to_bytes(b)
flag = part1 + part2
print(f"\n[SUCCESS] Flag: {flag.decode()}")
except Exception as e:
print(f"[-] Decode failed: {e}")

# [SUCCESS] Flag: ISCTF{164a3221-7306-4024-88c3-4ef557b86895}

小蓝鲨的费马谜题


题目描述:小蓝鲨在一次网络探险中发现了一个神秘的加密系统。他发现这个系统好像使用了费马小定理来保护重要信息,但是又好像不太一样。小蓝鲨设法截获了系统的加密输出,但不知道如何解密,你可以帮帮它吗?


打开,还有25KB的output.txt

import random
import math

p = get_prime(1024)
q = get_prime(1024)
n = p * q
e = 65537

m = bytes_to_long(flag)
c = pow(m, e, n)

bases = get_primes_up_to(100)

hints = []
for i in range(len(bases)):
for j in range(i+1, len(bases)):
hint_value = (pow(bases[i], p-1, n) + pow(bases[j], p-1, n)) % n
hints.append((bases[i], bases[j], hint_value))

根据给定的hint:H(a,b) = a ^ (p - 1) + b ^ (p - 1) mod n

与费马小定理b ^ (p - 1) = c ^ (p - 1) = 1 (mod p)

得出p = gcd(H(a,b) - H(a , c), n)

所以,EXP:

import re
from math import gcd
from Crypto.Util.number import long_to_bytes, inverse

path = "D:\\task\\output.txt"
with open(path, "r", encoding="utf-8") as f:
txt = f.read()

m_n = re.search(r"n\s*=\s*([0-9]+)", txt)
m_e = re.search(r"e\s*=\s*([0-9]+)", txt)
m_c = re.search(r"c\s*=\s*([0-9]+)", txt)
if not (m_n and m_e and m_c):
print("未能从文件中读取 n/e/c,请确认输出格式与样例一致。")
raise SystemExit(1)

n = int(m_n.group(1))
e = int(m_e.group(1))
c = int(m_c.group(1))
print("Loaded n,e,c")

hint_tuples = re.findall(r"Hint\s*\d+:\s*(\d+),\s*(\d+),\s*([0-9]+)", txt)
hints = [(int(a), int(b), int(val)) for (a,b,val) in hint_tuples]
print("Loaded", len(hints), "hints")

from collections import defaultdict
groups1 = defaultdict(list)
groups2 = defaultdict(list)
for idx, (a,b,val) in enumerate(hints, start=1):
groups1[a].append((idx,a,b,val))
groups2[b].append((idx,a,b,val))

def try_pairs(pairs):
results = []
for i in range(len(pairs)):
for j in range(i+1, len(pairs)):
idx1,a1,b1,H1 = pairs[i]
idx2,a2,b2,H2 = pairs[j]
delta = (H1 - H2) % n
g = gcd(delta, n)
if 1 < g < n:
results.append((idx1, a1,b1, idx2, a2,b2, g))
return results

found = []
for a, items in groups1.items():
if len(items) >= 2:
res = try_pairs(items)
if res:
found.extend(res)

if found:
print("Found factor(s) by matching same base1:")
for it in found:
print(it)
else:
print("No factor found by matching same base1. Trying same base2...")
found2 = []
for b, items in groups2.items():
if len(items) >= 2:
res = try_pairs(items)
if res:
found2.extend(res)
if found2:
print("Found factor(s) by matching same base2:")
for it in found2:
print(it)
found = found2

if not found:
print("No factor found by same-base heuristics. Scanning all pairs (this may take some seconds)...")
all_pairs = []
for i in range(len(hints)):
for j in range(i+1, len(hints)):
a1,b1,H1 = hints[i]
a2,b2,H2 = hints[j]
delta = (H1 - H2) % n
g = gcd(delta, n)
if 1 < g < n:
all_pairs.append((i+1,a1,b1,j+1,a2,b2,g))
if all_pairs:
print("Found by scanning all pairs:")
for it in all_pairs:
print(it)
found = all_pairs
else:
print("每个 base1 的 hint 数量:")
for a, items in sorted(groups1.items()):
print("base1 =", a, "count =", len(items))
raise SystemExit(2)

g = found[0][-1]
p = int(g)
if n % p != 0:
print("警告: gcd 没能整除n:", p)
raise SystemExit(3)

q = n // p
print("\nRecovered p =", p)
print("Recovered q =", q)

phi = (p-1)*(q-1)
d = inverse(e, phi)
m = pow(c, d, n)
try:
flag = long_to_bytes(m)
print("\nflag (bytes):", flag)
try:
print("flag (utf-8):", flag.decode())
except:
pass
except Exception as exc:
print("转换为 bytes 失败,m 的值:", m)
raise

# flag: ISCTF{M0dIFi3D_f3RM47_7H30r3m_I5_fUn_8U7_h4rD3r!}
Comments