mapleCTF2023

# PREFACE: maple 前四个题做的挺快的还,看题的队甚至小冲分了一下(什)
# 最后那个安卓确实出的有点问题,怎么都搜不出来,总的来说算是还好的比赛吧
# 10.2: 算是出了,不过确实看着别的师傅有点算法高手的

# baby_rev

就是一些 check,比较容易,后面注意分两段输入,否则可能有问题

from z3 import *
# 创建 Solver 实例
solver = Solver()
a = Int('a')
b = Int('b')
# solver.add(a * 4 + b * 5 == 127)
# solver.add(a > 0)
# solver.add(b > 0)
# solver.add(a + b < 30)
# solutions = []
# while solver.check() == sat:
#     model = solver.model()
#     a_val = model[a].as_long()
#     b_val = model[b].as_long()
#     solutions.append((a_val, b_val))
#     solver.add(Or(a != a_val, b != b_val))
# for sol in solutions:
#     print(sol)
# ---
solver.add((a * 6 + b * 13) % 3 == 1)
solver.add((a * 6 + b * 13) % 4 == 3)
solver.add((a * 6 + b * 13) % 5 == 1)
solver.add(a + b <= 6)
if solver.check() == sat:
    print(solver.model())
# ---

exp.py

for _ in range(18):
    print(9,end='')
for _ in range(11):
    print(5,end='')
for _ in range(3):
    print(4,end='')
print(2,end='')
print()
print(80673,end='')
# 999999999999999999555555555554442
# 80673

# JavieScript

源程序看起来就有各种问题…

不过按照字符数量直接爆破即可

import hashlib
import string
def hash_string(s):
    return hashlib.sha256(s.encode()).hexdigest()
possible_values_for_one = '1234567890abcdef'
possible_values_for_two = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?/\\\'"'
# possible_values_for_two = string.printable[:-5]  # -5 to exclude whitespace and control characters
base_fleg = "maple{"
for i in possible_values_for_two:
    for val_one in possible_values_for_two:
        for char_two in possible_values_for_two:
            temp_fleg = base_fleg + val_one  + 'a' + i + 'a' + char_two * 4 + 'as' + "_are_a_mId_FruiT}"
            # print(temp_fleg)
            if hash_string(temp_fleg) == "bfe06d1e92942a0eca51881a879a0a9aef3fe75acaece04877eb0a26ceb8710d":
                print("Found fleg:", temp_fleg)
                break
                
# maple{baNannnnas_are_a_mId_FruiT}

# tarpit

没见过的虚拟机,可以调着看他大概的行为(这里的注释不一定完全准确)

image-20231001202626013

其本身的 opcode 非常有规律,每五个数字会反复出现,而且有明显的指令 0 4 8,简单调试猜测可以发现它会统计该数字的出现次数,直接转化成 ascii,脚本提取即可(这样提取会把中间的一些指令段识别进来,加了个 count >= 40 的判断就刚好可以筛掉)

exp.py

opcode = [...]
count = 0
h = 0
d = 0x71
flag = ''
for i in range(1, len(opcode), 5):
    h = opcode[i]
    if (d == h):
        count += 1
    elif (d != h):
        if (count >= 40):
            flag += chr(count + 1)
        d = h
        count = 0
print(flag)
# maple{m1n5ky_m4ch1n35_4r3_c00l}

# most_harmless

挺奇妙的,也算虚拟机吧?但是跑不起来,远程也跑不动,应该确实效率不好说…

app.py 好理解,就是拿 flag 去拼接成一个语法,放在 out.py 里面跑类型,但是这玩意初见以为是 lambda 算子那种图灵完备可以做很多事情的东西,这里只是做了一个映射,距离说明就是这里实际上就是串联了一串字符而已

image-20231001204403663

然后有一个指示,从 29 开始(一开始没看见,搜了一下它的头尾,发现确实是 29,然后映射出来就行楽)

exp.py

data = {'01':'12','01':'12','12':'08','12':'08','02':'30','02':'30','30':'06','30':'06','03':'05','03':'05','04':'16','04':'16','05':'45','05':'45','06':'46','06':'46','07':'24','07':'24','08':'61','08':'61','09':'58','09':'58','10':'68','10':'68','11':'19','11':'19','13':'40','13':'40','14':'38','14':'38','15':'65','15':'65','16':'67','16':'67','17':'11','17':'11','18':'02','18':'02','19':'39','19':'39','20':'27','20':'27','21':'43','21':'43','22':'51','22':'51','23':'63','23':'63','24':'59','24':'59','25':'62','25':'62','26':'53','26':'53','27':'10','27':'10','28':'69','28':'69','29':'31','29':'31','31':'57','31':'57','32':'25','32':'25','33':'34','33':'34','34':'66','34':'66','35':'28','35':'28','36':'09','36':'09','37':'23','37':'23','38':'03','38':'03','39':'70','39':'70','40':'32','40':'32','41':'55','41':'55','42':'52','42':'52','43':'60','43':'60','44':'01','44':'01','45':'64','45':'64','46':'71','46':'71','47':'20','47':'20','48':'17','48':'17','49':'56','49':'56','50':'33','50':'33','51':'15','51':'15','52':'07','52':'07','53':'04','53':'04','54':'22','54':'22','55':'42','55':'42','56':'36','56':'36','57':'49','57':'49','58':'41','58':'41','59':'21','59':'21','60':'44','60':'44','61':'13','61':'13','62':'37','62':'37','63':'14','63':'14','64':'54','64':'54','65':'50','66':'26','66':'26','65':'50','67':'47','67':'47','68':'35','68':'35','69':'48','69':'48','70':'18','70':'18'}
a = '29'
b = ''
count = 0
list = []
while(b != '71'):
    # print(a,end=' -> ')
    list.append(a)
    b = data[a]
    a = b
    count += 1
