NBJS bridge abuseParticle Media · qb9.c() allowlist prefix-bypass → native bridge from attacker-controlled WebView URL
Any host matching <allowed-host>.<attacker-domain> passes qb9.c(), e.g.
https://thescoopz.com.evil.com. When the victim opens the mini-game / web URL in the Scoopz WebView,
NBJS is bridged (isBridgeAllowedForCurrentUrl() = true) and non-ad native actions are permitted.
Bridge object: addJavascriptInterface(bridge, "NBJS") // window.NBJS
Entry point: NBJS.callNative(JSON) @JavascriptInterface
JSON fields: { action: string, callback: string }
callback is JS expression evaluated as: javascript:<callback>({"success":1,...})
Gates:
isBridgeAllowedForCurrentUrl() -> qb9.c(getOriginalUrl()) || legacyMiniGameMode
isActionPermittedForCurrentPage(action) -> true for ALL actions EXCEPT the 4 ad
actions (preload/show x interstitial/reward), which need legacy + allowlist.
=> The ONLY effective gate for getLoginInfo / nbRequest / openBrowser / share /
follow / openProfile / selectLocation ... is the qb9.c() URL allowlist.
qb9.b(host, allowed) bypass:
host == allowed (exact)
host.endsWith("." + allowed) (subdomain)
host.startsWith(allowed + ".") <-- BUG: attacker host passes
getLoginInfo returns:
userId, userName, nickName, avatarUrl, isGuest,
userToken (= session cookie sc_t_3), deviceId, os, version, abInfo,
zipcode, locationName, lat, lon
nbRequest({path, method, query}) -> authenticated API call with session cookie.
openBrowser(url) / openWeb(url) -> navigate WebView to arbitrary URL.