CTF-Crypto Record

This a simple record of Crypto in CTF.

古典密码

古典密码这一块直接看 Misc 那篇文章吧

现代密码

前置知识

模运算的基本规则

分配律

$$
(a+b)\mod p=(a\mod p+b\mod p)\mod p\(ab)\mod p=(a\mod pb\mod p)\mod p\a^b\mod p=((a\mod p)^b)\mod p
$$

结合律

$$
((a+b)\mod p+c)\mod p=(a+(b+c)\mod p)\mod p\((ab)\mod pc)\mod p=(a*(b*c)\mod p)\mod p
$$

$$
若a\equiv b\mod p,对于任意的c,都有(a+c)\equiv (b+c)\mod p\若a\equiv b\mod p,对于任意的c,都有(ac)\equiv (bc)\mod p\若a\equiv b\mod p,对于任意的c,有c\equiv d\mod p,\则(a+c)\equiv (b+d)\mod p\(ac)\equiv(bd)\mod p
$$

费马小定理

$$
对于任意素数q和任意整数a,其a不是p的倍数,则\a^{p-1}\mod p=1
$$

扩展欧几里得算法

RSA

基础知识

素数:一个数如果除了1与它本身之外没有其他的因数,那么这个数就被称为素数。
合数:如果一个数大于1,且该数本身不是素数,那么这个数就是一个合数。
互质数:如果两个整数a,b的最大公因数gcb(a,b)=1,那么称a,b两数互质。
欧拉函数值:设m为正整数,则1,2,3,4…….,m中与m互素的整数的个数记为φ(m)叫做欧拉函数,欧拉函数的值叫做欧拉函数值。如果m为素数,那么φ(m)=n-1;如果m=pq,且p和q互素,那么φ(m)=(p-1)*(q-1)
取模运算与同余的概念:如果存在一个正整数m与两个整数a,b,如果a-b能够被m整除,也就是说m|(a-b),那么a和b模m同余 记做:a=b mod m
模指数运算:模指数运算即先进行指数运算,之后再进行取模运算。记做:a=b^e mod m
逆元:如果 gcd(a,b)=1 且 a * x ≡ 1(mod b) ,那么 x 为 a 的逆元。可以说 x 为 a 的倒数(a^-1) (或模反元素)
模运算重要定理:若a≡b (mod p),则对于任意的正整数c,都有(a * c) ≡ (b * c) (mod p);

最简单的RSA加密过程:

1、选择一对不相等且足够大的质数 p、q

2、计算p、q的乘积n

3、计算n的欧拉函数:的∮(n)=(p-1)*(q-1)

4、选一个与∮(n)互质的整数e :1<e<∮(n)

5、计算出e对于∮(n)的模反元素:ed mod ∮(n)=1

6、公钥:KU(e,n)

7、私钥:KR(d,n)

pow(x, y, z):等同于pow(x, y) mod z

明文 M:加密 M^e mod n = C 或者 C = pow(M, e, n)

密文 C: 解密 C^d mod n = M 或者 M = pow(c, d, n)

例子:

s1: p=3 q=11
s2: n=pq=33
s3:∮(n)=(p-1)
(q-1)=20
s4: 取e=3
s5: 找到d=7   (3d mod 20)=1
s6: KU=(e,n)=(3,33)
s7: KR=(d,n)=(7,33)
若M=20
C = M^e mod n=14
解密原理同上

一些脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def EX_GCD(a,b,arr): #扩展欧几里得  
    if b == 0:  
        arr[0] = 1  
        arr[1] = 0  
        return a  
    g = EX_GCD(b, a % b, arr)  
    t = arr[0]  
    arr[0] = arr[1]  
    arr[1] = t - int(a / b) * arr[1]  
    return g  
  
def ModReverse(a,n): #ax=1(mod n) 求a模n的乘法逆x  
    arr = [0,1,]  
    gcd = EX_GCD(a,n,arr)  
    if gcd == 1:  
        return (arr[0] % n + n) % n  
    else:  
        return -1  
  
e = 4  
d = 7  
print(ModReverse(e,d))

基础RSA加密脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import *  
import gmpy2  
  
msg = 'flag is :testflag'  
hex_msg=int(msg.encode("hex"),16)  
#hex_msg=bytes_to_long(msg)  
print(hex_msg)  
p=getPrime(100)  
q=getPrime(100)  
n=p*q  
e=0x10001  
phi=(p-1)*(q-1)  
d=gmpy2.invert(e,phi) #求逆元  
print("d=",hex(d))  
c=pow(hex_msg,e,n)  
print("e=",hex(e))  
print("n=",hex(n))  
print("c=",hex(c))