data_chr = {'01' : 'a','01' : 'a','12' : 'f','12' : 'f','02' : 'a','02' : 'a','30' : 'n','30' : 'n','03' : 'a','03' : 'a','04' : 'c','04' : 'c','05' : 'd','05' : 'd','06' : 'd','06' : 'd','07' : 'e','07' : 'e','08' : 'e','08' : 'e','09' : 'e','09' : 'e','10' : 'e','10' : 'e','11' : 'e','11' : 'e','13' : 'f','13' : 'f','14' : 'g','14' : 'g','15' : 'h','15' : 'h','16' : 'h','16' : 'h','17' : 'h','17' : 'h','18' : 'h','18' : 'h','19' : 'i','19' : 'i','20' : 'i','20' : 'i','21' : 'i','21' : 'i','22' : 'i','22' : 'i','23' : 'l','23' : 'l','24' : 'm','24' : 'm','25' : 'm','25' : 'm','26' : 'm','26' : 'm','27' : 'm','27' : 'm','28' : 'n','28' : 'n','29' : 'n','29' : 'n','31' : 'o','31' : 'o','32' : 'o','32' : 'o','33' : 'o','33' : 'o','34' : 'o','34' : 'o','35' : 'o','35' : 'o','36' : 'p','36' : 'p','37' : 'p','37' : 'p','38' : 'r','38' : 'r','39' : 'r','39' : 'r','40' : 'r','40' : 'r','41' : 's','41' : 's','42' : 's','42' : 's','43' : 's','43' : 's','44' : 's','44' : 's','45' : 's','45' : 's','46' : 's','46' : 's','47' : 't','47' : 't','48' : 't','48' : 't','49' : 't','49' : 't','50' : 't','50' : 't','51' : 't','51' : 't','52' : 't','52' : 't','53' : 'u','53' : 'u','54' : 'w','54' : 'w','55' : 'y','55' : 'y','56' : 'y','56' : 'y','57' : '_','57' : '_','58' : '_','58' : '_','59' : '_','59' : '_','60' : '_','60' : '_','61' : '_','61' : '_','62' : '_','62' : '_','63' : '_','63' : '_','64' : '_','64' : '_','65' : '_','66' : '_','66' : '_','65' : '_','67' : '_','67' : '_','68' : '_','68' : '_','69' : '_','69' : '_','70' : '_','70' : '_'}
print('maple{',end='')
for i in list:
    print(data_chr[i],end='')
print('}')
# maple{no_type_system_is_safe_from_pl_grads_with_too_much_time_on_their_hands}

# Artificial

jadx 逆不了,然后学长那里知道 GDA 这东西(确实好用)

逻辑还算简单,给了张表 [(x,y), value],求出所有 value 和为 0x11dbe28 的 x,y,将 x,y 从小到大连起来,然后整个做 sha256,然后取前 16 位做 aes 的 key 解密获得 flag

但是数据规模是 3000 * 1e7, 目前说是题目有点问题?

# 10.1 21:09 增加了长度判断 999

image-20231001212212857

感觉… 不好说…

# 10.2:结论:这里是个最小生成树,what?
# 先上脚本,和前面分析的一样,出来的东西排序 + sha256 + 取前 16 位解密 AES 即可
# 但是为啥是最小生成树,还得研究一下…
n
with open("map.txt", "r") as f:
    exec(f.read())
mp = list(map(lambda x : list(map(int, x.split(','))), mp))
mp.sort(key = lambda x : x[2])
fa = []
for i in range(1001):
    fa.append(i)
def find(x):
    if fa[x] == x:
        return x
    ret = find(fa[x])
    fa[x] = ret
    return ret
ans = 0
cnt = 0
_a = []
for a, b, res in mp:
    ffa = find(a)
    ffb = find(b)
    if (ffa == ffb):
        continue
    fa[ffa] = ffb
    ans += res
    cnt += 1
    _a.append(str(a) + ',' + str(b) + ',' + str(res))
with open("ans.txt", "w") as f:
    f.write(str(_a))