【CTF WriteUp】2020中央企业”新基建“网络安全技术大赛决赛部分Crypto题解

Crypto

FI_Crypto_onepiece

题目

public.pem

-----BEGIN PUBLIC KEY-----
MDowDQYJKoZIhvcNAQEBBQADKQAwJgIhAMJjauXD2OQ/+5erCQKPGqxsC/bNPXDr
yigb/+l/vjDdAgEC
-----END PUBLIC KEY-----

onepiece.enc
在这里插入图片描述

解答

首先提取公钥信息

openssl rsa -pubin -text -modulus -in pubkey.pem

在这里插入图片描述
根据参数知e=2,本题为类Rabin算法,需要分解n。使用yafu分解n
在这里插入图片描述使用rabin算法解码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from libnum import n2s,s2n

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

n = 0xC2636AE5C3D8E43FFB97AB09028F1AAC6C0BF6CD3D70EBCA281BFFE97FBE30DD
e = 2
c = s2n(open('onepiece.enc','rb').read())
p = 319576316814478949870590164193048041239
q = 275127860351348928173285174381581152299
# Rabin算法,首先计算mp和mq
mp = pow(c,(p+1)/4,p)
mq = pow(c,(q+1)/4,q)
# 然后找到yp和yq,使得 yp*p + yq*q = 1
yp, yq = egcd(p,q)
# 根据以下公式计算出四个解,看哪个是正确的
r0 = ( yp*p*mq + yq*q*mp ) % n
r1 = n - r0
s0 = ( yp*p*mq - yq*q*mp ) % n
s1 = n - s0
print n2s(r0),n2s(r1),n2s(s0),n2s(s1)

在这里插入图片描述得到的结果再进行一次ROT13,即为flag

Fi_Crypto_modk

题目

class Unbuffered(object):
   def __init__(self, stream):
       self.stream = stream
   def write(self, data):
       self.stream.write(data)
       self.stream.flush()
   def __getattr__(self, attr):
       return getattr(self.stream, attr)
import sys
sys.stdout = Unbuffered(sys.stdout)
import signal
signal.alarm(600)
import os
from Crypto.Cipher import AES
from Crypto.Util.number import *
os.chdir("/home/ctf")
flag=open("flag","rb").read()
import random
import hashlib
p=getPrime(512)
q=getPrime(512)
n=p*q
m=bytes_to_long(flag)
e=0x10001
d=inverse(e,(p-1)*(q-1))
k=random.randint(1,128)

def run():
    print "enc system"
    while 1:
        choice=raw_input("choice:")
        if choice=="1":
            print pow(m,e,n)
        elif choice=="2":
            print n
        elif choice=="3":
            tmp=int(raw_input())
            print pow(tmp,d,n)%k

        else:
            print "wrong choice"



if __name__ == '__main__':
    run()

解答

本题代码中给出了三种操作:操作1返回c;操作2返回n;操作3则输入一个值c0,系统解密后得到m0,然后返回m0%k,其中k为1~128中的随机数。

首先尝试去掉不确定项k。注意到e是已知的,如果我们依次以pow(1, e, n)、pow(2, e, n)、…、pow(128, e, n)作为操作3的输入,则返回内容应当依次为1%k、2%k、…、128%k 。由于k取值不超过128,因此这一序列中首个为0的项对应的就是k。

在求出了k以后,我们发现题目依然比较复杂,所以尝试研究简单取值下的情况。当k=2时,输入c,返回结果为pow(c, d, n)%2。我们知道pow(m, e, n) = c,于是有

pow(2m, e, n) = (2m)**e (mod n) = 2**e * m**e (mod n) = 2**e * c (mod n)

因此在已知c、e、n的情况下,可以利用2 ** e * c作为密文输入,这样解出来的明文是2m % n,返回结果为其奇偶性。注意到n是奇数,且2m < 2n,所以若结果是偶数,说明2m大小并未超过n,即m位于(0, n/2)之间;否则,2m超过了n会自动减n变成奇数,此时m位于(n/2, n)之间。利用得到的2 ** e * c作为新的密文继续进行上述操作,可逐步限定m的取值范围,最终直接确定明文m。(此方法即为RSA LSB Oracle Attack)

那么当k较大时情况如何呢?我们注意到,只要k是偶数,就可以用返回值模2的结果来代替返回值,采用上述k=2的方法进行解题。由于每次访问k随机生成,因此多试几次即可解答本题。

完整解题脚本如下:

#!/usr/bin/env python
#-*- coding: utf-8 -*-
from pwn import *
from Crypto.Util.number import long_to_bytes

# context.log_level="debug"
p = process("./modk")

def sendmessage(text):
    p.sendline("3")
    p.sendline(text)
    return int(p.recvline().strip()[7:])

e = 0x10001
p.recvline()

# get c
p.sendline("1")
c = int(p.recvline().strip()[7:])
print "c = %s" % str(c)

