Blog · Threat intel · · 23 min read · by huntback Research

Inside SOÛL SOCIETY: automated exploitation, AI agent orders and a Telegram drug platform

An exposed workspace on 146.19.213[.]192 brought together a Go scanner, a Python exploitation panel, credential-validation routines and instructions addressed to named agents. Alongside those files were backups of a licensed Telegram storefront platform, including catalogues advertising drugs.

The recovered material offers a detailed view of how the environment was designed to operate. Domain lists feed campaigns. A scanner identifies technology, exposed content and possible vulnerabilities. Python scripts attempt follow-up actions. A credential subsystem extracts candidate secrets, tests or classifies them, stores results and sends selected findings to Telegram.

There is evidence consistent with external database extraction and one recorded successful AWS identity check. There are also lab fixtures, failed attempts, operator-supplied keys and success labels that require substantial qualification.

The AI question is more specific. Persistent instructions and references to Hermes profiles strongly suggest agent assistance. The inspected scanner and panel, however, make their operational decisions through conventional code. We did not recover model invocation traces or agent conversations establishing that an LLM independently selected and executed a successful external intrusion.

In brief

  • One host, one crew, two businesses. 146.19.213[.]192, which the operator's own reports call the "soulsociety attack VPS", served its whole workspace as an open directory on port 9999: an exploitation panel, a Go scanner, credential tooling, agent orders and backups of a Telegram drug-shop platform.
  • A production line for intrusions. Domain lists feed campaigns, the scanner fingerprints each site, a rule table picks follow-up scripts, and a credential pipeline pairs, tests and pushes secrets to Telegram. The panel watcher logged 31,651 target URLs and 12,245 exploit sessions while it ran.
  • Named targets. The crew kept evidence files on betting, crypto, telecom, retail and media targets in France, Switzerland and beyond, written up on 1 and 2 November 2026. Its own verdicts record no access on most of them. A French vehicle-administration service is the exception: a 44-table schema and its administrator records were extracted.
  • AI agents with orders. ORDERS.md and SUKUNA_ORDERS.md give named agents standing instructions ("Tout est autorisé"); reports are signed by the agent Sukuna and call targets "authorised by the Commander". The scanner and panel themselves decide with ordinary code.
  • A Telegram drug platform. Kagura, sold by licence to client shops, runs storefront bots with catalogues of cannabis, ketamine, ecstasy and cocaine, with delivery, loyalty points and referrals.

What we examined

huntback scanned 146.19.213[.]192 after it surfaced in our data and harvested what its open directory served.

The harvest holds 1,032 distinct files: 1,031 came down in three zip parts and the last, a 382 MB archive over the zip size cap, was fetched on its own. An earlier 500-file zip overlapped those parts rather than adding to them. We verified the three parts against their manifest hashes and the archive against its recorded SHA-256.

The files included source code, compiled scanner material, operational notes, HTTP logs, a panel watcher log, credential exports and nested backups. One backup contained an older panel database and 12 additional database snapshots. Another contained PHP development and lab material. The final archive contained shop software, client backups, media and further nested ZIPs.

Our method was static: archive inspection, source-level control-flow reconstruction, read-only SQLite queries, saved-output comparison and log parsing. We did not execute recovered programs, replay exploits, authenticate with recovered keys or contact the listed targets. Hash verification confirms the relationship between supplied files and their manifests; it does not independently establish the authenticity of every record within those files.

Targets are named where the operator wrote them up; we describe what the crew did and the outcome its own files record, never a target's unpatched weaknesses. Credentials, personal information, customer identifiers, private keys and operational authentication values are omitted. Internal artifact names are retained where they help another analyst locate evidence.

The architecture: a panel coordinates the work

The central Python application identifies itself as SOÛL SOCIETY. The recovered implementation uses FastAPI and SQLite. Caddy configuration places a web interface and API in front of a loopback panel on port 8455. A separate configuration routes a collector path to a loopback service on port 8787. The collector executable itself was not present in the examined backup, so its complete request handling could not be reconstructed.

The source describes the following relationship:

recovered code paths, not a record of every branch executingDomain imports and queuequeue_bot.py: .bot .cash .credit.bank .trading .payup to 2,000 targets a batchSOÛL SOCIETY panelFastAPI + SQLiteloopback :8455 behind Caddyimports, campaigns, dispatchllm-scannerGo, 31 to 32 modulescrawl, fingerprint, exposed files-exploit flag is a no-opPython follow-upspick_exploits(): rule lookup+ edge recon on every targetSecret extractionenv files, configs, cloud keysdatabase URLs, walletsValidationpairing, provider checks,token and wallet inspectionSQLite resultssessions, credentials, proofsTelegramselected secrets in plaintextCollectorroute to loopback :8787executable not recovered
This diagram represents recovered code paths, not a claim that every branch executed successfully.

