• [0CTF/TCTF 2022] real_magic_dlog


    没时间作,只作了一道题。不过有时间估计也是只作这一个题。

    题目看上去很简单:

    1. 选过一个proof就是爆破一个sha256,4个字节
    2. 生成17字节的随机数,后边的东西就都是自己输入的了
    3. 输入p(头部为随机数),e,data
    4. 由data生成sha384,唯一的要求:
      pow(int(data, 16), E, P) == int(sha384(data).hexdigest(), 16) % P

      由于sha384不能逆,所以也就别想这东西了。

    1. #!/usr/bin/env python3
    2. import random
    3. import signal
    4. import socketserver
    5. import string
    6. from Crypto.Util.number import *
    7. from hashlib import sha256, sha384
    8. from os import urandom
    9. from secret import flag
    10. LEN = 17
    11. class Task(socketserver.BaseRequestHandler):
    12. def __init__(self, *args, **kargs):
    13. super().__init__(*args, **kargs)
    14. def proof_of_work(self):
    15. random.seed(urandom(8))
    16. proof = ''.join([random.choice(string.ascii_letters + string.digits + '!#$%&*-?') for _ in range(20)])
    17. digest = sha256(proof.encode()).hexdigest()
    18. self.dosend('sha256(XXXX + {}) == {}'.format(proof[4: ], digest))
    19. self.dosend('Give me XXXX:')
    20. x = self.request.recv(10)
    21. x = (x.strip()).decode('utf-8')
    22. if len(x) != 4 or sha256((x + proof[4: ]).encode()).hexdigest() != digest:
    23. return False
    24. return True
    25. def dosend(self, msg):
    26. try:
    27. self.request.sendall(msg.encode('latin-1') + b'\n')
    28. except:
    29. pass
    30. def timeout_handler(self, signum, frame):
    31. raise TimeoutError
    32. def recv_fromhex(self, l):
    33. passwd = self.request.recv(l).strip()
    34. passwd = bytes.fromhex(passwd.decode('latin-1'))
    35. return passwd
    36. def handle(self):
    37. try:
    38. signal.signal(signal.SIGALRM, self.timeout_handler)
    39. signal.alarm(50)
    40. if not self.proof_of_work():
    41. self.dosend('You must pass the PoW!')
    42. return
    43. signal.alarm(60)
    44. magic = urandom(LEN)
    45. magic_num = bytes_to_long(magic)
    46. self.dosend(magic.hex())
    47. self.dosend('P:>')
    48. P = int(self.request.recv(100).strip(), 16)
    49. self.dosend('E:>')
    50. E = int(self.request.recv(100).strip(), 16)
    51. self.dosend('data:>')
    52. data = self.request.recv(100).strip()
    53. num1 = int(data, 16)
    54. if P >> (384 - LEN * 8) == magic_num and isPrime(P):
    55. data2 = sha384(data).hexdigest()
    56. num2 = int(data2, 16)
    57. if pow(num1, E, P) == num2 % P:
    58. self.dosend(flag)
    59. else:
    60. self.dosend('try harder!!!')
    61. except TimeoutError:
    62. self.dosend('Timeout!')
    63. self.request.close()
    64. except:
    65. self.dosend('Wtf?')
    66. self.request.close()
    67. class ThreadedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    68. pass
    69. if __name__ == "__main__":
    70. HOST, PORT = '0.0.0.0', 15555
    71. server = ThreadedServer((HOST, PORT), Task)
    72. server.allow_reuse_address = True
    73. server.serve_forever()

    最可恶的就是有时间限制:60秒。

    由于sha384不能逆,所以data也就基本是定值了,可以操作的就是P和E,实际上就是求离散对数。由于有时间限制,就要想办法弄个数可以很容易的求出离散对数。

    离散对数的安全性要求P-1的最大素因子足够大,所以解这个题就要爆破一个最大素因子很小的P-1。又由于头部固定,我的P爆破从邻接头部后边爆破,这样P-1后尾部全是2可以快速缩小。

    1. #sagemath
    2. def aaa(p0):
    3. c = int(sha384(b'11').hexdigest(), 16)
    4. p0 = p0<<(384-17*8)
    5. for i in range(0x10000):
    6. tp = p0 + (i<<(384-17*8 - 16)) + 1
    7. if is_prime(tp):
    8. l = int(factor(tp-1)[-1][0]).bit_length()
    9. if l<48:
    10. print('p = ', hex(tp)[2:])
    11. print('e = ', hex(int(discrete_log(mod(c,tp),mod(17,tp))))
    12. return
    13. aaa(0x46b3d5cb67ecabdd14ed8997f372d4e911)

    然后组装就有个问题了,我机子上sage和pwntools组合不上,只能先把取得的东西放sage运算再把结果复制回去。经过几次手抖的过程后终于解决。

    1. from pwn import *
    2. from Crypto.Util.number import *
    3. from hashlib import sha256, sha384
    4. import string
    5. def proof(tail, s):
    6. tab = string.ascii_letters + string.digits + '!#$%&*-?'
    7. for i in tab:
    8. for j in tab:
    9. for k in tab:
    10. for l in tab:
    11. msg = i+j+k+l+tail
    12. r = sha256(msg.encode()).hexdigest()
    13. if r == s:
    14. print(" "+i+j+k+l)
    15. return i+j+k+l
    16. p = remote('202.120.7.219', 15555)
    17. context.log_level = 'debug'
    18. '''
    19. sha256(XXXX + Q6quUUnOKw693Z*k) == 8c0452ca7ca220652104d4bd0dd72f6fc25f0bee622b9b3f53c97741b52bfcd6
    20. Give me XXXX:
    21. '''
    22. p.recvuntil(b'sha256(XXXX + ')
    23. tail = p.recvuntil(b') == ', drop=True).decode()
    24. s = p.recvuntil(b'\n', drop=True).decode()
    25. m = proof(tail, s)
    26. p.sendlineafter(b'Give me XXXX:\n', m.encode())
    27. #b'1adb38ceb59d18365bc9aa199955af2da1\n'
    28. data = p.recvline()[:-1]
    29. print(data)
    30. p0 = int(data, 16)
    31. print('p0 = ', hex(p0))
    32. sp = input("P:")
    33. p.sendlineafter(b'P:>\n', sp)
    34. se = input("E:")
    35. p.sendlineafter(b'E:>\n', se)
    36. p.sendlineafter(b'data:>\n', b'11') #0x11 = 17
    37. p.interactive()

    另外,由于要运算离散对数,g不能大,一般如果没特殊情况可以取2,但是2,3经常会有无法求解的情况(含因子)所以这里用的17,其实3有时候也行。最大因子当然越小越好,但太小不容易遇到测试两次后发现一个44位的,就随便写了48,因为这块也需要节省时间。

    最后看看交互的过程:

    1. $ py a.py
    2. [+] Opening connection to 202.120.7.219 on port 15555: Done
    3. [DEBUG] Received 0x64 bytes:
    4. b'sha256(XXXX + cRZm&a!8X3i$28YJ) == b23f429bace252093acf4b3ab0d059fa8a52102e30cb495e7b912560903209a9\n'
    5. [DEBUG] Received 0xe bytes:
    6. b'Give me XXXX:\n'
    7. [DEBUG] Sent 0x5 bytes:
    8. b'C6u7\n'
    9. [DEBUG] Received 0x23 bytes:
    10. b'779d13f0f1ffda1735928284fd3f240801\n'
    11. [DEBUG] Received 0x4 bytes:
    12. b'P:>\n'
    13. [DEBUG] Sent 0x62 bytes:
    14. b'779d13f0f1ffda1735928284fd3f24080111790000000000000000000000000000000000000000000000000000000001\n'
    15. b'\n'
    16. [DEBUG] Received 0x4 bytes:
    17. b'E:>\n'
    18. [DEBUG] Sent 0x61 bytes:
    19. b'3fb7c5754190aa6425ba81af4b68a09154d708572f7491c56999fdae75a7787e83a90409311c2f7502c0779ebfb5e98\n'
    20. b'\n'
    21. [DEBUG] Received 0x7 bytes:
    22. b'data:>\n'
    23. [DEBUG] Sent 0x3 bytes:
    24. b'11\n'
    25. [*] Switching to interactive mode
    26. [DEBUG] Received 0x31 bytes:
    27. b'flag{Hope_you_can_solve_by_smoothness_this_time}\n'
    28. flag{Hope_you_can_solve_by_smoothness_this_time}

  • 相关阅读:
    【广州华锐互动】AR远程连接专家进行协同管理,解放双手让协同更便捷
    力扣:674. 最长连续递增序列
    “我转行做测试开发的这一年多,月薪5K变成了24K”
    HttpServletRequest接口详解
    一篇彻底带你理解微服务
    《大数据面试通关》(第十四讲)——10 大业务场景 500 个离线实时指标
    Android Studio插件开发 - Dora SDK的IDE插件
    Linux中SSHD_CONFIG文件详细注解,便于配置
    毛玻璃员工卡片悬停效果
    mybatis foeahe 批量插入 删除 修改
  • 原文地址:https://blog.csdn.net/weixin_52640415/article/details/126942198