Skip to content

Commit 53b47a5

Browse files
committed
Fixed: Inspect all IDAT chunks and reject trailing bytes in noWebshellInPNG
The previous implementation returned after the first IDAT chunk, leaving any payload in subsequent IDAT chunks or bytes appended after IEND unreachable. PNG (RFC 2083) explicitly permits multiple IDAT chunks whose data must be concatenated before decompression. The loop now accumulates all IDAT chunks into a single buffer and terminates on IEND. After IEND, any trailing bytes cause the file to be rejected. inflate() is called once on the fully concatenated payload, as the spec requires. (cherry picked from commit f8a1118)
1 parent 8852cc0 commit 53b47a5

1 file changed

Lines changed: 30 additions & 17 deletions

File tree

framework/security/src/main/java/org/apache/ofbiz/security/SecuredUpload.java

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.awt.Image;
2424
import java.awt.Transparency;
2525
import java.awt.image.BufferedImage;
26+
import java.io.ByteArrayOutputStream;
2627
import java.io.DataInputStream;
2728
import java.io.File;
2829
import java.io.FileInputStream;
@@ -698,27 +699,39 @@ private static boolean noWebshellInPNG(File file) {
698699
return false;
699700
}
700701

701-
try (DataInputStream stream = new DataInputStream(new FileInputStream(file));) {
702-
byte[] data = new byte[8];
703-
stream.readFully(data); //Read PNG Header
702+
try (DataInputStream stream = new DataInputStream(new FileInputStream(file))) {
703+
byte[] header = new byte[8];
704+
stream.readFully(header); // Read PNG signature
705+
ByteArrayOutputStream idatBuffer = new ByteArrayOutputStream();
706+
byte[] nameBuf = new byte[4];
704707
while (true) {
705-
data = new byte[4];
706-
stream.readFully(data); //Read Length
707-
int length = ((data[0] & 0xFF) << 24)
708-
| ((data[1] & 0xFF) << 16)
709-
| ((data[2] & 0xFF) << 8)
710-
| (data[3] & 0xFF); //Byte array to int
711-
stream.readFully(data); //Read Name
712-
String name = new String(data); //Byte array to String
708+
byte[] lenBuf = new byte[4];
709+
stream.readFully(lenBuf); // Read chunk length
710+
int length = ((lenBuf[0] & 0xFF) << 24)
711+
| ((lenBuf[1] & 0xFF) << 16)
712+
| ((lenBuf[2] & 0xFF) << 8)
713+
| (lenBuf[3] & 0xFF);
714+
stream.readFully(nameBuf); // Read chunk type
715+
String name = new String(nameBuf);
713716
if (name.equals("IDAT")) {
714-
data = new byte[length];
715-
stream.readFully(data); //Read Data
716-
return inflate(data);
717-
} else { //Don't care about other chunks
718-
data = new byte[length + 4]; //Data length + 4 byte CRC
719-
stream.readFully(data); //Skip Data and CRC.
717+
byte[] chunkData = new byte[length];
718+
stream.readFully(chunkData); // Read data
719+
idatBuffer.write(chunkData);
720+
stream.readFully(new byte[4]); // Skip CRC
721+
} else if (name.equals("IEND")) {
722+
stream.readFully(new byte[4]); // Skip CRC
723+
break; // IEND marks end of PNG datastream
724+
} else {
725+
stream.readFully(new byte[length + 4]); // Skip data and CRC
720726
}
721727
}
728+
// Reject any bytes appended after IEND
729+
if (stream.read() != -1) {
730+
Debug.logError("================== Not saved for security reason, PNG has trailing bytes after IEND ==================", MODULE);
731+
return false;
732+
}
733+
// Inflate all concatenated IDAT chunks
734+
return inflate(idatBuffer.toByteArray());
722735
} catch (IOException error) {
723736
Debug.logError("================== Not saved for security reason, wrong PNG IDAT (weird) ==================" + error, MODULE);
724737
return false;

0 commit comments

Comments
 (0)