Is CSGO NET Profit Helper by Saullo safe?

High risk

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.…

luismiguelprsv2.7.4Chrome Web Store
78Risk

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
What actually happens
You did this

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 did this

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.

Fields assembled for the roll submission
  • Roll ID
    66199374621 (illustrative)

    Links the report to the specific case drop or upgrade result you received.

  • Platform account ID
    76561198012345678 (illustrative)

    Lets the receiver tie the roll event back to your CSGO.net account session.

  • Action type
    upgrade

    Shows whether the roll came from opening a case or completing an upgrade.

  • Streamer tag
    saullo

    Adds the attribution label sent with the roll event.

Captured request
POSThttps://livedrop.ludraapi.site/api/rolls

Source review identifies this POST endpoint and JSON content type. The recorded session did not capture a roll POST body.

Headers
Content-Type
application/json
Captured request
GEThttps://www.saullodrops.live/live-drop

Earlier recorded traffic observed this GET while the content script was active on CSGO.net; it has no request body.

The code that does this

Roll-reporting code paths in the shipped bundle

Readable version

Readable current-user assignment

assets/index.tsx-Bc3anMlx.js
handleSocketMessage(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.js
async 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.js
async 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()  }}
Hosts involved in the roll and live-drop paths
    • 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
What actually happens
You did this

You click to participate in a giveaway on CSGO.net.

The button appears inside the extension's giveaway participation modal.

The extension did this

The extension sends a rendered page image with the giveaway participation request.

The screenshot travels as the `depositImageBase64` field.

Fields in the giveaway participation request
  • Rendered page image
    data: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 ID
    daily_2026_07_13 (illustrative)

    Identifies which giveaway entry the screenshot is attached to.

  • Account hash
    9f6b4e2a8d1c0b73 (illustrative)

    Links the giveaway entry to the account hash used in the extension's giveaway flow.

  • Invoice and amount
    invoice 18477291, amount 2500 (illustrative)

    Adds transaction context to the giveaway participation request.

Captured request
POSThttps://canaldosaullo.com.br/api/csgonet/participate

Recorded evidence observed a POST to this URL with a depositImageBase64 key containing PNG data; the complete request body was not recorded.

Headers
Content-Type
application/json
The code that does this

Screenshot capture and service-worker upload

Readable version

Readable screenshot helper

assets/index.tsx-Bc3anMlx.js
async 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.js
const 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.js
function 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.js
import {  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  }})
Host receiving the giveaway participation request
    • 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
What actually happens
You did this

The user opens or upgrades a case.

The extension did this

The extension writes rollID, userID, rollType, and a timestamp to the developer's Firestore database.

Captured request
POSTfirestore.googleapis.com
Fields in the Firestore write
  • Stored roll identifiers
    rollID: 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 activity
    case-open event on csgo.net (illustrative)

    The event shows that the user opened or upgraded a case on CSGO NET.

Updated 30 September 2026jnejejhhlcdameemiolcnjhhcdplfjhp