The distinction between the scanner and the panel matters. The scanner's -exploit flag is explicitly a no-op in the reviewed source: exploitation is delegated to the panel. Conversely, the panel's apparent exploit count includes sessions in which only reconnaissance may run. A product label or table name is not a reliable description of impact.

Recovered code: older main.go, line 71.

	flagExploit   = flag.Bool("exploit", false, "sans effet ici : l'exploitation des CVE est lancée par le panel")

The flag description places exploitation outside the scanner. The panel supplies that separate orchestration layer; the binary name alone tells us little about the decision process.

Importing and queuing targets

The panel accepts pasted lists and uploaded target files. The import path normalises URLs, performs reachability checks and creates persistent target and batch records. In the older implementation, imported targets are automatically inserted with the panel's authorized field set to 1. That is an application field, not independent evidence that a target owner granted permission.

Recovered code: older panel_main.py, import handler, lines 1309 to 1311.

            cur = conn.execute(
                "INSERT INTO targets (url, category, authorized, status) VALUES (?, 'import', 1, ?)",
                (canonical, status))

The literal 1 is assigned during import. The database flag therefore cannot substantiate authorization on its own.

A newer script, queue_bot.py, implements a continuing campaign queue. It checks for an existing running campaign, excludes targets already represented in scan or completed campaign records, checks free disk space and selects a batch from an ordered list of top-level domains. The configured sequence is .bot, .cash, .credit, .bank, .trading and .pay, with up to 2,000 target IDs per batch. The script describes invocation every five minutes; that schedule is a statement in the script, not a recovered cron execution history.

A separate, older systemd timer schedules scan passes every two hours, with a random delay. These are distinct automation mechanisms from different snapshots.

Session notes describe domain-zone access, filtering parked or dead sites and continuing campaigns across large imported sets. Those notes help explain the targeting model, but their stated results are operator claims unless corroborated by other artifacts.

What the Go scanner actually does

The older source registers 32 modules; a newer exported modules.go registers 31. The snapshots should not be combined into a single supposedly current build. The recovered scanner binary is an unstripped Linux x86-64 Go executable. We did not establish that its machine code exactly matches either source snapshot.

The scanner builds a per-target HTTP context, checks connectivity, calibrates responses from random nonexistent paths and shares cached responses between modules. Its crawler discovers links, forms, JavaScript assets, robots directives and sitemap paths. Other modules examine exposed configuration files, JavaScript secrets, database and SMTP exposure, TLS names, application fingerprints and vulnerability-related signals.

It uses concurrency at both target and module level. The reviewed implementation includes deadlines, response-size limits, partial-result collection, configurable jitter and target-specific throttling when it detects WAF-related conditions. Those are implementation features, not independently measured throughput or stealth guarantees.

Several checks are detection heuristics. Version evidence or an error response may justify investigation without establishing exploitability. An especially useful example is the module named injection: it submits a canary to an LLM endpoint and looks for the canary in the response. An endpoint repeating requested text does not, by itself, demonstrate a prompt-injection security boundary failure. The older panel invocation also explicitly disables that canary check.

We found no LLM planning loop in the inspected scanner execution path. Its name, llm-scanner, should not be read as proof that a model selects its actions.

Recovered code: older modules.go, lines 270 to 272.

		rnd := make([]byte, 8)
		rand.Read(rnd)
		path := "__ss_" + hex.EncodeToString(rnd) + "__"

This is the source of the random nonexistent-path pattern discussed in the defensive leads. It is baseline calibration, not a model-generated probe. The prefix is useful for correlation but is not exclusive attribution evidence.

From findings to follow-up scripts

The panel's pick_exploits() function maps categories, module identifiers and product-related text to Python scripts. It deduplicates script-and-argument combinations and systematically adds an edge-reconnaissance stage.

This is rule-based dispatch.

how a scanner finding becomes a follow-up script (older panel_main.py, pick_exploits)Finding{"module":"cve_xxxx", ...}Normaliselower-case the module,strip the cve_ prefixCategoriescategory set:module, product wordsLookupdictionary:category to scriptsSubprocess queuededuplicated script + argument pairs,plus an edge-reconnaissance stage on every targetexploit_sessions rowstatus read from script output strings(heuristic markers, see below)no model call, prompt or planning step anywhere in this path
The selection is a normalisation step and a dictionary lookup. The status written afterwards comes from string markers in each script's output.

