首页
社区
课程
招聘
2025 全国大学生信息安全竞赛初赛 Reverse WriteUp
发表于: 2025-12-29 09:54 3763

2025 全国大学生信息安全竞赛初赛 Reverse WriteUp

2025-12-29 09:54
3763

页面中有一个「登录」按钮,点击后会调用 WASM 中的authenticate函数。

前端会计算一个 check 值:

因此要 枚举时间戳,找到一个能满足该前缀要求的 check

最终提交格式:

使用 PowerShellrelease.wasm.map 中提取 TypeScript 源码,这些源码原本就是有注释的:

assembly_index.ts 中可以看到核心认证逻辑:

签名函数:

自定义 Base64:

自定义 HMAC-SHA256 关键点:

总结不同点:

HTML 最后一行注释:

所以可以确定:

对每一个候选时间戳:

用自定义 Base64 编码密码 admin

构造 message

使用时间戳字符串做key,按**自定义 HMAC-SHA256** 计算签名。

HMAC 结果再做自定义 Base64,得到 signature

构造最终 JSON:

计算 check = MD5(JSON.stringify(finalJSON))

判断 check 是否以 ccaf33e3512e31f3 开头。

根据文件时间(题目提示「爆肝到周一凌晨」对,题目描述是这样的):

推测时间戳大致落在 2025-12-22 00:00:00 ~ 02:00:00(UTC+8)之间,直接在该区间内暴力搜索即可。

Node.js 脚本如下:

爆破结果:

scripts/flag.gd 中初始化:

仅依赖这里的 key 还不足以得到最终 flag,因为游戏过程中 key 会被修改

scripts/game_manager.gd

也就是说,当获得第一个金币时:

再次回到 flag.gd 的加密逻辑(AES-ECB):

**目标:**找到一个输入,对 修改后的 key (FbnBglFbnBglOoO!)做 AES-ECB 加密,密文为

可以直接在本地用 Python 复现 Godot 的 AES 行为。

字符生成函数 f

可以看成 Fibonacci 序列模 16 后索引一个 16 字节表,再配合特定递推生成的 v

Fibonacci 序列模 16 是周期性的,周期为 24。预先把 F(n) mod 16 的 24 项打表即可。

运行结果:

从生成的日志中可以定位 main.main

在逆向过程中,iupHvc2q4 包含核心逻辑(而且主要部分都在混淆过函数名的函数下,那种一眼就能看到干什么的函数都显得不重要了):

iupHvc2q4.(*H1eV17y).Run 主要逻辑:

函数 HaNDRB_IhET

byte_98F158 内容:

0x99 异或后的 ASCII

这与 pcap中每个应用层负载开头的 8 字节完全一致。

OnJCbKpp 中调用 AES 相关函数,密钥位于只读段:

转为 ASCII

这是一条 32 字节 AES 密钥,用于 AES‑GCM

结合逆向代码和流量分析,可得每条消息格式:

解密后 payloadzstd 压缩的 protobuf

大致流程:

脚本如下:

查找 base32 字符串

将其以 base32 解码即可得到 flag:

import { authenticate } from "./build/release.js";
    // 初始化 WASM 模块
async function initWasm() {
      const wasmStatus = document.getElementById('wasm-status');
      const loginForm = document.getElementById('login-form');
      const loginBtn = document.getElementById('login-btn');
      const loginSpinner = document.getElementById('login-spinner');
      const statusMessage = document.getElementById('status-message');
      const errorMessage = document.getElementById('error-message');
      const passwordInput = document.getElementById('password');
      const togglePasswordBtn = document.getElementById('toggle-password');
       
      try {
 
        // 初始化完成
        wasmStatus.textContent = 'WASM 已加载';
        wasmStatus.classList.add('text-success');
         
        // 切换密码可见性
        togglePasswordBtn.addEventListener('click', function() {
          const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
          passwordInput.setAttribute('type', type);
           
          const icon = this.querySelector('i');
          const text = this.querySelector('span');
           
          if (type === 'text') {
            icon.classList.remove('fa-eye-slash');
            icon.classList.add('fa-eye');
            text.textContent = '隐藏';
          } else {
            icon.classList.remove('fa-eye');
            icon.classList.add('fa-eye-slash');
            text.textContent = '显示';
          }
        });
         
        // 登录表单提交处理
        loginForm.addEventListener('submit', async function(e) {
          e.preventDefault();
           
          // 显示加载状态
          loginBtn.disabled = true;
          loginSpinner.classList.remove('hidden');
          statusMessage.classList.add('hidden');
           
          try {
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;
             
            // 调用 WASM 中的 authenticate 函数
            const authResult = authenticate(username, password);
            const authData = JSON.parse(authResult);
             
            // 模拟发送到服务器
            console.log('发送到服务器的数据:', authData);
             
            // 模拟服务器响应
            simulateServerRequest(authData)
              .then(response => {
                if (response.success) {
                  // 登录成功
                  alert('登录成功!');
                } else {
                  // 登录失败
                  showError(response.message || '登录失败,请重试');
                }
              })
              .catch(error => {
                console.error('登录错误:', error);
                showError('网络错误,请稍后重试');
              })
              .finally(() => {
                // 恢复按钮状态
                loginBtn.disabled = false;
                loginSpinner.classList.add('hidden');
              });
             
          } catch (error) {
            console.error('WASM 处理错误:', error);
            showError('内部错误,请联系管理员');
             
            // 恢复按钮状态
            loginBtn.disabled = false;
            loginSpinner.classList.remove('hidden');
          }
        });
         
        // 显示错误消息
        function showError(message) {
          errorMessage.textContent = message;
          statusMessage.classList.remove('hidden');
           
          // 添加动画效果
          const errorBox = statusMessage.querySelector('div');
          errorBox.classList.add('animate-shake');
          setTimeout(() => {
            errorBox.classList.remove('animate-shake');
          }, 500);
        }
         
        // 模拟服务器请求
        function simulateServerRequest(data) {
          return new Promise(resolve => {
            // 模拟网络延迟
            setTimeout(() => {
              // 实际应用中这里应该是真实的 API 请求
              // 这里仅作演示,使用本地判断
              const check = CryptoJS.MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex);
              if (check.startsWith("ccaf33e3512e31f3")){
                resolve({ success: true });
              }else{
                resolve({ success: false });
              }
            }, 1000);
          });
        }
         
      } catch (error) {
        console.error('WASM 加载失败:', error);
        wasmStatus.textContent = 'WASM 加载失败';
        wasmStatus.classList.add('text-danger');
         
        // 禁用登录按钮
        loginBtn.disabled = true;
        loginBtn.classList.add('bg-neutral-400');
        loginBtn.classList.remove('bg-primary', 'hover:bg-primary/90');
      }
    }
     
    // 页面加载完成后初始化 WASM
 window.addEventListener('load', initWasm);
  
