0%

2020 GXZYCTF lancet

思路

  • 访问服务器可得e、n,每次访问的值是随机的。服务器上可选加密或者解密两种方式。正常解法应该是构造(c*2)^e % n,返回值除以2便是m。
  • 但是由于服务器返回的解密结果不全,经验证仅为结果的最后一位。
  • 通过其返回结果可判断,由于n为奇数,flag最后字符为’}’因此m也为奇数。m乘以任意一个偶数模n肯定为奇数,除非m>n
  • 因此构造(c(2^i))^e % n 爆破出i,即恰好当(c(2^i))^e % n的返回值为偶数时可得n 约等于m*i
  • 通过上述方法爆破i可求出flag
  • 由于比赛时该题没做出来,后来复现时服务器关闭了,现放VN脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    from pwn import *
    from base64 import b64encode as enc
    from Crypto.Util.number import *
    import decimal

    def oracle(c):
    fuck = enc(long_to_bytes(c))
    l = len(fuck)
    r.recvuntil('want here')
    r.sendline('2')
    r.recvuntil('send how long you want to decrypt')
    r.send(str(l))
    r.recvuntil('send the message in base64 encode')
    r.send(fuck)
    res = r.recvline()
    res = r.recvline()
    log.info(res)
    if 'res:1' in res:
    return 1
    elif 'res:0' in res:
    return 0

    def partial(n,e,c):
    global c_of_2
    k = n.bit_length()
    decimal.getcontext().prec = k
    lower = decimal.Decimal(0)
    upper = decimal.Decimal(n)
    c_of_2 = pow(2, e, n)
    c = (c * c_of_2) % n
    for i in range(k):
    possible_plaintext = (lower + upper) / 2
    flag = oracle(c)
    if not flag:
    upper = possible_plaintext
    else:
    lower = possible_plaintext
    c = (c * c_of_2) % n
    print i, flag, int(upper - lower)
    return int(upper)


    r = remote('121.37.174.33',9999)
    r.recvuntil('n:')
    n = r.recvuntil('\ne:',drop=True)
    n = int(n)
    r.recvuntil('flag:')
    c = r.recvuntil('\nyou',drop=True)
    c = int(c)
    log.success(hex(n))
    log.success(hex(c))
    e = 65537


    print(partial(n,e,c))