Hundreds of GitHub Python Repos Compromised via Account Takeover and Force-Push

stepsecurity.io · varunsharma07 · 19 hours ago · view on HN · threat-intel
quality 7/10 · good
0 net
AI Summary

StepSecurity discovered ForceMemo, an ongoing campaign compromising hundreds of GitHub accounts via the GlassWorm malware (distributed through malicious VS Code/Cursor extensions) to inject obfuscated, Solana blockchain-based C2 malware into Python repositories. Attackers use stolen GitHub credentials to force-push malicious code while preserving original commit metadata, affecting popular projects like Django and ML research repositories.

Entities
StepSecurity ForceMemo GlassWorm GitHub Python PyPI Django Streamlit Solana Cursor VS Code amirasaran/django-restful-admin BierOne wecode-bootcamp-korea HydroRoll-Team
ForceMemo: Hundreds of GitHub Python Repos Compromised via Account Takeover and Force-Push - StepSecurity Back to Blog News ForceMemo: Hundreds of GitHub Python Repos Compromised via Account Takeover and Force-Push The StepSecurity threat intelligence team was the first to discover and report on an ongoing campaign — which we are tracking as ForceMemo — in which an attacker is compromising hundreds of GitHub accounts and injecting identical malware into hundreds of Python repositories. The earliest injections date to March 8, 2026, and the campaign is still active with new repos continuing to be compromised. Varun Sharma View LinkedIn March 14, 2026 Share on X Share on X Share on LinkedIn Share on Facebook Follow our RSS feed Table of Contents Loading nav... The StepSecurity threat intelligence team has discovered an ongoing campaign in which an attacker is compromising hundreds of GitHub accounts and injecting identical malware into hundreds of Python repositories . The earliest injections date to March 8, 2026, and the campaign is still active with new repos continuing to be compromised . The attack targets Python projects — including Django apps, ML research code, Streamlit dashboards, and PyPI packages — by appending obfuscated code to files like setup.py , main.py , and app.py . Anyone who runs pip install from a compromised repo or clones and executes the code will trigger the malware. The compromised setup.py in amirasaran/django-restful-admin (70 stars) — obfuscated malware is appended at the end of the legitimate file. A pip install . or python setup.py install would execute the malware The attacker gains access to developer accounts, takes the latest legitimate commit on each repo's default branch, rebases it with malicious code appended, and force-pushes — making it appear as if nothing changed. The commit message, author, and author date are all preserved from the original. We have filed security issues on the most notable affected repositories to notify maintainers. This is an ongoing campaign. We are actively tracking this attack and will continue to update this blog post as new information becomes available. Some of these repositories may still contain the malicious code. If you use any Python packages installed directly from GitHub, check that the code on the default branch matches the last legitimate commit from the original author. GitHub Search for Affected Repos The full list of affected repositories can be found by searching GitHub for the malware's marker variable: GitHub Code Search: lzcdrtfxyqiplpd How the Attack Works Phase 1: Account Takeover via GlassWorm Credential Theft The account takeover mechanism has been identified: the compromised developers were infected with the GlassWorm malware through malicious VS Code and Cursor extensions. GlassWorm's stage 3 payload includes a dedicated credential theft module that harvests GitHub tokens from multiple sources: git credential fill , VS Code extension storage, ~/.git-credentials , and the GITHUB_TOKEN environment variable. Once the attacker has these credentials, they use them to force-push malware into all of the victim's repositories. A Reddit user reported discovering that "null" had committed to most of their repos — and traced the infection back to a compromised Cursor extension . An independent analysis of the GlassWorm payload confirmed it steals GitHub and npm credentials, validates them against the GitHub API, and exfiltrates them to attacker-controlled servers. The evidence for account-level compromise is clear: when an account with multiple repositories is taken, every repo under that account gets injected . For example, user BierOne has had 6 repos compromised, the organization wecode-bootcamp-korea has had 6 repos hit, and HydroRoll-Team has had 6. Phase 2: Stealth Injection via Force-Push The injection method is sophisticated. Rather than opening a pull request or creating a new commit (both of which would be visible in the repo's activity feed), the attacker: Takes the latest legitimate commit on the default branch Rebases it, appending obfuscated malware to a key Python file ( setup.py , main.py , app.py , etc.) Force-pushes to the default branch The commit message and author date are preserved from the original commit — only the committer date reveals the tampering. The committer email is also set to the string "null" across many of the malicious commits, which appears to be a fingerprint of the attacker's tooling. The rebased commit on amirasaran/django-restful-admin — the commit message and author are preserved from the legitimate merge, but the committer date reveals the actual attack date (March 10, 2026). Here are the date discrepancies we found across the most notable repositories: amirasaran/request_validator — author date: 2017-04-24, committer date: 2026-03-10 ( 9 year gap ) BierOne/relation-vqa — author date: 2019-04-11, committer date: 2026-03-13 ( 7 year gap ) BierOne/bottom-up-attention-vqa — author date: 2021-06-01, committer date: 2026-03-13 ( 5 year gap ) biodatlab/siriraj-assist — author date: 2024-03-19, committer date: 2026-03-13 ( 2 year gap ) amirasaran/django-restful-admin — author date: 2024-11-15, committer date: 2026-03-10 ( 16 month gap ) uknfire/tsmpy — author date: 2025-06-04, committer date: 2026-03-08 ( 9 month gap ) KeithSloan/ImportNURBS — author date: 2025-08-28, committer date: 2026-03-10 ( 6 month gap ) BierOne/ood_coverage — author date: 2024-10-25, committer date: 2026-03-12 ( 5 month gap ) KeithSloan/GDML — author date: 2026-02-06, committer date: 2026-03-11 ( 33 day gap ) The GitHub Events API captures push events with before and after commit SHAs. For amirasaran/django-restful-admin , we can see the exact moment the default branch was replaced: // March 10, 21:58 UTC — master force-pushed with malicious code { "type": "PushEvent", "actor": "amirasaran", // compromised account "created_at": "2026-03-10T21:58:02Z", "ref": "refs/heads/master", "before": "260ca635...", // clean commit (legitimate PR #16 merge) "after": "17849e1b..." // malicious rebased commit } The GitHub Events API for amirasaran/django-restful-admin — the PushEvent at 2026-03-10T21:58:02Z shows the force-push replacing the clean commit (260ca63) with the malicious one (17849e1). The before SHA ( 260ca635 ) is the legitimate merge commit from PR #16. The after SHA ( 17849e1b ) is the attacker's rebased commit with malware appended to setup.py . Because the attacker uses the compromised account's own credentials, the push appears to come from the repo owner. Legitimate activity on amirasaran/django-restful-admin — PR #16 was merged normally (commit 260ca635). The attacker then rebased this merge commit with malicious code and force-pushed to master. Phase 3: Solana Blockchain C2 The injected code is appended to the end of whatever Python file the attacker targets. It's obfuscated with three layers: base64 decoding, zlib decompression, and XOR decryption (key: 134). The variable names are randomized 15-character strings, but the base64 payload blob is identical across all compromised repos , stored in a variable named lzcdrtfxyqiplpd . # Obfuscation wrapper (appended to end of legitimate Python file) # -*- coding: utf-8 -*- aqgqzxkfjzbdnhz = __import__('base64') wogyjaaijwqbpxe = __import__('zlib') idzextbcjbgkdih = 134 qyrrhmmwrhaknyf = lambda d, o: bytes([b ^ idzextbcjbgkdih for b in d]) lzcdrtfxyqiplpd = '<4,800-character base64 blob>' runzmcxgusiurqv = wogyjaaijwqbpxe.decompress( aqgqzxkfjzbdnhz.b64decode(lzcdrtfxyqiplpd)) ycqljtcxxkyiplo = qyrrhmmwrhaknyf(runzmcxgusiurqv, idzextbcjbgkdih) exec(compile(ycqljtcxxkyiplo, '<>', 'exec')) After deobfuscation, the malware executes the following sequence: The malware first checks if the system is Russian — examining locale settings, timezone, and UTC offset. If the system is Russian, execution is skipped entirely. This is a well-known pattern in Eastern European cybercrime operations to avoid targeting domestic systems. # Comments in the deobfuscated code are in Russian: # "Эмуляция объекта Buffer из Node.js" (Emulation of Node.js Buffer object) # "Получение подписей для адреса Solana" (Getting signatures for Solana address) # "Проверка, находится ли система в России" (Checking if system is in Russia) Instead of connecting to a traditional C2 server that could be taken down, the malware reads its instructions from the Solana blockchain . It queries a specific Solana address for transaction memos containing JSON data with a payload URL: Solana C2 address: BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC The malware tries 9 different Solana RPC endpoints as fallbacks, making it highly resilient to any single endpoint being blocked: api.mainnet-beta.solana.com solana-mainnet.gateway.tatum.io go.getblock.us solana-rpc.publicnode.com api.blockeden.xyz solana.drpc.org solana.leorpc.com solana.api.onfinality.io solana.api.pocket.network Using the blockchain as a C2 channel means the attacker can update the payload URL at any time by posting a new transaction — and no one can delete or censor the instructions once they're on-chain. GitHub code search for the marker variable across the BierOne account — all repositories contain the identical malware, demonstrating account-level compromise. Phase 4: Payload Execution Once the malware retrieves the payload URL from the Solana memo, it: Downloads Node.js v22.9.0 from nodejs.org to the user's home directory (cross-platform: Windows/macOS/Linux, x64/ARM) Fetches the encrypted JavaScript payload from the URL, receiving an IV and secret key in HTTP response headers Writes a JS file ( i.js ) that decrypts and executes the payload via the downloaded Node.js Creates a persistence file ( ~/init.json ) with a 2-day recheck timer to avoid repeated execution The final JS payload is encrypted with AES, making static analysis of the second stage impossible without the server-side key. Based on the infrastructure pattern (Solana C2 + Node.js execution + AES encryption + CIS exclusion), this is consistent with known crypto wallet stealer / infostealer campaigns. Harden-Runner Analysis: Catching the Malware in Action To confirm the malware behavior, we ran the compromised setup.py from amirasaran/django-restful-admin in a controlled GitHub Actions environment with StepSecurity Harden-Runner monitoring all network activity. The results confirm the full attack chain described above. Within seconds of executing python3 setup.py , Harden-Runner captured the following network activity from the python3.12 process: Solana C2 query (T+10s) — DNS resolution of api.mainnet-beta.solana.com (208.91.111.195:443) — the malware querying the blockchain for its C2 instructions Payload URL fetch (T+20s) — Connection to 217.69.0.159:80 — the URL decoded from the Solana transaction memo Node.js download (T+21s) — DNS resolution of nodejs.org (172.66.128.70:443) — downloading Node.js v22.9.0 to execute the encrypted JavaScript payload Node.js extracted (T+24s) — /home/runner/node-v22.9.0-linux-x64/bin/node deployed — Harden-Runner automatically detected the new binary and attached TLS monitoring The GitHub Actions build log from the controlled execution — after setup.py --version prints 1.1.3 (legitimate), the malware immediately downloads Node.js v22.9.0 from nodejs.org and extracts it to the runner's home directory. Full workflow run . Harden-Runner network insights from the controlled execution — the malware's outbound connections to Solana RPC endpoints and nodejs.org are clearly visible, alongside the normal GitHub Actions infrastructure traffic. None of these connections belong in a Python project's CI/CD pipeline. A setup.py has no legitimate reason to contact Solana RPC endpoints, download Node.js, or connect to unknown IPs. Harden-Runner's network egress monitoring flags exactly this kind of anomalous activity. Add Harden-Runner to your workflows to detect compromised dependencies before they can exfiltrate data. The full workflow run with Harden-Runner insights is available at: StepSecurity Insights Dashboard . Protect your CI/CD pipelines StepSecurity monitors outbound network connections from your GitHub Actions runners, detects anomalous activity like the ForceMemo campaign, and secures your software supply chain. Start Free with StepSecurity → Solana C2 On-Chain Analysis Because the attacker uses the Solana blockchain as a C2 channel, the full history of C2 instructions is permanently recorded on-chain and available for analysis. We queried the C2 address BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC and decoded all transaction memos. The C2 address is a wallet, not a smart contract The attacker owns the private key for this address and uses Solana's Memo program to post JSON instructions. The malware on victim machines queries this address via Solana RPC, reads the latest memo, decodes the base64 link field, and connects to the resulting URL. The blockchain only records the attacker's side — victim connections happen off-chain to the payload servers. The campaign predates the GitHub injections The earliest transaction on the C2 address dates to November 27, 2025 — over three months before the first GitHub repo injections on March 8, 2026. The address has 50 transactions total, with the attacker regularly updating the payload URL, sometimes multiple times per day. This suggests the attacker was targeting other infection vectors before pivoting to GitHub repos. Decoded payload URLs reveal 6 C2 server IPs Each memo contains a base64-encoded HTTP URL pointing to the current payload server. Decoding all memos reveals the attacker has rotated through 6 different server IPs over the campaign's lifetime: 45.32.151.157 — active December 2025 (earliest) 45.32.150.97 — active February 2026 217.69.11.57 — active February 2026 217.69.11.99 — active February–March 2026 217.69.0.159 — active March 13, 2026 (current — confirmed by Harden-Runner) 45.76.44.240 — active March 13, 2026 The 45.x IPs are in the Vultr hosting range. The 217.69.x IPs are in a Russian hosting range — consistent with the CIS exclusion behavior and Russian-language code comments in the malware. Direct C2 configuration exposed on-chain One transaction from February 25, 2026 contains a direct C2 configuration instead of a payload link — likely posted during a testing or reconfiguration phase: { "c2server": "http://217.69.11.99:5000", // Main C2 server "checkIp": "http://217.69.11.99", // Victim IP fingerprinting "dht_data": "217.69.11.99:10000" // DHT peer-to-peer fallback } This reveals three components of the attacker's infrastructure: a C2 server on port 5000, a victim fingerprinting endpoint (likely checking the victim's IP and geolocation before serving a payload), and a DHT (Distributed Hash Table) node on port 10000 — a peer-to-peer fallback that makes the C2 infrastructure even more resilient to takedowns. Funding trail The C2 address was funded on November 27, 2025 by wallet G2YxRa6wt1qePMwfJzdXZG62ej4qaTC7YURzuh2Lwd3t , which currently holds ~495 SOL . The C2 address itself holds only 0.006 SOL — just enough for transaction fees to post memo updates. Connection to the GlassWorm Campaign The Solana C2 address used by ForceMemo - BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC - is the same wallet address used by the GlassWorm malware campaign , a self-propagating worm that has been targeting VS Code and OpenVSX extensions since October 2025. This was first documented by Koi Security in their analysis of GlassWorm's Wave 4 (December 2025), which pivoted to macOS with trojanized crypto wallets. The overlap is not just the wallet. Aikido Security reported that the GlassWorm actor compromised 151+ GitHub repositories between March 3–9, 2026 using invisible Unicode characters to hide payloads - with the decoded payload fetching its C2 instructions from the same Solana wallet. This means two parallel waves were hitting GitHub repos in early March 2026 from the same actor: GlassWorm wave (March 3–9): 151+ repos, invisible Unicode injection - reported by Aikido ForceMemo wave (March 8–13): 240+ repos, account takeover + force-push with obfuscated Python - reported by StepSecurity (this blog) The two campaigns use different delivery methods and different code obfuscation (Unicode steganography vs. base64/zlib/XOR), but share the same Solana C2 infrastructure. This strongly suggests ForceMemo is a new delivery vector operated by the GlassWorm threat actor , expanding from VS Code extensions to mass GitHub account takeover. What the attacker is likely stealing While we cannot see the final JavaScript payload (it is AES-encrypted with a key delivered via HTTP headers), the infrastructure pattern is consistent with known crypto wallet stealer / infostealer campaigns: CIS country exclusion — classic Eastern European cybercrime operational security Node.js runtime — ideal for accessing browser extension data (crypto wallets like MetaMask, Phantom), stored credentials, and cookies AES encryption of payloads — prevents static analysis and signature-based detection Victim IP fingerprinting ( checkIp endpoint) — allows the attacker to filter or customize payloads per victim 2-day persistence timer — designed for sustained access, not one-time exfiltration The most likely targets are browser crypto wallet extensions (seed phrases, private keys), stored credentials and session cookies , and SSH keys . Scope of the Campaign So far, we have identified hundreds of Python repositories across hundreds of GitHub accounts injected with identical malware, and the number continues to grow. The targeted repos include Django web applications, machine learning research code, Streamlit dashboards, Flask APIs, and Python packages installable via pip install from GitHub URLs. Several of the compromised repos have setup.py files — meaning a pip install directly from the repo executes the malware during installation. The full list of affected repositories can be found by searching GitHub for the malware's marker variable: GitHub Code Search: lzcdrtfxyqiplpd GitHub code search for the malware marker variable lzcdrtfxyqiplpd — hundreds of results across Python repositories. Account-Level Compromise: Repeat Victims The strongest evidence for account-level compromise is the pattern of multiple repositories being hit per account. These numbers are growing as the campaign continues: wecode-bootcamp-korea (Organization) — 6 repos compromised HydroRoll-Team (Organization) — 6 repos compromised BierOne (User) — 6+ repos compromised gnlxpy (User) — 6 repos compromised Fo2sh88 (User) — 6 repos compromised watercrawl (Organization) — 4 repos compromised tavasolireza (User) — 4 repos compromised BishalBudhathoki (User) — 4 repos compromised iperformance (User) — 4 repos compromised amirasaran (User) — 3 repos compromised KeithSloan (User) — 2 repos compromised File Types Targeted The attacker's tooling selects the most prominent Python entry point in each repo: main.py — most common target (~70 repos) setup.py — triggers on pip install . (~25 repos) app.py — Flask/Streamlit apps (~25 repos) manage.py — Django projects (~20 repos) app/__init__.py — package init files (~8 repos) Various: streamlit_app.py , run.py , config.py , cli.py , noxfile.py Timeline Campaign Timeline November 27, 2025 — Earliest activity on the Solana C2 address. Funding wallet G2YxRa... transfers SOL and first payload URLs point to 45.32.151.157 . The attacker is likely targeting other infection vectors before GitHub repos. December 2025 – February 2026 — Attacker rotates through payload servers ( 45.32.151.157 , 45.32.150.97 , 217.69.11.57 , 217.69.11.99 ), posting ~40 memo transactions with updated payload URLs. March 8 — Earliest GitHub repo injections detected: metalogico/issued , uknfire/tsmpy , gnlxpy/* repos, wecode-bootcamp-korea/* repos March 10 — Major wave: amirasaran/* repos (including 70-star django-restful-admin), KeithSloan/ImportNURBS , watercrawl/* repos March 11 — Continued: KeithSloan/GDML March 12 — BierOne/ood_coverage (34-star ICLR paper) March 13 — Latest wave: BierOne/bottom-up-attention-vqa , BierOne/relation-vqa , biodatlab/siriraj-assist , HydroRoll-Team/HydroRoll March 14 — First repos begin reverting (e.g., KeithSloan/GDML restored at 14:05 UTC). StepSecurity publishes initial findings and files security issues on affected repos. Ongoing — Campaign remains active. We are continuing to monitor and will update this post. Indicators of Compromise Solana C2 address: BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC Solana funding wallet: G2YxRa6wt1qePMwfJzdXZG62ej4qaTC7YURzuh2Lwd3t Marker variable in code: lzcdrtfxyqiplpd XOR decryption key: 134 Committer email (fingerprint): "null" (string) Node.js version downloaded: v22.9.0 Persistence file: ~/init.json JS payload file: i.js (in script directory) Code comments language: Russian CIS exclusion: Skips execution on Russian locale/timezone C2 Payload Server IPs (from on-chain analysis) 45.32.151.157 (active Dec 2025) 45.32.150.97 (active Feb 2026) 217.69.11.57 (active Feb 2026) 217.69.11.99 (active Feb–Mar 2026, C2 server on :5000, DHT on :10000) 217.69.0.159 (active Mar 2026 — current) 45.76.44.240 (active Mar 2026) Solana RPC Endpoints Contacted api.mainnet-beta.solana.com solana-mainnet.gateway.tatum.io go.getblock.us solana-rpc.publicnode.com api.blockeden.xyz solana.drpc.org solana.leorpc.com solana.api.onfinality.io solana.api.pocket.network Why We Call It ForceMemo We are tracking this campaign as ForceMemo , derived from its two most distinctive technical artifacts: Force — the attacker injects malware by force-pushing to the default branch of compromised repositories. This technique rewrites git history, preserves the original commit message and author, and leaves no pull request or commit trail in GitHub's UI. No other documented supply chain campaign uses this injection method. Memo — the malware uses Solana blockchain transaction memos as its command-and-control channel, reading payload URLs from memo data attached to transactions on a specific Solana address. This makes the C2 instructions immutable and censorship-resistant. ‍ How to Check If You're Affected If you install Python packages directly from GitHub (e.g., pip install git+https://github.com/... ) or clone and run Python repos: Search for the marker variable in any Python files you've cloned: grep -r "lzcdrtfxyqiplpd" . Check for ~/init.json on your system — this is the malware's persistence file Check for downloaded Node.js in your home directory: ls ~/node-v22* Check for i.js in any recently-cloned project directories Review git commit history of repos you've cloned — look for commits where the committer date is significantly newer than the author date Disclosure We filed security issues on the most notable affected repositories to notify maintainers: amirasaran/django-restful-admin #17 amirasaran/request_validator #3 BierOne/bottom-up-attention-vqa #9 BierOne/ood_coverage #6 metalogico/issued #7 biodatlab/siriraj-assist #2 KeithSloan/ImportNURBS #15 StepSecurity Threat Intelligence StepSecurity threat intelligence was the first to discover and publicly report on this campaign. The team is actively monitoring the situation and will continue to update this post. StepSecurity continuously monitors the open source and CI/CD ecosystem for emerging threats — including supply chain attacks, compromised GitHub Actions, malicious packages, and account takeover campaigns like this one. StepSecurity customers receive threat intelligence alerts directly in their dashboard, with actionable guidance on whether they are affected and how to remediate. How Harden-Runner Detects This Attack Harden-Runner monitors network traffic, file system changes, and process activity on GitHub Actions runners. It is designed to catch exactly the kind of anomalous behavior that supply chain attacks like ForceMemo produce. When we ran the compromised setup.py with Harden-Runner in audit mode, it captured every outbound connection the malware made: api.mainnet-beta.solana.com:443 — Solana blockchain C2 query 217.69.0.159:80 — Payload URL fetched from the Solana memo nodejs.org:443 — Node.js v22.9.0 download All three connections were flagged as "Not in baseline" — meaning they had never been seen in any previous run of the workflow. A Python setup.py has no legitimate reason to contact Solana RPC endpoints, download Node.js, or connect to unknown IPs. Once a baseline is established, Harden-Runner can block any outbound connection that is not in the allowed list. This would have prevented the malware from reaching the Solana C2, downloading Node.js, or exfiltrating data. Protect your CI/CD pipelines StepSecurity monitors outbound network connections from your GitHub Actions runners, detects anomalous activity like the ForceMemo campaign, and secures your software supply chain. Start Free with StepSecurity → Blog Explore Related Posts ‍ ForceMemo: Hundreds of GitHub Python Repos Compromised via Account Takeover and Force-Push The StepSecurity threat intelligence team was the first to discover and report on an ongoing campaign — which we are tracking as ForceMemo — in which an attacker is compromising hundreds of GitHub accounts and injecting identical malware into hundreds of Python repositories. The earliest injections date to March 8, 2026, and the campaign is still active with new repos continuing to be compromised. Varun Sharma View LinkedIn March 14, 2026 Read Dev Machine Guard Is Now Open Source: See What's Really Running on Your Developer Machine Your developer machine is running AI agents, MCP servers, IDE extensions, and hundreds of packages. Do you know which ones? Now there's a free, open-source way to find out. Ashish Kurmi View LinkedIn March 12, 2026 Read xygeni-action Compromised: C2 Reverse Shell Backdoor Injected via Tag Poisoning The official Xygeni GitHub Action (xygeni-action) was compromised on March 3, 2026, when an attacker using stolen maintainer credentials injected a full C2 reverse shell backdoor and silently moved the mutable v5 tag to the malicious commit - affecting all repositories referencing @v5 without any visible change to their workflow files. The v5 tag remains poisoned as of March 9; users should immediately pin to v6.4.0 or a specific commit SHA, and StepSecurity's Harden-Runner would have detected and blocked the C2 callback to 91.214.78.178. Varun Sharma View LinkedIn March 9, 2026 Read LinkedIn Github X Request a Demo Start Free System Status About Docs Pricing Contact Us Product Tour © 2026 All rights reserved Privacy Policy Terms of Service