import { authenticate } from "./build/release.js";
    // 初始化 WASM 模块
async function initWasm() {
      const wasmStatus = document.getElementById('wasm-status');
      const loginForm = document.getElementById('login-form');
      const loginBtn = document.getElementById('login-btn');
      const loginSpinner = document.getElementById('login-spinner');
      const statusMessage = document.getElementById('status-message');
      const errorMessage = document.getElementById('error-message');
      const passwordInput = document.getElementById('password');
      const togglePasswordBtn = document.getElementById('toggle-password');
       
      try {
 
        // 初始化完成
        wasmStatus.textContent = 'WASM 已加载';
        wasmStatus.classList.add('text-success');
         
        // 切换密码可见性
        togglePasswordBtn.addEventListener('click', function() {
          const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
          passwordInput.setAttribute('type', type);
           
          const icon = this.querySelector('i');
          const text = this.querySelector('span');
           
          if (type === 'text') {
            icon.classList.remove('fa-eye-slash');
            icon.classList.add('fa-eye');
            text.textContent = '隐藏';
          } else {
            icon.classList.remove('fa-eye');
            icon.classList.add('fa-eye-slash');
            text.textContent = '显示';
          }
        });
         
        // 登录表单提交处理
        loginForm.addEventListener('submit', async function(e) {
          e.preventDefault();
           
          // 显示加载状态
          loginBtn.disabled = true;
          loginSpinner.classList.remove('hidden');
          statusMessage.classList.add('hidden');
           
          try {
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;
             
            // 调用 WASM 中的 authenticate 函数
            const authResult = authenticate(username, password);
            const authData = JSON.parse(authResult);
             
            // 模拟发送到服务器
            console.log('发送到服务器的数据:', authData);
             
            // 模拟服务器响应
            simulateServerRequest(authData)
              .then(response => {
                if (response.success) {
                  // 登录成功
                  alert('登录成功!');
                } else {
                  // 登录失败
                  showError(response.message || '登录失败,请重试');
                }
              })
              .catch(error => {
                console.error('登录错误:', error);
                showError('网络错误,请稍后重试');
              })
              .finally(() => {
                // 恢复按钮状态
                loginBtn.disabled = false;
                loginSpinner.classList.add('hidden');
              });
             
          } catch (error) {
            console.error('WASM 处理错误:', error);
            showError('内部错误,请联系管理员');
             
            // 恢复按钮状态
            loginBtn.disabled = false;
            loginSpinner.classList.remove('hidden');
          }
        });
         
        // 显示错误消息
        function showError(message) {
          errorMessage.textContent = message;
          statusMessage.classList.remove('hidden');
           
          // 添加动画效果
          const errorBox = statusMessage.querySelector('div');
          errorBox.classList.add('animate-shake');
          setTimeout(() => {
            errorBox.classList.remove('animate-shake');
          }, 500);
        }
         
        // 模拟服务器请求
        function simulateServerRequest(data) {
          return new Promise(resolve => {
            // 模拟网络延迟
            setTimeout(() => {
              // 实际应用中这里应该是真实的 API 请求
              // 这里仅作演示,使用本地判断
              const check = CryptoJS.MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex);
              if (check.startsWith("ccaf33e3512e31f3")){
                resolve({ success: true });
              }else{
                resolve({ success: false });
              }
            }, 1000);
          });
        }
         
      } catch (error) {
        console.error('WASM 加载失败:', error);
        wasmStatus.textContent = 'WASM 加载失败';
        wasmStatus.classList.add('text-danger');
         
        // 禁用登录按钮
        loginBtn.disabled = true;
        loginBtn.classList.add('bg-neutral-400');
        loginBtn.classList.remove('bg-primary', 'hover:bg-primary/90');
      }
    }
     
    // 页面加载完成后初始化 WASM
 window.addEventListener('load', initWasm);
  