Recovered code: older panel_main.py, inside pick_exploits(), lines 120 to 126.

    for f in findings:
        if isinstance(f, dict):
            mod = str(f.get("module") or "").lower()
            if mod:
                cats.add(mod)
                if mod.startswith("cve_"):
                    cats.add(mod[4:])

Here the panel takes a module name from a finding, normalises it and derives a category. A later dictionary lookup selects follow-up scripts. There is no model response or planning prompt in this selection logic. The registered follow-ups cover a mixture of technology discovery, exposed-service inspection, file disclosure, application-specific exploitation and credential extraction. The panel starts subprocesses, captures output, applies time budgets and records sessions and results in SQLite. Separate wrapper code translates between the scripts' different argument interfaces.

The recovered PHP work includes source packages, multiple proof-of-concept trees, build profiles, lab harnesses and debugger-oriented material. At a high level, it attempts to exploit PHP request-memory lifecycle faults and then verify execution or extract configuration data. We did not reproduce the memory-corruption chains or test their portability.

The PHP identifiers are not merely invented labels in a README. PHP's own advisories document CVE-2026-109618 and CVE-2026-109619 and describe remote-code-execution conditions and build-dependent reliability. That external fact does not validate every capability claimed by the recovered tools.12

The credential pipeline is a central objective

The system is designed to turn exposed content and follow-up output into usable authentication material. The recovered extraction and harvesting code handles environment files, application configurations, process environments, source content, cloud credentials, database connection details and wallet-related material.

Candidates are classified and deduplicated. Context extraction and pairing routines attempt to reconstruct complete combinations, such as an AWS access key with its secret, or a database host with a username and password. Validation routines perform provider requests or protocol-specific authentication. Other routines calculate local properties, inspect token structure or derive cryptocurrency addresses and query associated activity.

These operations do not all provide the same evidence. Successful service authentication is stronger than passing a checksum. A JWT that parses is not necessarily accepted by a service. A public extended wallet key can describe funds without granting spending authority. A zero-balance lab key is not a stolen funded wallet.

The panel can persist candidate values and supporting content in SQLite and send selected secrets in plaintext Telegram messages. Additional database extraction code inventories tables, prioritises account-related tables and can write CSV samples for transmission. This is a collection capability in the source, not proof that every supported database was accessed.

Who they went after

Next to the tooling sat evidence files named after targets: EVIDENCE.md, EXODUS_EVIDENCE.md, XO_EVIDENCE.md, SWISSCOM_EVIDENCE.md and SWISSCOM_CVE.md, DESKOIN_EVIDENCE.md, POUPETTE_EVIDENCE.md, and three reports, fdesouche_report.md, histoiredor_report.md and opticalcenter_report.md. Each is a structured write-up in French, dated 1 or 2 November 2026, giving the attack base as 146.19.213[.]192. Two are signed by the agent Sukuna, and one records its target as "declared authorised by the Commander".

targets with their own evidence file, 1 to 2 November 2026, and the outcome those files recordFrench vehicle-administration servicevehicle paperwork, FRschema and 8 admin records takenbreachedExodus XO for Agentsagent walletsaccount + 4 test wallets createdfootprint leftHistoire d'Orjewellery retail, FR326,249 password tries, old hostno accessfdesouche.comnews site, FRaccounts enumerated, passwords triednot establishedBetsson Franceonline betting, FRstaging and back-office hosts mappedno access recordedDeskoincrypto paymentsperimeter and sign-in mappedno access recordedExoduscrypto walletperimeter and APIs mappedno accessSwisscomtelecom, CHperimeter mapped, versions checkedno exploit runOptical Centeroptical retail, FRstore-locator forms testedno accessPoupette Storeretail, FRlocked Shopify store probedno access
Outcomes are the operator's own verdicts. We name targets but leave out the weaknesses the files describe.

The pattern is wide and mostly unsuccessful. The files show the crew mapping certificates and subdomains, fingerprinting software versions, matching them against fresh CVEs and the PHP2Shell kit, and trying sign-in, form and API abuse. Most end in the operator's own words: no access obtained, no exploit run, nothing written. Betting, crypto wallets and payments sit next to a Swiss telecom, French retailers and a French news site, which fits the queue's .bot-to-.pay targeting rather than any single sector.

Three files record more than reconnaissance:

  • Exodus XO for Agents. On the agent-wallet platform's internal domain, the crew registered an account through an open sign-up and created four test wallets and an OAuth client, listed in the file as "to clean up". Its tests found other users' wallets isolated.
  • Histoire d'Or. Against a shared hosting box that once served the jeweller, the crew ran 326,249 password attempts in about 35 minutes. It recorded no credentials and concluded the box no longer served the brand's content.
  • fdesouche.com. The report confirms dozens of real account names and describes a password campaign. The files we read do not record a successful login.

