Recognizing local privilege escalation in ABBYY FineReader
I continue my series about Windows applications privilege escalation discovery. Previously on this series: Steam (CVE-2019-14743, CVE-2019-15316, CVE-2019-17180) and Origin (CVE-2019-19247, CVE-2019-19248). But today it is not about a gaming launcher, but a desktop application ABBYY FineReader.
TL;DR: I am going to tell how you can escalate privileges from user to NT AUTHORITY\SYSTEM in 10 minutes, using the licensing component of ABBYY FineReader. This vulnerability was assigned CVE-2019-20383, here is a link to ABBYY website: https://support.abbyy.com/hc/ru/articles/360008536920-%D0%98%D1%81%D1%82%D0%BE%D1%80%D0%B8%D1%8F-%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B9-FineReader-15.
I downloaded a FineReader trial version from ABBYY website and quickly tested it on whether it is relevant to search for privilege escalation vulnerabilities. The answer was «yes» – there was a service in this product (judging by the name «ABBYY network license server», it is licensing related), which is launched with NT AUTHORITY\SYSTEM privileges by default. I launched ProcMon and started looking closely at the service’s behavior.
A file placed at «C:\ProgramData\ABBYY\FineReader\15\Licenses\Licensing.cnt» grabbed my attention. The service readed something from it, wrote something to it. It looked like an interesting testing candidate. Let’s look at the «C:\ProgramData\ABBYY\FineReader\15\Licenses» folder. This folder has «All - FullControl» legacy ACL from its parent directory «C:\ProgramData\ABBYY\FineReader\15» , which means that it is possible to remove its contents, including «Licensing.cnt».
The service, on discovering that the file was removed, tries to create it in its own strange way. It creates a file named like «tmpXXXX-YYYYYYYYY.tmp», writes some data to it, then renames it to «Licensing.cnt».
Here is a ProcMon log, where this operation is performed twice
For the first time it occurs at 20:36, then at 20:46. Between this timestamps I removed the file again, so it could be created one more time.
Rectangle 1 in the picture shows the situation when the service discovers that the file is missing. Rectangle 2 shows the fact of temporary «.tmp» file creation. Rectangle 3 – renaming of temporary file. Rectangle 4 – repeating of the same operations 10 minutes later.
Let us revise the name format of «tmpXXXX_YYYYYYYYY.tmp». XXXX will always be the same in the context of a single running process; moreover, it is an identifier of thread performing this task. YYYYYYYY is not a constant, but if we look at two consecutive launches (values: 430210515 and 430810515) a hypothesis occurs that this is some timestamp – the difference between numbers (600000) surprisingly matches with 10 minutes difference between launches. A few more test confirmed our assumption.
Summarizing this part – any user can remove «C:\ProgramData\ABBYY\FineReader\15\Licenses\Licensing.cnt» file, then, in cycle, he can constantly look for contents in «C:\ProgramData\ABBYY\FineReader\15\Licenses» folder and at some point discover a file named «tmpXXXX_YYYYYYYYY.tmp» there. A user is now prepared — after removing the file he will know exactly at what time and with what name the file will be created next time.
Now we are going to play with symlinks
Creating symlink from file to file requires administrative rights. But the requirement could be bypassed. Let’s see how we can create symlink from “C:\abc\1” to “C:\def\2”.
First, let’s create an NTFS reparse point (sometimes called NTFS mount point) from “C:\abc” directory to “\RPC Control\”. “\RPC Control\” is not a regular directory, it can’t be viewed by explorer. It’s a system object directory, which stores, for example, named mutexes, events and other similar objects. It’s not clear, why redirection with NTFS reparse point works with it, most likely because similar abstractions are used for directories in filesystem and object directories. There are two requirements for reparse point creation: a source directory must be empty and a user must have write permission on the dir.
It is possible to create symlink from object directory to file without administrative permissions. Let’s create symlink “\RPC Control\1” <-> “C:\def\2”. So all operations on “C:\abc\1” will be redirected to “C:\def\2”.
Originally the process looks like this:
If we know the exact moment of the next attempt to perform this operation, we can create the following symlinks (I skip the current directory name, which is «C:\ProgramData\ABBYY\FineReader\15\Licenses\»):
tmpXXXX_YYYYYYYYA.tmp <-> C:\test\l1\proxy
tmpXXXX_YYYYYYYYB.tmp <-> C:\test\l1\proxy
tmpXXXX_YYYYYYYYC.tmp <-> C:\test\l1\proxy
tmpXXXX_YYYYYYYYZ.tmp <-> C:\test\l1\proxy
Where YYYYYYYYA, YYYYYYYYB, YYYYYYYYC, … YYYYYYYYZ are different timestamps in YYYYYYYY + 10 minutes area (is case the timestamp is running a little bit late).
Then we create a link:
C:\test\l1\proxy <-> C:\test\l2\nope
Note that in reality none of these files exist. They are needed to perform two redirections so the service would work with «C:\test\l2\nope» file, if, for example, the service addresses tmpXXXX_YYYYYYYYB.tmp.
As soon as we discover a creation of «C:\test\l2\nope» file, there should be created another two symlinks:
C:\test\l1\proxy <-> C:\test\l2\payload
Licensing.cnt <-> C:\target\path
The service will continue to write to «C:\test\l2\nope», but it will perform the renaming already after following a new symlink. Thus, instead of renaming, for example «tmpXXXX_YYYYYYYYC.tmp» to «Licensing.cnt», it will rename (replace) «C:\test\l2\payload» to «C:\target\path». In fact, we can place any file with any content in any directory with NT AUTHORITY\SYSTEM permissions.
Schematically it looks like this:
Colored parts represent what occurs because of symlinks.
To escalate privileges using the given primitive is easy – it is possible to replace system process’ dll with your own etc. At this point, I contacted ABBYY representatives and reported the vulnerability.
According to ABBYY, at this moment the vulnerability is fixed.
25.11.2019 – vulnerability discovery
26.11.2019 – requested a security contact from vendor
26.11.2019 – sent the vulnerability report to vendor
09.12.2019 – vulnerability is confirmed by vendor
10.01.2020 – vulnerability is fixed
22.01.2020 – vulnerability is assigned CVE-2019-20383
19.02.2020 – this article publication
Vasily Kravets, Lead Expert