基础RSA解密脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import binascii  
import gmpy2  
n=0x80b32f2ce68da974f25310a23144977d76732fa78fa29fdcbf  
p=780900790334269659443297956843  
q=1034526559407993507734818408829  
e=0x10001  
c=0x534280240c65bb1104ce3000bc8181363806e7173418d15762  
  
phi=(p-1)*(q-1)  
d=gmpy2.invert(e,phi)  
m=pow(c,d,n)  
print(hex(m))  
print(binascii.unhexlify(hex(m)[2:].strip("L")))  
#print("c=", long_to_bytes (m))

dp泄露
给出公钥n,e以及dp
求解私钥d脚本:

1
2
3
4
5
6
7
8
9
def getd(n,e,dp):  
    for i in range(1,e):  
        if (dp*e-1)%i == 0:  
            if n%(((dp*e-1)/i)+1)==0:  
                p=((dp*e-1)/i)+1  
                q=n/(((dp*e-1)/i)+1)  
                phi = (p-1)*(q-1)  
                d = gmpy2.invert(e,phi)%phi  
                return d

低加密指数广播攻击
如果选取的加密指数较低,并且使用了相同的加密指数给一个接受者的群发送相同的信息,那么可以进行广播攻击得到明文。
这个识别起来比较简单,一般来说都是给了三组加密的参数和明密文,其中题目很明确地能告诉你这三组的明文都是一样的,并且e都取了一个较小的数字。

1
2
3
4
5
6
7
8
import binascii,gmpy2  
  
def CRT(mi, ai):  
    assert(reduce(gmpy2.gcd,mi)==1)  
    assert (isinstance(mi, list) and isinstance(ai, list))  
    M = reduce(lambda x, y: x * y, mi)  
    ai_ti_Mi = [a * (M / m) * gmpy2.invert(M / m, m) for (m, a) in zip(mi, ai)]  
    return reduce(lambda x, y: x + y, ai_ti_Mi) % M

公钥n由多个素数因子组成
给出e,n,c;
已知:n=p*q*r*s,公钥n是由四个素数相乘得来的

如果四个素数接近,使用yafu直接分解

解题脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import binascii  
import gmpy2  
p=1249559655343546956371276497499  
q=1249559655343546956371276497489  
r=1249559655343546956371276497537  
s=1249559655343546956371276497423  
e=0x10001  
c=0x22fda6137013bac19754f78e8d9658498017f05a4b0814f2af97dc2c60fdc433d2949ea27b13337961ef3c4cf27452ad3c95  
n=p*q*r*s  
phi=(p-1)*(q-1)*(r-1)*(s-1)  
d=gmpy2.invert(e,phi)  
m=pow(c,d,n)  
print(binascii.unhexlify(hex(m)[2:].strip("L")))

小明文攻击  低加密指数攻击
给出n,c,e;

明文过小,导致明文的e次方仍然小于n
直接对密文e次开方,即可得到明文
解密:

 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
import binascii  
import gmpy2  
n=  
e=0x3  
c=  
m=gmpy2.iroot(c, 3)[0]  
print(binascii.unhexlify(hex(m)[2:].strip("L")))

明文的三次方虽然比n大但是大不了多少  
爆破即可  
解密脚本  
import binascii  
import gmpy2  
n=  
e=0x3  
c=  
i = 0  
while 1:  
    res = gmpy2.iroot(c+i*n,3)  
    if(res[1] == True):  
        m=res[0]  
        print(binascii.unhexlify(hex(m)[2:].strip("L")))  
        break  
    print "i="+str(i)  
    i = i+1

共模攻击
若干次加密,e不同,n相同,m相同。就可以在不分解n和求d的前提下,解出明文m。
如果已知:n1,n2,c1,c2,e1,e2,并且其中n1=n2的话,即C1=m^e1  mod n ;C2=m^e2  mod n
脚本:

 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
