21May1

huangx607087 5月的切题

0.Intruduction

之前4月准备更新2篇博客的,不过自己因为准备六级所以只能在4月9日写了一篇博客就结束了(。5月份虽然还是紧张地备考,自己还是抽出来了一点点时间来打打CTF。简单做了一些题目。。这个时候距离六级也就剩20几天了。六月份还有期末考试。可以说这个是自己第二阶段CTF的学学习的最后一篇文章。等7月7日的暑假,自己CTF进入第三阶段,到时候争取能跟寒假一样有个突破,然后9月说不定能进线下很显然这是不可能的,被Nu1L大佬压制,注定只有在X1c中摸鱼的份了

5月15日,ciscn做出了三道密码题,1200分,死而无憾(ciscnRSA那道题过于简单,故博客中不放这道题的wp)

本博客常用符号:$|E|$表示集合$E$内元素的个数,或者椭圆曲线上的点数。$\dfrac{\ln}{\ln 2}$表示某个数字的比特位数,$\dfrac{\ln}{\ln 10}$表示十进制位数,$\mathrm{int}(x)$表示向下取整,$\mathrm{int}(x+0.5)$表示四舍五入

1.[CISCN 2021]imageencrypt

这道题是一个对一张图片的加密(实际上是对一个数组的加密),题目给出了加密代码、附加明文和附加密文

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
#Chall.py
import random
from flag import flag,image,r,key1,key2
import md5
assert(flag[:5]=='CISCN')
assert(len(str(r))==3)
data = ''.join(map(chr,image))
assert(flag[6:-1] == md5.new(data).hexdigest())
assert(key1<256)
assert(key2<256)
x0 = random.random()
x0 = round(x0,6)
def generate(x):
return round(r*x*(3-x),6)
def encrypt(pixel,key1,key2,x0,m,n):
num = m*n/8
seqs = []
x = x0
bins = ''
tmp = []
for i in range(num):
x = generate(x)
tmp.append(x)
seqs.append(int(x*22000))
for x in seqs:
bin_x = bin(x)[2:]
if len(bin_x) < 16:
bin_x = '0'*(16-len(bin_x))+bin_x
bins += bin_x
assert(len(pixel) == m*n)
cipher = [ 0 for i in range(m) for j in range(n)]
for i in range(m):
for j in range(n):
index = n*i+j
ch = int(bins[2*index:2*index+2],2)
pix = pixel[index]
if ch == 0:
pix = (pix^key1)&0xff
if ch == 1:
pix = (~pix^key1)&0xff
if ch == 2:
pix = (pix^key2)&0xff
if ch == 3:
pix = (~pix^key2)&0xff
cipher[index] = pix
return cipher
flagimage = image
testimage = []
for i in range(16*16):
testimage.append(random.randint(0,255))
print testimage
print encrypt(testimage,key1,key2,x0,16,16)
print encrypt(flagimage,key1,key2,x0,24,16)

通过审计代码可以发现:题目中一共有4个未知量:$x_0,r,key_1,key_2$。加密过程也很简单:通过$x_0,r$两个数字通过函数$f(x)=rx(3-x)$不断迭代生成一个序列,然后根-据序列里的值来确定明文对哪个值进行异或。

Step 1:求$key$值

根据个人做题习惯:先将给出来的附加明文、附加密文、flag密文写入given.py,分别命名为Pg,Cg,Cf

很显然,由于对明文的加密仅仅是对$key_1,key_2$的异或操作,因此我们可以先对$Pg,Cg$两个数组进行一次异或,可以得到$key_1,key_2$在集合$(78,177,86,169)$中。其中不难发现$78$的取反是$177$,$86$的取反是$169$。所以$key_1$的取值有两种$(78,177)$,$key_2$的取值有两种$(86,169)$,并且$key_1,key_2$的值进行调换是不等价的,也就是要关注顺序,又是两种可能性。因此,可能的$(key_1,key_2)$的数对一共有$8$种,因此我们可以利用这$8$种组合生成对应的加密指令串,也就是题目encrypt函数中的bins

这一步的代码如下:

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
from given import Pg,Cg
def Generate(key1,key2):
global K1
s=""
for i in K1:
if(i==key1):
s+="00"
if(i==(255-key1)):
s+="01"
if(i==key2):
s+="10"
if(i==(255-key2)):
s+="11"
assert len(s)==512
print(s)
K1,K2=[],[]
for i in range(len(Pg)):
K1.append((Pg[i]^Cg[i])&0xff)
K2.append((~Pg[i]^Cg[i])&0xff)
print(K1)
print(K2)
Generate(78,86)
Generate(78,169)
Generate(177,86)
Generate(177,169)
Generate(86,78)
Generate(86,177)
Generate(169,78)
Generate(169,177)

我们可以将生成的$8$个字符串写如possible.py文件中,定义数组psb储存这$8$个字符串,分别对应上面的八个可能的组合密钥,possible.py文件内容如下:

1
2
3
4
5
6
7
8
psb=['0001...1001',
'0001...1101',
'0100...1000',
'0100...1100',
'1011...0011',
'1110...0010',
'1011...0111',
'1110...0110',]

Step 2:求$r,x_0$值

也就是说,我们已经知道了密钥对$(key_1,key_2)$的八个可能组合,现在还有$(x_0,r)$这个组合我们还不知道,$x_0$是六位小数在区间内$[0.000000,0.999999]$,$r$是一个未知的数字。

根据题目中assert(len(str(r))==3)可知:$r$的取值有两个范围,一个是$[0.0,9.9]$内的一位小数,还有一个是$[100,999]$之间的三位数。不过根据简单的分析,由于题目中对产生的小数有乘$22000$的操作,假设当$x=0.1$时,$100×0.1×2.9×22000=638000$,远超$16$位整数最大值$65536$,因此$r$只有$[0.1,9.9]$之间。

我一开始猜的$r=5.3$,结果运行加密脚本突然报错了,然后通过输出的数据,我发现迭代几次之后,数字的值就远远超过了$10^{300}$的数量级。说明$r$一定是有个范围的。

自己尝试了一下,发现当$r≤1.3$的时候整个迭代序列是收敛的,$r>1.3$的时候迭代序列是发散的(因为函数图像左边可以走到负无穷)。所以,我们可以得出:$r$一共有$13$种取值方案,$x_0$一共有$10^6$中可能取值,因此对于一组密钥的枚举,一共是一千三百万种可能性,进行简单的剪枝还是可枚举的——只需要枚举一位根据可能性判断一位,一旦不符合直接return 0,进行下一轮的枚举即可。

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
from possible import *
answer=psb[0]
keyz=[(78,86),(78,169),(177,86),(177,169),(86,78),(86,177),(169,78),(169,177)]
Standard=[]
def generate(x,r):
return round(r*x*(3-x),6)
def BruteForce(key1,key2,x0,m,n,r):
num = m*n//8
seqs = []
x = x0
bins = ''
tmp = []
for i in range(num):
x = generate(x,r)
tmp.append(x)
seqs.append(int(x*22000))
if(seqs!=Standard[:len(seqs)]):
return 0
return 1
for T in range(8):
print('Breaking '+str(T+1)+'th Possibility')
k1,k2=keyz[T]
answer=psb[T]
Standard=[]
for i in range(0,len(answer),16):
Standard.append(int(answer[i:i+16],2))
x0=0
while x0<0.999999:
x0=round(x0+0.000001,6)
r=0
while r<1.3:
r=r+0.1
if(BruteForce(k1,k2,x0,16,16,r)):
print('Find(k1,k2,x0,r)!\n',k1,k2,x0,r)
break
'''
Result:
Breaking 7th Possibility
Find(k1,k2,x0,r)!
169 78 0.840264 1.2
'''

Step 3:整合结果,求出答案

我们成功找出了$x_0=0.840264,r=1.2$,这样四个未知数所有的内容就全被我们知道了。扩展一下操作串,然后解密回去就行了

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
from hashlib import md5
from Crypto.Util.number import *
from given import Cf
x0,r=0.840264,1.2
def generate(x):
return round(r*x*(3-x),6)
def encrypt(key1,key2,x0,m,n):
num = m*n//8
seqs = []
x = x0
bins = ''
tmp = []
for i in range(num):
x = generate(x)
tmp.append(x)
seqs.append(int(x*22000))
for x in seqs:
bin_x = bin(x)[2:]
if len(bin_x) < 16:
bin_x = '0'*(16-len(bin_x))+bin_x
bins += bin_x
return bins
decstr=encrypt(169,78,0.840264,24,16)
Pf=[]
for i in range(len(Cf)):
op=int(decstr[i*2:i*2+2],2)
#print(op)
if(op==0):
Pf.append(Cf[i]^169)
if(op==1):
Pf.append(Cf[i]^86)
if(op==2):
Pf.append(Cf[i]^78)
if(op==3):
Pf.append(Cf[i]^177)
print(Pf)
S=0
for i in Pf:
S*=256
S+=i
S=long_to_bytes(S)
print('CISCN{'+md5(S).hexdigest()+'}')
#CISCN{7fa176002ced947e49f1752c1eb9dd62}

2.CopperMove

Step 1:初步观察代码,确定算法

还是先看一下题目

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
56
57
58
59
60
61
from Crypto.Util.number import *
from math import sqrt, gcd
import random
BITS = 512
f = open("flag.txt", "rb")
flag = f.read()
f.close()
def get_prime(nbit):
while True:
p = getPrime(nbit)
if p % 3 == 2:
return p
def gen(nbit):
p = get_prime(nbit)
q = get_prime(nbit)
if q > p:
p, q = q, p
n = p * q
bound = int(sqrt(2 * n)) // 12
while True:
x = random.randint(1, round(sqrt(bound)))
y = random.randint(1, bound) // x
zbound = int(((p - q) * round(n ** 0.25) * y) // (3 * (p + q)))
z = zbound - ((p + 1) * (q + 1) * y + zbound) % x
e = ((p + 1) * (q + 1) * y + z) // x
if gcd(e, (p + 1) * (q + 1)) == 1:
break
gifts = [int(bin(p)[2:][:22], 2), int(bin(p)[2:][256:276], 2)]
return n, e, gifts
def add(p1, p2):# ECC Add
if p1 == (0, 0):
return p2
if p2 == (0, 0):
return p1
if p1[0] == p2[0] and (p1[1] != p2[1] or p1[1] == 0):
return (0, 0)
if p1[0] == p2[0]:
tmp = (3 * p1[0] * p1[0]) * inverse(2 * p1[1], n) % n
else:
tmp = (p2[1] - p1[1]) * inverse(p2[0] - p1[0], n) % n
x = (tmp * tmp - p1[0] - p2[0]) % n
y = (tmp * (p1[0] - x) - p1[1]) % n
return (int(x), int(y))
def mul(n, p):
r = (0, 0)
tmp = p
while 0 < n:
if n & 1 == 1:
r = add(r, tmp)
n, tmp = n >> 1, add(tmp, tmp)
return r
n, e, hint = gen(BITS)
pt = (bytes_to_long(flag[:len(flag) // 2]), bytes_to_long(flag[len(flag) // 2:]))
c = mul(e, pt)
f = open("output.txt", "w")
f.write(f"n = {n}\n")
f.write(f"e = {e}\n")
f.write(f"h1 = {hint[0]}\n")
f.write(f"h2 = {hint[1]}\n")
f.write(f"c = {c}\n")
f.close()

初次代码审计,可能并看不出什么,不过这里我们可以发现这样两个情况:

一是生成$e$的方式非常复杂,并且$p,q\equiv 2 \pmod 3$。

二是add、mul函数是二元组的计算,并且看似非常复杂,像坐标的计算

根据条件二,可以判断出这应该是ECC,根据条件一,可以判断出这里用的是KMOV-91算法,相关内容见博客上发布的ECCNotes3(2021.3.25)

Step 2:二次审计代码,定参数性质

通过审计代码,我们可以发现:$\dfrac{\ln x}{\ln 2}=254,\dfrac{\ln y}{\ln 2}<260,\dfrac{\ln n}{\ln 2}=\dfrac{\ln e}{\ln 2}=1024$。并且$\dfrac{\ln }{\ln 2}(ex-yn)=535$。

由于这里不知道WienerAttack怎么做,不过我们可以将这个特殊化成RSA中低解密指数广播(虽然只有一组)攻击的形式,构造低解密指数攻击的格,就可以把$x$求出来了。(相关方法见文章VNCTF WriteUp,2021.4.9发布

然后根据$y=\dfrac{ex}n$,可以很快的求出$y$的值。然后就可以求出余数$k$。

然后我们可以这样进行推导:由
$$
k=y(p+q+1)-z
$$
可以推出
$$
k=y(p+q+1)-\mathrm{zbound}-[(\phi y-\mathrm{zbound})\mod x]
$$
其中$\phi=(p+1)(q+1)$,其意义是椭圆曲线上的点数,不是RSA中的$\phi=(p-1)(q-1)$!

然后我们根据条件,又可以推出
$$
k=y(p+q+1)+{\text{int}(\dfrac{(p-q)\text{int}(\sqrt[4]n+0.5)y}{3(p+q)}})-[\phi y-\mathrm{zbound} \mod x]
$$
由于最后一项一定小于$x$,而第二项与$\dfrac{(p-q)\text{int}(\sqrt[4]n+0.5)y}{3(p+q)}$真实值不超过$1$。因此上面的式子除以$y$,就是$K=p+q+1+\dfrac{(p-q)\text{int}(\sqrt[4]n+0.5)y}{3(p+q)}$的值。而此时的未知量,只剩下了$p,q$。

因此我们可以得到这样的式子:
$$
p-q=\dfrac{3(p+q)(K-1-(p+q))}{\mathrm{int}(\sqrt[4]{n}+0.5)}
$$
又根据$(p+q)^2-(p-q)^2=4n$,假设$p+q=s$,之前的结果可以得到这样的结果
$$
4n=s^2-\dfrac{\mathrm{int}(9s^2(K-1-s)^2)}{\mathrm{int}^2(\sqrt[4]n +0.5)}
$$
然后用二分法求解即可。

求出来之后,就可以知道分解$p,q$,求出解密指数$d$,题目出解。

这里椭圆曲线是$y^2=x^3+b$,相关内容见ECC Notes3中的KMOV91

上一下解密代码

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
from Crypto.Util.number import *
e=
n=
Cx=
Cy=
M=matrix(ZZ,[[2^512,e],[0,n]])
M=M.LLL()
x=(abs(M[0][0])>>512)
y=e*x//n
k=e*x-y*n
K=k//y
l,r=0,K
for i in range(515):
s=(l+r)//2
v=s*s-int(s*s*9*(K-1-s)*(K-1-s))//(round(n**0.25)*round(n**0.25))
if(v<4*n):
l=s
else:
r=s
pandq=r
d=inverse(e,n+pandq+1)
b=(Cy**2-Cx**3)%n
E=EllipticCurve(Zmod(n), [0, b])
C=E(Cx,Cy)
P=d*C
flag=long_to_bytes(P[0])+long_to_bytes(P[1])
print(flag)
#CISCN{e91fef4ead7463b13d00bda65f540477}

2021虎符CTF得到这道题是这个题目的扩展版,只不过这里$\ln x=0.38\ln n$,提供了三组数据,也一样解。

REFERENCE

3.[XDCTF RSA]

2021年5月7日,做出了shallow出的题目,死而无憾

还是先按照惯例看一下题目

1
2
3
4
5
6
7
8
9
10
from Crypto.Util.number import *
from secret import flag
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 65537
print(n)
print(pow(bytes_to_long(flag) , e , n))
_q = int(bin(q)[2:][::-1] , 2)
print(p ^ _q)

题目给出了$e,n$的同时,还给出了$p$的值与$q’$异或之后的值,其中$q’$是$q$的比特位完全反过来对应的值.

之前自己在网上见到过一个题目,条件是$q$的比特位为$p$的比特位倒过来,也就是$p=q’$。这就说明那道题是这一题的一个特殊情况:$p \text { xor } q’=0$。所以可以认为这两题是同一个题目

所以我们只需要一位一位,同时枚举$p,q$的高位就可以了,这样顺便就能把对应的低位求出来。因此枚举是$256$轮而不是$512$轮。这边还有一点,就是异或后的值不足$512$个比特位,因此我们要对$a$(也就是异或的值)的高位进行补$0$。

然后我们可以就可以开始我们的dfs操作了,不过这里有几个剪枝点可以简单的提一下:

1.根据同余的性质可知:无论在多少进制下,只要两个数的最后$n$位固定下来了,那么这两个数的乘积的最后$n$位也被固定了。因此我们只要发现枚举时两个数乘出来最后的比特位与$n$值对应的比特位不一致,那么我们就把这个状态舍弃掉

2.由于我们是一位一位的从$p,q$的高位开始枚举,因此实际上随着枚举的进行,如果假设中间未被枚举到的数位全是$0$的话,$p,q$的值应该是逐渐变大的。因此在枚举中,如果发现$p,q$两个数的乘积超过了$n$,就又可以说明这一种情况不符合条件了,也需要通过continue将其舍弃,这样我们就可以给枚举值定上限了。

3.与此同时,我们还可以给枚举值定下限。假设我们枚举的时候,中间未被枚举到的位全是$1$,那么我们枚举时就可以认为$p,q$两个值是不断缩小的,而此时我们假设的$p,q$两个数乘积还不到$n$,就说明这种情况也不符合题意,我们可以通过这个方式给枚举值定下限。而如果我们想假设这两个数未被枚举到的比特位全是$1$的话,可以认为是$2^{511-\mathrm{Round}}\mathrm{CurP}$(具体原因可以自己尝试一下)。

这样设置这三个筛选条件,就可以获得$p,q$的值解密了。

还有两题实在做不出来,因为自己tcl

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
from Crypto.Util.number import *
from given import e,n,c,a
bina=bin(a)[2:]
bina='0'*(512-len(bina))+bina
def dfs(P,Q,Round):
if Round==256:
if P*Q==n:
global p,q
p,q=P,Q
return 1
for i in range(2):
for j in range(2):
CurP=P+i*(2**(511-Round))+(int(bina[Round])^j)*(2**Round)
CurQ=Q+j*(2**(511-Round))+(int(bina[511-Round])^i)*(2**Round)
if CurP*CurQ>n:
continue
if (CurP+2**(511-Round))*((CurQ+2**(511-Round)))<n:
continue
if (CurP*CurQ)%(2**(Round+1))!=n%(2**(Round+1)):
continue
dfs(CurP,CurQ,Round+1)
return 0
#------Main Below------#
p,q=None,None
dfs(0,0,0)
assert p!=None and q!=None
phi=(p-1)*(q-1)
d=inverse(e,phi)
print(long_to_bytes(pow(c,d,n)))

4.[CSAWQual 2019]byte_me

XDCTF中shallow出了一个块密码题,是CBC模式,但自己太菜了没做出来,然后我在buu上找到了一个同样的块密码,不过这里用的是EBC模式,关于块密码题,我会在第三阶段的CTF学习中,会专门在一篇博客中对几种块密码的加密模式进行一个比较详细的阐述.

ECB模式,即电子密码本,对每一块明文逐个加密就可以得到密文(最后一个块要填充)。该模式具有简单、高效的特点,然而缺点也很明显,就是密文很有规律(因为使用的是同一个密钥),容易在主动攻击中被破解。在DES/3DES加密中,取用的块长度为8,而在AES加密中,块长度为16。

该模式有个很显著的特点:既不需要计数器也不需要初始向量iv

首先我们看一下加密代码

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
import random
import string
from Crypto.Cipher import AES
def pad(text, block_size):
pad_size = block_size - (len(text) % block_size)
if pad_size == 0:
pad_size = block_size
pad = chr(pad_size) * pad_size
return text + pad
def random_key(num):
return "".join(
[random.choice(string.ascii_letters + string.digits) for n in range(num)]
).upper()
key = random_key(16)
random_string = random_key(random.randint(1, 15))
def ecb_enc(text):
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(text)
def encryption_oracle(text, unknown):
text = pad(random_string + text + unknown, 16)
return ecb_enc(text)
def string_length_detect():
str1 = encryption_oracle("A" * 16)
def main():
decoded_flag=open('flag.txt','r').readline()
print(encryption_oracle(decoded_flag, "").encode("hex"))
while True:
userInput = raw_input("Tell me something: ")
print(encryption_oracle(str(userInput), decoded_flag).encode("hex"))
if __name__ == "__main__":
exit(main())

通过代码审计,我们可以得到这样一个关系:设明文为$m$,一个随机前缀$w$(长度在1到15位之间)。一开始给出$w+m$的加密结果$c$(见下图第一段),数了一下,一共$128$位,考虑到$w$的长度是1到15位,那么估计$m$的长度应该是32至​48​位之间的样子。也就是需要三个块。而这道题,是对AES-ECB模式的选择明文攻击,对服务器发送字符串$s$,最后服务器返回对$w+s+m$加密的密文

一开始自己没有思路的时候,对服务器发送了字符串a,aa,aaa,aaaa,… …,然后我发现当字符a的个数达到某个值的时候,最前面32位的密文保持不变——经过思考,这不变的32位密文,是随机前缀$w$通过填充字符a至16位时的密文。假设这一段字符a称为字符串$i$,那么我们取使加密结果不再改变的a字符的最小值作为字符串$i$,那么此时第一段加密结果,也就是最前面32位,就是字符串$w+i$的结果,第32至64位是$m$前16位的加密结果,称为$m_1$,再往后就是$m_2,m_3$,分别对应$m$的第17-32位和第33位及以后的加密结果。见下图第二段

然后,我们可以构造一段长度15,内容为#的字符串,称为$k$串,对服务器发送$i+k$,服务器返回$w+i+k+m$的密文,其中$w+i$这一个长度为16的字符串对应的密文前32位,而密文第 33至48位对应着$k$和$m$第一位的密文。这一次,我们可以对这一位进行爆破。见下图第三段灰色部分,为爆破的这一位。如果爆破时的某一个加密结果的33至48位与初始发送的33至48位一致,那么我们就认为这一位成功爆破。并存储下来作为$f$

然后我们就可以把$k$改成14个#,然后13个#,每次发送 $i+k+f$,返回$w+i+k+f+m$的密文。其中$w+i$为16位,$k+f$为16位,不破坏加密的整体性,见下图第四段和第五段,棕色是我们已知的$m_1$的部分,灰色是我们爆破的那一位

401

很快,我们知道了$m_1$的全部,也就是flag的前16位,见下面第六段**(注意下面这张图的标尺与上面一个图不一样!)**

这个时候,我们可以截取$m_1$的最后15位,然后仍然是爆破第整个序列第32位,使这两个地方的加密值相同,可以获得$m_2$的第一位。

然后截取$m_1$的最后14位,13位,这样我们可以知道的$m_2$的内容越来越多… …见下图第七段

同理,见下图第八段和第九段,同理可以知道$m_3$的所有内容

402

最后上一下脚本和运行结果

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
from Crypto.Util.number import *
from pwn import *
sh=remote("node3.buuoj.cn",29419)
C=sh.recvline(keepends=False)
#print(C)
#print(len(C))
#sh.interactive()
def getflag(sh,L,R):
global suffex,flag
for i in range(15,-1,-1):
print(i,flag[15:])
suff='#'*i
sh.recvuntil('ng: ')
#print(flag+'\n')
sh.send(suffex+suff+'\n')
for j in range(2):
rightseq=sh.recvline(keepends=False)
for j in range(32,128):
nowch=chr(j)
sh.recvuntil('ng: ')
sh.send(suffex+flag[-15:]+nowch+'\n')
for k in range(2):
getseq=sh.recvline(keepends=False)
if(getseq[32:64]==rightseq[L:R]):
flag=flag+nowch
break
flag='#'*15
recvsuff='0123456789abcdef0123456789abcdef'
suffex=''
for i in range(16):
suff='*'*(i+1)
sh.recvuntil('ng: ')
sh.send(suff+'\n')
for j in range(2):
curseq=sh.recvline(keepends=False)
if(curseq[:32]==recvsuff):
suffex='*'*i
break
else:
recvsuff=curseq[:32]
print(suffex)
getflag(sh,32,64)
getflag(sh,64,96)
getflag(sh,96,128)

403

5.[CISCN2018] Crackme Java

首先看一下题目,刚好最近学了一下java,能够搞懂题目的意思

然后简单的审计一下代码,发现使Elgamal,只不过$c_1,c_2$都是以$32$进制给出来的

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
import java.math.BigInteger;
import java.util.Random;
public class Test0 {
static BigInteger two =new BigInteger("2");
static BigInteger p = new BigInteger("11360738295177002998495384057893129964980131806509572927886675899422214174408333932150813939357279703161556767193621832795605708456628733877084015367497711");
static BigInteger h= new BigInteger("7854998893567208831270627233155763658947405610938106998083991389307363085837028364154809577816577515021560985491707606165788274218742692875308216243966916");
/*
Alice write the below algorithm for encryption.
The public key {p, h} is broadcasted to everyone.
@param val: The plaintext to encrypt.
We suppose val only contains lowercase letter {a-z} and numeric charactors, and is at most 256 charactors in length.
*/
public static String pkEnc(String val){
BigInteger[] ret = new BigInteger[2];
BigInteger bVal=new BigInteger(val.toLowerCase(),36);
BigInteger r =new BigInteger(new Random().nextInt()+"");
ret[0]=two.modPow(r,p);
ret[1]=h.modPow(r,p).multiply(bVal);
return ret[0].toString(36)+"=="+ret[1].toString(36);
}

/* Alice write the below algorithm for decryption. x is her private key, which she will never let you know.
public static String skDec(String val,BigInteger x){
if(!val.contains("==")){
return null;
}
else {
BigInteger val0=new BigInteger(val.split("==")[0],36);
BigInteger val1=new BigInteger(val.split("==")[1],36);
BigInteger s=val0.modPow(x,p).modInverse(p);
return val1.multiply(s).mod(p).toString(36);
}
}
*/
public static void main(String[] args) throws Exception {
System.out.println("You intercepted the following message, which is sent from Bob to Alice:");
System.out.println("eag0vit7sboilgcfu0fbkbrmjgs4pzi2oznmqrkey5h1bwicvborngscx050u8vpghi69xqjmotgrtj4vq8fgw9tzi916o034bu==ahcwy7c0qq5cnxdntssqrj972nhvzt5liqlq0cvv0o1fm2ee4205nemuy5tvkda0hyetu5a5xcqqov8exk901z5xebvkcdo3jiq1gj8pkxzhjaeg9z6syu58neijxy56am7d1l9grhgtgkkfc432wm6h3jr8y1xx");
System.out.println("Please figure out the plaintext!");
}
}
//eag0vit7sboilgcfu0fbkbrmjgs4pzi2oznmqrkey5h1bwicvborngscx050u8vpghi69xqjmotgrtj4vq8fgw9tzi916o034bu==ahcwy7c0qq5cnxdntssqrj972nhvzt5liqlq0cvv0o1fm2ee4205nemuy5tvkda0hyetu5a5xcqqov8exk901z5xebvkcdo3jiq1gj8pkxzhjaeg9z6syu58neijxy56am7d1l9grhgtgkkfc432wm6h3jr8y1xx

注意到$r$是random_integer,说明应该不超过$42$亿,简单枚举一下就可以知道$r$的值了

$r$解出来了就可以知道所有的内容了不过不知道最后应该交上去什么,不过西电大佬dbt是解出来的,不过他也忘记了,因为真的不好改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import *
def check(s):
if(s[:5]==b'flag{'):
return 1
for i in range(20):
if(s[i] not in range(32,128)):
return 0
return 1
p=11360738295177002998495384057893129964980131806509572927886675899422214174408333932150813939357279703161556767193621832795605708456628733877084015367497711
h=7854998893567208831270627233155763658947405610938106998083991389307363085837028364154809577816577515021560985491707606165788274218742692875308216243966916
c1='eag0vit7sboilgcfu0fbkbrmjgs4pzi2oznmqrkey5h1bwicvborngscx050u8vpghi69xqjmotgrtj4vq8fgw9tzi916o034bu'
c2='ahcwy7c0qq5cnxdntssqrj972nhvzt5liqlq0cvv0o1fm2ee4205nemuy5tvkda0hyetu5a5xcqqov8exk901z5xebvkcdo3jiq1gj8pkxzhjaeg9z6syu58neijxy56am7d1l9grhgtgkkfc432wm6h3jr8y1xx'
c1,c2=int(c1,36),int(c2,36)
r=1599939680
assert(pow(2,r,p)==c1)
e1=pow(h,r,p)
assert c2%e1==0
bval=c2//e1
table="0123456789abcdefghijklmnopqrstuvwxyz"
s=""
from base36 import *
print(dumps(bval))
#ciscncongratulationsthisisdesignedbyalibabasecurity2096022101

6.Conclusion

huangx607087 CTF学习的第二阶段至此就结束了。后面准备备考六级和期末考试了。

7月7日,博客会继续更新,第三阶段CTF学习开始,自己争取能够在以后有1次去线下看看各位大佬们的机会吧虽然我知道就算是去了大概率还是去摸鱼的,寒假自己的CTF水平有了个突破,争取这个暑假也能有个突破吧

不知不觉,今天都已经5月18日了,7月7日放暑假,也就是说自己大一就剩不到50天了(草

没想到自己已经走了这么远了啊… …怎么感觉昨天才入学啊woc……

争取到时候能有Am4dalao的一半水平吧(,哎(

打CTF能认识dw和dbt两位密码神,死而无憾

6月28日进入考试周,冲冲冲,争取每门课都能有60——不过我发现自己预习不完了怎么办,我只是不想补考(哎


21May1
http://example.com/2021/05/17/21May1/
作者
huangx607087
发布于
2021年5月17日
许可协议