Skip to content
InfraHunter
All research
Threat ResearchTLP:CLEARTLP:CLEAR — Disclosure is not limited. This material may be shared publicly without restriction.

From JBRJ to JUCEC: Exposed Infrastructure Reveals a Broad Brazilian Government Intrusion Campaign

A forensic look at exposed attack directories containing a confirmed JBRJ compromise, a JUCEC intrusion toolkit, credential-harvesting infrastructure, webshells, persistence mechanisms, and a staged Linux kernel privilege-escalation exploit.

Published
Reading time
34 min
Analyst
@volrant136
Region
Brazil
  • Cybersecurity
  • Open Directory
  • SQL Injection
  • Active Directory
  • Webshell
  • Kernel Exploit
cover

Hunt.io Attack Capture (opens in a new tab) flagged three open directories on a single Malaysian VPS between August 27 and August 29, 2026, on ports 9998, 18080, and 18082.

The evidence confirms active compromise of the Brazilian Federal Biodiversity Research Institute, a reusable Zimbra and Active Directory hunting kit already inside a Brazilian state government network, and a portable Linux kernel privilege-escalation toolkit staged alongside the other attack infrastructure.

Let's walk through what was recovered.

Key Findings

  • Hunt.io tracked three open directories at 111.90.145[.]92 (Kuala Lumpur, Malaysia, Shinjiru Technology Sdn Bhd, AS45839) on ports 9998, 18080, and 18082.

  • Port 9998 documents a confirmed, multi-stage compromise of the Instituto de Pesquisas Jardim Botânico do Rio de Janeiro (JBRJ), Brazil's federal botanical research institute.

  • Port 18082 is a self-contained "z-kit" already deployed inside JUCEC, the Junta Comercial do Estado do Ceará (Ceará's state Board of Trade), a Brazilian state government agency.

  • Port 18080 is a clean, multi-language mirror of a public POC for CVE-2026-31431 ("Copy Fail"), a real Linux kernel local privilege escalation bug.

Host Infrastructure

Hunt.io lists 111.90.145[.]92 (opens in a new tab) under Shinjiru Technology Sdn Bhd (AS45839), based in Kuala Lumpur, Malaysia, with three open-directory warnings on record: two dated 2026-08-27 and one dated 2026-08-29.

Image
Figure 1: Hunt.io IP intelligence for 111.90.145.92.

At 111.90.145[.]92:9998 (opens in a new tab), an exposed open directory contained 289 files with no subdirectories, totaling approximately 615 KB. Hunt.io recorded the directory on 2026-08-29.

Image
Figure 2: Exposed directory on 111.90.145[.]92:9998, containing 289 files totaling approximately 615 KB.

At 111.90.145[.]92:18082 (opens in a new tab), the exposed opendir contains 434 files across four subdirectories, totaling approximately 553 KB. The directory was observed on 2026-08-27, indicating a second exposed service on the same infrastructure.

Image
Figure 3: Exposed directory on 111.90.145[.]92:18082, containing 434 files across four subdirectories

A third instance was accessible at 111.90.145[.]92:18080 (opens in a new tab), exposing 87 files across two subdirectories with a total size of approximately 1.83 MB. Hunt.io recorded this directory on 2026-08-27, providing another exposed directory on the same host.

Image
Figure 4: Exposed “Attack Capture File Manager” directory on 111.90.145[.]92:18080, containing 87 files across two subdirectories.

The table below shows the full scope.

PortCapturedFilesSizeTheme
99982026-08-27 to 08-29289 files615 KBConfirmed compromise of a Brazilian federal biodiversity institute (JBRJ)
180822026-08-27434 files553 KBZimbra, Active Directory, and helpdesk hunting kit inside JUCEC (Ceará state government)
180802026-08-2787 files1.83 MBPortable dropper for CVE-2026-31431, a Linux kernel privilege escalation bug

Compromise of the Rio de Janeiro Botanical Garden Research Institute (JBRJ)

The scripts in 111.90.145[.]92:9998 share the local path prefix /root/pentest/florariojaneiro/loot/, matching the target subdomain florariojaneiro.jbrj.gov.br (opens in a new tab).

A copy of the target's page source, saved as extra_painel_php, confirms the domain with the tag <meta property="og:url" content="//florariojaneiro.jbrj.gov.br/">.

The evidence carries enough timestamps to reconstruct roughly two days of the intrusion.

Timeline
Figure 5: Timeline showing important compromise indicators.

Stolen Internal Documentation

The files appear in two naming formats: the original filename and a second version with the internal repository path flattened into the filename (extra_docs_PAINEL_CONSULTAS_SQL_md, corresponding to docs/PAINEL_CONSULTAS_SQL.md referenced in AGENTS.md). The copies are identical and contain JBRJ internal data, including the staging host, .env path, and SQL audit panel source. This indicates the files came from the compromised host.

File(s)What it is
AGENTS.md, extra_AGENTS_md, extra__agents_README_mdJBRJ's own onboarding file for AI coding agents, explicitly naming "Cursor, Codex, Claude Code, local automation, etc." and describing the FloraRJ dev server and .env credential layout
DECISIONS.mdJBRJ's internal architectural decision log
PAINEL_SQL.md, extra_docs_PAINEL_CONSULTAS_SQL_md, extra_docs_PAINEL_DIAGNOSTICO_md, extra_painel_phpInternal documentation and source for JBRJ's own SQL audit panel
RELATORIO_E8.md, SPEC001.md, TEST_PLAN.md, extra_docs_specs_001-security_*JBRJ's own security spec and the results of its automated test suite: 9 passed, 15 failed, 2 skipped
extra_docs_specs_003-infra-security_scripts_run_infra_tests_phpJBRJ's own infrastructure/CSP test runner output
_js_custom.js, _js_pages_cadlote.js, _js_pages_cadusuario.js, _js_pages_consusuario.js, login.jsFront-end JavaScript pulled from the live application
flora_swagger.json, jabot_swagger.jsonStolen API definitions for two of JBRJ's applications
florario_README.md, jabotimage_README.md, florarj_relatorio.xlsxJBRJ's own project descriptions and a report export
Image
Figure 6: AGENTS.md, JBRJ's own internal onboarding file for coding agents, naming Cursor, Codex, and Claude Code by name and describing the FloraRJ codebase layout and credential file conventions.

The Exploitation Chain: Two Bugs, One Portal

The open directory contains 77 ipt_*.py scripts driving an attack chain against ipt.jbrj.gov.br, JBRJ's GBIF Integrated Publishing Toolkit (IPT) instance. The scripts target two separate vulnerabilities, which the operator's tooling labels as CVE-2026-71880 and CVE-2026-71878.

LabelBug classEntry pointScripts
CVE-2026-71880 (opens in a new tab)FreeMarker SSTImanage/metadata-basic.do resource metadata fieldsipt_check_ssti.py, ipt_preview_ssti.py, ipt_ssti.py, ipt_ssti_500.py, ipt_ssti_ctrl.py, ipt_ssti_inc.py, ipt_ssti_ls.py, ipt_ssti_sq.py, ipt_ssti_rce.py
CVE-2026-71878 (opens in a new tab)Missing auth on first-run setup flowsetupDefaultAdministrator.doipt_cve71878.py, ipt_cve_interno.py, ipt_race.py

The scripts can be summarized as follows:

  • ipt_check_ssti.py and ipt_preview_ssti.py locate the injection point.

  • ipt_ssti.py, labeled internally as CVE-2026-71880, exploits a FreeMarker ?interpret server-side template injection through IPT resource metadata.

    Image
    Figure 7: ipt_ssti.py shows automated FreeMarker SSTI testing against the JBRJ IPT application.
  • ipt_ssti_500.py, ipt_ssti_ctrl.py, ipt_ssti_inc.py, ipt_ssti_ls.py, ipt_ssti_sq.py, and ipt_ssti_rce.py iterate on that SSTI, capturing error bodies, testing file reads, and eventually publishing a private IPT resource whose description field carries the payload.

  • ipt_cve71878.py and ipt_cve_interno.py target a second, separately labeled bug, CVE-2026-71878, by posting to IPT's setupDefaultAdministrator.do endpoint to create an extra administrator account under the email ipt.ops.2026@jbrj.org and a password redacted here as Jb_REDACTED_.

    Image
    Figure 8: ipt_cve71878.py shows an automated exploitation of the IPT administrator creation flaw.
  • ipt_race.py runs the CVE-2026-71878 exploit as a race condition, flooding IPT's origin-validation threads so isBaseURLValid() times out and the check can be bypassed.

  • ipt_sql_source.py, ipt_fileurl.py, ipt_fileurl_mp.py, ipt_next.py, ipt_analyze.py, ipt_commit.py, ipt_cte_write.py, and ipt_dblink.py build out IPT's own JDBC "SQL source" and DuckDB integration features into a file-read and command-execution primitive, including forcing committed writes via dblink_exec so they survive IPT's normal transaction rollback.

  • ipt_duck_files.py, ipt_duck_sqli.py, ipt_duckdb.py, and ipt_duckdb2.py push that further, using DuckDB's read_blob, read_csv, and COPY functions to read arbitrary host files and write new ones through IPT's own database layer.

From SSTI to a JSP Web Shell and Confirmed Code Execution

After gaining file-write access, the attacker used 14 scripts (ipt_jsp_fauna.py through ipt_jsp_fauna13.py) to develop and update a JSP web shell. The shell was written through IPT’s DuckDB file-write function rather than a separate upload flaw, while other scripts handled the remaining post-exploitation steps:

ScriptPurpose
ipt_jsp_cont.pyContinued shell interaction, first io_uring capability probe
ipt_jsp_decrypt.py, ipt_jsp_secrets.pyDecrypting and reading application secrets through the shell
ipt_jsp_fstab.py, ipt_jsp_recon.py, ipt_jsp_recon2.py, ipt_jsp_svcs.pyHost and mount reconnaissance
ipt_jsp_iouring.py, ipt_jsp_lpe_cves.pyLocal privilege escalation preconditions, described by the operator's own docstring as testing "recent Linux LPE CVE preconditions inside IPT tomcat container"
ipt_jsp_iptuser.py, ipt_jsp_pg.py, ipt_jsp_sqlxml.pyIPT and PostgreSQL configuration extraction
ipt_jsp_privesc.py, ipt_jsp_privesc2.py, ipt_jsp_su.pyContainer privilege-escalation enumeration
ipt_jsp_sshhost.pyDrops a JSch SSH client into the container to spray port 22 on nearby hosts

ipt_tomcat_rce.py, ipt_tomcat_shell.py, and ipt_tomcat_pivot.py drive the same shell to enumerate the Tomcat service, write a cleaner command-execution JSP, and pivot toward other internal hosts. ipt_persist.py plants a backup copy on the container's EFS mount, with an inline comment telling the tool not to touch /srv/ipt/config, and ipt_restore.py re-drops the JSP if a container rebuild wipes it.

Image
Figure 9: IPT DuckDB-based command execution and host reconnaissance.

The directory contains 29 raw output files, all prefixed with rce_, capturing the output returned by the executed commands.

FindingFileValue
Shell identityrce_id.txtuid=999(tomcat) gid=999(tomcat) groups=999(tomcat)
Kernel/OSrce_uname.txtLinux 66f0afa1a05f 6.8.0-1047-aws, Ubuntu 24.04
Cloud regionrce_hosts.txtDNS search domain sa-east-1.compute.internal, AWS São Paulo
File write confirmedrce_write_jsp.txt, rce_write_txt.txtJSP and marker files written under the Tomcat webroot at 10:44 UTC
IPT configrce_ipt_props.txtBase URL and administrator contact address from IPT's own properties file
Config file inventoryrce_aws_files.txtEvery .properties/config file discovered under the Tomcat webapps directory
Image
Image
Figure 10: rce_id.txt and rce_uname.txt, confirming interactive command execution as the Tomcat service account.

Two Stolen Sets of AWS Temporary Credentials

extract_imds.py, ipt_imds_role.py, ipt_imds_ssti.py, and ipt_imds_url.py abuse the IPT/Tomcat foothold to reach the EC2 Instance Metadata Service. The operator recovered two sets of temporary AWS credentials from the instance metadata service, associated with two IAM roles.:

Source fileIAM roleRetrieved (UTC)Expired (UTC)AccessKeyIdSecretAccessKey
aws_ec2_idcred.json(unnamed instance role)07:56:0814:15:55AS_REDACTED_zC_REDACTED_
aws_ssm_role.json, rce_imds_role.txt_redacted_09:54:1415:54:53AS_REDACTED_xV_REDACTED_

ipt_aws_enum.py and ipt_aws_enum2.py use the boto3 SDK with the stolen role keys to enumerate AWS services region by region.

Image
Figure 11: aws_ssm_role.json, one of two stolen AWS IMDS temporary credential sets recovered from the compromised Tomcat container.

ipt_aws_logs.py uses the same credentials to query CloudWatch log groups, streams, and events, likely seeking additional secrets or evidence of the intrusion in JBRJ's logging infrastructure.

Image
Figure 12: ipt_aws_logs.py querying CloudWatch logs using the stolen SSM role credentials.

rce_imds_list.txt and rce_imds_net.txt capture the IAM role name and the container's internal network details that enabled the IMDS pivot.

Credential Harvesting Across JBRJ's Application Stack

With access to IPT, the operator started targeting other systems. access.txt, a notes file kept by the operator, records the accounts they tried, the passwords that worked, and brief notes about what each account could access. The full scope is easier to understand in one table before looking at how each application was accessed:

ApplicationSourceRole/Note
GBIF IPTipt_cve71878.py, access.txtRogue admin account created by the operator
JABOTaccess.txtStaff/administrator, account ID X
JABOTspray_edalcin.pyNamed administrator account the insert scripts were told not to touch directly
jabotimageaccess.txt, jabotimage_admin.pyADMINISTRADOR, named in comments as the system's original developer
jabotimageaccess.txtOPERADOR
CITSmartipt_img_citsmart.pyIT service management console
CKANckan_verify.pyOpen-data portal
Fauna do Brasil (Jardim DB)access.txtDecrypted from a JDBC connection pool secured with a PBE passphrase
Fauna do Brasil (Jardim DB)access.txtSame decryption pass
Fauna do Brasil (application login)access.txtUnlocks a self-built PostgreSQL instance separate from Jardim
Fauna do Brasil (self-created operator)access.txtInserted directly into fauna_ca.op_c_a with profile level 8 (GerenteDeUsuarios)
Fauna do Brasil (existing librarian)access.txtLive, pre-existing account, not created by the operator
Fauna do Brasil (five staff/partner accounts)fauna_auth_hashes.txticmbio and sibbr are Brazil's federal conservation agency and national biodiversity information system; wcmc is an international conservation-monitoring partner
GBIF.BR organization tokenaccess.txtIdentical string to the CKAN secret above; the operator reused one secret across two unrelated systems
DataCite API keyaccess.txtControls DOI minting for JBRJ's published datasets
IPT web-service passwordaccess.txtFound alongside the Jardim database secrets
PostgreSQL (Jardim schema)ipt_insert_ops.py, ipt_write_user.pyOperator's own account inserted directly into the Jardim and jabot_image databases

JABOT and jabotimage: A Ten-Script Account-Takeover Pipeline

The ten scripts form a step-by-step workflow for JABOT and its image system. The docstrings in each script describe the changes made at each stage, making it possible to follow the process from one script to the next:

i
Figure 13: Ten-script JABOT and jabotimage pipeline showing the progression from authentication to data access.

The sequence of unlocking accounts, cracking a reset token, setting a new password for a real staff account, and then moving into file inclusion and upload-based RCE testing shows a deliberate, staged compromise.

Image
Figure 14: "recadastro" used in the jabot_takeover.py script connected with a staff account gives the operator a working login.

Fauna do Brasil: A Second, Self-Built Database Foothold

The credentials in access.txt provided access beyond the five bcrypt-hashed application accounts. The notes also exposed database credentials, privileged accounts, API credentials, and reused secrets across multiple systems:

  • A Java PBE-protected JDBC connection pool was listed under the internal label "Carla Maria Luise". The operator manually decrypted it and recovered plaintext credentials for florarj and ipt, both associated with the Jardim database.

  • The credential fauna/!f_REDACTED_ provided access to a separate PostgreSQL environment. The environment was PostgreSQL 15.1 on Ubuntu and included the fauna and fauna_ca databases.

  • The PostgreSQL environment exposed supabase_admin and postgres superuser accounts.

  • The operator wrote directly to fauna_ca.op_c_a and created an account named iptops26 with password Op_REDACTED_ and profile level 8 (GerenteDeUsuarios, or user manager).

  • The same iptops26 credentials were also added to PostgREST's authentication table, providing an API-level identity in addition to the database record.

  • The notes contained credentials for an existing librarian account, rforzza / fl_REDACTED_, along with other staff accounts protected by weak passwords.

  • Other exposed secrets included a GBIF.BR organization token and a DataCite API key used for DOI minting for JBRJ datasets.

  • The GBIF.BR organization token was also reused as the password for the CKAN centralit account, linking credentials across two otherwise separate systems.

Image
Figure 15: access.txt shows the operator's own running notes.

Note: fauna_crack.py runs the hashes against rockyou.txt and a separate fauna_wl.txt wordlist. The custom list includes the target's name as well as plaintext passwords that had already been recovered from IPT and Jardim. This shows that credentials found in one part of the environment were being reused to test access to another.

CKAN, CITSmart, and the Downstream RDS Instance

ipt_pg_dump.py, ipt_pg_grants.py, ipt_pg_plain.py, ipt_pg_plain2.py, and ipt_pg_users.py query JBRJ's PostgreSQL instance, identified in the code comments as JBRJ RDS instance, through IPT's SQL source feature. The scripts specifically look for short, plaintext-recoverable passwords in the Jardim application schema.

ckan_rds.txt documents a separate PostgreSQL finding involving an internally labeled CVE-2026-42031 (opens in a new tab) ts_rewrite issue. The issue was confirmed against the ckan_default role, which was not a superuser, on RDS host running PostgreSQL 16.13.

  • The flaw provided a CONNECT path to multiple databases.

  • The affected databases covered several parts of the institute's infrastructure, including the data catalog, GIS, and IT service management systems.

  • The operator could also enumerate admin database roles on the instance and named staff accounts.

  • ckan_users.json contains a dump of the CKAN user table, including account IDs, creation dates, sysadmin flags, and named staff and partner-organization accounts.

  • The dump also includes accounts belonging to JBRJ IT staff that were marked with the sysadmin flag.

Image
Figure 16: ckan_users.json, a harvested dump of the CKAN portal's user table, showing sysadmin flags, creation timestamps, and named staff and partner-organization accounts.

Note: reuse_flora.py and reuse_gbif_pw.py try passwords recovered elsewhere in the campaign against CITSmart, CKAN, and Matomo. spray_edalcin.py and spray_plain.py test a small set of weak, organization-related passwords against the same applications. The end of access.txt also contains persistence notes, including a hidden loot directory at /srv/ipt/logs/.dw/ and a note that daily access is handled through ipt_sh.py "id".

Kernel Exploit Staging on the Compromised Container

iouring_probe.c is a small, self-contained io_uring_setup() syscall probe used to check whether the target kernel exposes the io_uring interface before attempting an io_uring-based local privilege escalation. ipt_jsp_iouring.py runs the same check remotely through the JSP shell, while ipt_jsp_lpe_cves.py tests preconditions for recent Linux LPE CVEs, as described in its own docstring.

Note: The scripts show that the operator was testing ways to move beyond the low-privileged tomcat account.

Image
Figure 17: iouring_probe.c, a minimal io_uring capability check staged alongside the confirmed Tomcat RCE and matched by ipt_jsp_lpe_cves.py's remote CVE-precondition probe.

A Zimbra, Active Directory, and Helpdesk Hunting Kit Live Inside Junta Comercial do Estado do Ceará

The open directory at 111.90.145[.]92:18082 contains 321 files, most of which are small PHP, PowerShell, Python, C#, or compiled JAR utilities.

The files appear to have been deployed for activity against JUCEC, the Junta Comercial do Estado do Ceará (Ceará's state Board of Trade), and its e-Simples business registration portal.

The servers below were identified from IP addresses that recur across dozens of scripts:

RoleHow it was reached
Active Directory domain controllerLDAP bind, then SMB as _redacted_\Administrator
File server, JUCEC document and backup shares, "Núcleo de Tecnologia"SMB as _redacted_.sync and _redacted_
Openfire XMPP/chat server, port 9090Directory-traversal to an admin JSP endpoint
Microsoft SQL ServerLive connection string with an application account
Zimbra mail serverSOAP admin probing, SMTP spraying
Operator's own WampServer relay inside the networkAlready fully controlled, used to stage further attacks
Unidentified internal hostProbed, not confirmed compromised

A Shared Webshell Key Ties the Kit Together

I found the same hardcoded key, Hx_Redacted_, in 34 different webshells across the directory, spanning PHP, ASP.NET, JSP, Python, and PowerShell. The analysis shows that the webshells support directory listing, file reading, and command execution, as shown in the table below.

File(s)PlatformWhat it does
mini.php, gate.phpPHPBase shell with three core functions: directory listing (ls), file reading (cat), and command execution (c)
g.aspxASP.NET / IISSame base commands, ported to execute cmd.exe on IIS hosts
hxok.jspJava / TomcatSame base commands, ported to execute /bin/sh on Tomcat hosts
p.phpPHPAdds cu to fetch an arbitrary URL with curl.exe, and smtp to speak SMTP over a raw socket to a Zimbra server while spoofing noreply@jucec.local
setup.phpPHPUses GLPI's GLPIKey and Toolbox::sodiumDecrypt classes to decrypt and return stored SMTP, proxy, and database passwords as JSON
lang-english.phpPHPDisguised as a language file; its drop2 parameter fetches p.php from the operator's port 18082 directory and writes it to two IIS webroots
da.phpPHPObfuscated 50 KB loader that builds function names with chr(), decodes a hex-encoded blob, and executes it at runtime
drop.ps1PowerShellCopies the shell to the operator's internal WampServer relay over SMB and confirms access with whoami
drop.pyPythonUses the planted shell to deploy a ScriptCase NetMake config-decoder probe and zadm.php, the Zimbra password-spray script discussed earlier
gsc_run.pyPythonGeneral-purpose command runner for the key-gated shell
sc.pyPythonCore ScriptCase task runner over HTTP
scclean.pyPythonDeletes dropped files after a task is completed
scfin.pyPythonVerifies that a task completed successfully
scinv.pyPythonInventories installed applications on the reached host
scpersist.pyPythonRe-deploys the shell if it is removed
scsvc.pyPythonChecks service status on the reached host
scof.pyPythonOpenfire-specific variant of the command runner
sc-* and persist-* scriptsPowerShell / PHPCopies the shell onto newly reached hosts
z*-run.ps1 scriptsPowerShellZimbra-specific launchers for the same shell
zcb.txt, marker.txtTextMarker files containing HIT or pwn, used to confirm that a new location is writable before staging additional files

Zimbra Mail Server: SOAP Reconnaissance, Authentication Bypass Testing, and SMTP Spraying

The port 18082 directory contains more than 50 scripts and support files related to Zimbra. The main findings are:

  • Access paths: The scripts cover unauthenticated admin SOAP reconnaissance on port 7071, account-level SOAP authentication, SMTP on ports 25 and 587, and direct SSH access.

  • Credential spraying: nspray.ps1 tests ten credential pairs against four named JUCEC staff members across jucec.ce.gov.br and esimples.ce.gov.br.

  • SMTP testing: smtptryp.ps1 tests another ten passwords against a single mailbox.

  • Mailbox access: zmail.py successfully logs into glpi@esimples.ce.gov.br, obtains a live Zimbra session token, and uses it to search the account's inbox. This is the only confirmed mailbox compromise found in the directory.

  • Credential reuse: The same credentials are later tested against Gmail IMAP.

  • Internal relay: The already-compromised host 10.25.x.x is used to relay SOAP traffic, making the requests appear to originate from an internal system.

  • File deletion: zrm.ps1 deletes eighteen named files after the activity is complete.

Zimbra attack chain big
Figure 18: The complete Zimbra compromise chain against 10.25.x.x, from unauthenticated SOAP fingerprinting to a confirmed mailbox login (zmail.py).

Active Directory: Working Domain Credentials and NTDS Extraction

Thirteen scripts handled the initial directory enumeration using the reused _redacted_.sync credential. They connected to the domain controller and enumerated the environment before elevated access was available. Two scripts, next.ps1 and smb2.ps1, later used the recovered _redacted_\Administrator credential to access the domain's administrative shares.

  • Initial enumeration: Thirteen scripts used _redacted_.sync to query the domain controller and enumerate the directory.

  • Administrator access: next.ps1 and smb2.ps1 used the recovered _redacted_\Administrator credential with full domain administrator privileges.

  • Administrative shares: Both scripts accessed the C$ and ADMIN$ shares directly.

  • Lateral movement: The activity then included SSH-based lateral movement.

  • Shadow copy access: A Volume Shadow Copy was triggered remotely through WMI.

  • Credential database extraction: The scripts accessed ntds.dit, SYSTEM, and SECURITY from the shadow-copy device path, allowing the domain's password hashes to be extracted without accessing the live database files.

Ad ntds flowchart v2
Figure 19: Domain recon to NTDS extraction: from a low-privilege LDAP bind to domain-admin share access, Volume Shadow Copy, and direct extraction of ntds.dit, SYSTEM, and SECURITY.

File Share Hunting and Data Exfiltration

A large group of PowerShell scripts systematically walks JUCEC's internal file shares looking for backups and sensitive documents.

The activity ranges from broad share enumeration to targeted collection of specific files and directories.

  • Backup and file-share discovery: huntbkp.ps1, huntc.ps1, huntex.ps1, huntftp.ps1, huntip.ps1, huntip2.ps1, huntn.ps1, huntdir.ps1, huntz.ps1, huntti.ps1, bkp.ps1, bkp2.ps1, bkp3.ps1, bkp4.ps1, and bkp5.ps1 enumerate JUCEC's internal shares and backup infrastructure, including "Centro de Eventos" folder, and the D:\JUCEC share.

  • Targeted file collection: huntcs.ps1, ticopy.ps1, ticopy2.ps1, tifind.ps1, and tifind2.ps1 search for and copy PGFN_Acessos.csv from the Núcleo de Tecnologia directory.

  • Directory archiving: sczip.ps1 and scbox.ps1 locate and archive the Núcleo de Tecnologia directory through _redacted_.local.

  • Zimbra file collection: getxlsx.ps1 copies spreadsheet files containing "Zimbra" in their filenames from a mailing-list share.

  • Local backup discovery: readbkp.ps1 inventories the C:\Backup Sistemas directory on the drop host.

  • Administrator profile discovery: scuser.ps1 enumerates the local Administrator's Desktop, Documents, and Downloads folders.

  • File and software discovery: arqfind.ps1 searches for cached share listings, while findbr.ps1 checks for Chrome installation paths.

  • Host reconnaissance: nutec.ps1 checks host liveness, and svcs.ps1 enumerates listening TCP ports and running services on the drop host.

GLPI Helpdesk: Database Dump and Persistence Through a Scheduled Task

gsql.py, gsenha.py, gtkt.py, gldump.py, and gldump.sh all authenticate to a local MySQL instance against database glpidb, using the password redacted as ju_REDACTED_, dumping mail collector configuration (including any stored SMTP credentials), ticket follow-up content, and searching ticket bodies for keyword matches.

cron.txt is a crontab entry that copies a cached plugin metadata file over the top of GLPI's own setup script every 25 minutes, a lightweight file-based persistence mechanism, and gfin.py/gfix.py install and verify that crontab entry.

Image
Figure 20: Malicious cron job configured to run every 25 minutes.

Additionally, gclean.py is the corresponding cleanup script, removing leftover Python files from /tmp and running a final gfix.py pass. gpersist.py separately drops a mini.php-style webshell for GLPI-adjacent persistence, and gsc_run.py is a thin command-runner wrapper against that shell.

osTicket: Configuration Hunting and CAPTCHA Harvesting

huntost.ps1 searches JUCEC's web roots for osTicket configuration strings, effectively bridging the GLPI and osTicket hunting efforts described below.

Image
Figure 21: PowerShell reconnaissance script that searches local and network-shared web directories for osTicket configuration files, credentials, SMTP settings, salts, and other sensitive indicators, then writes matching file paths to huntost.txt.

ostfind.php, ostfind2.php, and ostgrep.php scan internal IP ranges and JUCEC web roots for osTicket's ost-config.php file and its secret salt.

ostbak.ps1 retrieves backup copies of the same configuration file over HTTP. ostm.php and ostmysql.php then test a short list of MySQL root and osTicket credentials against port 3306 on the identified host, including several root password combinations.

ostopen.php, along with ostbody.txt and scpbody.txt, submits or replays pre-built osTicket support-ticket requests, including one populated with the real name and email address of a JUCEC staff member (_redacted_@jucec.ce.gov.br). This appears to test whether the ticketing form could be abused.

ost_captcha.png and ost_captcha_live.png contain CAPTCHA images collected from the login form. osthi.php is a one-line PHP version fingerprinting probe, while ostlink.ps1 and ostscan.ps1 perform additional connectivity and link-discovery checks against the same host.

Openfire Chat Server Compromise

A large family targets Openfire and the path from unauthenticated access to persistent tooling runs through five stages:

StageScriptsResult
1. Directory traversalofjsp.ps1, ofjsp2.ps1Reaches /setup/setup-s/%u002e%u002e/%u002e%u002e/plugins/..., an admin-only JSP execution path, without authentication
2. Log exposureoflog.php, oflog3.php, oflog4.php, oflog.htmlPulls Openfire's web-accessible log viewer
3. Admin loginofadm.ps1, ofadmjsp.ps1, oflogin.ps1, oflogin.phpCredential pair openfire / 0P_REDACTED_
4. Shell deploymentofcmd.php, ofsh.php, ofire.php, ofchk.ps1Command-execution shells dropped post-login
5. Disguised plugin uploadofadd.php, ofup.php through ofup10.ps1, of5.ps1 through of13.ps1Three compiled jars (healthcheck.jar, syshealth.jar, sysprobe.jar) uploaded as fake monitoring plugins, each beaconing to ofbeacon.php

jrun.ps1 uses a captured Openfire session cookie (JSESSIONID plus CSRF token) to run authenticated commands directly, including reading the server's XMPP and Kerberos keytabs.

Image
Figure 22: PowerShell-based Openfire command-execution script querying system credentials, local services, and archived messages for sensitive information.

Microsoft SQL Server: JUCEC's Internal EasyCapture Application

sql2.ps1, sqldump.ps1, and sqlq.ps1 connect directly to a Microsoft SQL Server instance using the connection string _REDACTED_, querying both the named application database and the server's own master database.

sqmore.php/sqmorego.ps1 and sqscan.php/sqrun.ps1 do broader port scanning of nearby internal hosts for the same class of service.

sqpwn.php/sqpwngo.ps1, sqweb.php/sqgo.ps1, sqwm.php, sqwm2.php/sqwmgo.ps1, sqint.php/sqintgo.ps1, sqauth.php/sqau.ps1, sq10000.php/sq10kgo.ps1, and sqsmb.php/sqsmb.ps1 form a related family of SOAP and web-service probes and SMB session tests, reusing the same _redacted_\google.sync credential over SMB.

mc.py and mcget.py test an exposed Memcached instance, capable of leaking whatever keys the operator could guess or brute-force from that cache.

Internal Drop Server Management and Cleanup

Another internal host appears to function as an operator-controlled internal staging and relay server within JUCEC's network. The tooling stored on the host can be grouped into the following activities:

  1. Application discovery and ScriptCase enumeration

    • scapps.ps1 and scfast.ps1 enumerate five application directories: alan, auxiliar, dados, jaux, and mac.
    • sclook.ps1 checks whether a NetMake ScriptCase development environment is installed.
    • findcfg.php, cfg2.php, cfg3.php, cfg4.php, and cfg5.php locate and extract JUCEC's internal ScriptCase configuration files, including a production database configuration and an SQLite database containing ScriptCase developer credentials.
    • dec.php and decsc.php decrypt ScriptCase's proprietary configuration format to recover plaintext configuration values.
  2. Command execution and tool staging

    • scmysql.ps1, scof.py, scget.py, scget2.py, scinv.py, scw.py, scw2.py, scfin.py, scsvc.py, and sc.py act as lightweight command-execution wrappers.
    • soap.zip, usoap.zip, usoap2.zip, and usoap3.zip, together with their corresponding PowerShell launchers, provide additional compressed toolsets for deployment.
  3. Webshells and additional payload delivery

    • d.z is a raw binary blob containing an embedded .php file signature.
    • da.php is a heavily obfuscated, multilayer-encoded PHP loader that uses base64 encoding and custom string transformations before dynamically constructing an eval call.
    • Its behavior is consistent with a generic obfuscated backdoor rather than a payload specific to Zimbra or JUCEC.
    • scclean.py, scdec.ps1, and scdrop.ps1 support staged-file cleanup and decryption activities on the host.
  4. Browser and session-data collection

    • chacc.php searches Chrome's saved-preferences data for stored JUCEC-related session information.
    • chmail.ps1 and chmail2.ps1 interact directly with the Chrome process using native Windows API calls through inline C# code.
    • chtask.ps1 and runchrome.bat terminate and relaunch Chrome under a specified user profile, apparently to obtain a fresh and unlocked browser session.
  5. Google Cloud Directory Sync credential decryption

    • dec.bat and dec2.bat invoke Google Cloud Directory Sync's encrypt-util.exe to decrypt stored configuration secrets, including an operator-held encrypted value represented here as s5_REDACTED_.
    • decgcds.ps1 and decoauth.ps1 independently implement the same AES decryption process in PowerShell and operate on captured encrypted values labeled ldap and def.
    • jar.bat and jarls.ps1 inspect GCDS's DirSync.jar, searching for cryptographic routines using strings such as Encrypt, Crypto, Cipher, PBE, and Generate. This appears to support the subsequent decryption attempts.
  6. Captured Google authentication material

    • ga_ac2dm.txt, ga_ah.txt, ga_gm.txt, and ga_oauthmail.txt contain captured Google ClientLogin requests associated with _redacted_@jucec.ce.gov.br.
    • The requests contain a password in URL-encoded form, represented here as ju_REDACTED_, and request device-sync, mail, and OAuth-related tokens.
    • gauth.txt is another variant of the same authentication request and identifies its source as Jucec-gcds-1.0.
  7. Direct Gmail session access through Chrome DevTools

    • gdump.cs, gdump3.cs, and gdump4.cs are successive versions of a C# utility that connects to Chrome's local DevTools Protocol endpoint at 127.0.0.1:9222.
    • The utilities locate an already authenticated Gmail tab and interact with the session through the browser debugging interface, allowing mail to be accessed without directly supplying the account password.
    • glogin.cs performs a related function by automating Gmail login through the same debugging interface.
    • These tools require Chrome to be running with remote debugging enabled and therefore appear intended for use on a previously accessed workstation.
  8. Scheduled-task persistence

    • mk.bat, sc-mk.bat, persist.ps1, persist-sc.ps1, and persist-sc2.ps1 create or maintain a Scheduled Task named WaaSHealthCheck.
    • The task is designed to resemble a legitimate Windows Update health-check task.
    • It periodically copies a disguised PHP payload, display.dll.php, from C:\ProgramData\Microsoft\Windows\Caches into the web root under names such as hx.php or cache.php.
    • This provides a persistence mechanism independent of the webshells already deployed.
  9. Persistence monitoring and secondary payload deployment

    • sc-q.php/sc-q.ps1 and sc-task.php query the status of the WaaSHealthCheck task and identify the currently logged-in user.
    • task1.ps1 extends the same copy operation to a second destination disguised as an oficios/imagens document or image directory.
    • runc2.ps1 through runc5.ps1, runcfg.ps1, rungrep.ps1, runls.ps1, and runsoap.bat provide simple copy-and-execute functionality for staging PHP and SOAP payloads on the internal server.
  10. Network probing and auxiliary utilities

    • probe.py provides generic connectivity testing.
    • sites.py checks URL responses.
    • listen.py implements a raw TCP listener on port 18099.
    • untar.py retrieves and extracts a remote archive from http://x.x.x.x/mbox.tgz.
  11. Cleanup and removal

    • rmd.ps1 and rmfin.ps1 provide final cleanup functionality.
    • These scripts remove the operator's staging directory and specified residual files from the compromised web root.

Target Scope Beyond JUCEC: Eighteen Rio Grande do Norte Municipal Councils

vfy.sh runs the same Hx_redacted_ webshell key against eighteen municipal government domains in the state of Rio Grande do Norte.

  • cmielmomarinho.rn.gov.br
  • camaramunicipaldeextremoz.rn.gov.br
  • japi.rn.gov.br
  • messiastargino.rn.gov.br
  • piparosepasseios.com.br
  • saaeextremoz.com.br
  • camarabomjesus.rn.gov.br
  • camaramunicipaldearez.rn.gov.br
  • camarasjc.rn.gov.br
  • cmcaicaradonorte.rn.gov.br
  • cmmessiastargino.rn.gov.br
  • cmparazinho.rn.gov.br
  • cmsaobentodonorte.rn.gov.br
  • cmserracaiada.rn.gov.br
  • cmserranegradonorte.rn.gov.br
  • caraubas.rn.leg.br
  • serradesaobento.rn.leg.br
  • taipu.rn.leg.br
Image
Figure 23: vfy.sh target list, showing fourteen Rio Grande do Norte municipal câmara (city council) domains checked against the same shared webshell key used inside JUCEC.

Port 18080: A Kernel Privilege Escalation Dropper Ready to Deploy

The smallest directory contains a faithful mirror of the public CVE-2026-31431 (“Copy Fail”) Linux kernel exploit.

The vulnerability affects the kernel’s algif_aead interface and can allow an unprivileged user to modify file contents in the page cache by exploiting a crafted failed decryption request. This can be abused to alter files such as setuid-root binaries or /etc/passwd, enabling local privilege escalation.

The key files in the exposed directory are listed below:

CategoryFiles
DocumentationREADME.md plus four translations (README.ja.md, README.ko.md, README.ru.md, README.zh-cn.md), CITATION.cff, LICENSE-MIT, LICENSE-LGPL, SECURITY.md, Makefile
Exploit sourceexploit.c, exploit-passwd.c, payload.c, vulnerable.c, utils.c, utils.h, compat.h
Pre-built binariespayload, payload.o, utils.o, exploit.__rev2, vulnerable.__rev2
Vendored nolibc runtime66 header files across thirteen CPU architectures, letting the exploit compile to a single static binary with no glibc/musl dependency

exploit.c is the primary dropper. It walks the embedded payload four bytes at a time, patches each window into the target's page cache, then executes it:

C

1for (off_t off = 0; (size_t)off < len; off += 4) {
2    unsigned char window[4] = { 0, 0, 0, 0 };
3    size_t take = (len - (size_t)off >= 4) ? 4 : len - (size_t)off;
4    memcpy(window, PAYLOAD + off, take);
5    if (patch_chunk(file_fd, off, window) < 0) { ... }
6}
7execl("/bin/sh", "sh", "-c", cmd, (char *)NULL);

exploit-passwd.c takes a different approach from variants that target setuid binaries. Instead, it manipulates the cached contents of /etc/passwd so that the current user’s account is temporarily associated with a privileged UID, then invokes su. Authentication still relies on /etc/shadow, but the altered account information can cause the subsequent privilege transition to behave incorrectly. The approach does not require an embedded payload and relies on the fact that /etc/passwd is normally readable by unprivileged users.

payload.c is the payload produced by exploit.c. It is a minimal static program that uses the vendored nolibc implementation, so it does not depend on the system’s C library at runtime. vulnerable.c is a non-destructive checker that determines whether the target is potentially exploitable without actually performing the page-cache corruption.

Image
Figure 24: exploit.c's header comment, documenting the AF_ALG/splice page-cache mutation technique and the exact kernel commit range it targets, in the project author's own words.

Operator Assessment

Portuguese-language documentation, variable names, and comments run through both the port 9998 and port 18082 directories, and every named target across both, JUCEC, e-Simples, JBRJ, and the eighteen Rio Grande do Norte municipalities, is Brazilian. The recovered tooling uses Brazilian Portuguese terminology and references multiple Brazilian public-sector and federal research targets.

Mitigations

  • Patch internet-facing applications and prioritize critical vulnerabilities.
  • Remove hardcoded secrets from source code, scripts, and configuration files.
  • Prevent credential reuse and rotate compromised credentials immediately.
  • Apply least privilege to privileged and service accounts.
  • Secure Active Directory and monitor unusual SMB, LDAP, WMI, and credential-dumping activity.
  • Harden cloud metadata access and enforce least-privilege IAM permissions.
  • Monitor for webshells and unexpected changes to web-facing files.
  • Protect browser sessions and restrict remote debugging interfaces.
  • Secure internal staging servers with proper network access controls.
  • Harden email platforms with MFA and protection against password spraying.
  • Monitor persistence mechanisms such as scheduled tasks, cron jobs, SSH keys, and unauthorized services.
  • Segment critical systems to limit lateral movement.
  • Rotate credentials after compromise, including application, database, domain, cloud, SSH, and session credentials.
  • Hunt for IOCs and attacker behavior across endpoint, network, identity, cloud, and application telemetry.

Conclusion

The exposed directories examined in this investigation provide a detailed view into a multi-stage intrusion operation targeting Brazilian public-sector and research environments.

The evidence includes a confirmed compromise of JBRJ's IPT environment, subsequent access to application and database infrastructure, harvesting of AWS temporary credentials, credential reuse across multiple services, webshell deployment, and preparation for privilege escalation.

A separate directory contained an extensive toolkit targeting JUCEC, including Zimbra, Active Directory, Openfire, GLPI, osTicket, SQL Server, internal file shares, and browser sessions.

The same infrastructure also contained a staged Linux kernel privilege-escalation toolkit and tooling referencing additional Brazilian government targets.

Several recurring weaknesses stand out: exposed services, insufficiently protected credentials, credential reuse, excessive privileges, weak segmentation, and inadequate monitoring of persistence and lateral-movement activity. The investigation also demonstrates how an initial application compromise can become significantly more serious when cloud credentials, database access, administrative accounts, and internal network connectivity are subsequently exposed.

Organizations operating internet-facing applications should therefore treat application security, identity protection, cloud credential isolation, network segmentation, and continuous threat hunting as interconnected controls rather than separate security functions.

MITRE ATT&CK Mapping

Technique IDNameEvidence
T1190Exploit Public-Facing ApplicationSSTI and admin-creation flaws against GBIF IPT; directory traversal against Openfire's plugin-admin path
T1068Exploitation for Privilege EscalationCVE-2026-31431 kernel LPE dropper staged on the same infrastructure; io_uring capability probes run against the compromised Tomcat container
T1505.003Server Software Component: Web ShellJSP shells deployed through IPT's DuckDB file functions; mini.php, gate.php, g.aspx, hxok.jsp, and da.php, all gated behind a shared key
T1078Valid AccountsDomain administrator SMB access as redacted\Administrator; Openfire admin login; reused google.sync AD service account across a dozen scripts
T1110.001 / T1110.002Brute Force: Password Guessing / Crackingfauna_crack.py bcrypt cracking; spray_plain.py and spray_edalcin.py default-password spraying; smtptryp.ps1 SMTP password spraying against Zimbra
T1552.001Unsecured Credentials: Credentials In FilesHardcoded domain and application passwords across dozens of PowerShell and PHP scripts; access.txt operator notes recording working credentials
T1552.005Unsecured Credentials: Cloud Instance Metadata APIipt_imds_role.py and ipt_imds_url.py abusing SSTI/SSRF to reach two separate AWS IMDS credential sets
T1555.003Credentials from Web Browsersglogin.cs and gdump*.cs hijacking an authenticated Gmail session through the Chrome remote-debugging protocol
T1003.003OS Credential Dumping: NTDSntds.ps1 and cpntds.bat triggering remote Volume Shadow Copy creation and reading ntds.dit directly from the shadow device path
T1021.002Remote Services: SMB/Windows Admin Sharesnext.ps1 and smb2.ps1 mounting C$ and ADMIN$ as redacted\Administrator
T1053.005Scheduled Task/Job: Scheduled Taskmk.bat and persist.ps1 creating a WaaSHealthCheck scheduled task for recurring webshell redeployment
T1213Data from Information Repositoriesckan_users.json full user-table dump; AGENTS.md, PAINEL_SQL.md, and related internal JBRJ documentation harvested from the target's own repository
T1074Data Stagedhuntbkp.ps1 and ticopy.ps1 family locating and copying PGFN_Acessos.csv and other files out of JUCEC's internal backup and technology-team file shares

Indicators of Compromise

Network infrastructure

IndicatorASNProviderCountryContext
111.90.145[.]92:9998AS45839Shinjiru Technology Sdn BhdMalaysiaOpen directory holding the confirmed JBRJ/IPT/Tomcat compromise
111.90.145[.]92:18080AS45839Shinjiru Technology Sdn BhdMalaysiaOpen directory holding the CVE-2026-31431 kernel exploit dropper
111.90.145[.]92:18082AS45839Shinjiru Technology Sdn BhdMalaysiaOpen directory holding the Zimbra/AD/helpdesk hunting kit

Files, tools, and identifiers

IndicatorTypeContext
CVE-2026-31431CVE reference"Copy Fail" Linux kernel LPE, packaged as exploit.c/exploit-passwd.c in the port 18080 directory
CVE-2026-71878 (operator label)Internal CVE referenceGBIF IPT setupDefaultAdministrator.do rogue-admin creation flaw, cited by ipt_cve71878.py and ipt_cve_interno.py
CVE-2026-71880 (operator label)Internal CVE referenceGBIF IPT FreeMarker ?interpret SSTI, cited by ipt_ssti.py
CVE-2026-42031 (operator label)Internal CVE referencePostgreSQL ts_rewrite issue used against a JBRJ downstream RDS instance, documented in ckan_rds.txt
WaaSHealthCheckWindows Scheduled Task nameDisguised persistence task created by mk.bat and persist.ps1 on JUCEC hosts
jucec.ce.gov.br, esimples.ce.gov.brTarget domainsCeará state Board of Trade (JUCEC) and its e-Simples business registration portal
Eighteen *.rn.gov.br / *.rn.leg.br domainsTarget domainsRio Grande do Norte municipal câmara (city council) sites, checked in vfy.sh with the same webshell key used inside JUCEC
ipt.jbrj.gov.br, jabotimage.jbrj.gov.br, ckan.jbrj.gov.brTarget domainsJBRJ's GBIF IPT portal, JABOT image system, and CKAN open-data portal

Share

X(opens in a new tab)

Published by @volrant136