The one target where the files show data leaving is a French vehicle-administration service, below.

Saved results: stronger evidence, narrower conclusions

what each saved result can carryclaimrecordedcorroboratedhighFour older RCE sessionsoperator's own PHP and React labsDatabase extraction collectionscripts, schema and admin export agreeOne AWS identity check1 of 213 pairs; proves identity, not accessLater external rce_proofmarker with no stored proof or output17 LLM provider keyssource: operateur, no target URLoperator-supplied, not stolen
How far each saved result goes, from the strongest to the weakest. The detail for each follows.

A database extraction collection

The clearest evidence consistent with external data extraction concerns a French vehicle-administration service. Target-specific scripts, a saved schema, an administrator export and metadata logs corroborate one another. We are withholding the target's domain and vulnerable route.

qfs_schema.json records 44 tables. Selected saved counts are:

Data categoryRecorded rows
Users15,826
Orders7,822
Messages26,213
Document-file records73,549
Administrators8

qfs_admins.json contains eight administrator records with account fields, email addresses, roles and password-field values. Matching code is designed to obtain those fields through error-based SQL injection. This combination is stronger evidence than a scanner verdict alone.

The password-field values must not be casually described as cracked passwords or a known hash algorithm. The eight records contain two distinct 64-character values. We did not determine the storage scheme or recover plaintext passwords.

The remaining limitations are substantial: we have saved artifacts rather than independently obtained victim-side logs, and we did not verify target ownership, authenticity of the exported data or authorization for the activity. The evidence supports a collection consistent with external database extraction, not an independently confirmed breach notification.

A document inventory is not a document haul

manifest.log reports 91,179,642,019 bytes, approximately 91.18 decimal GB, by summing recorded document sizes. Its sample filenames suggest identity, insurance and vehicle-purchase documents.

The matching script queries metadata and aggregate values. These artifacts do not show that 91 GB of documents were downloaded. We recovered limited metadata samples, not the underlying document collection. The sum describes the recorded inventory, and may not represent deduplicated physical storage.

One recorded AWS identity check

aws_pairs_results.json contains 213 credential-pair results: 212 invalid and one marked valid with an account identifier and IAM principal ARN. Matching validation code calls AWS STS GetCallerIdentity.

aws_pairs_results.json: 213 access-key pairs tested with STS GetCallerIdentity210 InvalidClientTokenId2 SignatureDoesNotMatch1 valid identityGetCallerIdentity needs no permissions: a success names the caller and proves nothing about what it can reach
One square per tested pair.

That is consistent with one successful identity check. It does not establish broad AWS access. AWS documents that this operation reports the calling identity and requires no permissions; a successful identity response cannot demonstrate access to S3 data, IAM administration or another protected service.3

There is also a provenance problem. aws_accounts.py writes selected account descriptions and validation statuses directly into the panel database. Other code attempts to pair credentials and update results. Labels are mutable operational data, not immutable evidence from AWS.

A persistence-oriented script can attempt IAM key creation, but it checks an account allowlist. Its presence is evidence of capability and an intended workflow, not evidence that a durable key was successfully created. We did not use any recovered credential.

Operator keys mixed with collected candidates

Two exports list the same 17 provider keys: 15 Groq, one OpenAI and one OpenRouter. Every exported record has source: operateur and no target URL. One key is recorded as quota-exhausted.

Those exports cannot be presented as 17 keys stolen from targets. They show operator-supplied material in this system; they do not establish where the operator originally obtained it. Likewise, the recovered multi-provider LLM gateway demonstrates a way to use upstream API keys, not proof that stolen keys funded the scanning operation.

RCE evidence: labs and a later external marker

The older recovered panel.db contains 24 targets, 56 scan runs, 36 exploit sessions and 55 credential records. Four sessions have rce status. Three target the operator's own PHP lab service; the fourth targets a loopback React lab. A separate confirmed result concerns that same local React environment.

The newer watcher adds an external rce_proof credential marker. This is an important recorded claim, but the watcher stores neither its proof text nor underlying script output. We did not recover the newer database required to inspect that result.

The panel's source explains why restraint is necessary. Some result classifications are based on strings in subprocess output. The marker set includes command-output patterns but also broader proof, file-read and exfiltration wording. Negation checks attempt to reduce false positives, yet classification remains heuristic. A database status or notification is not a substitute for the underlying result.

