Skip to content

Commit f8a1118

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.
1 parent 11f4e52 commit f8a1118

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;
@@ -712,27 +713,39 @@ private static boolean noWebshellInPNG(File file) {
712713
return false;
713714
}
714715

715-
try (DataInputStream stream = new DataInputStream(new FileInputStream(file));) {
716-
byte[] data = new byte[8];
717-
stream.readFully(data); //Read PNG Header
716+
try (DataInputStream stream = new DataInputStream(new FileInputStream(file))) {
717+
byte[] header = new byte[8];
718+
stream.readFully(header); // Read PNG signature
719+
ByteArrayOutputStream idatBuffer = new ByteArrayOutputStream();
720+
byte[] nameBuf = new byte[4];
718721
while (true) {
719-
data = new byte[4];
720-
stream.readFully(data); //Read Length
721-
int length = ((data[0] & 0xFF) << 24)
722-
| ((data[1] & 0xFF) << 16)
723-
| ((data[2] & 0xFF) << 8)
724-
| (data[3] & 0xFF); //Byte array to int
725-
stream.readFully(data); //Read Name
726-
String name = new String(data); //Byte array to String
722+
byte[] lenBuf = new byte[4];
723+
stream.readFully(lenBuf); // Read chunk length
724+
int length = ((lenBuf[0] & 0xFF) << 24)
725+
| ((lenBuf[1] & 0xFF) << 16)
726+
| ((lenBuf[2] & 0xFF) << 8)
727+
| (lenBuf[3] & 0xFF);
728+
stream.readFully(nameBuf); // Read chunk type
729+
String name = new String(nameBuf);
727730
if (name.equals("IDAT")) {
728-
data = new byte[length];
729-
stream.readFully(data); //Read Data
730-
return inflate(data);
731-
} else { //Don't care about other chunks
732-
data = new byte[length + 4]; //Data length + 4 byte CRC
733-
stream.readFully(data); //Skip Data and CRC.
731+
byte[] chunkData = new byte[length];
732+
stream.readFully(chunkData); // Read data
733+
idatBuffer.write(chunkData);
734+
stream.readFully(new byte[4]); // Skip CRC
735+
} else if (name.equals("IEND")) {
736+
stream.readFully(new byte[4]); // Skip CRC
737+
break; // IEND marks end of PNG datastream
738+
} else {
739+
stream.readFully(new byte[length + 4]); // Skip data and CRC
734740
}
735741
}
742+
// Reject any bytes appended after IEND
743+
if (stream.read() != -1) {
744+
Debug.logError("================== Not saved for security reason, PNG has trailing bytes after IEND ==================", MODULE);
745+
return false;
746+
}
747+
// Inflate all concatenated IDAT chunks
748+
return inflate(idatBuffer.toByteArray());
736749
} catch (IOException error) {
737750
Debug.logError("================== Not saved for security reason, wrong PNG IDAT (weird) ==================" + error, MODULE);
738751
return false;

0 commit comments

Comments
 (0)