check = MD5(JSON.stringify(authData));
// 要求:check 必须以 "ccaf33e3512e31f3" 开头
check = MD5(JSON.stringify(authData));
// 要求:check 必须以 "ccaf33e3512e31f3" 开头
flag{正确的check值}
flag{正确的check值}
$map = Get-Content -Raw -Path .\[release.wasm.map](8b2K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4u0W2L8r3g2S2M7$3g2Q4x3X3g2%4j5i4y4E0i4K6u0W2L8h3q4H3i4K6t1&6 | ConvertFrom-Json
$idx = $map.sources.IndexOf('assembly/index.ts')
$map.sourcesContent[$idx] | Out-File -Encoding ascii .\assembly_index.ts
 
$map = Get-Content -Raw -Path .\[release.wasm.map](b61K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4u0W2L8r3g2S2M7$3g2Q4x3X3g2%4j5i4y4E0i4K6u0W2L8h3q4H3i4K6t1&6 | ConvertFrom-Json
$idx = $map.sources.IndexOf('assembly/base64.ts')
$map.sourcesContent[$idx] | Out-File -Encoding ascii .\assembly_base64.ts
$map = Get-Content -Raw -Path .\[release.wasm.map](347K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4u0W2L8r3g2S2M7$3g2Q4x3X3g2%4j5i4y4E0i4K6u0W2L8h3q4H3i4K6t1&6 | ConvertFrom-Json
$idx = $map.sources.IndexOf('assembly/index.ts')
$map.sourcesContent[$idx] | Out-File -Encoding ascii .\assembly_index.ts
 
$map = Get-Content -Raw -Path .\[release.wasm.map](241K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4u0W2L8r3g2S2M7$3g2Q4x3X3g2%4j5i4y4E0i4K6u0W2L8h3q4H3i4K6t1&6 | ConvertFrom-Json
$idx = $map.sources.IndexOf('assembly/base64.ts')
$map.sourcesContent[$idx] | Out-File -Encoding ascii .\assembly_base64.ts
export function authenticate(username: string, password: string): string {
  // 1. Base64编码密码
  const encodedPassword = encode(stringToUint8Array(password));
  //console.log(encodedPassword);
  // 2. 获取当前时间戳(毫秒)
  const timestamp = Date.now().toString();
  //console.log(timestamp);
  // 3. 构建原始JSON消息
  const message = `{"username":"${username}","password":"${encodedPassword}"}`;
  //console.log(message);
  // 4. 使用HMAC-SHA256签名
  const signature = signMessage(message, timestamp);
  //console.log(signature);
  // 5. 构建最终JSON消息
  const finalMessage = `          {"username":"${username}","password":"${encodedPassword}","signature":"${signature}"}`;
 
  return finalMessage;
  //return "ok";
}
export function authenticate(username: string, password: string): string {
  // 1. Base64编码密码
  const encodedPassword = encode(stringToUint8Array(password));
  //console.log(encodedPassword);
  // 2. 获取当前时间戳(毫秒)
  const timestamp = Date.now().toString();
  //console.log(timestamp);
  // 3. 构建原始JSON消息
  const message = `{"username":"${username}","password":"${encodedPassword}"}`;
  //console.log(message);
  // 4. 使用HMAC-SHA256签名
  const signature = signMessage(message, timestamp);
  //console.log(signature);
  // 5. 构建最终JSON消息
  const finalMessage = `          {"username":"${username}","password":"${encodedPassword}","signature":"${signature}"}`;
 
  return finalMessage;
  //return "ok";
}
function signMessage(message: string, secret: string): string {
  const messageBytes = String.UTF8.encode(message);
  const secretBytes = String.UTF8.encode(secret);
  /**
  const messageBytesPtr = changetype<usize>(messageBytes);
  const secretBytesPtr = changetype<usize>(secretBytes);
   
  const hashInput = new ArrayBuffer(messageBytes.byteLength + secretBytes.byteLength);
  const hashInputPtr = changetype<usize>(hashInput);
  memory.copy(hashInputPtr, messageBytesPtr, messageBytes.byteLength);
  memory.copy(hashInputPtr + messageBytes.byteLength, secretBytesPtr,   secretBytes.byteLength);
 
  const signatureBytes = new ArrayBuffer(32);
  const signatureBytesPtr = changetype<usize>(signatureBytes);
  init();
  update(hashInputPtr, hashInput.byteLength);
  final(signatureBytesPtr)
  */
  const signatureBytes = hmacSHA256(secretBytes,messageBytes);
   
  return encode(ArrayBufferToUint8Array(signatureBytes));
}   
function signMessage(message: string, secret: string): string {
  const messageBytes = String.UTF8.encode(message);
  const secretBytes = String.UTF8.encode(secret);
  /**
  const messageBytesPtr = changetype<usize>(messageBytes);
  const secretBytesPtr = changetype<usize>(secretBytes);
   
  const hashInput = new ArrayBuffer(messageBytes.byteLength + secretBytes.byteLength);
  const hashInputPtr = changetype<usize>(hashInput);
  memory.copy(hashInputPtr, messageBytesPtr, messageBytes.byteLength);
  memory.copy(hashInputPtr + messageBytes.byteLength, secretBytesPtr,   secretBytes.byteLength);
 
  const signatureBytes = new ArrayBuffer(32);
  const signatureBytesPtr = changetype<usize>(signatureBytes);
  init();
  update(hashInputPtr, hashInput.byteLength);
  final(signatureBytesPtr)
  */
  const signatureBytes = hmacSHA256(secretBytes,messageBytes);
   
  return encode(ArrayBufferToUint8Array(signatureBytes));
}   
// @ts-ignore: decorator
@lazy
  const PADCHAR = "=";
// @ts-ignore: decorator
@lazy
//  const ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; //这是标准base64映射表
const ALPHA = "NhR4UJ+z5qFGiTCaAIDYwZ0dLl6PEXKgostxuMv8rHBp3n9emjQf1cWb2/VkS7yO";//这是本题中使用的新表
   /**
    * Encode Uint8Array as a base64 string.
    * @param bytes Byte array of type Uint8Array.
    */
   export function encode(bytes: Uint8Array): string {
     let i: i32, b10: u32;
      
     const extrabytes = (bytes.length % 3);
     let imax = bytes.length - extrabytes;
     const len = ((bytes.length / 3) as i32) * 4 + (extrabytes == 0 ? 0 : 4);
     let x = changetype<string>(__new(<usize>(len << 1), idof<string>()));
  
     if (bytes.length == 0) {
       return "";
     }
  
     let ptr = changetype<usize>(x) - 2;
     for (i = 0; i < imax; i += 3) {
       b10 =
         ((bytes[i] as u32) << 16) |
         ((bytes[i + 1] as u32) << 8) |
         (bytes[i + 2] as u32);
       store<u16>(ptr+=2, (ALPHA.charCodeAt(b10 >> 18) as u16));
       store<u16>(ptr+=2, (ALPHA.charCodeAt(((b10 >> 12) & 63)) as u16));
       store<u16>(ptr+=2, (ALPHA.charCodeAt(((b10 >> 6) & 63)) as u16));
       store<u16>(ptr+=2, (ALPHA.charCodeAt((b10 & 63)) as u16));
     }
  
     switch (bytes.length - imax) {
       case 1:
         b10 = (bytes[i] as u32) << 16;
         store<u16>(ptr+=2, ((ALPHA.charCodeAt(b10 >> 18)) as u16));
         store<u16>(ptr+=2, ((ALPHA.charCodeAt((b10 >> 12) & 63)) as u16));
         store<u16>(ptr+=2, ((PADCHAR.charCodeAt(0)) as u16));
         store<u16>(ptr+=2, ((PADCHAR.charCodeAt(0)) as u16));
         break;
       case 2:
         b10 = ((bytes[i] as u32) << 16) | ((bytes[i + 1] as u32) << 8);
         store<u16>(ptr+=2, ((ALPHA.charCodeAt(b10 >> 18)) as u16));
         store<u16>(ptr+=2, ((ALPHA.charCodeAt((b10 >> 12) & 63)) as u16));
         store<u16>(ptr+=2, ((ALPHA.charCodeAt((b10 >> 6) & 63)) as u16));
         store<u16>(ptr+=2, ((PADCHAR.charCodeAt(0)) as u16));
         break;
     }
  
     return x;
   }
// @ts-ignore: decorator
@lazy
  const PADCHAR = "=";
// @ts-ignore: decorator
@lazy
//  const ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; //这是标准base64映射表
const ALPHA = "NhR4UJ+z5qFGiTCaAIDYwZ0dLl6PEXKgostxuMv8rHBp3n9emjQf1cWb2/VkS7yO";//这是本题中使用的新表
   /**
    * Encode Uint8Array as a base64 string.
    * @param bytes Byte array of type Uint8Array.
    */
   export function encode(bytes: Uint8Array): string {
     let i: i32, b10: u32;
      
     const extrabytes = (bytes.length % 3);
     let imax = bytes.length - extrabytes;
     const len = ((bytes.length / 3) as i32) * 4 + (extrabytes == 0 ? 0 : 4);
     let x = changetype<string>(__new(<usize>(len << 1), idof<string>()));
  
     if (bytes.length == 0) {
       return "";
     }
  
     let ptr = changetype<usize>(x) - 2;
     for (i = 0; i < imax; i += 3) {
       b10 =
         ((bytes[i] as u32) << 16) |
         ((bytes[i + 1] as u32) << 8) |
         (bytes[i + 2] as u32);
       store<u16>(ptr+=2, (ALPHA.charCodeAt(b10 >> 18) as u16));
       store<u16>(ptr+=2, (ALPHA.charCodeAt(((b10 >> 12) & 63)) as u16));
       store<u16>(ptr+=2, (ALPHA.charCodeAt(((b10 >> 6) & 63)) as u16));
       store<u16>(ptr+=2, (ALPHA.charCodeAt((b10 & 63)) as u16));
     }
  
     switch (bytes.length - imax) {
       case 1:
         b10 = (bytes[i] as u32) << 16;
         store<u16>(ptr+=2, ((ALPHA.charCodeAt(b10 >> 18)) as u16));
         store<u16>(ptr+=2, ((ALPHA.charCodeAt((b10 >> 12) & 63)) as u16));
         store<u16>(ptr+=2, ((PADCHAR.charCodeAt(0)) as u16));
         store<u16>(ptr+=2, ((PADCHAR.charCodeAt(0)) as u16));
         break;
       case 2:
         b10 = ((bytes[i] as u32) << 16) | ((bytes[i + 1] as u32) << 8);
         store<u16>(ptr+=2, ((ALPHA.charCodeAt(b10 >> 18)) as u16));
         store<u16>(ptr+=2, ((ALPHA.charCodeAt((b10 >> 12) & 63)) as u16));
         store<u16>(ptr+=2, ((ALPHA.charCodeAt((b10 >> 6) & 63)) as u16));
         store<u16>(ptr+=2, ((PADCHAR.charCodeAt(0)) as u16));
         break;
     }
  
     return x;
   }
function hmacSHA256(key: ArrayBuffer, message: ArrayBuffer): ArrayBuffer {
  const blockSize = 64; // SHA256 ????????64 ???
 
  // ??????
  const keyPtr = changetype<usize>(key);
  const paddedKey = new ArrayBuffer(blockSize);
  const paddedKeyPtr = changetype<usize>(paddedKey);
  if (key.byteLength > blockSize) {
    // ????????????????????????????????n      init();
      update(keyPtr, key.byteLength);
      final(paddedKeyPtr);
  }else{
    // ????????????
      memory.copy(paddedKeyPtr, keyPtr, key.byteLength);
      fill(paddedKeyPtr + key.byteLength, 0, blockSize - key.byteLength)
  }
  //console.log(ArrayBufferToUint8Array(paddedKey).toString());
 
  // ??? ipad ??opad
  const ipad = new ArrayBuffer(blockSize);
  const opad = new ArrayBuffer(blockSize);
  const ipadPtr = changetype<usize>(ipad);
  const opadPtr = changetype<usize>(opad);
  for (let i = 0; i < blockSize; i++) {
      store<u8>(ipadPtr + i , load<u8>(paddedKeyPtr + i) ^ 0x76);
      store<u8>(opadPtr + i , load<u8>(paddedKeyPtr + i) ^ 0x3C);
  }
  //console.log(ArrayBufferToUint8Array(ipad).toString());
  //console.log(ArrayBufferToUint8Array(opad).toString());
 
  // ??? innerHash
  const innerInput = new ArrayBuffer(ipad.byteLength + message.byteLength);
  const innerInputPtr = changetype<usize>(innerInput);
  const messagePtr = changetype<usize>(message)
  memory.copy(innerInputPtr, ipadPtr, ipad.byteLength);
  memory.copy(innerInputPtr + ipad.byteLength, messagePtr, message.byteLength);
  //console.log(ArrayBufferToUint8Array(innerInput).toString());
 
  init();
  update(innerInputPtr,innerInput.byteLength);
  //update(ipadPtr,ipad.byteLength);
  //update(messagePtr,message.byteLength);
  const innerHash = new ArrayBuffer(32);
  const innerHashPtr = changetype<usize>(innerHash);
  final(innerHashPtr);
  //console.log(ArrayBufferToUint8Array(innerHash).toString());
 
  // ??? outerHash
  const outerInput = new ArrayBuffer(opad.byteLength + innerHash.byteLength);
  const outerInputPtr = changetype<usize>(outerInput);
  memory.copy(outerInputPtr, innerHashPtr, innerHash.byteLength);
  memory.copy(outerInputPtr + innerHash.byteLength, opadPtr, opad.byteLength);
  //console.log(ArrayBufferToUint8Array(outerInput).toString());
 
  init();
  update(outerInputPtr,outerInput.byteLength);
  //update(opadPtr,opad.byteLength);
  //update(innerHashPtr,innerHash.byteLength);
  const outerHash = new ArrayBuffer(32);
  const outerHashPtr = changetype<usize>(outerHash);
  final(outerHashPtr);
  //console.log(ArrayBufferToUint8Array(outerHash).toString());
 
  return outerHash;
}
function hmacSHA256(key: ArrayBuffer, message: ArrayBuffer): ArrayBuffer {
  const blockSize = 64; // SHA256 ????????64 ???
 
  // ??????
  const keyPtr = changetype<usize>(key);
  const paddedKey = new ArrayBuffer(blockSize);
  const paddedKeyPtr = changetype<usize>(paddedKey);
  if (key.byteLength > blockSize) {
    // ????????????????????????????????n      init();
      update(keyPtr, key.byteLength);
      final(paddedKeyPtr);
  }else{
    // ????????????
      memory.copy(paddedKeyPtr, keyPtr, key.byteLength);
      fill(paddedKeyPtr + key.byteLength, 0, blockSize - key.byteLength)
  }
  //console.log(ArrayBufferToUint8Array(paddedKey).toString());
 
  // ??? ipad ??opad
  const ipad = new ArrayBuffer(blockSize);
  const opad = new ArrayBuffer(blockSize);
  const ipadPtr = changetype<usize>(ipad);
  const opadPtr = changetype<usize>(opad);
  for (let i = 0; i < blockSize; i++) {
      store<u8>(ipadPtr + i , load<u8>(paddedKeyPtr + i) ^ 0x76);
      store<u8>(opadPtr + i , load<u8>(paddedKeyPtr + i) ^ 0x3C);
  }
  //console.log(ArrayBufferToUint8Array(ipad).toString());
  //console.log(ArrayBufferToUint8Array(opad).toString());
 
  // ??? innerHash
  const innerInput = new ArrayBuffer(ipad.byteLength + message.byteLength);
  const innerInputPtr = changetype<usize>(innerInput);
  const messagePtr = changetype<usize>(message)
  memory.copy(innerInputPtr, ipadPtr, ipad.byteLength);
  memory.copy(innerInputPtr + ipad.byteLength, messagePtr, message.byteLength);
  //console.log(ArrayBufferToUint8Array(innerInput).toString());
 
  init();
  update(innerInputPtr,innerInput.byteLength);
  //update(ipadPtr,ipad.byteLength);
  //update(messagePtr,message.byteLength);
  const innerHash = new ArrayBuffer(32);
  const innerHashPtr = changetype<usize>(innerHash);
  final(innerHashPtr);
  //console.log(ArrayBufferToUint8Array(innerHash).toString());
 
  // ??? outerHash
  const outerInput = new ArrayBuffer(opad.byteLength + innerHash.byteLength);
  const outerInputPtr = changetype<usize>(outerInput);
  memory.copy(outerInputPtr, innerHashPtr, innerHash.byteLength);
  memory.copy(outerInputPtr + innerHash.byteLength, opadPtr, opad.byteLength);
  //console.log(ArrayBufferToUint8Array(outerInput).toString());
 
  init();
  update(outerInputPtr,outerInput.byteLength);
  //update(opadPtr,opad.byteLength);
  //update(innerHashPtr,innerHash.byteLength);
  const outerHash = new ArrayBuffer(32);
  const outerHashPtr = changetype<usize>(outerHash);
  final(outerHashPtr);
  //console.log(ArrayBufferToUint8Array(outerHash).toString());
 
  return outerHash;
}
<!-- 测试账号 admin 测试密码 admin -->
<!-- 测试账号 admin 测试密码 admin -->
username = "admin"
password = "admin"
username = "admin"
password = "admin"
const crypto = require('crypto');
 
// 自定义 Base64 字母表
const ALPHA = "NhR4UJ+z5qFGiTCaAIDYwZ0dLl6PEXKgostxuMv8rHBp3n9emjQf1cWb2/VkS7yO";
 
function base64Custom(buf) {
  let out = '';
  let i = 0;
  for (; i + 2 < buf.length; i += 3) {
    const b10 = (buf[i] << 16) | (buf[i + 1] << 8) | buf[i + 2];
    out += ALPHA[(b10 >> 18) & 63];
    out += ALPHA[(b10 >> 12) & 63];
    out += ALPHA[(b10 >> 6) & 63];
    out += ALPHA[b10 & 63];
  }
  const rem = buf.length - i;
  if (rem === 1) {
    const b10 = buf[i] << 16;
    out += ALPHA[(b10 >> 18) & 63];
    out += ALPHA[(b10 >> 12) & 63];
    out += '=';
    out += '=';
  } else if (rem === 2) {
    const b10 = (buf[i] << 16) | (buf[i + 1] << 8);
    out += ALPHA[(b10 >> 18) & 63];
    out += ALPHA[(b10 >> 12) & 63];
    out += ALPHA[(b10 >> 6) & 63];
    out += '=';
  }
  return out;
}
 
function sha256(buf) {
  return crypto.createHash('sha256').update(buf).digest();
}
 
// 自定义 HMAC (ipad/opad 常量不同)
function hmacSHA256(key, message) {
  const blockSize = 64;
  let paddedKey = Buffer.alloc(blockSize, 0);
  if (key.length > blockSize) {
    sha256(key).copy(paddedKey, 0);
  } else {
    key.copy(paddedKey, 0);
  }
  const ipad = Buffer.alloc(blockSize);
  const opad = Buffer.alloc(blockSize);
  for (let i = 0; i < blockSize; i++) {
    const b = paddedKey[i];
    ipad[i] = b ^ 0x76;
    opad[i] = b ^ 0x3C;
  }
  const innerHash = sha256(Buffer.concat([ipad, message]));
  const outerHash = sha256(Buffer.concat([innerHash, opad]));
  return outerHash;
}
 
const username = 'admin';
const password = 'admin';
const encodedPassword = base64Custom(Buffer.from(password, 'utf8'));
const message = `{"username":"${username}","password":"${encodedPassword}"}`;
const messageBytes = Buffer.from(message, 'utf8');
 
const prefix = 'ccaf33e3512e31f3';
const start = Date.parse('2025-12-22T00:00:00+08:00');
const end = Date.parse('2025-12-22T02:00:00+08:00');
 
for (let ts = start; ts < end; ts++) {
  const key = Buffer.from(String(ts), 'utf8');
  const sigBytes = hmacSHA256(key, messageBytes);
  const signature = base64Custom(sigBytes);
  const finalMessage = `{"username":"${username}","password":"${encodedPassword}","signature":"${signature}"}`;
  const check = crypto.createHash('md5').update(finalMessage).digest('hex');
  if (check.startsWith(prefix)) {
    console.log('FOUND', ts, check);
    break;
  }
}
const crypto = require('crypto');
 
// 自定义 Base64 字母表
const ALPHA = "NhR4UJ+z5qFGiTCaAIDYwZ0dLl6PEXKgostxuMv8rHBp3n9emjQf1cWb2/VkS7yO";
 
function base64Custom(buf) {
  let out = '';
  let i = 0;
  for (; i + 2 < buf.length; i += 3) {
    const b10 = (buf[i] << 16) | (buf[i + 1] << 8) | buf[i + 2];
    out += ALPHA[(b10 >> 18) & 63];
    out += ALPHA[(b10 >> 12) & 63];
    out += ALPHA[(b10 >> 6) & 63];
    out += ALPHA[b10 & 63];
  }
  const rem = buf.length - i;
  if (rem === 1) {
    const b10 = buf[i] << 16;
    out += ALPHA[(b10 >> 18) & 63];
    out += ALPHA[(b10 >> 12) & 63];
    out += '=';
    out += '=';
  } else if (rem === 2) {
    const b10 = (buf[i] << 16) | (buf[i + 1] << 8);
    out += ALPHA[(b10 >> 18) & 63];
    out += ALPHA[(b10 >> 12) & 63];
    out += ALPHA[(b10 >> 6) & 63];
    out += '=';
  }
  return out;
}
 
function sha256(buf) {
  return crypto.createHash('sha256').update(buf).digest();
}
 
// 自定义 HMAC (ipad/opad 常量不同)
function hmacSHA256(key, message) {
  const blockSize = 64;
  let paddedKey = Buffer.alloc(blockSize, 0);
  if (key.length > blockSize) {
    sha256(key).copy(paddedKey, 0);
  } else {
    key.copy(paddedKey, 0);
  }
  const ipad = Buffer.alloc(blockSize);
  const opad = Buffer.alloc(blockSize);
  for (let i = 0; i < blockSize; i++) {
    const b = paddedKey[i];
    ipad[i] = b ^ 0x76;
    opad[i] = b ^ 0x3C;
  }
  const innerHash = sha256(Buffer.concat([ipad, message]));
  const outerHash = sha256(Buffer.concat([innerHash, opad]));
  return outerHash;
}
 
const username = 'admin';
const password = 'admin';
const encodedPassword = base64Custom(Buffer.from(password, 'utf8'));
const message = `{"username":"${username}","password":"${encodedPassword}"}`;
const messageBytes = Buffer.from(message, 'utf8');
 
const prefix = 'ccaf33e3512e31f3';
const start = Date.parse('2025-12-22T00:00:00+08:00');
const end = Date.parse('2025-12-22T02:00:00+08:00');
 
for (let ts = start; ts < end; ts++) {
  const key = Buffer.from(String(ts), 'utf8');
  const sigBytes = hmacSHA256(key, messageBytes);
  const signature = base64Custom(sigBytes);
  const finalMessage = `{"username":"${username}","password":"${encodedPassword}","signature":"${signature}"}`;
  const check = crypto.createHash('md5').update(finalMessage).digest('hex');
  if (check.startsWith(prefix)) {
    console.log('FOUND', ts, check);
    break;
  }
}
时间戳:1766334550699
check:ccaf33e3512e31f36228f0b97ccbc8f1
时间戳:1766334550699
check:ccaf33e3512e31f36228f0b97ccbc8f1
flag{ccaf33e3512e31f36228f0b97ccbc8f1}
flag{ccaf33e3512e31f36228f0b97ccbc8f1}
<!-- Brute-force timestamp helper (CTF) -->
  <div class="mt-6 text-center text-sm text-neutral-500">
    <button id="bruteforce-btn" class="mt-2 px-4 py-2 rounded-lg bg-secondary text-white">?????</button>
    <div id="bruteforce-status" class="mt-2 text-neutral-400">???</div>
  </div>
<script type="module">
    import { authenticate } from "./build/release.js";
 
    const bfBtn = document.getElementById('bruteforce-btn');
    const bfStatus = document.getElementById('bruteforce-status');
 
    bfBtn.addEventListener('click', async () => {
      const prefix = "ccaf33e3512e31f3";
      const username = "admin";
      const password = "admin";
      const start = Date.parse("2025-12-22T00:00:00+08:00");
      const end = Date.parse("2025-12-22T02:00:00+08:00");
 
      const oldNow = Date.now;
      bfStatus.textContent = "???...";
      bfBtn.disabled = true;
 
      for (let ts = start; ts < end; ts++) {
        Date.now = () => ts;
        const authResult = authenticate(username, password);
        const data = JSON.parse(authResult);
        const check = CryptoJS.MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex);
        if (check.startsWith(prefix)) {
          bfStatus.textContent = `FOUND: ${ts} | ${check}`;
          Date.now = oldNow;
          bfBtn.disabled = false;
          return;
        }
      }
 
      Date.now = oldNow;
      bfStatus.textContent = "???";
      bfBtn.disabled = false;
    });
</script>
<!-- Brute-force timestamp helper (CTF) -->
  <div class="mt-6 text-center text-sm text-neutral-500">
    <button id="bruteforce-btn" class="mt-2 px-4 py-2 rounded-lg bg-secondary text-white">?????</button>
    <div id="bruteforce-status" class="mt-2 text-neutral-400">???</div>
  </div>
<script type="module">
    import { authenticate } from "./build/release.js";
 
    const bfBtn = document.getElementById('bruteforce-btn');
    const bfStatus = document.getElementById('bruteforce-status');
 
    bfBtn.addEventListener('click', async () => {
      const prefix = "ccaf33e3512e31f3";
      const username = "admin";
      const password = "admin";
      const start = Date.parse("2025-12-22T00:00:00+08:00");
      const end = Date.parse("2025-12-22T02:00:00+08:00");
 
      const oldNow = Date.now;
      bfStatus.textContent = "???...";
      bfBtn.disabled = true;
 
      for (let ts = start; ts < end; ts++) {
        Date.now = () => ts;
        const authResult = authenticate(username, password);
        const data = JSON.parse(authResult);
        const check = CryptoJS.MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex);
        if (check.startsWith(prefix)) {
          bfStatus.textContent = `FOUND: ${ts} | ${check}`;
          Date.now = oldNow;
          bfBtn.disabled = false;
          return;
        }
      }
 
      Date.now = oldNow;
      bfStatus.textContent = "???";
      bfBtn.disabled = false;
    });
</script>
利用项目直接解包: [a3bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6s2c8q4u0q4g2r3!0G2L8s2y4Q4x3V1k6Y4k6s2y4V1k6h3y4G2L8i4m8Q4x3V1k6J5k6h3I4W2j5i4y4W2M7#2)9J5c8Y4c8S2k6#2)9J5c8Y4j5J5i4K6u0W2y4q4)9J5k6e0m8Q4y4f1c8Q4x3U0S2Z5N6s2c8H3M7#2)9K6b7g2)9J5c8W2)9J5c8X3N6A6N6r3S2#2j5W2)9J5k6h3y4G2L8g2)9J5c8V1N6p5f1V1g2f1L8$3!0D9M7#2)9J5c8X3N6V1M7$3c8W2j5$3!0E0M7q4)9J5c8Y4u0W2L8r3g2S2M7$3g2K6i4K6u0r3N6r3q4Y4i4K6u0r3N6U0u0Q4x3X3f1@1i4K6u0W2x3q4)9J5z5b7`.`. //Godot引擎可以解包源码
利用项目直接解包: [c60K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6s2c8q4u0q4g2r3!0G2L8s2y4Q4x3V1k6Y4k6s2y4V1k6h3y4G2L8i4m8Q4x3V1k6J5k6h3I4W2j5i4y4W2M7#2)9J5c8Y4c8S2k6#2)9J5c8Y4j5J5i4K6u0W2y4q4)9J5k6e0m8Q4y4f1c8Q4x3U0S2Z5N6s2c8H3M7#2)9K6b7g2)9J5c8W2)9J5c8X3N6A6N6r3S2#2j5W2)9J5k6h3y4G2L8g2)9J5c8V1N6p5f1V1g2f1L8$3!0D9M7#2)9J5c8X3N6V1M7$3c8W2j5$3!0E0M7q4)9J5c8Y4u0W2L8r3g2S2M7$3g2K6i4K6u0r3N6r3q4Y4i4K6u0r3N6U0u0Q4x3X3f1@1i4K6u0W2x3q4)9J5z5b7`.`. //Godot引擎可以解包源码
extends CenterContainer
 
@onready var flagTextEdit: Node = $PanelContainer / VBoxContainer / FlagTextEdit
@onready var label2: Node = $PanelContainer / VBoxContainer / Label2
 
static var key = "FanAglFanAglOoO!"
var data = ""
 
func _on_ready() -> void :
    Flag.hide()
 
func get_key() -> String:
    return key
 
func submit() -> void :
    data = flagTextEdit.text
 
    var aes = AESContext.new()
    aes.start(AESContext.MODE_ECB_ENCRYPT, key.to_utf8_buffer())
    var encrypted = aes.update(data.to_utf8_buffer())
    aes.finish()
 
    if encrypted.hex_encode() == "d458af702a680ae4d089ce32fc39945d":
        label2.show()
    else:
        label2.hide()
 
func back() -> void :
    get_tree().change_scene_to_file("res://scenes/menu.tscn")
extends CenterContainer
 
@onready var flagTextEdit: Node = $PanelContainer / VBoxContainer / FlagTextEdit
@onready var label2: Node = $PanelContainer / VBoxContainer / Label2
 
static var key = "FanAglFanAglOoO!"
var data = ""
 
func _on_ready() -> void :
    Flag.hide()
 
func get_key() -> String:
    return key
 
func submit() -> void :
    data = flagTextEdit.text
 
    var aes = AESContext.new()
    aes.start(AESContext.MODE_ECB_ENCRYPT, key.to_utf8_buffer())
    var encrypted = aes.update(data.to_utf8_buffer())
    aes.finish()
 
    if encrypted.hex_encode() == "d458af702a680ae4d089ce32fc39945d":
        label2.show()
    else:
        label2.hide()
 
func back() -> void :
    get_tree().change_scene_to_file("res://scenes/menu.tscn")
@onready var fan = $"../Fan"
 
var score = 0
 
func add_point():
    score += 1
    if score == 1:
        Flag.key = Flag.key.replace("A", "B")
        fan.visible = true
@onready var fan = $"../Fan"
 
var score = 0
 
func add_point():
    score += 1
    if score == 1:
        Flag.key = Flag.key.replace("A", "B")
        fan.visible = true
FanAglFanAglOoO! -> F an A gl F an A gl OoO!
                   B    B      (A→B)
最终 key = FbnBglFbnBglOoO!
FanAglFanAglOoO! -> F an A gl F an A gl OoO!
                   B    B      (A→B)
最终 key = FbnBglFbnBglOoO!
func submit() -> void :
    data = flagTextEdit.text
 
    var aes = AESContext.new()
    aes.start(AESContext.MODE_ECB_ENCRYPT, key.to_utf8_buffer())
    var encrypted = aes.update(data.to_utf8_buffer())
    aes.finish()
 
    if encrypted.hex_encode() == "d458af702a680ae4d089ce32fc39945d":
        label2.show()
    else:
        label2.hide()
 
func back() -> void :
    get_tree().change_scene_to_file("res://scenes/menu.tscn")
func submit() -> void :
    data = flagTextEdit.text
 
    var aes = AESContext.new()
    aes.start(AESContext.MODE_ECB_ENCRYPT, key.to_utf8_buffer())
    var encrypted = aes.update(data.to_utf8_buffer())
    aes.finish()
 
    if encrypted.hex_encode() == "d458af702a680ae4d089ce32fc39945d":
        label2.show()
    else:
        label2.hide()
 
func back() -> void :
    get_tree().change_scene_to_file("res://scenes/menu.tscn")
d458af702a680ae4d089ce32fc39945d
d458af702a680ae4d089ce32fc39945d
import sys
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
 
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
 
# 初始key
initial_key = "FanAglFanAglOoO!"
 
# 得分为1后,key中的'A'被替换为'B'
final_key = initial_key.replace("A", "B")
print(f"初始 key: {initial_key}")
print(f"最终 key: {final_key}")
 
# 目标加密结果
target_encrypted_hex = "d458af702a680ae4d089ce32fc39945d"
target_encrypted = bytes.fromhex(target_encrypted_hex)
 
print(f"\n目标密文 (hex): {target_encrypted_hex}")
print(f"目标密文 (bytes): {target_encrypted}")
print(f"密文长度: {len(target_encrypted)} bytes")
 
# 使用AES-ECB模式解密
try:
    # 创建AES cipher对象 (ECB模式)
    cipher = AES.new(final_key.encode('utf-8'), AES.MODE_ECB)
 
    # 解密
    decrypted = cipher.decrypt(target_encrypted)
 
    # 尝试去除padding
    try:
        flag = unpad(decrypted, AES.block_size).decode('utf-8')
        print(f"\n[OK] 成功解密 (带padding): {flag}")
    except:
        # 如果去除padding失败,直接解码
        flag = decrypted.decode('utf-8').rstrip('\x00')
        print(f"\n[OK] 成功解密 (无padding): {flag}")
 
    # 验证 - 重新加密看是否匹配
    cipher_verify = AES.new(final_key.encode('utf-8'), AES.MODE_ECB)
    # Godot的AES会自动padding到16字节的倍数
    flag_padded = flag.encode('utf-8')
    if len(flag_padded) % 16 != 0:
        flag_padded = pad(flag_padded, AES.block_size)
 
    encrypted_verify = cipher_verify.encrypt(flag_padded)
 
    if encrypted_verify.hex() == target_encrypted_hex:
        print(f"[OK] 验证成功!加密结果匹配")
    else:
        print(f"[FAIL] 验证失败")
        print(f"  预期: {target_encrypted_hex}")
        print(f"  实际: {encrypted_verify.hex()}")
 
    print(f"\n{'='*50}")
    print(f"FLAG: {flag}")
    print(f"{'='*50}")
 
except Exception as e:
    print(f"\n[FAIL] 解密失败: {e}")
    import traceback
    traceback.print_exc()
import sys
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
 
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
 
# 初始key
initial_key = "FanAglFanAglOoO!"
 
# 得分为1后,key中的'A'被替换为'B'
final_key = initial_key.replace("A", "B")
print(f"初始 key: {initial_key}")
print(f"最终 key: {final_key}")

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 2025-12-29 17:44 被江树编辑 ,原因: 增加wp
上传的附件:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回