Recovered code: older panel_main.py, output classification, lines 1611 to 1613.

            RCE_MARKS = ("uid=", "PROOF:", "PROOF ", "RCE:", "lu :", "octets lus",
                         "exfiltr", "fichier(s) extrait", "credentials db extraits",
                         "credentials extraits", "creds extraits", "db_password")

The marker list mixes command-output strings with file-reading and exfiltration language. Elsewhere the function checks negations and treats some credential extraction separately. Even with those checks, a matching string is an internal heuristic, not an independent execution measurement.

Our conclusion is therefore precise: recoverable command-output evidence supports lab RCE; a later external RCE claim exists but is not independently substantiated by the available trace.

What the activity counts establish

The newer panel_watch.log contains 72,389 lines. Parsing that exact file produced:

Recorded itemCountInterpretation
Target-addition records with distinct URLs31,651URLs added while the watcher was observing
Distinct scan-start IDs15,831Observed scan starts, including possible repeat targets
New exploit-session events12,245Session creation, not successful exploitation
New credential-record events2,172Candidate records, not unique usable credentials
panel_watch.log: events at first sighting while the watcher ran (not lifetime totals, not outcomes)URLs added31,651distinct URLs, not organisationsScan starts15,831distinct scan IDs, repeats possibleExploit sessions12,24512,117 running, 82 found, 46 failedCredential records2,1721,802 unverified, 298 invalid, 37 blocked, 33 valid, 2 bench
Bars share one scale. Almost every exploit session was first seen while still running; only 33 credential records were valid at first sighting.

The exploit-session events comprise 12,117 running, 82 found and 46 failed states at first observation. Credential events comprise 1,802 unverified, 298 invalid, 37 blocked, 33 valid and two bench/test records.

Those figures are not totals for the lifetime of the operation. The watcher initialises its counters from the existing database and does not replay historical rows. Its opening message already references existing records. URLs also do not equal unique organisations or victims.

More importantly, panel_watch.py logs new exploit rows but does not follow their later status changes. Most sessions are first observed while running. Their final outcomes cannot be reconstructed from that log. It does monitor some scan-result changes, which is a different mechanism.

Recovered code: panel_watch.py, lines 59 to 63.

            for r in q(conn, "SELECT rowid,target_id,script,status,substr(proof,1,70) p "
                             "FROM exploit_sessions WHERE rowid>?", (seen["exploit_sessions"],)):
                log("EXPLOIT %s cible=%s [%s] %s" % (r["script"], r["target_id"],
                                                     r["status"], (r["p"] or "").replace("\n", " ")))
                seen["exploit_sessions"] = max(seen["exploit_sessions"], r["rowid"])

The cursor advances to the highest observed rowid. An update to an existing session does not create a greater row ID, so that session is not selected again by this query. This is the concrete reason that thousands of initial running events cannot be converted into final outcomes.

why 12,117 sessions stay 'running' in the log: the watcher only reads rows above its cursorrowid 101runningrowid 102runningrowid 103runningrowid 104runningrowid 105failedrowid 103later: foundcursor after poll 1: max rowid seen = 103after poll 2: 105poll 1 logs rows 101 to 103 as running; poll 2 reads only rowid > 103, so the UPDATE on 103 is never logged.
A simplified illustration of the query above, with invented row numbers.

The watcher records clock times without dates or timezone offsets. We therefore do not assign precise dates to its individual events or calculate a campaign rate from its clock sequence.

The agent instructions: strong context, missing execution traces

ORDERS.md and SUKUNA_ORDERS.md address a persistent workflow involving Ishura, Jarvis and Sukuna. Other files reference Hermes directories, profiles and skill material. The notes attribute code repairs and operational instructions to agents, describe continuity between sessions and direct work through reconnaissance, validation and exploitation.

One short instruction reads: Tout est autorisé. Another describes continuing without waiting for a further green light. These are recovered instructions to the environment's agents, not evidence of permission from the owners of external targets.

Together, the files strongly suggest agent assistance with development or operation. They do not identify a particular model provider, establish human identity or show a model's actual decisions. Formatting, comments and references to an LLM are insufficient to attribute code authorship.

We distinguish three layers:

Program automationqueuing, scanning, rule-based follow-up dispatchestablished by code and logsAgent assistanceORDERS.md, SUKUNA_ORDERS.md, Hermes profiles, session notesstrong indicationsAutonomous successful intrusionwould need model and tool traces plus external resultsnot established
The recovered software can automate offensive work without an LLM in its execution loop.
LayerWhat the collection supports
Program automationDirect code and log evidence of queuing, scanning and follow-up dispatch
Agent assistancePersistent instructions, agent references and continuity notes
Autonomous successful intrusionNot established without model/tool traces and underlying external results

