\n

\n

Fix and optimization process for the score-theft bug in the 【Mei Sheng】program. Date: 2026.7.25
Template used【MSH-Kaiyun Sports】Download link:
Let the pictures speak for themselves
【Mei Sheng】shut down operations and handed over to【Xinghui】. I contacted customer service asking if there was any way to resolve the malicious attacks on the program that caused accounts to be randomly credited with points.
The official reply was: we don’t participate in operations, and there’s no way to recover the points. Developing your own program would be relatively safer. Free programs, open and transparent—without a certain level of technical ability it’s like running around naked! You bear the risk of losses from vulnerabilities yourself.

Recording the score-theft process and repair log here for fellow countrymen to reference.

Key finding: Tracing linc123’s abnormal funds (hard evidence from MySQL binlog)

🔴 July 8th abnormal operations after registration:

| Time | Thread ID | Operation | Content | Anomaly |
| ——– | ——— | ————— | ————————- | ——————– |
| 23:06:08 | – | INSERT users | Registered linc123 | Normal |
| 23:06:32 | 30951 | UPDATE users | balance=100, isagent=1 | 🔴 24 seconds after registration, balance and agent status modified directly! |
| 23:06:41 | 30964 | INSERT user_api | Created PT platform account | Normal |
| 23:06:44 | 30968 | Transfer flow | transfer_logs + balance=0 | Normal game transfer flow |

🔴 July 15th large-amount abnormal fund operations:

| Time | Thread ID | Operation | Content | Anomaly |
| ——– | ——— | ——————– | ————- | ————– |
| 17:23:59 | 144787 | UPDATE users | balance=10000 | 🔴 No deposit record, balance modified directly |
| 17:24:10 | 144822 | INSERT transfer_logs | money=10000 | Normal transfer record |
| 17:24:11 | 144822 | UPDATE users | balance=0 | Normal deduction |
| 17:24:20 | 144839 | UPDATE users | balance=50000 | 🔴 Again no deposit, balance modified directly |
| 17:24:26 | 144849 | UPDATE users | balance=30000 | 🔴 Same as above |
| 17:25:00 | 144906 | UPDATE users | balance=25000 | 🔴 Same as above |

Most likely root cause

This was caused by an anomaly in the third-party game platform (PT) callback logic or some kind of automated script:

Normal flow: user requests transfer → code UPDATE users → INSERT transfer_logs → call PT API
Abnormal behavior: PT callback or script directly UPDATE users.balance, then executes the transfer flow

Alternatively:

Game platform test account / bot account:
– Automatically granted 100 balance after registration (isagent=1 is the marker)
– Large-amount testing / volume inflation on July 15th
– High-frequency betting in a single session (ae1emgufqtef)
– Accounted for 98.7% of the entire platform’s betting volume

Problems exposed in the system (this is not a single bug, but multiple missing safeguards)

Problem 1: Registration risk control completely missing (severe)

• No CAPTCHA
• No phone number binding
• No email verification
• No IP restrictions
• Bots can register in bulk

Problem 2: No balance source verification (severe)

• User had no deposit records, yet the game platform had balance
• The system directly allowed withdrawals with no review whatsoever
• Should restrict: users who haven’t deposited cannot withdraw game balances

Problem 3: No bot detection (severe)

• Single session: 156 bets in 50 minutes (3 bets per minute)
• Accounted for 98.7% of the entire platform’s betting volume
• The system raised no alerts at all

Problem 4: Inconsistent code logic (moderate)

Incorrect outgoing record in PayController::transfer():

$transferlog->after_money = $user->balance-$amount; // ❌ Wrong!

The correct version should be:

$transferlog->after_money = $user->balance; // ✅ Current balance

This caused after_money in transfer_logs to show negative values (e.g., -10000).

Problem 5: No audit logs (severe)

• The user_operate_logs table is completely empty
• No operations can be traced
• Backend balance adjustments are unrecorded

Meisheng Template Multi-Game Platform — Risk Control and Architecture Iteration Log

I. Program Overview

