场景实操开场卷

2#glass

反编译apk发现调用了so

file

找到so中目标函数。

file

根据代码意思不难得出明文flag进行三次函数调用后得到密文,密文符合即flag正确。

file

file

file

根据代码倒着写出解密脚本即可。

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)))

file

得到flag。

CISCN{6654d84617f627c88846c172e0f4d46c}

8#tiny traffic

过滤数据包,找到目标数据GET /testGET /secret,并保存成文件。

file

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编码。

file

第一题成功解码出短信内容。

file

依次得到前四条短信为:

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";
});

file

保存为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的一部分。

file

然后根据之前的提示进行拼接,得到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)

file

得到四个可能的key值1771697886

file

因为根据上面的日志发现78177是二选一的,86169也一样,所以可以假设法带入所有8种情况,求出每轮的ch

file

然后根据每个ch等价于每个x(2字节16位)的每两位,可以写出逆算xseqs的脚本。

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)

file

又根据题目脚本里的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都近似相等时,说明key1key2代入正确。

file

所以得到r = 1.2key1 = 169key2 = 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)

file

得到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

file

ida打开可发现如下主函数。

file

大概就是判断输入长度==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特征存在e2e3,还是套脚本。

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高位,所以套个爆破pq的脚本。

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}