The recovered software can automate offensive work without an LLM in its execution loop. That fact and the agent-oriented documents can both be true.

Kagura: another platform in the same workspace

The approximately 400 MB archive initially omitted from the multipart export had a misleadingly broad scanner-related filename. Its contents were predominantly Kagura shop and licensing software, client backups and media.

one exposed directory, two kinds of software146.19.213[.]192Python http.server, port 9999directory listing of the workspaceSOÛL SOCIETY workspaceFastAPI panel and SQLite snapshotsGo scanner source and binarycredential exports, Telegram helpersORDERS.md, SUKUNA_ORDERS.mdKagura shop platformTelegram shop bots (Python, aiogram)licence bot: renew, suspend, reactivatePHP licence endpointclient backups for four shops, one a testoperational notes:run Kagura services,exclude shop domainsfrom scansa Telegram recipient in the export helper also appears in shop authorisation state (identifier withheld)
The offensive workspace and the storefront platform sat in the same exposed directory, and the operator's notes connect them.

The application uses Telegram bots, Python and a PHP web component. Features include customer approval, product catalogues, baskets, delivery scheduling, order tracking, reviews, loyalty points and referrals. A separate licensing interface creates, renews, suspends and reactivates client licences.

Recovered code: Kagura licence bot bot.py, lines 52 to 55.

        [InlineKeyboardButton(text="Renouveler", callback_data=f"renew_{licence_key}")],
        [InlineKeyboardButton(text="Suspendre (Bloquer)", callback_data=f"suspend_{licence_key}")],
        [InlineKeyboardButton(text="Réactiver (Débloquer)", callback_data=f"reactivate_{licence_key}")],
        [InlineKeyboardButton(text="Supprimer définitivement", callback_data=f"delete_{licence_key}")],

These callbacks expose distinct licence-lifecycle actions. Their presence supports a multi-client administration model; it does not prove that licences were sold or that customer orders were fulfilled. Multiple client directories contain saved customer state and accounting entries.

Several catalogues explicitly advertise drugs, including cannabis varieties, ketamine and ecstasy tablets. Other descriptions are consistent with cocaine marketing. We have omitted prices, quantities, purchasing details and customer information.

The earlier operational notes specifically discuss running Kagura services, retaining client bots, moving backups onto the scanner server and excluding shop domains from scans. The recovered archive supplies matching code and directory structures. This corroborates a relationship between the offensive workspace and the storefront platform beyond coincidental filenames.

It does not establish that the same human owned every shop, that every licence was sold, or that saved orders resulted in completed drug transactions. Some client material is explicitly labelled as testing. Possession of backups is also not, on its own, proof of ownership. The defensible observation is that this workspace stored and documented operational involvement with a platform containing drug-shop catalogues.

The operator's workspace was exposed too

The newer listener.log begins with a Python HTTP server listening on port 9999. We parsed 702,074 GET, POST or HEAD access records from 1,758 source IPs, with embedded dates from 5 to 7 November 2026. The count excludes malformed requests and lines that do not match those access-record formats.

listener.log on port 9999: 702,074 parsed requests from 1,758 sources, 5 to 7 November 20264,2675 Nov461,3116 Nov236,4967 Novrequests per day (no timezone in the log)responses304 = 407,854 (not a fresh download)200 = 293,604, 404 = 610, 301 = 6sourcesone source: 586,097 requests (83%), unattributed1,757 others: 115,977
Parsed from the 85,049,838-byte version of listener.log.

Successful HTTP responses appear for operational instructions, backup directories and a plaintext key-file path. This supports exposure of sensitive workspace content, although a server log is not proof of complete receipt of every response body.

One source IP accounts for 586,097 parsed requests. We do not attribute that client or other visitors: collectors, researchers, automated crawlers and unrelated parties can all appear in an exposed-directory log. They are not a list of collaborators or compromised machines.

The operational notes also describe an earlier unauthenticated API exposure. The recovered Caddy snapshot contains authentication for the API and frontend. That configuration corroborates the later architecture, but does not independently prove the earlier incident or its claimed readers.

What defenders can use

The primary association is historical: 146.19.213[.]192 hosted the workspace represented by this collection. It should not be treated as proof that every service, visitor or linked host was malicious, or that the address retains the same role after the collection date.