The Meisheng Template Multi-Game Platform is a dual-line gaming platform built on Laravel + Dcat Admin, running two independent lines: .vip and .top, covering a total of 9 domains (admin/api/wap/pc.dajian168.net plus .top/.xyz variants). The frontend and backend are separated: PC/WAP are independent static frontends, while the admin panel and API are Laravel services. Both admin/API lines share the same MySQL database and integrate with the /ley/ family of third-party game APIs (PT/PG/pgs, etc.) via TgService.php. User wallets and third-party game balances are mutually transferable; deposits currently go through a manual review process.

───

II. Version Iterations

v0.1 Data and Rebate Fixes (2026-05-23)

• Settled bet records showing win/loss amount as 0.00 in the admin panel → fixed the crawling and write-back logic in CrawGameRecord.php.
• Rebates/agent commissions triggered repeatedly → deduplicated using transfer_logs.betid, and fixed the scheduled task trigger logic.
• Missing historical data → performed segmented backfill of bet records from 2026-04-29 to the fix date.
• Unclear “rebate eligible” display in admin → changed to “rebate status” (not generated / pending claim / rebated).
• User 15239920255 missing 2 bet records → backfilled individually.
• USDT deposits not recording exchange rate → now written to recharge.usdt_rate, with historical data backfilled.
• Frontend showing blank when userInfo.usdtrate is missing → added a fallback display.
• Third-party platform PG switched to pgs → updated apis.api_code and game_lists.platform_name accordingly, keeping both lines consistent.

v0.2 Operations and UX Fixes (2026-07-14 ~ 2026-07-15)

• VIP lock fix: added vip_lock field to the users table; AuthController::upuserlevel(), Pass::upuserlevel(), and UserController::saving() all now check the lock to prevent locked users from being auto-downgraded to VIP1.
• WAP blank page fix: PHP-FPM 7.3 workers were stuck, causing API timeouts of 30+ seconds; resolved by fully restarting PHP-FPM.
• WAP .xyz 403 fix: corrected the Nginx server_name (wap.dajian168.com → wap.dajian168.net), added an SSL certificate, and pointed the .xyz line’s API to api.dajian168.net.
• Admin 500 fix: storage/logs/ ownership was incorrectly root → changed to www:www.
• VIP level changes not taking effect: Dcat Admin Form saving had an empty() misjudgment when reading $form->vip; changed to read request data directly via $form->input(‘vip’).
• Domain SSL decision: confirmed with the owner that all .top domains (admin/wap/api/pc) use HTTP only, with no SSL deployment; disabled 443 listeners in .top configs, removed HTTP→HTTPS redirects, and fixed the resulting Nginx syntax errors.

v1.0 Emergency Stop-Loss (2026-07-16)

Incident: user linc123 stole 26,417 RMB in credits via PT test balance.

Fixes:

• Added PayController::checkUserRecharge() to check whether a user has a successful deposit record or a balance greater than 0.
• PayController::transfer(), transAll(), and IndexController::transToTgAccount() now reject users who have never deposited.
• Fixed after_money calculation errors: changed $user->balance-$amount / $user->balance+$amount to $user->balance in all cases (3 locations).
• Fixed User_Api null pointer issues in PayController (3 locations): automatically creates a User_Api record when none exists, ensuring api_money and save() logic work correctly.
• Backup location: /root/dajian168_backup_20260716/.

Verification: non-depositing users calling /api/transfer receive 403 “You have not deposited”; users with deposits pass the check.

v2.0 Registration Security Hardening (2026-07-18)

• Added audit_status (0=pending / 1=approved / 2=rejected), reviewed_at, reviewer_id, and audit_reason fields to the users table.
• Registration endpoint now uses validate() (username 4-20 chars, password 8-20 chars, realname 2-20 chars).
• Limited to 3 account registrations per IP within 24 hours, recording reg_ip and sourceurl.
• New registrations default to audit_status=0; unapproved accounts receive 403 on login.
• Set audit_status=2, isblack=1, status=0 for linc123 as a double-layer ban.
• Disabled APP_DEBUG (.env synchronized across both lines).
• System level: iptables blocks external access to port 3306, allowing only 127.0.0.1; rules persisted to /etc/iptables.rules and auto-restored via rc.local.
• Backup location: /root/dajian168_backup_20260718_secure/.

v2.1 Admin Review Buttons (2026-07-18)

• Added two Dcat Admin batch actions: AuditPass / AuditRefuse.
• Approve: sets audit_status=1, status=1, and writes reviewed_at, reviewer_id, audit_reason.
• Reject: sets audit_status=2, status=0, and writes the same fields as above.
• Admin user grid now includes review status, registration IP, and review time columns.
• File MD5 checksums identical across both lines (.vip and .xyz).