# get n
p.sendline("2")
n = int(p.recvline().strip()[7:])
print "n = %s" % str(n)

# get k
k = 0
for i in range(1, 129):
    res = sendmessage(str(pow(i, e, n)))
    if(res == 0):
        k = i
        break

# we need k is even
if(k % 2 == 1):
    print "k = %s" % str(k)
    exit(0)
else:
    print "k = %s" % str(k)

# RSA LSB Oracle Attack
import decimal
decimal.getcontext().prec = 1024
lower = decimal.Decimal(0)
upper = decimal.Decimal(n)
c_of_2 = pow(2, e, n)

for i in range(1024):
    c = (c * c_of_2)%n
    res = sendmessage(str(c))
    possible = (lower+upper)/2
    if(res % 2 == 1):
        lower = possible
    else:
        upper = possible

print long_to_bytes(int(upper))

在这里插入图片描述

FI_Crypto_eco

题目

class Unbuffered(object):
   def __init__(self, stream):
       self.stream = stream
   def write(self, data):
       self.stream.write(data)
       self.stream.flush()
   def __getattr__(self, attr):
       return getattr(self.stream, attr)
import sys
sys.stdout = Unbuffered(sys.stdout)
import os
from Crypto.Cipher import AES
from Crypto.Util.number import *
import random
os.chdir("/home/ctf")
flag=open("flag","rb").read()
random.seed(os.urandom(32))

def encode(n):
    a = 0
    for i in bin(n)[2:]:
        a = a << 1
        if (int(i)):
            a = a ^ n
        if a >> 256:
            a = a ^ 0x10000000000000000000000000000000000000000000000000000000000000223L
    return a

def run():
    print "rco"
    while 1:
        pt=raw_input("pt:").decode("hex")
        m=bytes_to_long(pt)
        k=random.getrandbits(32)
        if m==k:
            print flag
        else:
            print encode(k)

if __name__ == '__main__':
    run()

解答

(本题为标准数学题解题方法中的 观察——猜想——证明 步骤,不过对于解题而言,猜想得出结论就足够了)
注意到,题目中的encode函数对于固定的输入,其输出也是固定的。我们尝试打印一些输出:

0 0
1 1
2 4
3 5
4 16
5 17
6 20
7 21
8 64
9 65
10 68
11 69
12 80
13 81
14 84
15 85
......

观察发现encode函数满足如下规律:

1.  encode(2x+1) = encode(2x) + 1
2.  encode(2x) = 4 * encode(x)

分析代码,发现encode的输入为32位时,输出最多不超过64位,因此if(a>>256)根本不可能触发,至此已经可以写出解题代码了。首先写出decode将每轮随机数还原,然后凑齐梅森旋转的连续624个状态后,推断下一个状态,输入获取flag。完整解题代码如下:

#!/usr/bin/env python
#-*- coding: utf-8 -*-
from pwn import *
from randcrack import RandCrack

context.log_level="debug"
p = process("./eco")

def padding16(text):
    padding_length = 16 - len(text) % 16
    return text + chr(padding_length) * padding_length

def decode(num):
    res = ""
    while(num>0):
        if(num % 2 == 1):
            res = "1" + res
            num -= 1
        else:
            res = "0" + res
            num /= 4
    sum = 0
    for i in res:
        if int(i):
            sum += 1
        else:
            sum *= 2
    return sum

rc = RandCrack()

p.recvline()
for i in range(624):
    p.sendline("01")
    s = decode(int(p.recvline().strip()[3:]))
    rc.submit(s)

key = rc.predict_randrange(0, 2**32-1)
p.sendline(hex(key)[2:])
p.interactive()

在这里插入图片描述作为CTF题目,到此已经结束了。但是在数学上,我们还需要尝试理解刚才我们发现的结论。首先来看加密过程,对于一个数的二进制表示,我们以101(二进制1100101)为例,其加密步骤是这样的:
在这里插入图片描述101是奇数,属于2k+1,我们将其改为2k,查看发生的变化:
在这里插入图片描述注意到,当末尾的1变成0时,每一行参与运算的数最后一位都发生了变化。由于当且仅当第k位为1时该处才有一行,所以第k位为1时,该变化影响的是第k+l-1位,其中l是二进制串长度。将影响位全部提出汇总,可以发现恰好是从第l位开始的该数字的二进制表示。

另一方面,末尾的1变成0时,最后一行不再参与运算,即原有的从第l位开始的该数字的二进制表示不再参与运算。由于运算是异或,此时二者除最后一位外恰好完全抵消,仅最后一位由1变0,此即encode(2x+1) = encode(2x) + 1

再来看2倍关系的变化

在这里插入图片描述这似乎更好理解一些:0在异或运算中不影响结果,所以末尾加0(乘以2)只是在结果后边补充了两个0(乘以4),即encode(2x) = 4 * encode(x)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值