2021宁波市第四届网络安全大赛(练习平台)RSA部分

挺久没有做过CTF了,明天就要打比赛了,临时抱佛脚,撸两道rsa,找找手感。

公钥文件泄露

题目给了两个文件:pub.key和flag.enc:

​ pub.key

-----BEGIN PUBLIC KEY-----
MDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAMAzLFxkrkcYL2wch21CM2kQVFpY9+7+
/AvKr1rzQczdAgMBAAE=
-----END PUBLIC KEY-----

​ flag.enc(乱码)

ZF:9Mw 骓骼)P恂黮?沕悴[嘟??

解题思路挺多的,这个没啥好说的(虽然因为很久没有做过,卡了很久)

  1. 只想简单解题的话,直接找个在线解密的解密的网站(我就不提供了,自己找吧),然后把这两个文件放上去解密就行了。

  2. 稍微复杂点的化,就是用openssl可以查看到公钥(n,e),大数分解n,得到p,q,然后写个python脚本

    openssl rsa -pubin -in pub.key -text 
    

    2021宁波市第四届网络安全大赛(练习平台)RSA部分_第1张图片

    写个脚本,将上面的modulus里的的字符做个分割,组成16进制数。

    from rsa import PublicKey, transform, core, common
    import rsa
    import gmpy2
    
    """第一种方法"""
    
    temp = []
    temp = hex("00:aa:08".split(':'))
    n = ''
    for i in temp:
        n += i
    n = eval(n)
    print(n)
    # 将n进行大数分解,得到p,q
    p = 285960468890451637935629440372639283459
    q = 304008741604601924494328155975272418463
    e = 65537
    phi = (p-1)*(q-1)	# 欧拉函数
    d = gmpy2.invert(e,phi) #计算模逆元d
    key = rsa.PrivateKey(n,e,int(d),p,q)	# 有了n,e,d,p,q之后 使用rsa模块生成私钥
    with open('./flag.enc','rb+') as f:
        f = f.read()
        print(rsa.decrypt(f,key))	# 解密文件
    
    """ 第二中方法:直接使用rsa模块去查看n,e """
    whit open("./pub.key","rb+") as k, open('./flag.enc','rb+') as f:
        k = k.read()
        public_key = PublicKey.load_pkcs1_openssl_pem(k)	#通过rsa模块的内容去读取公钥的信息
        print(public_key)	# 这里会输出一个 n 和 e 的元组
        # 将n进行大数分解,得到p,q
        p = 285960468890451637935629440372639283459
        q = 304008741604601924494328155975272418463
        e = 65537
    	key = rsa.PrivateKey(n, e, int(d), p, q)
    
        f = f.read()
        print(rsa.decrypt(f,key))
    """
    # 在使用第二种方法的是后,其实是有个坑的。因为我自己很久没有做rsa了。
    # 很多东西都要重新开始学,这个脚本最初也是照着网上的脚本写的(网上的脚本贴贴在下面)
    # 他用的是core.decrypt_int()方法去解密的。这没有什么问题,只是需要做个数据格式转换。
    # 但是 PublicKey.load_pkcs1(PUBLIC_KEY) 这个地方就有坑了。
    # rsa使用的密钥文件有两种格式:
    # 第一种: 
    	-----BEGIN PUBLIC KEY-----
    	.........
    	-----END PUBLIC KEY-----
    # 第二中
    	-----BEGIN RSA PUBLIC KEY-----
    	.........
    	-----END RSA PUBLIC KEY-----
    # PublicKey.load_pkcs1()方法都的是第二种密钥格式
    # PublicKey.load_pkcs1_openssl_pem() 方法的的是第一种密钥格式
    # 而且:
    #
    # 两种方法返回的数据是不同的!
    # 两种方法返回的数据是不同的!
    # 两种方法返回的数据是不同的!
    #
    # 重要的事情说三遍(你细品我现在的心情)
    #
    """
    

    脚本在这里摘自 作者:窗户

    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    import sys
    #rsa
    from rsa import PublicKey, common, transform, core
    def f(cipher, PUBLIC_KEY):
    	public_key = PublicKey.load_pkcs1(PUBLIC_KEY)
    	encrypted = transform.bytes2int(cipher)
    	decrypted = core.decrypt_int(encrypted, public_key.e, public_key.n)
     	text = transform.int2bytes(decrypted) 
     	if len(text) > 0 and text[0] == '\x01':
      		pos = text.find('\x00')
      		if pos > 0:
      			return text[pos+1:]
      		else:
      			return None
    fn = sys.stdin.readline()[:-1]
    public_key = sys.stdin.readline()[:-1]
    x = f(open(fn).read(), open(public_key).read())
    print x
    

Baby_RSA(我也不知道应该算什么分类)

题目直接给了一个加密脚本。或不多说,先上脚本:(原脚本里面没有任何注释,大部分的东西都在注释里了)

## 看着上面一堆的导入库,然后用到的却不多,头秃

import sympy	# 这个是解题的核心库,pip装一下就好了
import random
from gmpy2 import gcd, invert	# gmpy2,没啥好说的,rsa加解密的核心库
from Crypto.Util.number import getPrime, isPrime, getRandomNBitInteger, bytes_to_long, long_to_bytes	
# Crypto 这个库有点恶心人。网上大部分教程都是直接 pip install pycryptodemo 完事了
# 然后你会发现,屁,照样报错,解决办法是: 
# 你去python库安装的目录的Python38\Lib\site-packages 
# (找不到的话CMD,where python,就能去到python的根目录里)
# 把 crypto 目录改成 Crypto 就能用了
# 原因我也不知道,还没有去查过库
from z3 import *



flag = b"nsfocus{xxxx}"		# 这个flag的样式是很重要(记号1)
base = 65537

# 这个函数全程没有用到过,我不知道他是干嘛用的,迷惑我们的?迷茫
def GCD(A):
    B = 1
    for i in range(1, len(A)):
        B = gcd(A[i-1], A[i])
    return B

# 取 p 的函数,挺有意思的
def gen_p():
    P = [0 for i in range(16)]
    # 获取了一个随机的质数赋给 P[0] ,而后的每个个值都是前一个质数的下一个质素
    P[0] = getPrime(128)
    for i in range(1, 17):				# 做题的的时候眼抽了,没看到这里重新更新了P[]的大小
        P[i] = sympy.nextprime(P[i-1])	# 还很蒙蔽的说下面循环给17不是超界了
        
        # sympy.nextprime() 获取参数的下一个质数
        # 我很自然的想到了 获取前一个质数方法,然后发现真的有,这是这个题目的解题核心
    
    print("P_p :", P[9]) ##### 注意 这个P[9],这样我们就能把整个P[]都给不全了
    
    n = 1
    for i in range(17):
        n *= P[i]
    p = getPrime(1024)
    factor = pow(p, base, n)
    # 看到这里,有没有发现点什么?
    # 没错,就是在这个地方做了 rsa加密,加密了p。一个简单的欧拉函数(phi)变形的加密
    # 数学解题思路在下面
    print("P_factor :", factor)
								##### 注意 这里返回的是 p 的下一个质数
    return sympy.nextprime(p)	##### 注意 这里返回的是 p 的下一个质数
								##### 注意 这里返回的是 p 的下一个质数
    
    							##### 你没想错,我又踩坑了
# 取 Q 的函数,这个就真的有意思了。
def gen_q():
    sub_Q = getPrime(1024)
    Q_1 = getPrime(1024)
    Q_2 = getPrime(1024)
    
    # 上面去了三个质数,没啥好说的,下面的这个表达式
    
    #做个记号,后面要用(记号2)
    Q = sub_Q ** Q_2 % Q_1		##### 没错就是这里,整个题目最精妙的地方  
    
    # 下面的这些都没啥好说的
    print("Q_1: ", Q_1)
    print("Q_2: ", Q_2)
    print("sub_Q: ", sub_Q)
    
    return sympy.nextprime(Q)		# 这个地方返回的也是 Q 的下一个 质数

if __name__ == "__main__":
    _E = base
    _P = gen_p()
    _Q = gen_q()
    # 这个地方是用来保证 _E 和 phi(欧拉函数)互质用的(方便求d)
    assert (gcd(E, (_P - 1) * (_Q - 1)) == 1)
    
    M = bytes_to_long(flag)	 # 做了个数据类型转换,方便加密
    
    # 最终的加密
    _C = pow(M, _E, P * Q)				### 这个地方小坑, P 是 _P, Q 是 _Q
    									### 我当时傻乎乎以为是上面的 return 里面的p和q
    print("Ciphertext = ", _C)


# 下面是一大串数据,看看就好了
'''
P_p : 206027926847308612719677572554991143421
P_factor : 213671742765908980787116579976289600595864704574134469173111790965233629909513884704158446946409910475727584342641848597858942209151114627306286393390259700239698869487469080881267182803062488043469138252786381822646126962323295676431679988602406971858136496624861228526070581338082202663895710929460596143281673761666804565161435963957655012011051936180536581488499059517946308650135300428672486819645279969693519039407892941672784362868653243632727928279698588177694171797254644864554162848696210763681197279758130811723700154618280764123396312330032986093579531909363210692564988076206283296967165522152288770019720928264542910922693728918198338839
Q_1:  103766439849465588084625049495793857634556517064563488433148224524638105971161051763127718438062862548184814747601299494052813662851459740127499557785398714481909461631996020048315790167967699932967974484481209879664173009585231469785141628982021847883945871201430155071257803163523612863113967495969578605521
Q_2:  151010734276916939790591461278981486442548035032350797306496105136358723586953123484087860176438629843688462671681777513652947555325607414858514566053513243083627810686084890261120641161987614435114887565491866120507844566210561620503961205851409386041194326728437073995372322433035153519757017396063066469743
sub_Q:  168992529793593315757895995101430241994953638330919314800130536809801824971112039572562389449584350643924391984800978193707795909956472992631004290479273525116959461856227262232600089176950810729475058260332177626961286009876630340945093629959302803189668904123890991069113826241497783666995751391361028949651
Ciphertext =  3456235890998996195610328576070818608212146115114054814560270509307541871219901924317906285783345502450407903297515749909925247057656479099156326718535149969298044687730717978909742604675188350201879798460569721955402966098556911054579289216533244883556427607787129063524341478675272236515424795594246542809803123825660181435151517009926989152291239580046450083599706908982941859840129662960907854690675582551816049328631412344203956716526620975585430081122484860480186571618841845367637670136996691613216081512920967200668918375537812973064141095933513440342389661883780888908448851245758659782134089407794948873910
'''

ok,看到这里,基本上代码都读懂了,那我们就开始填坑了。

第一个坑 —— get_p() 函数:
def gen_p():
    P = [0 for i in range(16)]
    P[0] = getPrime(128)
    for i in range(1, 17):				
        P[i] = sympy.nextprime(P[i-1])
   	n = 1
    print("P_p :", P[9]) 
    for i in range(17):
        n *= P[i]
    p = getPrime(1024)
    factor = pow(p, base, n)
    print("P_factor :", factor)								
    return sympy.nextprime(p)								

上面也说了这是一个简单的欧拉函数的加密。

首先,已知 P[9],也就是P_p,我们就能给整个P[]数组给初始化。所以 n 的值也就能够计算出来了。

import sympy
import gmpy2
P = []
P[9] = P_p
for i in range(8,-1,-1):
	P[i] = sympy.prevprime(P[i+1])
for i in range(10,17):
    P[i] = sympy.nextprime(P[i-1])
n = 1
for i in range(1,17):
    n *= P[i]

接下来就到了欧拉函数部分了,已知n,e,要求d,最简单的方法就是通过欧拉函数。那么,欧拉函数是多少?

提醒一下,碰到简单n,e题目的时候,我们通过将将n分解曾p,q两个质数,然后用 phi = (p-1)*(q-1) 来计算。那这个地方呢?也去分解n?

其实,出题的时候就已经给你分好了。P[]数组里面的值都是质数!

而 n = P[1] * p[2] * …*P[16]

所以 phi(n) = phi(P[1]) * phi(P[2]) * … * phi(P[16])

所以 phi(n) = (P[1] - 1)*(P[2] -1)*(P[3] - 1)* … * (P[16] - 1)

所以 欧拉函数也有了,p,不久放出来了吗!(咳,有点味道)

phi = 1
for i in range(1,17):
	phi *= (P[i] - 1)
d = gmpy2.invert(base, phi)
p = pow(factor, d, n)
return sympy.nextprime(p)

OK,接下来第二个坑(天坑)

get_q()函数
def gen_q():
    sub_Q = getPrime(1024)
    Q_1 = getPrime(1024)
    Q_2 = getPrime(1024)
    Q = sub_Q ** Q_2 % Q_1
    print("Q_1: ", Q_1)
    print("Q_2: ", Q_2)
    print("sub_Q: ", sub_Q)
    return sympy.nextprime(Q)

看到上面的算法,你第一件想到的事情是什么?我想到的是sub_Q,Q_1,Q_2的结果都给了,跑不久完事了。如果你也是这样想的,那恭喜你,和我一样掉坑了,等到宇宙爆炸的都不一定能拿到结果。

让我们来看看(记号2),这里的运算时乘方,不是乘法,注意 是乘方,乘方,乘方。

一个1024位的数字做1024为数值次的乘方。怎么算?拿头算?

而这个地方精妙的就是后面的取模了。这就在告诉我们,Q你是拿不到了,但Q肯定是 Q_1 > Q > 0 之间的质数。

没了。

所以,到了这里,就是在变相的告诉你,最后的加密用到的q。而且q要么很小,靠近0,要么很大,靠近Q_1。

那思路不久清晰了吗:

​ p 已知了,Q_1已知,q自己去爆破 (用sympy.nextprime() 方法和sympy.prevprime()方法) ,e 已知了。

所以代码可以撸起来了。

import gmpy2
import sympy
from Crypto.Util.number import long_to_bytes
p = return sympy.nextprime(p)
q = Q_1		# 或者 q = 1
c = Ciphertext
Q_2 = 1
while True:
	n = p * q
	phi = (p-1) * (q-1)
	d = gmpy2.invert(e, phi)
	m = pow(c, d, n)
	m = long_to_bytes(m).decode('utf-8','ignore')
	print(m)
	q = sympy.prevprime(q)		# q = sympy.nextprime(q)	和前面的q=1配套使用

这样就可以爆破出结果了,不过还有一个问题,就是它没有终止条件。所以,这个时候就要用到(记号1)的flag格式了

# 只需要在 m = long_to_bytes(m).decode('utf-8','ignore')之后的任意位置加一个if判断就行了
if "nsfocus" in m:
	break

为了高效的爆出结果,我们肯定是选在两头都跑(1和Q_1),所以可以加个多线程上去。不过我比较菜,不会多线程,所以我选择开两个文件,嘿嘿,技术不够,脚本来凑。

完整脚本:

import sympy
import gmpy2
from Crypto.Util.number import long_to_bytes

e = 65537
P = [0 for i in range(17)]
P_p = 206027926847308612719677572554991143421
P[9] = P_p
P_factor = 213671742765908980787116579976289600595864704574134469173111790965233629909513884704158446946409910475727584342641848597858942209151114627306286393390259700239698869487469080881267182803062488043469138252786381822646126962323295676431679988602406971858136496624861228526070581338082202663895710929460596143281673761666804565161435963957655012011051936180536581488499059517946308650135300428672486819645279969693519039407892941672784362868653243632727928279698588177694171797254644864554162848696210763681197279758130811723700154618280764123396312330032986093579531909363210692564988076206283296967165522152288770019720928264542910922693728918198338839

n = P[9]
phi = P[9] - 1
for i in range(8,0,-1):
    P[i] = sympy.prevprime(P[i+1])
    n *= P[i]
    phi *= (P[i] - 1)

for i in range(10,17,1):
    P[i] = sympy.nextprime(P[i-1])
    n *= P[i]
    phi *= (P[i] - 1)

d = gmpy2.invert(e,phi)



final_p = pow(P_factor, d, n)
final_p = sympy.nextprime(final_p)

Q_1 = 103766439849465588084625049495793857634556517064563488433148224524638105971161051763127718438062862548184814747601299494052813662851459740127499557785398714481909461631996020048315790167967699932967974484481209879664173009585231469785141628982021847883945871201430155071257803163523612863113967495969578605521

final_q = 1		#final_q = Q_1 

Ciphertext =  3456235890998996195610328576070818608212146115114054814560270509307541871219901924317906285783345502450407903297515749909925247057656479099156326718535149969298044687730717978909742604675188350201879798460569721955402966098556911054579289216533244883556427607787129063524341478675272236515424795594246542809803123825660181435151517009926989152291239580046450083599706908982941859840129662960907854690675582551816049328631412344203956716526620975585430081122484860480186571618841845367637670136996691613216081512920967200668918375537812973064141095933513440342389661883780888908448851245758659782134089407794948873910

while True:
    n = final_p * final_q
    phi = (final_p - 1) * (final_q - 1)
    try:
        d = gmpy2.invert(e,phi)
        m = pow(Ciphertext, d, n)
        m = long_to_bytes(m).decode('utf-8',"ignore")
        if "nsfocus" in m:
            print("m: ",m)
            break
    except:
        pass
    
    final_q = sympy.nextprime(final_q)	# final_q = sympy.prevprime(final_q)

其实,这题目就真的是在考数学。一手拓展欧几里得算法求逆元,然后同模余的运算法则。会了这一题,应该算是rsa算法有了初步的认知了。

先上解题代码:

import gmpy2
from gmpy2 import gcd
from Crypto.Util.number import long_to_bytes
n =  17083941230213489700426636484487738282426471494607098847295335339638177583685457921198569105417734668692072727759139358207667248703952436680183153327606147421932365889983347282046439156176685765143620637107347870401946946501620531665573668068349080410807996582297505889946205052879002028936125315312256470583622913646319779125559691270916064588684997382451412747432722966919513413709987353038375477178385125453567111965259721484997156799355617642131569095810304077131053588483057244340742751804935494087687363416921314041547093118565767609667033859583125275322077617576783247853718516166743858265291135353895239981121

gift =  2135492653776686212553329560560967285303308936825887355911916917454772197960682240149821138177216833586509090969892419775958406087994054585022894165950768427741545736247918410255804894522085720642952579638418483800243368312702566458196708508543635051350999572787188236243275631609875253617015664414032058822919469443284453403064076232765024248435543326597418851751586308514540124571309152787559712950209357825576896132278045112177910266019741013995106579484868768251084453338417115483515132869594712162052362083414163954681306259137057581036657441897428432575924018950961141822554251369262248368899977337886190114104
c =   11896048663970780362228781837489466486466282266282525774268696785272105376496162776613405022882264737932390195829622289249358353406571295574018064222013763168773667801755802797093469517945242168621127603773017861402501708822613876791515803039151959944370904767218422806768104905816096919472743978664055791890321098068209891550854674116923626647321385265228978238829509815935768090750750110601100943820432504952197382099637084090991156423394140353352175855468352060270942119322431162691631220150197779269032493785754953460073099974345959080880071039856065496031922388307631138393107747419702153992404456267795192911174

e_orign = 54722
e = 27361

for k in range(1,8):
    d = gmpy2.invert(e,k*gift)
    m = gmpy2.powmod( c, d, n)
    m = gmpy2.iroot(m,2)
    
    m = long_to_bytes(m[0]).decode('utf-8','ignore')

    print('m : ',m)

先自己试着去理解一以下把,解析后面再补。我困了。该睡觉了。

你可能感兴趣的:(CTF,安全,unctf,rsa,密码学)