v3.0 Deep Hardening (2026-07-18)

• PayController::transfer() and transAll() now enforce four layers of risk control:
1. Must pass the deposit check (checkUserRecharge).
2. Rate limiting: ≤ 3 transfers per minute, ≤ 10 per hour, ≤ 50 per day.
3. Newly registered users limited to ≤ 1000 RMB per transaction within 24 hours.
4. Single transfer cap of 50,000 RMB.
• Member\PayController::notify() and fourwaynotify() now include:
• IP whitelist (configurable via system_config.notify_ip_whitelist).
• Full callback logging.
• Amount anti-tampering: the database order amount is authoritative; the callback amount is not used directly.
• Replay protection: only orders with state=1 are processed.
• Hardcoded secret e9afed057f4*************** replaced with env(‘FOURWAY_SECRET’), configured in .env.
• End-to-end tests passed: register → pending-approval login 403 → admin approval → login success → non-depositor transfer 403 → admin deposit → new user over-limit 403 → rate limit 429.
• Backup location: /root/dajian168_backup_20260718_v3/.

v3.1 Registration Compatibility and CORS Fixes (2026-07-20)

• Fixed WAP/PC registration page freezing: root cause was AuthController’s paypassword rule min:8 conflicting with the frontend’s 6-digit withdrawal password; the original Controller::validate() output directly via echo+die, bypassing CORS and causing the browser to hang.
• Fix approach:
1. Controller::validate() now throws a ValidationException, returning a standard JSON 422.
2. App\Exceptions\Handler::render() adds CORS headers to exception responses.
3. AuthController paypassword rule changed from min:8 → min:6.
• Changes synchronized across both lines (.vip and .xyz).
• Verified in production: short-password registration returns 422 with complete Access-Control-Allow-Origin headers.
• Backup: *.bak.corsfix_20260720.

───

III. Current Limitations and To-Dos

1. Registration security: graphical captcha, SMS verification, and device fingerprinting are not yet enabled; there is a risk of bulk registration bots.
2. Admin permissions: administrators use single-point permissions; integrating RBAC or 2FA is recommended.
3. Payment flow: deposits still go through manual review (balance credited manually after state=1→2); no automated payment channel is integrated; notify/fourwaynotify are currently defensive implementations.
4. Domains and CDN: the .top line has been decided to use HTTP only, but HTTPS for wap/api/pc.dajian168.com still returns 200 by default via the CDN; owner confirmation is needed on whether to disable this in the CDN console.
5. Historical data consistency: after switching PG to pgs, some historical reports still show platform_type as PG.
III. Current Limitations and To-Dos

1. Registration security: graphical captcha, SMS verification, and device fingerprinting are not yet enabled; there is a risk of bulk registration bots.
2. Admin permissions: administrators use single-point permissions; integrating RBAC or 2FA is recommended.
3. Payment flow: deposits still go through manual review (balance credited manually after state=1→2); no automated payment channel is integrated; notify/fourwaynotify are currently defensive implementations.
4. Domains and CDN: the .top line has been decided to use HTTP only, but HTTPS for wap/api/pc.dajian168.com still returns 200 by default via the CDN; owner confirmation is needed on whether to disable this in the CDN console.
5. Historical data consistency: after switching PG to pgs, some historical reports still show platform_type as PG.
6. Test account: ceshi1’s password does not match and needs to be reset.
7. Static assets: some PC/WAP CSS/SVG resources return 404; unrelated to this round of risk-control hardening, but it affects page completeness.

───

IV. Results Achieved

• All four credit-theft paths are now blocked: non-depositing users, unapproved accounts, newly registered accounts, and high-frequency bots can no longer complete transfers or log in.
• Abnormal fund outflows are controllable: single-transaction/daily/frequency limits intercept abnormally large transfers.
• The registration entry point is controllable: per-IP rate limiting, manual review, and field validation significantly raise the cost of bulk registration and freebie exploitation.

#php-custom-dev #dev-iteration-log #custom-dev-log #meisheng-credit-theft-bug #xinghui-credit-theft-bug #meisheng-vulnerability #xinghui-vulnerability

\n

\n\n