def egcd(a, b):  
    if a == 0:  
      return (b, 0, 1)  
    else:  
      g, y, x = egcd(b % a, a)  
      return (g, x - (b // a) * y, y)  
def modinv(a, m):  
    g, x, y = egcd(a, m)  
    if g != 1:  
      raise Exception('modular inverse does not exist')  
    else:  
      return x % m  
s = egcd(e1, e2)  
s1 = s[1]  
s2 = s[2]  
if s1<0:  
   s1 = - s1  
   c1 = modinv(c1, n)  
elif s2<0:  
   s2 = - s2  
   c2 = modinv(c2, n)  
m=(pow(c1,s1,n)*pow(c2,s2,n)) % n  
  
s0, s1, s2 = gmpy2.gcdext(e1, e2)  
if s1 < 0:  
    s1 = -s1  
    c1 = gmpy2.invert(c1, n)  
elif s2 < 0:  
    s2 = -s2  
    c2 = gmpy2.invert(c2, n)  
m = gmpy2.powmod(c1, s1, n)*gmpy2.powmod(c2, s2, n) % n  
print(binascii.unhexlify(hex(m)[2:].strip("L")))

Hash长度扩展攻击

原理简单来说就是:

已知:1.secret的长度

2.data的值

3.secret+data的MD5值

我们就能求出 secret+data+任意值的MD5

工具

1.Hashpump(已在WSL中安装)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#用法如下  
cd HashPump && ./hashpump  
#签名就是我们之前说的secret  
Input Signature: 571580b26c65f306376d4f64e53cb5c7  
Input Data: adminadmin  
Input Key Length: 15  
Input Data to Add: 123  
961a38ded0b8553041ca20dd34e8e189  
adminadmin\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x00\x00123  
#这里要注意,最后输出的是两部分拼接后的data  
#发送时要两部分分开来发,比如username=admin,password=admin\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x00\x00123  
#并且最后BP发包的时候,要把\x替换为%

例题

1.http://ctf5.shiyanbar.com/web/kzhan.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#payload  
POST /web/kzhan.php HTTP/1.1  
Host: ctf5.shiyanbar.com  
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0  
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8  
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2  
Accept-Encoding: gzip, deflate  
Referer: http://ctf5.shiyanbar.com/web/kzhan.php  
Content-Type: application/x-www-form-urlencoded  
Content-Length: 149  
Origin: http://ctf5.shiyanbar.com  
Connection: close  
Cookie: sample-hash=571580b26c65f306376d4f64e53cb5c7; source=1; getmein=961a38ded0b8553041ca20dd34e8e189  
Upgrade-Insecure-Requests: 1  
  
username=admin&password=admin%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%c8%00%00%00%00%00%00%00123

刷题记录

buuoj-RSA1

1
2
3
4
5
6
7
8
import gmpy2  
  
p = 473398607161  
q = 4511491  
e = 17  
#使用invert求模反元素  
d = int(gmpy2.invert(e, (p-1)*(q-1)))  
print(d)

buuoj-rsarsa

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import gmpy2  
  
p = 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483  
q = 11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407  
e = 65537  
c = 83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034  
n = p*q  
phi = (p-1)*(q-1)  
d = gmpy2.invert(e, phi)  
m = pow(c, d, n)  
print(m)

buuoj-RSA1(dp&dq泄露)

$$
d_p=d \mod (p-1)\m_p=c^d\mod p\m_q=c^d\mod q\d=d_p+k*(p-1)
$$

$$
\begin{cases}mp=d_p \mod p\mq=d_q\mod q \end{cases}
$$

$$
c^d=m_p+k_1p\k_1p=mq-mp-k_2q\pi\equiv1+k_3q\c^d=mp+ip*(mq-mp)-(k_1k_2+k_3)pq\m=c^d\mod n=(mp+ip(mq-mp))\mod n
$$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import gmpy2  
from Crypto.Util.number import *  
  
p = 8637633767257008567099653486541091171320491509433615447539162437911244175885667806398411790524083553445158113502227745206205327690939504032994699902053229  
q = 12640674973996472769176047937170883420927050821480010581593137135372473880595613737337630629752577346147039284030082593490776630572584959954205336880228469  
dp = 6500795702216834621109042351193261530650043841056252930930949663358625016881832840728066026150264693076109354874099841380454881716097778307268116910582929  
dq = 783472263673553449019532580386470672380574033551303889137911760438881683674556098098256795673512201963002175438762767516968043599582527539160811120550041  
c = 24722305403887382073567316467649080662631552905960229399079107995602154418176056335800638887527614164073530437657085079676157350205351945222989351316076486573599576041978339872265925062764318536089007310270278526159678937431903862892400747915525118983959970607934142974736675784325993445942031372107342103852  
n = p*q  
i = gmpy2.invert(p, q)  
mp = gmpy2.powmod(c, dp, p)  
mq = gmpy2.powmod(c, dq, q)  
m = (mp+i*p*(mq-mp)) % n  
print(m)  
print(long_to_bytes(m))
0%