Is CSGO NET Profit Helper by Saullo safe?
CSGO.NET Profit Helper is high risk. A case open or upgrade on CSGO.net reads the user ID from the page WebSocket and posts results to livedrop.ludraapi.site/api/rolls, linking roll ID, account ID, and a 'saullo' tag. It also loads saullodrops.live; no POST body captured.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Roll and account IDs sent after CSGO.net wins
A case open or upgrade on CSGO.net reads the user ID from the page WebSocket and posts results to livedrop.ludraapi.site/api/rolls, linking roll ID, account ID, and a 'saullo' tag.
It also loads saullodrops.live; no POST body captured.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You open a case or run an upgrade on CSGO.net.
The reporting code runs after those actions and after the result passes the extension's value checks.
The extension posts a roll event that links the roll ID to your platform account ID.
The request also states whether the event came from a case open or an upgrade.
- Roll ID66199374621 (illustrative)
Links the report to the specific case drop or upgrade result you received.
- Platform account ID76561198012345678 (illustrative)
Lets the receiver tie the roll event back to your CSGO.net account session.
- Action typeupgrade
Shows whether the roll came from opening a case or completing an upgrade.
- Streamer tagsaullo
Adds the attribution label sent with the roll event.
Source review identifies this POST endpoint and JSON content type. The recorded session did not capture a roll POST body.
- Content-Type
- application/json
Earlier recorded traffic observed this GET while the content script was active on CSGO.net; it has no request body.
Roll-reporting code paths in the shipped bundle
Readable current-user assignment
assets/index.tsx-Bc3anMlx.jshandleSocketMessage(t) { var a; const n = this.parseEventData(t); if (n.msg === "ping") { this.socket.send(JSON.stringify({ msg: "pong" })); return } n.msg === "changed" && n.collection === "users" && ((a = n.fields) != null && a.id) && (console.log("profit-helper: User received", n.fields), this.currentUserId = n.fields.id ?? null, chrome.runtime.sendMessage({ type: "user-data-received", data: { ...n.fields, token: this.getUserToken() } }))}Readable case-open handler
assets/index.tsx-Bc3anMlx.jsasync handleCaseOpenResults() { const t = await this.getCaseOpenProfits(); if (!t) return; const n = this.socketService.getCurrentUserId(); if (!n) { console.warn("profit-helper: User ID not found"); return } const a = this.casePrice ?? 0; for (const s of t) s.dropPrice <= 5 || s.dropPrice <= a || (await fetch("https://livedrop.ludraapi.site/api/rolls", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ rollId: s.rollID, userId: n.toString(), type: "case", streamer: "saullo" }) }), this.firebaseService.addRolls({ rollID: s.rollID, userID: n, rollType: "case" }))}Readable upgrade handler
assets/index.tsx-Bc3anMlx.jsasync handleUpgradeResult(t) { if (t.currentTarget.getAttribute("action") !== "runUpgrade" || this.handlingUpgradeResult) return; if (this.handlingUpgradeResult = !0, await tp() !== "success") { this.resetUpgradeListener(); return } const s = this.socketService.getCurrentUserId(); if (!s) { console.warn("profit-helper: User ID not found"), this.resetUpgradeListener(); return } try { const [c] = await this.socketService.requestUserUpgrades(1); if (c.upgradedTo.price_usd <= 5) { this.resetUpgradeListener(); return } await fetch("https://livedrop.ludraapi.site/api/rolls", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ rollId: c.pfRollID, userId: s.toString(), type: "upgrade", streamer: "saullo" }) }), this.firebaseService.addRolls({ rollID: c.pfRollID, userID: s, rollType: "upgrade" }) } catch (c) { console.error("profit-helper: Error sending upgrade data:", c) } finally { this.resetUpgradeListener() }}- livedrop.ludraapi.site
Receives case and upgrade roll events from the current shipped code.
- www.saullodrops.live
Earlier recorded traffic observed a live-drop GET request to this host while the content script was active.
Giveaway entry sends a page screenshot
Entering a CSGO.net giveaway renders the selected row with html2canvas, converts it to a PNG, sends it as `depositImageBase64`.
The worker posts giveaway ID, hash, invoice, amount, screenshot to canaldosaullo.com.br; body wasn't captured.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You click to participate in a giveaway on CSGO.net.
The button appears inside the extension's giveaway participation modal.
The extension sends a rendered page image with the giveaway participation request.
The screenshot travels as the `depositImageBase64` field.
- Rendered page imagedata:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII= (illustrative format)
Can reveal whatever was visible in the selected CSGO.net table row when you entered the giveaway.
- Giveaway IDdaily_2026_07_13 (illustrative)
Identifies which giveaway entry the screenshot is attached to.
- Account hash9f6b4e2a8d1c0b73 (illustrative)
Links the giveaway entry to the account hash used in the extension's giveaway flow.
- Invoice and amountinvoice 18477291, amount 2500 (illustrative)
Adds transaction context to the giveaway participation request.
Recorded evidence observed a POST to this URL with a depositImageBase64 key containing PNG data; the complete request body was not recorded.
- Content-Type
- application/json
Screenshot capture and service-worker upload
Readable screenshot helper
assets/index.tsx-Bc3anMlx.jsasync function lb(l) { let t = await hw(l, { scale: 1, backgroundColor: "#000000", logging: !1, useCORS: !0 }); if (t.width > Xg) { const n = Xg / t.width, a = Math.round(t.width * n), s = Math.round(t.height * n), c = document.createElement("canvas"); c.width = a, c.height = s; const f = c.getContext("2d"); f && (f.drawImage(t, 0, 0, a, s), t = c) } return t.toDataURL("image/png")}Readable giveaway click handler
assets/index.tsx-Bc3anMlx.jsconst k = async (z, iA) => { if (X(null), !n || !a || !s || s <= 0) { X("Dados do depósito ou conta inválidos."); return } if (!Q) { X("Linha da tabela não disponível para gerar o comprovante."); return } I(z), W(null); try { const fA = await lb(Q), sA = await EQ(iA, n, a, s, { depositImageBase64: fA }); kh(iA, a), W({ kind: z, data: sA }) } catch (fA) { const sA = fA instanceof Error ? fA.message : "Erro ao participar."; cb(sA) && kh(iA, a), X(sA) } finally { I(null) }}Readable content-script message
assets/index.tsx-Bc3anMlx.jsfunction bQ(l, t, n, a, s) { return new Promise((c, f) => { chrome.runtime.sendMessage({ type: "PARTICIPATE_CSGONET_GIVEAWAY", giveawayId: l, userHash: t, invoice: n, amount: a, ...s ? { depositImageBase64: s } : {} }, d => { const B = chrome.runtime.lastError; if (B) { f(new Error(B.message)); return } if (!d) { f(new Error("Sem resposta do service worker.")); return } if (d.ok) { try { c(cp(d.data)) } catch (g) { f(g instanceof Error ? g : new Error("Resposta inválida.")) } return } f(new Error(d.error)) }) })}Readable service-worker POST
assets/background.ts-D4MbCsr9.jsimport { S as u} from "./saulloSiteOrigin-rzoQxhHJ.js";chrome.runtime.onMessage.addListener((t, d, a) => { if ((t == null ? void 0 : t.type) === "PARTICIPATE_CSGONET_GIVEAWAY") { const i = typeof t.giveawayId == "string" ? t.giveawayId.trim() : "", e = typeof t.userHash == "string" ? t.userHash.trim() : "", s = typeof t.invoice == "string" ? t.invoice.trim() : "", p = typeof t.amount == "number" && Number.isFinite(t.amount) && t.amount > 0 ? Math.floor(t.amount) : 0, o = typeof t.depositImageBase64 == "string" && t.depositImageBase64.length > 0 ? t.depositImageBase64 : void 0; return (async () => { const f = `${u}/api/csgonet/participate`, n = { giveawayId: i, userHash: e, invoice: s, amount: p }; o && (n.depositImageBase64 = o); try { const r = { ...n }; typeof r.depositImageBase64 == "string" && (r.depositImageBase64 = `[base64 ${r.depositImageBase64.length} chars]`), console.log("[profit-helper] participate request", { url: f, method: "POST", body: r }); const l = await fetch(f, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(n) }), c = await l.json().catch(() => ({})); if (!l.ok) { const h = c && typeof c == "object" && "message" in c && typeof c.message == "string" ? c.message : `participate falhou (${l.status}).`; console.log("[profit-helper] participate response", { ok: !1, status: l.status, body: c }), a({ ok: !1, error: h }); return } console.log("[profit-helper] participate response", { ok: !0, status: l.status, body: c }), a({ ok: !0, data: c }) } catch (r) { console.log("[profit-helper] participate response", { ok: !1, error: r instanceof Error ? r.message : String(r) }), a({ ok: !1, error: r instanceof Error ? r.message : "Falha de rede." }) } })(), !0 }})- canaldosaullo.com.br
Receives giveaway participation details and the depositImageBase64 PNG data URL.
Roll activity is written to the developer's Firestore database
Opening or upgrading a case can write your roll activity to the developer's Firestore database at firestore.googleapis.com, storing roll identifiers, your user ID, roll type, and a timestamp tied to your account.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
The user opens or upgrades a case.
The extension writes rollID, userID, rollType, and a timestamp to the developer's Firestore database.
- Stored roll identifiersrollID: 123456, userID: 7656119..., rollType: case-open (illustrative)
Roll ID, user ID, and roll type connect a case-open or upgrade event to a specific account.
- CSGO NET roll activitycase-open event on csgo.net (illustrative)
The event shows that the user opened or upgraded a case on CSGO NET.