For environments with relevant telemetry, useful investigation leads include:

  • The distinctive SoulSociety/2.0 token in the recovered scanner's User-Agent. It is a low-cost correlation clue, not an exclusive signature.
  • Random nonexistent paths with the observed __ss_ prefix and __ suffix, especially alongside configuration-file and application probes. One path alone is weak evidence.
  • Sequences of secret-file requests, JavaScript/source-map discovery and product-specific follow-ups against the same service.
  • Unusual web-worker activity followed by configuration reads, new files or outbound callbacks, assessed with host telemetry rather than HTTP status codes alone.
  • Provider authentication or identity-check events involving exposed keys, including checks that succeed without proving useful privileges.
  • Exposure of backups, configuration files and private key material through directory-serving services.

Remediation should follow the affected product's current vendor guidance. For the PHP flaws discussed here, consult PHP's advisories rather than applying workarounds copied from the recovered tools.12 Where an exposed credential is confirmed to belong to your organisation, investigate its use, revoke or rotate it and review dependent access.

Indicators

IndicatorTypeRole
146.19.213[.]192IPv4, AS200019 (AlexHost)SOÛL SOCIETY workspace and attack VPS; ports 22, 80, 443 and 9999 (Python http.server open directory, 5 to 7 Nov 2026)
SoulSociety/2.0User-Agent tokenRecovered scanner's User-Agent
/__ss_[0-9a-f]{16}__URL path patternScanner baseline calibration against random nonexistent paths
Loopback :8455 behind CaddyHost artifactSOÛL SOCIETY panel (FastAPI + SQLite)
Loopback :8787 behind CaddyHost artifactCollector route for callbacks
/opt/soul/panel/, /var/log/soul/exfil.logHost pathsPanel install and collector log
llm-scannerBinary nameUnstripped Linux x86-64 Go scanner
Telegram bot deliveryExfiltration channelSelected secrets and database exports (recipients withheld)

SHA-256: evidence files

ArtifactSHA-256
_scanner_scripts_20261031-0050.tar.gz81a363f6ce8767ebe201213905cb3f2ace3cdf7354291e84093f1c48b8cd7936
soul-core-20261103-002222.tgz3d52326f46f4d3a91bbb433fa893ae7ac9c71209006b341ffe38cca13022e7e4
opt/soul/panel/panel_main.py615ee0214f9012e463a22010cb99f977b2680151dde1ae1094bae6f598edd9f5
opt/soul/panel/panel.dbc6fc3450e3f3324981321fbbac95d078167585e09117c8ce3bcc520d5ceb064e
Newer modules.go72c729ab7a90dda122ca8fe8555158c0086b24167000fdb0931b8e541b632acf
panel_watch.pyeed69640eb7ee0169dec46536b03d5b3164575b4514bce1a09841628c6f1156b
panel_watch.log (version analysed)b20ea5a9d817bf6256be7bf2728be4266988db97a06488730558ba7c015c38c3
listener.log (version analysed)756d5f3ded88e3dd0045f3d8687a6b448aecf3b7077a2bb8184d18a917dd06f6
ORDERS.md04a1fc0a7d805aea14aba1a7eb40a69a14bf31bc5a8b22bbfba5d624d43a0644
ETAT-SESSION.md90031a3729736c40f8b66d10e9928d14865fba9a31851dabf58c9570a3ff2204
qfs_admins.json8d195d30371caaae8ce4a07a185a519a4c89df1a0892d1bfacfb82024f5ebeda
qfs_schema.json6dd3172fff88d455966eb612c9753ba87fc45e42321ad5df08ef022bbae64a3d
manifest.logd99e4b65114643597ed62267c980c1103a08c29e6ed7eaf64a1d0c6ffff0a8ab
aws_pairs_results.json9048d3898d0b26a5cf98aa30e438eeb485201fbd0a2e388c19852b092121b8d9
aws_accounts.py9d0477c823fa9e65929dd8fafc3f35e6c32c68fe39b10903dd747a44da62a234

SHA-256: tooling

