Vulnerability Detail Report
Vulnerability Overview
- ZDID: ZD-2026-00801
- Vendor: 中部高中電資社團聯合會議
- Title: 中部高中電資社團聯合會議 gift issuing endpoint allows GET-triggered privileged point/ticket balance changes
- Introduction: Public source shows a privileged gift issuing route is exposed as GET, lacks handler-level CSRF protection, and accepts unbounded positive/negative count values that change point/ticket balances.
- 感謝函
- 風雲榜
處理狀態
目前狀態
-
新提交
-
已審核
-
已通報
-
已修補
-
未複測
-
公開
處理歷程
- 2026/06/02 22:28:06 : 新提交 (由 老狼 更新此狀態)
- 2026/06/10 12:45:56 : 審核中 (由 HITCON ZeroDay 服務團隊 更新此狀態)
- 2026/07/21 09:25:25 : 修補中 (由 組織帳號 更新此狀態)
- 2026/07/25 16:12:19 : 複測申請中 (由 組織帳號 更新此狀態)
- 2026/08/12 03:00:04 : 公開 (由 HITCON ZeroDay 平台自動更新)
詳細資料
- ZDID:ZD-2026-00801
- 通報者:skyknow (老狼)
- 風險:中
- 類型:跨站冒名請求 (Cross-Site Request Forgery, CSRF)
參考資料
漏洞說明: OWASP - Cross-Site Request Forgery (CSRF)
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
防禦措施: OWASP - Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
相關網址
https://github.com/SCAICT/SCAICT-uwu
https://github.com/SCAICT/SCAICT-uwu/blob/main/app.py
https://github.com/SCAICT/SCAICT-uwu/blob/main/cog/admin_gift.py
敘述
Summary
The public SCAICT/SCAICT-uwu Flask application contains a privileged gift issuing route at /api/send/<target_user_id>. The route is registered without methods, so Flask exposes it as a default GET endpoint. It performs a state-changing administrative action using the current browser session after checking the user's Discord role.
The route has no handler-level CSRF token/state validation and accepts count from the query string as any integer. It then directly applies that value to the recipient's point or ticket balance.
If this source path is deployed as written, an attacker can prepare a crafted link or other top-level browser navigation that causes a logged-in gift administrator to issue points/tickets to an attacker-chosen target user. The same path also accepts negative values, which can subtract balances if the request is accepted.
This report is intentionally source-backed only. I did not validate the production deployment or trigger the route with a real SCAICT/Discord session.
Affected Source
Program: HITCON ZeroDay - 中部高中電資社團聯合會議
Public source: https://github.com/SCAICT/SCAICT-uwu
Validated commit: ef9174f4893895083465af21b3c00aa36981a9d8
Component: Flask gift issuing route in app.py
In app.py, the gift endpoint is registered as a default GET route:
@app.route("/api/send/<int:target_user_id>")
def send(target_user_id):
Inside the handler, gift_type and count are read from query parameters:
gift_type = request.args.get("gift_type", "電電點")
gift_amount = request.args.get("count", 1)
gift_amount = int(gift_amount)
The source then uses that integer in the point/ticket update:
update user set point=point+%s where uid=%s
update user set ticket=ticket+%s where uid=%s
The reviewed handler does not contain a CSRF token, Origin, Referer, SameSite, or state-nonce check. A source-wide check of app.py did not find a global SESSION_COOKIE_SAMESITE or CSRF configuration.
The web store's own user actions /buyProduct and /rollSlot are explicit POST routes, while this gift-sending route remains a default GET route despite changing balance state.
The Discord slash-command gift path in cog/admin_gift.py rejects count <= 0, but the web route does not apply the same positive-count validation.
Local Validation
No production request was made. I used a local-only source model that reads the public source and inspects only the send() handler with Python AST.
Command:
python3 findings/20260601-2228-hitcon-scaict-gift-admin-get-csrf-negative-count/local-repro.py
Current local model summary:
{
"route_is_get_default": true,
"handler_count_from_query": true,
"handler_int_only_conversion": true,
"handler_has_positive_or_upper_bound": false,
"handler_has_csrf_origin_referer_samesite": false,
"app_has_global_session_samesite_config": false,
"handler_has_state_changing_sql_update": true,
"discord_side_effect_before_sql": true,
"slash_command_rejects_non_positive_count": true,
"network_requests": 0
}
Example crafted URL pattern:
https://<scaict-uwu-host>/api/send/123456789?gift_type=電電點&count=100000
The modeled SQL update is:
update user set point=point+%s where uid=%s
params: [100000, 123456789]
Negative-value variant:
https://<scaict-uwu-host>/api/send/123456789?gift_type=抽獎券&count=-3
The modeled SQL update is:
update user set ticket=ticket+%s where uid=%s
params: [-3, 123456789]
Because the real source path calls Discord/member/DM behavior before the SQL update, I did not perform live validation.
Impact
The conservative impact is unauthorized use of a privileged gift action and point/ticket balance integrity loss.
An attacker who can convince a logged-in gift administrator to open a crafted link may cause the admin's browser session to issue a large point/ticket gift to an attacker-chosen user. Negative counts show the same validation gap can also reduce balances.
I am not claiming account takeover, direct money loss, confirmed production exploitation, or access to any real SCAICT/Discord user data. Suggested severity is Low to Medium, depending on the production value and trust boundary of SCAICT Store points/tickets.
Safety Boundary
- No SCAICT production request.
- No Discord OAuth or Discord API request.
- No real account, token, guild, bot, student data, or third-party data.
- No gift issuance.
- No scanner, fuzzing, DoS, brute force, social engineering, public issue/PR, vendor contact outside HITCON, or live route validation.
- Deployment status and production cookie/session behavior were not tested.
修補建議
- Change `/api/send/<target_user_id>` to an explicit POST route.
- Add CSRF protection for browser-session administrative actions.
- Require `count` to be positive and within a reasonable maximum.
- Reuse the positive-count validation already present in the Discord slash-command gift path.
- Reject negative values at the database/model layer as defense-in-depth.
- Log and alert on unusually large gift issuance amounts.