• [WACON CTF 2023] WhiteArts


    本来想把几个密码题都弄明白了,可实在后边太难了.先根据WP复现1个.

    这个给了4个文件,看代码还插复杂,一点点理一下。

    1. import math
    2. import os
    3. from Generator import Generator1, Generator2, Generator3, Generator4, Generator5
    4. query_left = 266
    5. def guess_mode(G, query_num):
    6. for _ in range(query_num):
    7. q = bytes.fromhex(input("q? > "))
    8. inverse = input("inverse(y/n)? > ") == 'y'
    9. assert len(q) == G.input_size
    10. print(G.calc(q, inverse).hex())
    11. # Time to guess
    12. assert input("mode? > ") == str(G.mode) # 0(gen) / 1(random)
    13. def challenge_generator(challenge_name, Generator):
    14. global query_left
    15. print(f"#### Challenge = {challenge_name}")
    16. query_num = int(input(f"How many queries are required to solve {challenge_name}? > "))
    17. query_left -= query_num
    18. for _ in range(40):
    19. G = Generator()
    20. guess_mode(G, query_num)
    21. challenge_generator("Generator1", Generator1) #输入 '1'*16 + '0'*16 返回 startswith('0'*16) mode=0
    22. challenge_generator("Generator2", Generator2) #2次,输入 b'\x00'*7+b'\x01'*9, b'\x00'*16 前7字符相同
    23. if query_left < 0:
    24. print("You passed all challenges for EASY but query limit exceeded. Try harder :(")
    25. exit(-1)
    26. print("(Only for a junior division) Good job! flag_baby =", open("flag_baby.txt").read())
    27. challenge_generator("Generator3", Generator3)
    28. if query_left < 0:
    29. print("You passed all challenges for EASY but query limit exceeded. Try harder :(")
    30. exit(-1)
    31. print("Good job! flag_easy =", open("flag_easy.txt").read())
    32. challenge_generator("Generator4", Generator4)
    33. challenge_generator("Generator5", Generator5)
    34. if query_left < 0:
    35. print("You passed all challenges for HARD but query limit exceeded. Try harder :(")
    36. exit(-1)
    37. print("(Only for general/global divisions) Good job! flag_hard =", open("flag_hard.txt").read())

    这是主程序部分一共分5步,每一块都是先生成一对随机数对象,根据输入输出函数值。然后要求判断是用的哪个随机数函数,前两关通过得到flag_baby,第3关得到flag_easy,4、5关得到flag_hard。

    随机函数基本上分两类,一个是函数一个是随机。但都有缓存让相同的值会得到相同的结果。

    第1段:

    1. class Generator1:
    2. def __init__(self):
    3. self.mode = os.urandom(1)[0] & 1
    4. self.n = 8
    5. self.input_size = 2 * self.n
    6. self.RF_gen = RandomFunction(self.n)
    7. self.RF_random = RandomFunction(2 * self.n)
    8. def func_gen(self, q):
    9. L, R = q[:self.n], q[self.n:]
    10. L, R = R, xor(L, self.RF_gen.query(R))
    11. return L+R
    12. def func_random(self, q):
    13. return self.RF_random.query(q)
    14. def calc(self, q, inverse):
    15. assert inverse == False, "inverse query is not allowed for Generator1"
    16. ret_gen = self.func_gen(q)
    17. ret_random = self.func_random(q)
    18. print(self.mode, ret_gen, ret_random)
    19. if self.mode == 0:
    20. return ret_gen
    21. else:
    22. return ret_random

    模式0的加密方法

    LR
    加密后RL^T(R)

    显然R部的明文直接暴露,只要判断右侧即可。

    1. from pwn import *
    2. p = remote('175.118.127.63', 2821)
    3. def p_query(msg, reverse=b'n'):
    4. p.sendlineafter(b"? > ", msg)
    5. p.sendlineafter(b"inverse(y/n)? > ", reverse)
    6. return p.recvline().strip()
    7. #1
    8. p.sendlineafter(b"? > ", b'1')
    9. for _ in range(40):
    10. res = p_query(b'0'*32, b'n')
    11. if b'0'*16 == res[:16]:
    12. p.sendlineafter(b"mode? > ", b'0')
    13. else:
    14. p.sendlineafter(b"mode? > ", b'1')

    第2段:

    1. class Generator2:
    2. def __init__(self):
    3. self.mode = 0 #os.urandom(1)[0] & 1
    4. self.n = 8
    5. self.input_size = 2 * self.n
    6. self.RF_gen = RandomFunction(self.n)
    7. self.RF_random = RandomFunction(2 * self.n)
    8. def func_gen(self, q):
    9. L, R = q[:self.n], q[self.n:]
    10. L, R = R, xor(L, self.RF_gen.query(R))
    11. L, R = R, xor(L, self.RF_gen.query(R))
    12. return L+R
    13. def func_random(self, q):
    14. return self.RF_random.query(q)
    15. def calc(self, q, inverse):
    16. assert inverse == False, "inverse query is not allowed for Generator2"
    17. ret_gen = self.func_gen(q)
    18. ret_random = self.func_random(q)
    19. print(self.mode, ret_gen, ret_random)
    20. if self.mode == 0:
    21. return ret_gen
    22. else:
    23. return ret_random

    第2段,加密作了两次

    LRL:0R:0L:1R:0
    1RL^T(R)0T(0)01^T(0)
    2L^T(R)R^T(L^T(R))T(0)T(T(0))1^T(0)T(1^T(0))

    当两次使用右半都是相同左侧不同时显然两次的结果左半只相差左侧的值(L^T(R))

    1. #2
    2. p.sendlineafter(b"? > ", b'2')
    3. for _ in range(40):
    4. res1 = p_query(b'0'*32, b'n')
    5. res2 = p_query(b'1'+b'0'*31, b'n')
    6. if res1[2:16] == res2[2:16]:
    7. p.sendlineafter(b"mode? > ", b'0')
    8. else:
    9. p.sendlineafter(b"mode? > ", b'1')
    10. print(p.recvline())
    11. #(Only for a junior division) Good job! flag_baby = WACON2023{8c3deab3b7a89f9bc7941a201}

    第3段:

    1. class Generator3:
    2. def __init__(self):
    3. self.mode = 0 #os.urandom(1)[0] & 1
    4. self.n = 8
    5. self.input_size = 2 * self.n
    6. self.RF_gen = RandomFunction(self.n)
    7. self.RF_random = RandomPermutation(2 * self.n)
    8. def func_gen(self, q, inverse):
    9. if not inverse:
    10. L, R = q[:self.n], q[self.n:]
    11. L, R = R, xor(L, self.RF_gen.query(R))
    12. L, R = R, xor(L, self.RF_gen.query(R))
    13. L, R = R, xor(L, self.RF_gen.query(R))
    14. else:
    15. L, R = q[:self.n], q[self.n:]
    16. L, R = xor(R, self.RF_gen.query(L)), L
    17. L, R = xor(R, self.RF_gen.query(L)), L
    18. L, R = xor(R, self.RF_gen.query(L)), L
    19. return L+R
    20. def func_random(self, q, inverse):
    21. return self.RF_random.query(q, inverse)
    22. def calc(self, q, inverse):
    23. ret_gen = self.func_gen(q, inverse)
    24. ret_random = self.func_random(q, inverse)
    25. if self.mode == 0:
    26. return ret_gen
    27. else:
    28. return ret_random

    第3段可以选择逆向,可以作一个正向一个逆向,我想的方法比较复杂了需要4次,然而这里有个坑。一共只能处理266次,3、4都作4次的话前边一共11次后边只剩下255次,第5段作不了。

    第1,2次L:0R:1L:1R:1
    1T(1)11^T(1)
    T(1)1^T(T(1))1^T(1)1^T(1^T(1))
    1^1^T(T(1))T(1)^T(1^T(T(1)))1^T(1^T(1))1^T(1)^T(1^T(1^T(1)))
    第3,4次1^1^T(T(1))1^T(1)^T(1^T(T(1)))1^T(1^T(1))0^T(1)^T(1^T(1^T(1)))
    1^T(1)1^T(T(1))T(1)1^T(1^T(1))
    1^T(T(1))^T(1^T(1))1^T(1)T(T(1))^1^T(1^T(1))T(1)
    1^T(1)^T(1^T(T(1))^T(1^T(1)))1^T(T(1))^T(1^T(1))T(1)^T(1^T(T(1))^T(1^T(1)))1^T(T(1))^T(1^T(1))

    这样第3、4次得到的右部相同。后来看WP可以只用两次,显然他只是左右两部分加密后相反。

    第1次正向第2次反向
    0000
    0T(0)T(0)0
    T(0)T(T(0))T(T(0))T(0)
    T(T(0))T(0)^T(T(T(0)))T(0)^T(T(T(0)))T(T(0))
    1. p.sendlineafter(b"? > ", b'2')
    2. for _ in range(40):
    3. res1 = p_query(b'0'*32, b'n')
    4. res2 = p_query(b'0'*32, b'y')
    5. if res1[16:32] == res2[:16]:
    6. p.sendlineafter(b"mode? > ", b'0')
    7. else:
    8. p.sendlineafter(b"mode? > ", b'1')
    9. print(p.recvline())
    10. #Good job! flag_easy = WACon2023{930db8b4dedb8cb86f309521011a1039}

    第4段:

    1. class Generator4:
    2. def __init__(self):
    3. self.mode = os.urandom(1)[0] & 1
    4. self.n = 8
    5. self.input_size = 2 * self.n
    6. self.RF_gen = RandomPermutation(self.n)
    7. self.RF_random = RandomPermutation(2 * self.n)
    8. def func_gen(self, q, inverse):
    9. X, T = q[:self.n], q[self.n:]
    10. X = xor(X, T)
    11. X = self.RF_gen.query(X, inverse)
    12. X = xor(X, T)
    13. X = self.RF_gen.query(X, inverse)
    14. X = xor(X, T)
    15. return X
    16. def func_random(self, q, inverse):
    17. return self.RF_random.query(q, inverse)[:self.n]
    18. def calc(self, q, inverse):
    19. ret_gen = self.func_gen(q, inverse)
    20. ret_random = self.func_random(q, inverse)
    21. if self.mode == 0:
    22. return ret_gen
    23. else:
    24. return ret_random

    第4段只对左半加密,且间隔与右半部分异或。这个也想复杂了。

    第1,2次0,11,0
    T(T(1)^1)^1T(T(1))
    第3,4次T(T(1)^1),0T(T(1))^1,1
    T'(T(1)^1)T'((T(1)^1)^1

    通过两正两逆,得到的结果只差异或1

    WP的方法只用两次,第2次发送第1次的结果,右为0可以解加原值0

    发送0,0T(T(0)),0
    结果T(T(0))0
    1. p.sendlineafter(b"? > ", b'2')
    2. for _ in range(40):
    3. res1 = p_query(b'0'*32, b'n')
    4. res1 = p_query(res1+b'0'*16, b'y')
    5. if res1 == b'0'*16:
    6. p.sendlineafter(b"mode? > ", b'0')
    7. else:
    8. p.sendlineafter(b"mode? > ", b'1')

    第5段:

    1. class Generator5:
    2. def __init__(self):
    3. self.mode = os.urandom(1)[0] & 1
    4. self.n = 1
    5. self.input_size = self.n
    6. self.RF_gen = [RandomPermutation(self.n) for _ in range(4)]
    7. self.RF_random = RandomFunction(self.n)
    8. def func_gen(self, q):
    9. ret = bytes(1)
    10. for i in range(4):
    11. ret = xor(ret, self.RF_gen[i].query(q))
    12. return ret
    13. def func_random(self, q):
    14. return self.RF_random.query(q)
    15. def calc(self, q, inverse):
    16. assert inverse == False, "inverse query is not allowed for Generator2"
    17. ret_gen = self.func_gen(q)
    18. ret_random = self.func_random(q)
    19. if self.mode == 0:
    20. return ret_gen
    21. else:
    22. return ret_random

    第5段说难也不难,只用1位,可以处理256次而返回结果用的随机数也有个限制就是结果也不相同,如果256次的话返回值就是1-255,那么这些值异或结果就是0。只是坑在前边,必需给这里保留256次。也就是3,4两步不能都用4次,少一次就行。

    1. #5
    2. p.sendlineafter(b"? > ", b'256')
    3. for _ in range(40):
    4. v =0
    5. for i in range(256):
    6. v ^= bytes.fromhex(p_query(bytes([i]).hex().encode(),b'n').decode())[0]
    7. if v == 0:
    8. p.sendlineafter(b"mode? > ", b'0')
    9. else:
    10. p.sendlineafter(b"mode? > ", b'1')
    11. print(p.recvline())
    12. #(Only for general/global divisions) Good job! flag_hard = WACon2023{c7a47ff1646698d275602dce1355645684f743f1}
    13. p.interactive()

  • 相关阅读:
    知识图谱问答技术实践与探索
    系统集成项目管理工程师认证高频考点:编制项目范围管理计划
    webpack5 之 css与js相关
    【Android】ADB无线连接Android设备
    RocketMQ之消费者启动与消费流程
    认识Java中的类和对象(上)
    Mac环境下 gmssl编译iOS静态库
    Scratch游戏------打砖块(不用VIP)
    电脑硬盘里的文件能保存多久?电脑硬盘文件突然没了怎么办
    JAVAEE框架技术之14SSM综合案例
  • 原文地址:https://blog.csdn.net/weixin_52640415/article/details/133161392