场景实操开场卷
2#glass
反编译apk
发现调用了so
。
找到so
中目标函数。
根据代码意思不难得出明文flag进行三次函数调用后得到密文,密文符合即flag正确。
根据代码倒着写出解密脚本即可。
enc = bytearray(bytes.fromhex('A31AE3692FBB1A8465C2ADAD9E9605021F8E364FE1EBAFF0EAC4A82D42C76E3FB0D3CC78F9983F'))
s = b'12345678'
lens = 39
for j in range(0, lens, 8):
k = 0
while k != 8 and j + k < lens:
enc[j + k] ^= s[k]
k += 1
encenc = []
for i in range(0, lens, 3):
encenc += [
enc[i + 1] ^ enc[i + 2],
enc[i + 0] ^ enc[i + 1],
enc[i + 0] ^ enc[i + 1] ^ enc[i + 2]
]
from arc4 import ARC4
print(ARC4(s).encrypt(bytes(encenc)))
得到flag。
CISCN{6654d84617f627c88846c172e0f4d46c}
8#tiny traffic
过滤数据包,找到目标数据GET /test
、GET /secret
,并保存成文件。
对test
文件进行brotli
解码,得到proto
模板,于是推测secret
文件进行brotli
解码再解protobuf
即为明文。
import brotli
with open("test", 'rb') as f:
test = brotli.decompress(f.read())
with open("test.proto", 'wb') as f:
f.write(test)
syntax = "proto3";
message PBResponse {
int32 code = 1;
int64 flag_part_convert_to_hex_plz = 2;
message data {
string junk_data = 2;
string flag_part = 1;
}
repeated data dataList = 3;
int32 flag_part_plz_convert_to_hex = 4;
string flag_last_part = 5;
}
message PBRequest {
string cate_id = 1;
int32 page = 2;
int32 pageSize = 3;
}
进行模板化得到python
脚本。
protoc.exe .\test.proto --python_out=./
编写脚本进行解析protobuf
数据。
import brotli
with open("secret", 'rb') as f:
secret = brotli.decompress(f.read())
import test_pb2
PBResponse = test_pb2.PBResponse()
PBResponse.ParseFromString(secret)
print(PBResponse)
code: 200
flag_part_convert_to_hex_plz: 15100450
dataList {
flag_part: "e2345"
junk_data: "7af2c"
}
dataList {
flag_part: "7889b0"
junk_data: "82bc0"
}
flag_part_plz_convert_to_hex: 16453958
flag_last_part: "d172a38dc"
根据英文字面意思进行拼接flag。
from Crypto.Util.number import long_to_bytes
print("CISCN{", end="")
print(long_to_bytes(PBResponse.flag_part_convert_to_hex_plz).hex(), end="")
print(PBResponse.dataList[0].flag_part, end="")
print(PBResponse.dataList[1].flag_part, end="")
print(long_to_bytes(PBResponse.flag_part_plz_convert_to_hex).hex(), end="")
print(PBResponse.flag_last_part, end="")
print("}")
得到flag。
CISCN{e66a22e23457889b0fb1146d172a38dc}
场景实操二阶卷
6#隔空传话
首先打开data.txt
,根据题目描述猜测可能是互发短信,所以尝试PDU
编码。
第一题成功解码出短信内容。
依次得到前四条短信为:
SMSC#
Receipient:+8615030442000
Validity:Rel 4d
TP_PID:00
TP_DCS:00
TP_DCS-popis:Uncompressed Text
No class
Alphabet:Default
hello,bob!what is the flag?
Length:27
SMSC#
Receipient:+10086
Validity:Not Present
TP_PID:00
TP_DCS:00
TP_DCS-popis:Uncompressed Text
No class
Alphabet:Default
the first part of the flag is the first 8 digits of your phone number
Length:69
SMSC#
Receipient:+8615030442000
Validity:Rel 5d
TP_PID:00
TP_DCS:08
TP_DCS-popis:Uncompressed Text
No class
Alphabet:UCS2(16)bit
那其他部分呢
Length:6
SMSC#
Receipient:+8615030442000
Validity:Rel 1h
TP_PID:00
TP_DCS:08
TP_DCS-popis:Uncompressed Text
No class
Alphabet:UCS2(16)bit
看看你能从这些数据里发现什么?w465
Length:16.5
可知flag开头应该是CISCN{15030442
或者CISCN{86150304
。
因为编解码工具是网页版本,所以就写个js脚本导出剩下的短信。
var encs = `data.txt内容`;
encs = encs.split('\n');
encs.pop();
var msgs = "";
encs.forEach(function(enc){
msgs += getPDUMetaInfo(enc) + "\n\n";
});
保存为msgs.txt
。
然后写脚本进行按时间排序。
import re
ptn = re.compile(r'SMSC#\+10086\nSender:\+8615030442000\nTimeStamp:(.+?) GMT \?\nTP_PID:00\nTP_DCS:00\nTP_DCS-popis:Uncompressed Text\nNo class\nAlphabet:Default\n\n([0-9a-f]+)\nLength:([0-9]+)')
with open("msgs.txt", 'r') as f:
data = f.read()
msgs = ptn.findall(data)
print(msgs)
发现短信主体很像png
文件头,于是进行拼接并输出到msg.png
。
msgs.sort(key=lambda t:t[0])
png = b''.join(bytes.fromhex(msg[1]) for msg in msgs)
with open("msg.png", 'wb') as f:
f.write(png)
发现图片宽高不对,于是套个之前收着的crc爆破宽高脚本。
import binascii
import struct
import sys
data = bytearray(png[0x0c:0x1d])
crc32key = eval('0x'+str(binascii.b2a_hex(png[0x1d:0x21]))[2:-1])
n = 4095
for w in range(n):
width = bytearray(struct.pack('>i', w))
for h in range(n):
height = bytearray(struct.pack('>i', h))
for x in range(4):
data[x+4] = width[x]
data[x+8] = height[x]
crc32result = binascii.crc32(data) & 0xffffffff
if crc32result == crc32key:
print(width,height)
newpic = bytearray(png)
for x in range(4):
newpic[x+16] = width[x]
newpic[x+20] = height[x]
with open("msg.png", 'wb') as f:
f.write(newpic)
sys.exit()
得到手写flag的一部分。
然后根据之前的提示进行拼接,得到flag。
CISCN{15030442_b586_4c9e_b436_26def12293e4}
8#move
这题和虎符2019的simultaneous
这题很相似,只是简化了向量数,然后密文多了一层c = mul(e, pt)
,所以基本改一下现有脚本就行。
from Crypto.Util.number import inverse, long_to_bytes
def add(p1, p2):
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 = 80263253261445006152401958351371889864136455346002795891511487600252909606767728751977033280031100015044527491214958035106007038983560835618126173948587479951247946411421106848023637323702085026892674032294882180449860010755423988302942811352582243198025232225481839705626921264432951916313817802968185697281
e = 67595664083683668964629173652731210158790440033379175857028564313854014366016864587830963691802591775486321717360190604997584315420339351524880699113147436604350832401671422613906522464334532396034178284918058690365507263856479304019153987101884697932619200538492228093521576834081916538860988787322736613809
c = (6785035174838834841914183175930647480879288136014127270387869708755060512201304812721289604897359441373759673837533885681257952731178067761309151636485456082277426056629351492198510336245951408977207910307892423796711701271285060489337800033465030600312615976587155922834617686938658973507383512257481837605, 38233052047321946362283579951524857528047793820071079629483638995357740390030253046483152584725740787856777849310333417930989050087087487329435299064039690255526263003473139694460808679743076963542716855777569123353687450350073011620347635639646034793626760244748027610309830233139635078417444771674354527028)
# M = Matrix(ZZ, [[2**512, e],
# [0, -n]])
# GV = M.LLL()[0]
GV = (-352349739892292322999330613117511489276791137416050590322561272098135764509812116210543232353192946492907441641899508311181782397967869533800138447935440769558784357373166666205853969428654899524304252899588626355058283586558885888, -406850608655407486298019095013146348847805975120061760929682791882948049742096195978800022454159691659865169100330308708576847735609146508679126419372034710027124703842712262177437006326228856546452636094881051757653949488135598409)
x = -GV[0] >> 512
y = (e*x+GV[1])//n
k = e*x-n*y
K = k//y
def factor(K, N):
l = 0
r = K
for i in range(518):
s = (l+r)//2
v = s*s - (9*s*s*(K-1-s)*(K-1-s)//(round(N**0.25)*round(N**0.25)))
if v < 4*N:
l = s
else:
r = s
return r
S = factor(K, n)
d = inverse(e, n+1+S)
print(b''.join(long_to_bytes(i) for i in mul(d, c)))
CISCN{e91fef4ead7463b13d00bda65f540477}
9#imageencrypt
阅读题目。
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)
发现testimage
已知,然后密文每位和明文之间只经过一次变换,所以应当可以爆出可能存在的key
。
testimage = [205, 237, 6, 158, 24, 119, 213, 32, 74, 151, 142, 186, 57, 28, 113, 62, 165, 20, 190, 37, 159, 137, 196, 44, 97, 37, 7, 222, 220, 95, 4, 66, 0, 28, 199, 142, 95, 105, 119, 232, 250, 215, 60, 162, 91, 211, 63, 30, 91, 108, 217, 206, 80, 193, 230, 42, 221, 71, 136, 115, 22, 176, 91, 57, 61, 3, 87, 73, 250, 121, 51, 72, 83, 120, 77, 199, 236, 190, 249, 116, 45, 6, 134, 110, 149, 94, 214, 232, 153, 213, 119, 98, 81, 203, 240, 114, 240, 29, 122, 188, 156, 53, 128, 185, 40, 147, 245, 204, 47, 101, 80, 229, 41, 150, 28, 195, 25, 235, 119, 6, 192, 8, 73, 255, 159, 172, 77, 94, 254, 104, 236, 219, 141, 91, 195, 162, 97, 56, 252, 173, 163, 43, 167, 214, 50, 73, 115, 190, 254, 53, 61, 77, 138, 192, 15, 4, 190, 27, 37, 108, 101, 135, 90, 215, 106, 243, 112, 111, 106, 89, 143, 150, 185, 142, 192, 176, 48, 138, 164, 185, 61, 77, 72, 0, 17, 203, 210, 71, 186, 49, 162, 250, 218, 219, 195, 63, 248, 220, 155, 180, 219, 132, 219, 94, 144, 247, 211, 95, 70, 227, 222, 31, 69, 24, 13, 216, 185, 108, 137, 57, 186, 211, 55, 27, 158, 241, 223, 21, 134, 106, 152, 127, 187, 245, 246, 131, 176, 177, 228, 100, 112, 11, 84, 61, 193, 42, 41, 69, 229, 145, 254, 138, 3, 153, 123, 31]
cipher = [131, 92, 72, 47, 177, 57, 131, 118, 4, 38, 192, 19, 119, 82, 63, 143, 235, 165, 15, 140, 209, 223, 117, 133, 47, 148, 81, 144, 138, 246, 173, 235, 177, 181, 110, 39, 9, 192, 57, 166, 180, 153, 141, 19, 234, 157, 142, 80, 234, 197, 151, 152, 249, 143, 176, 155, 147, 17, 57, 194, 191, 254, 13, 144, 140, 85, 25, 248, 172, 208, 154, 249, 5, 201, 27, 137, 69, 23, 175, 34, 156, 72, 208, 32, 195, 16, 127, 65, 207, 131, 57, 203, 7, 98, 89, 36, 65, 75, 211, 21, 45, 132, 214, 239, 102, 58, 68, 130, 97, 204, 225, 76, 152, 216, 74, 149, 79, 165, 198, 72, 150, 94, 7, 177, 46, 226, 252, 247, 79, 62, 69, 106, 60, 21, 106, 236, 47, 145, 170, 28, 18, 101, 14, 152, 131, 7, 37, 15, 168, 99, 115, 27, 220, 150, 89, 82, 232, 170, 107, 221, 212, 46, 235, 129, 36, 66, 217, 222, 36, 15, 217, 192, 247, 192, 113, 230, 129, 196, 13, 247, 148, 228, 225, 86, 71, 133, 132, 238, 236, 127, 11, 83, 107, 141, 114, 150, 182, 146, 213, 250, 141, 53, 114, 16, 198, 70, 133, 17, 247, 173, 136, 73, 236, 78, 188, 150, 239, 58, 199, 136, 11, 122, 134, 77, 47, 167, 137, 188, 55, 195, 41, 49, 245, 92, 160, 213, 254, 0, 85, 205, 193, 69, 2, 140, 143, 155, 127, 236, 179, 199, 168, 35, 85, 40, 45, 174]
m = 16
n = 16
keys = set()
for i in range(m):
for j in range(n):
index = n * i + j
pix = testimage[index]
print(index, end=" ")
for key in range(0xff):
if (pix ^ key) & 0xff == cipher[index]:
print("ch=0/2", f"key={key}", end=" ")
keys.add(key)
break
for key in range(0xff):
if (~pix ^ key)&0xff == cipher[index]:
print("ch=1/3", f"key={key}", end=" ")
keys.add(key)
break
print("")
print(keys)
得到四个可能的key值177
、169
、78
、86
。
因为根据上面的日志发现78
和177
是二选一的,86
和169
也一样,所以可以假设法带入所有8种情况,求出每轮的ch
。
然后根据每个ch
等价于每个x
(2字节16位)的每两位,可以写出逆算x
即seqs
的脚本。
seqs = []
for i in range(0, 256, 8):
x = (chs[i+0] * 64 + chs[i+1] *16 + chs[i+2] *4 + chs[i+3] *1)*256 + (chs[i+4] * 64 + chs[i+5] *16 + chs[i+6] *4 + chs[i+7] *1)
seqs.append(x)
print(seqs)
又根据题目脚本里的x = generate(x);seqs.append(int(x*22000))
,可以接着推出近似前导x
,进而求出generate
函数中的近似r
。
x = 1
for i in seqs:
beforex = x
x = i / 22000
print(x / (3 - beforex) / beforex)
当所有输出的r
都近似相等时,说明key1
、key2
代入正确。
所以得到r = 1.2
、key1 = 169
、key2 = 78
。
然后根据r
的值以及seqs
中的序列,可以爆破出小数点后六位的初始值x0
。
def generate(x):
return round(r*x*(3-x),6)
for i in range(1000000):
x0 = i / 1000000
x = x0
flag = True
for j in seqs:
if int(generate(x)*22000) != j:
flag = False
break
else:
x = generate(x)
if flag:
print(x0)
得到x0 = 0.840264
最后根据题目对flagimage
进行求解即可。
cipher = [198, 143, 247, 3, 152, 139, 131, 84, 181, 180, 252, 177, 192, 25, 217, 179, 136, 107, 190, 62, 4, 6, 90, 53, 105, 238, 117, 44, 5, 116, 132, 195, 214, 171, 113, 209, 18, 31, 194, 174, 228, 212, 196, 14, 27, 41, 211, 56, 139, 135, 225, 214, 89, 122, 178, 212, 185, 231, 204, 150, 204, 212, 160, 142, 213, 173, 186, 166, 65, 238, 5, 32, 45, 31, 25, 189, 148, 38, 78, 79, 33, 56, 227, 48, 103, 163, 31, 189, 37, 124, 106, 249, 86, 188, 86, 233, 41, 250, 89, 7, 212, 234, 111, 104, 245, 102, 227, 96, 160, 67, 181, 13, 26, 192, 214, 210, 188, 84, 216, 215, 243, 72, 233, 2, 122, 166, 107, 251, 70, 128, 94, 190, 185, 210, 34, 85, 77, 29, 182, 77, 115, 208, 228, 252, 73, 198, 151, 70, 10, 97, 138, 235, 21, 117, 239, 102, 129, 2, 253, 80, 53, 61, 184, 220, 41, 82, 37, 140, 23, 143, 179, 53, 153, 113, 213, 211, 111, 197, 248, 65, 60, 69, 1, 81, 48, 254, 251, 89, 195, 8, 93, 190, 66, 174, 97, 175, 210, 191, 66, 112, 123, 128, 33, 230, 237, 104, 16, 192, 239, 173, 44, 10, 120, 231, 114, 151, 140, 63, 103, 44, 243, 222, 242, 73, 51, 46, 98, 137, 163, 152, 147, 95, 223, 3, 15, 112, 85, 215, 133, 131, 240, 239, 224, 195, 140, 124, 70, 156, 221, 241, 37, 245, 1, 99, 9, 157, 99, 150, 47, 118, 225, 16, 13, 141, 135, 99, 18, 119, 63, 160, 6, 247, 27, 68, 45, 199, 86, 193, 252, 21, 135, 32, 42, 103, 114, 241, 49, 249, 182, 52, 18, 155, 157, 61, 4, 246, 158, 52, 118, 242, 195, 54, 139, 232, 100, 31, 11, 233, 58, 100, 101, 137, 83, 145, 209, 7, 241, 96, 57, 148, 207, 29, 237, 124, 177, 166, 161, 20, 116, 122, 61, 71, 46, 82, 18, 157, 253, 130, 112, 66, 94, 57, 221, 243, 222, 192, 147, 5, 130, 201, 174, 26, 160, 16, 188, 103, 187, 11, 238, 182, 144, 4, 137, 33, 84, 100, 7, 239, 219, 83, 112, 189, 166, 58, 93, 141, 30, 198, 220, 196, 118, 172, 5, 45]
m = 24
n = 16
num = m*n//8
seqs = []
x = x0
bins = ''
for i in range(num):
x = generate(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
flagimage = [ 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
pix = cipher[index]
ch = int(bins[2*index:2*index+2],2)
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
flagimage[index] = pix
print(flagimage)
[136, 62, 185, 178, 49, 197, 213, 2, 251, 5, 178, 24, 142, 87, 151, 2, 198, 218, 15, 151, 74, 80, 235, 156, 39, 95, 35, 98, 83, 221, 45, 106, 103, 2, 216, 120, 68, 182, 140, 224, 170, 154, 117, 191, 170, 103, 98, 118, 58, 46, 175, 128, 240, 52, 228, 101, 247, 177, 125, 39, 101, 154, 246, 39, 100, 251, 244, 23, 23, 71, 172, 145, 123, 174, 79, 243, 61, 143, 24, 25, 144, 118, 181, 126, 49, 237, 182, 20, 115, 42, 36, 80, 0, 21, 255, 191, 152, 172, 240, 174, 101, 91, 57, 62, 187, 207, 82, 46, 238, 234, 4, 164, 171, 142, 128, 132, 234, 26, 105, 153, 165, 30, 167, 76, 203, 232, 218, 82, 247, 214, 247, 15, 8, 156, 139, 27, 3, 180, 224, 252, 194, 158, 77, 178, 248, 136, 193, 247, 92, 55, 196, 189, 67, 35, 185, 48, 215, 179, 179, 225, 132, 148, 9, 138, 103, 227, 140, 61, 89, 217, 229, 99, 215, 63, 100, 133, 222, 139, 81, 15, 149, 236, 168, 7, 102, 176, 173, 240, 149, 70, 244, 23, 243, 248, 208, 6, 156, 241, 12, 62, 45, 49, 136, 168, 187, 217, 70, 142, 94, 227, 122, 92, 209, 177, 195, 217, 218, 105, 41, 157, 66, 119, 67, 31, 130, 120, 52, 32, 18, 49, 34, 17, 145, 170, 89, 38, 27, 102, 52, 42, 65, 161, 182, 114, 194, 205, 16, 53, 139, 167, 115, 92, 87, 210, 95, 44, 210, 63, 158, 223, 183, 161, 91, 36, 201, 53, 92, 222, 105, 246, 80, 94, 170, 10, 132, 110, 0, 151, 77, 91, 209, 110, 100, 206, 195, 88, 103, 183, 7, 98, 163, 42, 44, 115, 82, 184, 200, 122, 56, 188, 106, 159, 221, 166, 213, 81, 162, 64, 116, 213, 43, 32, 5, 223, 135, 182, 64, 54, 111, 218, 126, 75, 92, 205, 231, 15, 8, 66, 34, 52, 115, 246, 96, 227, 92, 211, 76, 204, 217, 20, 239, 144, 139, 90, 136, 142, 197, 83, 43, 96, 248, 76, 17, 70, 13, 49, 18, 69, 95, 31, 198, 181, 32, 119, 253, 42, 73, 70, 106, 29, 38, 20, 232, 108, 244, 219, 72, 144, 109, 146, 32, 250, 83, 99]
然后回到题目开始,flag内容为data = ''.join(map(chr,image));md5.new(data).hexdigest()
,所以在python2里面求md5即可。
import md5
data = ''.join(map(chr,flagimage))
print('CISCN{'+md5.new(data).hexdigest()+'}')
得到flag。
CISCN{7fa176002ced947e49f1752c1eb9dd62}
10#baby_bc
对baby.bc
进行重编译。
llc baby.bc -o baby.s
gcc -c baby.s -o baby.o
得到二进制文件baby.o
。
ida打开可发现如下主函数。
大概就是判断输入长度==25
,由[0-5]
组成。
然后过了俩函数验证就能把输入md5一下当作flag内容。
char __fastcall f(__int64 a1)
{
signed __int64 v1; // rax
signed __int64 v2; // rcx
char v3; // dl
char v4; // dl
char v5; // dl
char v6; // dl
char v7; // dl
v1 = 4LL;
v2 = 0LL;
do
{
v3 = *(_BYTE *)(a1 + v1 - 4);
if ( m[v1 - 4] )
{
if ( v3 != '0' )
return 0;
}
else
{
if ( v3 == '0' )
return 0;
m[v1 - 4] = v3 - '0';
}
v4 = *(_BYTE *)(a1 + v1 - 3);
if ( m[v1 - 3] )
{
if ( v4 != '0' )
return 0;
}
else
{
if ( v4 == '0' )
return 0;
m[v1 - 3] = v4 - '0';
}
v5 = *(_BYTE *)(a1 + v1 - 2);
if ( m[v1 - 2] )
{
if ( v5 != '0' )
return 0;
}
else
{
if ( v5 == '0' )
return 0;
m[v1 - 2] = v5 - '0';
}
v6 = *(_BYTE *)(a1 + v1 - 1);
if ( m[v1 - 1] )
{
if ( v6 != '0' )
return 0;
}
else
{
if ( v6 == '0' )
return 0;
m[v1 - 1] = v6 - '0';
}
v7 = *(_BYTE *)(a1 + v1);
if ( m[v1] )
{
if ( v7 != '0' )
return 0;
}
else
{
if ( v7 == '0' )
return 0;
m[v1] = v7 - '0';
}
++v2;
v1 += 5LL;
}
while ( v2 < 5 );
return 1;
}
char c()
{
unsigned __int8 *v0; // rax
signed __int64 v1; // rdx
__int64 v2; // rsi
__int64 v3; // rsi
__int64 v4; // rsi
__int64 v5; // rsi
__int64 v6; // rax
__int64 v7; // rdx
__int64 v8; // rdx
__int64 v9; // rdx
__int64 v10; // rdx
_BYTE *v11; // rax
signed __int64 v12; // rdx
char v13; // cl
char v14; // cl
char v15; // cl
char v16; // cl
signed __int64 v17; // rdx
signed __int64 v18; // rsi
char result; // al
char v20; // al
char v21; // al
char v22; // al
char v23; // al
char v24; // al
int v25; // [rsp+0h] [rbp-10h]
__int16 v26; // [rsp+4h] [rbp-Ch]
int v27; // [rsp+8h] [rbp-8h]
__int16 v28; // [rsp+Ch] [rbp-4h]
v0 = &m[4];
v1 = 0LL;
while ( 1 )
{
v28 = 0;
v27 = 0;
v2 = *(v0 - 4);
if ( *((_BYTE *)&v27 + v2) )
break;
*((_BYTE *)&v27 + v2) = 1;
v3 = *(v0 - 3);
if ( *((_BYTE *)&v27 + v3) )
break;
*((_BYTE *)&v27 + v3) = 1;
v4 = *(v0 - 2);
if ( *((_BYTE *)&v27 + v4) )
break;
*((_BYTE *)&v27 + v4) = 1;
v5 = *(v0 - 1);
if ( *((_BYTE *)&v27 + v5) )
break;
*((_BYTE *)&v27 + v5) = 1;
if ( *((_BYTE *)&v27 + *v0) )
break;
++v1;
v0 += 5;
if ( v1 >= 5 )
{
v6 = 0LL;
while ( 1 )
{
v26 = 0;
v25 = 0;
v7 = (unsigned __int8)m[v6];
if ( *((_BYTE *)&v25 + v7) )
break;
*((_BYTE *)&v25 + v7) = 1;
v8 = (unsigned __int8)m[v6 + 5];
if ( *((_BYTE *)&v25 + v8) )
break;
*((_BYTE *)&v25 + v8) = 1;
v9 = (unsigned __int8)m[v6 + 10];
if ( *((_BYTE *)&v25 + v9) )
break;
*((_BYTE *)&v25 + v9) = 1;
v10 = (unsigned __int8)m[v6 + 15];
if ( *((_BYTE *)&v25 + v10) )
break;
*((_BYTE *)&v25 + v10) = 1;
if ( *((_BYTE *)&v25 + (unsigned __int8)m[v6 + 20]) )
break;
if ( ++v6 >= 5 )
{
v11 = &m[4];
v12 = 0LL;
while ( 1 )
{
v13 = n[4 * v12];
if ( v13 == 2 )
{
if ( *(v11 - 4) > *(v11 - 3) )
return 0;
}
else if ( v13 == 1 && *(v11 - 4) < *(v11 - 3) )
{
return 0;
}
v14 = n[4 * v12 + 1];
if ( v14 == 1 )
{
if ( *(v11 - 3) < *(v11 - 2) )
return 0;
}
else if ( v14 == 2 && *(v11 - 3) > *(v11 - 2) )
{
return 0;
}
v15 = n[4 * v12 + 2];
if ( v15 == 2 )
{
if ( *(v11 - 2) > *(v11 - 1) )
return 0;
}
else if ( v15 == 1 && *(v11 - 2) < *(v11 - 1) )
{
return 0;
}
v16 = n[4 * v12 + 3];
if ( v16 == 2 )
{
if ( *(v11 - 1) > *v11 )
return 0;
}
else if ( v16 == 1 && *(v11 - 1) < *v11 )
{
return 0;
}
++v12;
v11 += 5;
if ( v12 >= 5 )
{
v17 = 4LL;
v18 = 0LL;
while ( 1 )
{
v20 = o[v17 - 4];
if ( v20 == 2 )
{
if ( m[v17 - 4] < m[v17 + 1] )
return 0;
}
else if ( v20 == 1 && m[v17 - 4] > m[v17 + 1] )
{
return 0;
}
v21 = o[v17 - 3];
if ( v21 == 1 )
{
if ( m[v17 - 3] > m[v17 + 2] )
return 0;
}
else if ( v21 == 2 && m[v17 - 3] < m[v17 + 2] )
{
return 0;
}
v22 = o[v17 - 2];
if ( v22 == 2 )
{
if ( m[v17 - 2] < m[v17 + 3] )
return 0;
}
else if ( v22 == 1 && m[v17 - 2] > m[v17 + 3] )
{
return 0;
}
v23 = o[v17 - 1];
if ( v23 == 2 )
{
if ( m[v17 - 1] < m[v17 + 4] )
return 0;
}
else if ( v23 == 1 && m[v17 - 1] > m[v17 + 4] )
{
return 0;
}
v24 = o[v17];
if ( v24 == 2 )
{
if ( m[v17] < m[v17 + 5] )
return 0;
}
else if ( v24 == 1 && m[v17] > m[v17 + 5] )
{
return 0;
}
++v18;
v17 += 5LL;
result = 1;
if ( v18 >= 4 )
return result;
}
}
}
}
}
return 0;
}
}
return 0;
}
两个函数比较长,先来看第一个函数,转换成比较简单的python脚本后如下。
m = bytearray(bytes.fromhex("0000000000000000000000000400000000000300000000000000000000000000"))
def f(a1):
v1 = 4
for 没用的 in range(5):
v3 = a1[v1 - 4]
if m[v1 - 4]:
if v3 != '0':
return False
else:
if v3 == '0':
return False
m[v1 - 4] = v3 - '0'
v4 = a1[v1 - 3]
if m[v1 - 3]:
if v4 != '0':
return False
else:
if v4 == '0':
return False
m[v1 - 3] = v4 - '0'
v5 = a1[v1 - 2]
if m[v1 - 2]:
if v5 != '0':
return False
else:
if v5 == '0':
return False
m[v1 - 2] = v5 - '0'
v6 = a1[v1 - 1]
if m[v1 - 1]:
if v6 != '0':
return False
else:
if v6 == '0':
return False
m[v1 - 1] = v6 - '0'
v7 = a1[v1 - 0]
if m[v1 - 0]:
if v7 != '0':
return False
else:
if v7 == '0':
return False
m[v1 - 0] = v7 - '0'
v1 += 5
将代码逻辑化简后如下:
m = bytearray(bytes.fromhex("0000000000000000000000000400000000000300000000000000000000000000"))
def f(a1):
for i in range(25):
if m[i]:
if a1[i] != '0':
return False
else:
if a1[i] == '0':
return False
m[i] = ord(a1[i]) - 48
也就是把m前25字节0的给用输入的对应位填充,然后非0的输入对应位要是0。(也就是说填入的是[1-5])
然后看c函数,外层while的五次循环是判断m前25字节有没有0,没有就正确进入内层循环,即满足if ( v1 >= 5 )
。
接着的循环还是换了个遍历方向判断m前25字节有没有0,没有就正确进入内层循环,即满足if ( ++v6 >= 5 )
。
接着的循环是根据另一个数组n
的取值判断合法性,将m分成5个字节一组,0
则无所谓,1
则左大右小,2
则左小右大,满足则进入。
接着的循环是根据数组o
的取值判断合法性,将m纵向分成5个字节一组,0
则无所谓,1
则上小下大,2
则上大下小,满足则返回真。
所以最后问题就归结于三个数组的关系问题。
m = bytearray(bytes.fromhex("0000000000"+
"0000000000"+
"0000040000"+
"0000000300"+
"0000000000"))
n = bytearray(bytes.fromhex("00000001"+
"01000000"+
"02000001"+
"00000000"+
"01000100"))
o = bytearray(bytes.fromhex("0000020002"+
"0000000000"+
"0000000100"+
"0001000001"))
00 00 00 00>00
∨ ∨
00>00 00 00 00
00<00 04 00>00
∧
00 00 00 03 00
∧ ∧
00>00 00>00 00
于是代入数组就得到了如上所示的矩阵关系图。
首先因为4行4列的3要大于3行4列,而3行4列要大于3行5列,所以根据填空范围[1-5]可得,3行5列为1,3行4列为2。
然后当作数独写就行了。
最后得到矩阵如下:
01 04 02 05>03
∨ ∨
05>03 01 04 02
03<05 04 02>01
∧
02 01 05 03 04
∧ ∧
04>02 03>01 05
将已有的替换成0
即可得到输入1425353142350212150442315
。
md5一下即可得到flag。
CISCN{8a04b4597ad08b83211d3adfa1f61431}
场景实操冲刺卷
8#rsa
阅读题目。
from flag import text,flag
import md5
from Crypto.Util.number import long_to_bytes,bytes_to_long,getPrime
assert md5.new(text).hexdigest() == flag[6:-1]
msg1 = text[:xx]
msg2 = text[xx:yy]
msg3 = text[yy:]
msg1 = bytes_to_long(msg1)
msg2 = bytes_to_long(msg2)
msg3 = bytes_to_long(msg3)
p1 = getPrime(512)
q1 = getPrime(512)
N1 = p1*q1
e1 = 3
print pow(msg1,e1,N1)
print (e1,N1)
p2 = getPrime(512)
q2 = getPrime(512)
N2 = p2*q2
e2 = 17
e3 = 65537
print pow(msg2,e2,N2)
print pow(msg2,e3,N2)
print (e2,N2)
print (e3,N2)
p3 = getPrime(512)
q3 = getPrime(512)
N3 = p3*q3
print pow(msg3,e3,N3)
print (e3,N3)
print p3>>200
发现flag内容即为text
的md5,而text
被分成三份。
第一段rsa特征e1=3
,直接套脚本。
import gmpy2
from Crypto.Util.number import long_to_bytes
flag = ""
n = 123814470394550598363280518848914546938137731026777975885846733672494493975703069760053867471836249473290828799962586855892685902902050630018312939010564945676699712246249820341712155938398068732866646422826619477180434858148938235662092482058999079105450136181685141895955574548671667320167741641072330259009L
e = 3
c = 19105765285510667553313898813498220212421177527647187802549913914263968945493144633390670605116251064550364704789358830072133349108808799075021540479815182657667763617178044110939458834654922540704196330451979349353031578518479199454480458137984734402248011464467312753683234543319955893
for k in xrange(200000000):
if gmpy2.iroot(c + n * k,3 )[1]==1:
m = gmpy2.iroot(c + n * k, 3)[0]
# print k, m
flag += long_to_bytes(m)
break
第二段rsa特征存在e2
、e3
,还是套脚本。
n = 111381961169589927896512557754289420474877632607334685306667977794938824018345795836303161492076539375959731633270626091498843936401996648820451019811592594528673182109109991384472979198906744569181673282663323892346854520052840694924830064546269187849702880332522636682366270177489467478933966884097824069977L
e1 = 17
e2 = 65537
c1 = 54995751387258798791895413216172284653407054079765769704170763023830130981480272943338445245689293729308200574217959018462512790523622252479258419498858307898118907076773470253533344877959508766285730509067829684427375759345623701605997067135659404296663877453758701010726561824951602615501078818914410959610
c2 = 91290935267458356541959327381220067466104890455391103989639822855753797805354139741959957951983943146108552762756444475545250343766798220348240377590112854890482375744876016191773471853704014735936608436210153669829454288199838827646402742554134017280213707222338496271289894681312606239512924842845268366950
s = gmpy2.gcdext(e1, e2)
s1 = s[1]
s2 = s[2]
if s1 < 0:
s1 = - s1
c1 = gmpy2.invert(c1, n)
elif s2 < 0:
s2 = - s2
c2 = gmpy2.invert(c2, n)
m = pow(c1, s1, n) * pow(c2, s2, n) % n
flag += long_to_bytes(m)
第三段rsa特征是已知p
高位,所以套个爆破p
、q
的脚本。
from sage.all import *
n = 113432930155033263769270712825121761080813952100666693606866355917116416984149165507231925180593860836255402950358327422447359200689537217528547623691586008952619063846801829802637448874451228957635707553980210685985215887107300416969549087293746310593988908287181025770739538992559714587375763131132963783147L
p4 = 7117286695925472918001071846973900342640107770214858928188419765628151478620236042882657992902
e = 65537
pbits = 512
kbits = pbits - p4.nbits()
print(p4.nbits())
p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
if roots:
p = p4+int(roots[0])
print("p: "+str(p))
print("q: "+str(n//p))
得到:
p: 11437038763581010263116493983733546014403343859218003707512796706928880848035239990740428334091106443982769386517753703890002478698418549777553268906496423
q: 9918033198963879798362329507637256706010562962487329742400933192721549307087332482107381554368538995776396557446746866861247191248938339640876368268930589
代入rsa常规脚本。
p = 11437038763581010263116493983733546014403343859218003707512796706928880848035239990740428334091106443982769386517753703890002478698418549777553268906496423
q = 9918033198963879798362329507637256706010562962487329742400933192721549307087332482107381554368538995776396557446746866861247191248938339640876368268930589
e = 65537
c = 59213696442373765895948702611659756779813897653022080905635545636905434038306468935283962686059037461940227618715695875589055593696352594630107082714757036815875497138523738695066811985036315624927897081153190329636864005133757096991035607918106529151451834369442313673849563635248465014289409374291381429646
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
n = p * q
m = pow(c, d, n)
flag += long_to_bytes(m)
最后算一下md5。
import md5
print('CISCN{'+md5.new(flag).hexdigest()+'}')
CISCN{3943e8843a19149497956901e5d98639}
Comments NOTHING