GCVE-1988-2026-0492
Vulnerability from gna-1988 – Published: 2026-11-02 07:30 – Updated: 2026-11-02 07:30| Vendor | Product | Version | CPE status | |
|---|---|---|---|---|
| Synametrics | Syncrify |
Affected:
unknown
|
guessed |
{
"containers": {
"cna": {
"affected": [
{
"product": "Syncrify",
"vendor": "Synametrics",
"versions": [
{
"status": "affected",
"version": "unknown"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "disclosure via Fulldisclosure"
}
],
"descriptions": [
{
"lang": "en",
"value": "0day Rubbish Research Team is publicly disclosing a vulnerability in Synametrics\nSyncrify 6.2 build 1178, the closed-source incremental backup and\nfile-replication server from Synametrics Technologies, Inc. (United States). The\nproduct is written in Java, distributed for Linux as a tarball archive, and\nserved by an embedded Tomcat 9.0.113 through the vendor\u0027s in-house sradef\nframework, whose single front controller routes every request as\n/app?operation=\u003cop\u003e\u0026st=\u003cstep\u003e to a handler registered in WorkerMappings.xml.\n\nType and root cause. Path traversal, CWE-22 primary, with CWE-269 improper\nprivilege management and CWE-78 OS command injection as contributing\nclassifications that describe the deployment shape converting a file write into\nan execution rather than a defect inside the application. The PublicLink\nhandler, operation=pb, implements the server\u0027s PublicLink client protocol. Its\nchunked-upload branch, selected by st=u, reads a length-prefixed binary frame\nstraight from request.getInputStream(), which means the server\u0027s parameter\nparsing, form decoding and any framework-level parameter validation are all\nbypassed by construction: the field that decides the write destination is not a\nparameter. At PublicLink.java:70-110 the decompiled method reads four big-endian\nint32 values and one length-prefixed UTF-8 string, then does this:\n\n int protocol = readInt(in); // 4-byte big-endian\n int chunkNumber = readInt(in); // read, then not used to bound\n // or place the write\n int totalChunks = readInt(in); // read, then not used either\n int size = readInt(in); // declared length of the body\n String filename = readString(in); // [int32 len][UTF-8 bytes],\n // attacker-controlled\n\n File base = E(); // this user\u0027s PublicLinks folder\n File target = new File(base, filename);\n if (!target.getParentFile().exists()) {\n target.getParentFile().mkdirs();\n }\n BufferedOutputStream out = new BufferedOutputStream(\n new FileOutputStream(target, true)); // append mode\n byte[] buf = new byte[size];\n readFully(buf);\n out.write(buf);\n out.close();\n return 0; // 0 means success to the caller\n\nnew File(base, filename) is reached with no rejection of \"..\", no rejection of\npath separators, no base-name extraction and no canonical containment check.\nbase is /opt/Syncrify/PublicLinks/\u003cuser\u003e/\u003cgenerated-folder\u003e, five components\nbelow the filesystem root, so a chunk name of ../../../../../etc/cron.d/\u003cname\u003e\nleaves the per-user PublicLinks folder and lands a cron fragment, creating any\nmissing parent directory on the way because of the mkdirs() call. The branch\nreturns 0, f() at PublicLink.java:127 converts that into the client reply\ncode=\"1\" with \u003cpayload\u003e0\u003c/payload\u003e, and the host cron daemon then reads the\nfragment as a root-authored job definition and runs its command field through a\nshell on the next minute boundary. The application itself never constructs,\nquotes or invokes a command line, and every execution site in the product uses\nan argument vector with no shell, so this could not have been fixed by argument\nhygiene at the application layer; the execution happens one component later.\n\nThe identity reached was observed rather than inferred, and it is uid=0(root)\ngid=0(root) groups=0(root). Two independent artifacts establish that the writer\nprocess held uid 0: the fragment landed as -rw-r--r-- 1 root root at mode 0644,\nand a file\u0027s owner after creation is the effective uid of the process that\ncreated it; and the scheduled job\u0027s own output reported uid 0, the fragment\nnaming root in its user field, which a scheduler will not honour for a job file\nit does not already trust.\n\nWhat makes this an oversight rather than a design decision is the asymmetry\nwith the rest of the same tree. Three sibling families in the same product,\nwritten against the same client protocol, check the same kind of field and\nreject the same kind of content: the profile-upload, profile-fetch and\nfile-splitter handlers refuse names containing path separators by testing\ncharacter codes with indexOf(47) and indexOf(92); the multipart upload path\ncollapses the client-supplied name through getName() of a constructed File,\nwhich discards every directory component; and the download path runs the name\nthrough indexOf(\"..\") and answers 404 on a match. The chunk-upload branch calls\nnone of them. When one member of a family omits the defense all of its siblings\nimplement, the economical explanation is that the check was not written rather\nthan that it lives somewhere else. There is also no decoding step between the\nsocket and the constructor on this branch, so no encoding trick is involved and\nnone is needed: what the client puts in the frame is what the path is built\nfrom.\n\nHow confirmation works, and what was deliberately not reproduced. Confirmation\nis by host-side artifacts, not by the HTTP reply, and that distinction matters.\nThe reply is a success oracle, so an attacker learns from the network alone that\na write was accepted and can iterate on naming from there, but a successful\nreply is not proof of a successful destination: the code never inspects the\nresolved path, so a wrong traversal depth still yields code=\"1\" and simply\nwrites somewhere else with intermediate directories created as needed. The\nread-back of the marker file is the only sufficient signal. Four controls were\nexercised and the published tool automates one of them. A traversing name and a\nnon-traversing name were driven through identical authentication, identical\nframing and the identical route in the same session; the benign write was\naccepted and landed inside the user\u0027s own PublicLinks folder, and the assertion\nthat neither the cron fragment path nor the marker path existed is what isolates\nthe traversal as the cause of the outcome and of nothing else. A sibling-handler\ncontrol confirmed at the Java level that the multipart family\u0027s getName()\ncollapse, applied to a traversing name against the same PublicLinks base,\nreturns the bare final component and keeps the write inside the root. A dispatch\ncontrol built against st=f instead reached a different void E() overload at\nPublicLink.java:951 that parses a request parameter named ed as a Long and\nperformed no write anywhere; that control is what fixed the sink to this branch\nrather than to the handler in general, since an initial static reading had\nattributed the upload to st=f and the request built on that reading produced no\nfile at all. Authentication controls established that the credential gate is\nlive. The published advisory reproduces the identity string and the frame field\nlengths derived from the field values, but deliberately not the run\u0027s own byte\ncapture, which was not preserved; not the generated PublicLinks folder name,\nwhich embeds the run\u0027s timestamp; not the credential pair, which are mandatory\ntool arguments with no default; and not the per-run fragment name or the marker\npath. The payload was an identity capture into a marker file. No repository data\nwas read, no product file was modified or deleted, and the fragment and the\nmarker were removed after verification together with stopping the service.\n\nScoring. One score is published: 7.5 High,\nCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H. The arithmetic is shown so any\nreader can reproduce it with the CVSS 3.1 base algorithm and its\nround-up-to-the-tenth rule: ISS = 1 - (0.44 x 0.44 x 0.44) = 0.914816; Impact\nfor S:U = 6.42 x 0.914816 = 5.873119; Exploitability = 8.22 x 0.85 x 0.44 x\n0.62 x 0.85 = 1.620146; Base = Roundup(5.873119 + 1.620146) =\nRoundup(7.493265) = 7.5. AV:N is the honest classification of the component\nbecause the vulnerable component is an HTTP handler on a network-bound servlet\nconnector and the credential it accepts exists precisely so that remote backup\nclients can use it; the deployment-dependent exposure of that listener is priced\nin attack complexity rather than smuggled into the attack vector. AC:H carries\ntwo conditions outside the attacker\u0027s control: that the listener carrying this\nroute be reachable from the attacker\u0027s position, and that the host run a\nscheduler that scans /etc/cron.d. PR:L records that the least privileged\nprincipal proven sufficient is an authenticated backup user, the product\u0027s\nlowest-trust account type. Scope is Unchanged, for the identity reason given\nbelow.\n\nA conditional reading of 8.8 High, CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H,\nis published alongside it and is labelled conditional everywhere it appears. It\napplies only where the listener carrying /app?operation= is reachable from the\nattacker\u0027s network position, which is the normal operating mode for a backup\nserver whose clients push data to it remotely, and it removes the first of the\ntwo AC:H conditions by assuming that deployment posture. It computes as\nExploitability = 8.22 x 0.85 x 0.77 x 0.62 x 0.85 = 2.835255, Base =\nRoundup(5.873119 + 2.835255) = Roundup(8.708374) = 8.8. The published score is\n7.5 and never 8.8; a reader whose deployment keeps the portal on a management\nnetwork should read the 7.5.\n\nTwo further readings are published as explicitly rejected, both with their\narithmetic, so that a reader who disagrees with either call can see exactly what\nit is worth rather than take our word for it. The changed-scope variant of the\nsame metrics, 8.6 High,\nCVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H, is rejected because the identity\nreached is the same root identity the server process already holds on the same\nhost, so no component authority boundary is crossed by the outcome: the server\nprocess is root over this host by construction of the deployment, and the\ncompromised scheduler and the compromised server sit inside one authority\ndomain. Its arithmetic: Impact for S:C = 7.52 x (0.914816 - 0.029) - 3.25 x\n(0.914816 x 0.9731 - 0.02)^13 = 6.661336 - 0.533310 = 6.128026; Exploitability\nwith PR at the S:C value of 0.68 = 8.22 x 0.85 x 0.44 x 0.68 x 0.85 = 1.776934;\nBase = Roundup(1.08 x (6.128026 + 1.776934)) = Roundup(8.537357) = 8.6. The\nprivilege gain in this finding is real and substantial, and it is expressed by\nthe metrics meant to carry it, PR:L for the low-trust principal and C:H/I:H/A:H\nfor the totality of what root on this host controls; Scope is not the vehicle\nfor expressing privilege distance and using it that way would double-count the\nsame fact.\n\nThe second rejected reading is the one we want readers to notice, because it is\nthe one that could most easily be misused in our favour. Treating the loopback\nbinding we observed in our own testbed as though it were the shipped default\nyields 7.8 High, CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H: Exploitability =\n8.22 x 0.55 x 0.77 x 0.62 x 0.85 = 1.834577, Base = Roundup(5.873119 +\n1.834577) = Roundup(7.707696) = 7.8. We reject it, and rejecting it lowers the\nnumber we publish by 0.3 rather than raising it. That rejected local-vector\nreading of 7.8 is higher than the 7.5 we publish at AV:N. So the rejection is\nnot a scoring maneuver and cannot be read as one: choosing AV:N with AC:H is the\nconservative reading on this finding, and a reader who disagrees with us about\nthe factory default of that binding ends up with a larger figure than ours, not\na smaller one. The advisory says this itself, in those terms. The reason for the\nrejection is evidential rather than convenient: that loopback binding was\napplied by us, in the configuration, before any testing began, so that the host\ncarried no public exposure while research was in progress, and we hold no\nshipped artifact, no configuration template, no installer script excerpt and no\ndocumentation quotation, that states the factory value of that binding in either\ndirection. Not that it binds publicly by default, and not that it binds loopback\nby default: the premise is unestablished by artifact, which is exactly why the\nattack comple"
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-203",
"description": "CWE-203",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-22",
"description": "CWE-22",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-269",
"description": "CWE-269",
"lang": "en",
"type": "CWE"
},
{
"cweId": "CWE-78",
"description": "CWE-78",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-11-02T07:30:03Z",
"orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"shortName": "VULNARCHIVE"
},
"references": [
{
"tags": [
"technical-description",
"exploit"
],
"url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Oct/21"
},
{
"tags": [
"technical-description"
],
"url": "https://seclists.org/fulldisclosure/2026/Oct/21"
},
{
"url": "https://0day-rubbish.com"
},
{
"url": "https://0day-rubbish.com/blog/synametrics-syncrify-publiclink-chunk-upload-traversal-cron-root-rce"
},
{
"url": "https://github.com/Exploit-Garbage/0day-Rubbish"
},
{
"url": "https://nmap.org/mailman/listinfo/fulldisclosure"
},
{
"url": "https://seclists.org/fulldisclosure/"
}
],
"source": {
"defect": [
"https://seclists.org/fulldisclosure/2026/Oct/21"
],
"discovery": "EXTERNAL"
},
"title": "[0day-rubbish] Synametrics Syncrify 6.2 build 1178 authenticated PublicLink chunk-upload path traversal to /etc/cron.d and root command execution (7.5)",
"x_gcve": [
{
"recordType": "advisory",
"relationships": [],
"vulnId": "GCVE-1988-2026-0492",
"x_vulnarchive": {
"archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Oct/21",
"automated": true,
"contentSha256": "0198cf6df4cd4863eb42fe67179edce7b0dcb72d4de85a3d9766e1d1ac4ef071",
"evidenceScore": 11,
"messageId": "",
"originalUrl": "https://seclists.org/fulldisclosure/2026/Oct/21",
"policy": "vulnarchive-1",
"sourceFormat": "text/html",
"sourcePublishedAt": "2026-10-13T02:23:01Z"
}
}
]
}
},
"cveMetadata": {
"assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
"assignerShortName": "VULNARCHIVE",
"datePublished": "2026-11-02T07:30:03Z",
"dateUpdated": "2026-11-02T07:30:03Z",
"state": "PUBLISHED",
"vulnId": "GCVE-1988-2026-0492"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
How durations are calculated
Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.
Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.