Severity: Critical — invoice payment without ownership check + negative amounts → unlimited money creation and account draining
src/server/services/invoice/invoice.service.ts:88-131 (payInvoice) + src/server/services/invoice/invoice.controller.ts:53-58 (createInvoice):
// createInvoice — req.data passed straight through (fromIdentifier/toIdentifier/amount from client)
async createInvoice(req: Request<CreateInvoiceInput>) {
const data = await this._InvoiceService.createInvoice(req.data);
...
}
// payInvoice — no check that fromAccountId belongs to req.source
const fromAccount = await this._accountDB.getAccountById(req.data.fromAccountId, t);
...
const amount = invoice.getDataValue('amount');
if (accountBalance < amount) { throw ... } // negative amount always passes
await this._accountDB.transfer({ amount, fromAccount, toAccount, transaction: t }); // balance-amount / balance+amount
Exploits (any player):
- Money creation: create an invoice with
amount: -5000, pay it with your own account → your balance increases by 5000 (balance - (-5000)), the payee loses 5000. Repeat for unlimited money.
- Account draining:
payInvoice accepts any fromAccountId (sequential IDs, enumerable) — pay someone else's invoice using the VICTIM's account as the source → drain any account. Compare with handleInternalTransfer (transaction.service.ts:101) which uses getAuthorizedAccountById — payInvoice skips this entirely.
Same class of bug: handleExternalTransfer (transaction.service.ts:154) fetches fromAccount by req.data.fromAccountId with no ownership check → attacker adds their own external account, then transfers from the victim's account into it.
Also depositMoney (account.service.ts:407-460): no amount <= 0 check (withdraw has it at line 466) → DepositMoney with amount: -5000 on any accountId (no authorization check, unlike withdraw) → +5000 cash from nothing.
Verificat: transfer() (account.db.ts:93-94) applies -amount/+amount with no validation; createInvoice doesn't override identities server-side (compare createOnlineInvoice which sets from/toIdentifier from req.source).
Fix: createInvoice must set identities from req.source and require amount > 0; payInvoice/handleExternalTransfer/depositMoney must verify account ownership (getAuthorizedAccountById) and reject amount <= 0. I can share a reproduction script privately if useful.
Severity: Critical — invoice payment without ownership check + negative amounts → unlimited money creation and account draining
src/server/services/invoice/invoice.service.ts:88-131(payInvoice) +src/server/services/invoice/invoice.controller.ts:53-58(createInvoice):Exploits (any player):
amount: -5000, pay it with your own account → your balance increases by 5000 (balance - (-5000)), the payee loses 5000. Repeat for unlimited money.payInvoiceaccepts anyfromAccountId(sequential IDs, enumerable) — pay someone else's invoice using the VICTIM's account as the source → drain any account. Compare withhandleInternalTransfer(transaction.service.ts:101) which usesgetAuthorizedAccountById—payInvoiceskips this entirely.Same class of bug:
handleExternalTransfer(transaction.service.ts:154) fetchesfromAccountbyreq.data.fromAccountIdwith no ownership check → attacker adds their own external account, then transfers from the victim's account into it.Also
depositMoney(account.service.ts:407-460): noamount <= 0check (withdraw has it at line 466) →DepositMoneywithamount: -5000on any accountId (no authorization check, unlike withdraw) → +5000 cash from nothing.Verificat:
transfer()(account.db.ts:93-94) applies-amount/+amountwith no validation;createInvoicedoesn't override identities server-side (comparecreateOnlineInvoicewhich setsfrom/toIdentifierfromreq.source).Fix:
createInvoicemust set identities fromreq.sourceand requireamount > 0;payInvoice/handleExternalTransfer/depositMoneymust verify account ownership (getAuthorizedAccountById) and rejectamount <= 0. I can share a reproduction script privately if useful.