FileKindBytesSHA-256
dl_script.binbinary / memory capture311,5375b0a99c5a6c85f93a7ecc71e8bed644d3c6b809b63bb0b2288a8e0bc7ef04323
leak_859.binbinary / memory capture4,096e6a1174b9648f646536818a818584522086ac034bce706a3fa3be5615b5d1772
ps859.binbinary / memory capture16,384261a88bbca6911cfebc1e9b2ade766b5881074a75249b88063a359dfaef85320
pslive.binbinary / memory capture16,384d4bb15ccb714ec094a8302aee2ea7eec3c8862aa8349db6c2b06611ab5cbf42a
jcrack.cC helper source1,0971c80316d80efb85856c21545a0e584db95ae25dd9f6e92af43f8fa81ccb2da9b
rfc_8.2.34.cC helper source10,087179087712b48668adcd728aed272151073a648c13860ab5472fc0233e108099c
rfc_8.3.35.cC helper source10,0946d08016d11e37f9f707b2f55a56b2c0be1adaec239115777a02a1eade6fff0b4
bip39.goGo scanner source1,133dfcaece3b21a3946c62ddfff8b012becccae0f22c2aa11e332ba90ca507a2018
bip39_gen.goGo scanner source26,993de7068ccb7937c39e4bbf1716dee7b41f00c4533fac3e5341b0508ce0b88834b
bip39_test.goGo scanner source64553025acd4f142040cbac2dac8a8465e4a3b1c567dd2df37938f53259790daa01
catalog_gen.goGo scanner source12,86991cd4cdf6cf12ed2566e78dc801ff6b1f2dc88b34a1c696c06b082d6140c29cf
crawler.goGo scanner source16,436f02a76e9008b7570619569e4dab28e27176433041659383f7a6c3f4b5248aa75
crawler_debug_test.goGo scanner source306e25490fdaccb3deccebe313c0f081753a3709e70c2233db5a25c2921c041567e
main.goGo scanner source16,246172db34e19d55aec0cbcfe1fd16b92e8fe6d65d5ad2c0385218fcda2d265f7b2
main.goGo scanner source12,0250431a88661ddac970da9b51e95ea76ca741afb782901cbbd0eb56d8bba14febc
modules.goGo scanner source114,52672c729ab7a90dda122ca8fe8555158c0086b24167000fdb0931b8e541b632acf
targets.goGo scanner source2,744e75d0a8178d3bbc7fb9767f2185afe3ba927c3fec8136ed56bd8f8cb910b9495
_scanner_keep_20261031-0049.tar.gztooling archive151,150,4420b8883ea7896594e0bdb3b639dcce7b4f8f60e1fef14aa4eb6babb670805228b
_scanner_src_20261031-0049.tar.gztooling archive101,44004fbd775e2a036c1871346719202b05522ae13c5cc33b3bc73c1917296f3ca8b
AGENTS.mdagent / operator notes27,282bf6ff58eca9f107758a02da42e03892df87c11dd376915bc6573cb3e2d20f09b
export_ak_secrets.pyPython tooling1,114973406c85dd1cc762c702117ddc1dbf1a884a177fa8b4de2c3a336618de25fcb
export_all_pairs.pyPython tooling1,1878e7315a29da9b391cd842b301fe4f4e4209e247a7330659f14746da8ee434bd2
export_creds.pyPython tooling3,519fd4ec3ff7da19ecdc81e7360009c4d74273609e4e03c77ed0c57e4f2f24b2404
export_pairs.pyPython tooling1,2395929e6bdc990e3ed31f14243b4cff7b921fe730dd03b736dcdc1d5c603b9f71e
export_two.pyPython tooling1,559cceaa40b4ebb800c7b04252c1bc8319594a4d165e004bba25d593d71c82108a0
panel_battery.pyPython tooling5,307f51131319ace5670a3ba198017276bea4a538a0cf80561241c4828ab87dcda43
panels.pyPython tooling1,148f90dfa33971ecdc38d0b4b3a05c4df2efb3988a83a1a940b08e6c3a1b6bc173f
php2shell.pyPython tooling38,832b121242fed3f82ea76c2ca923dc75654417da6d6e8a8fae4b09ba00175571bf1
queue_bot.pyPython tooling3,77149e26bb7cd56c19f34914dcf2cb60f011ddd97b19d9eab305b9382b02389ee20
check_panel.shshell script2,380f97f0ebd0487116d6c879102a0305205a19d8401b3402d4ffc6d45596725485e
pkg.tgztooling archive37,85214c72704c08f6da7cd593246e934bcb538897088fcf4e22260ea2039b454ad00

Assessment

SOÛL SOCIETY is a working intrusion operation: an automated scanning and credential-processing line, run with AI agents under standing orders, that wrote up targets across betting, crypto, telecom, retail and media and extracted one French service's database. The same workspace kept Kagura, a licensed Telegram platform for drug shops.

The value of this exposure is the view it provides into the surrounding workflow: campaign selection, deterministic dispatch, secret validation, Telegram reporting, repeated development and a separate storefront platform. Those mechanisms are visible in the code even where an individual success claim cannot be verified.

Public references

  1. PHP Group, CVE-2026-109618: RCE via OOM corruption, published 21 October 2026; consulted 7 November 2026.
  2. PHP Group, CVE-2026-109619: RCE via stale global state, published 21 October 2026; consulted 7 November 2026.
  3. AWS, STS GetCallerIdentity API reference, consulted 7 November 2026.