12 Years Old NullPtr Dereference That We Found on Oracle VM VirtualBox
Introduction
I and Cem found a Null Pointer Dereference
bug on a header file of Oracle VM VirtualBox version 6.1.6. This post is shared on his blog as well.
To enumerate the input surface of the application, a simple fuzzer was developed. Then, we reported the issue via root cause to the vendor.
This issue has been fixed and addressed in version 6.1.6. More detailed information can be found on the following ticket link.
Simple Argument Fuzzer
To enumerate the input surface of the binary, we used an in-house built script. It extracts sub-commands and arguments by looking the output of each internalcommand like; loadmap, sethdparentuuid, dumphdinfo etc.
Our script creates possible subsets of each of internalcommands without any proper output purposes. On the gif below, how our script processes an example internalcommand is shown.
The script uses the proper values by parsing the config.yaml file.
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
32
33
34
35
filepath: "./"
filename: "filename"
fileformat:
- VDI
- VMDK
- VHD
- RAW
inputfile: "filename"
outputfile: "filename"
uuid: "{ded05829-f34d-4881-9cac-fc5ad72d23c1}"
vmname: "HTC One"
diskname: ""
list of partition numbers: "X"
todo: "X"
password: ""
seconds: "1"
-format:
- -VDI
- -VMDK
- -VHD
- -RAW
internalcommands:
- loadmap
- loadsyms
- sethduuid
- sethdparentuuid
- dumphdinfo
- listpartitions
- converttoraw
- converthd
- modinstall
- moduninstall
- debuglog
- passwordhash
- gueststats
Then baby fuzzer generates pre-payloads, hence it can pass the expected input values within the binary.
The pre-payloads which are generated by babyfuzzer are saved into a text file to be used in the fuzzing step.
In the fuzzing step, for each proper inputs in the pre-payloads text file are edited by replacing the “\{\{ — DUMMY — \}\}” string with many dummy payloads.
While every dummy payload is executed, our crash monitor script traces for the
Segmentation Fault - 11
errors.
Those bunch of codes mentioned above can be found on the GitHub repository.
Root Cause Analysis
We chose the following crasher command, and did a root cause analysis: VBoxManage internalcommands repairhd -format karray fireh
.
To do a backtrace analysis we checked VBoxManage.cpp
since virtualbox is an open source project then we found out how internalcommands argv values are used.
As seen on the screenshot above, the handler functions are mapped with the argv parameters.
The internalcommands
is mapped to the handleInternalCommands
hence we checked that one.
Since pszCmd
equals to repairhd
, CmdRepairHardDisk
function called by passing the following arguments.
a->argc — 1, &a->argv[1], &a-argv[1], a->virtualBox, a-session
As seen in the source code of CmdRepairHardDisk
, the expected program flow would be running properly until the following line.
vrc = VDRepair(pVDIfs, NULL, image.c_str(), format.c_str(), fFlags);
Our format.c_str()
is passed into VDRepair()
as 4th parameter as a pointer which is named pszBackend
. Therefore, we checked it in the source code of the function.
As seen on the screenshot above, the value which is pointed pszBackend
is passed into vdFindImageBackend
function as 1st argument. The returned value of this function is set to rc
variable in the same line as follows.
rc = vdFindImageBackend(pszBackend, &pszBackend);
As seen on the screenshot below, the vdFindImageBackend()
function returns rc which is set to VINF_SUCCESS
on the following line.
int rc = VINF_SUCCESS;
As seen on the source code of the err.h
below, VINF_SUCCESS
is set to 0 (zero).
This means rc equals to 0. As seen on the VD.cpp
rc passed into the RT_SUCCESS()
function in the following line.
If (RT_SUCCESS(rc))
RT_SUCCESS is defined in the vbox/trunk/include/iprt/errcore.h
as RT_LIKELY(RT_SUCCESS_NP(rc))
so rc
value is passed into the RT_SUCCESS_NP()
which is defined on the following piece of code.
The piece of code above has two conditions:
- If
RTERR_STRICT_RC
is defined before then the following works.
The following line calls success() method.
# define RT_SUCCESS_NP(rc) ( RTErrStrictType(rc).success() )
In the constructer of RTErrStrictType()
, protected, the value of m_rc is initialized as 0 (zero) in the following piece of code since it is int32_t.
Then the success() method always returns “True” since it checks 0≥0.
- If it is not defined before then the following works.
“VINF_SUCCESS” is set to “0” as we saw on the following file before.
Then the macro becomes always “True” since it is always 0≥0.
# define RT_SUCCESS_NP(rc) (int)(rc) >= VINF_SUCCESS
It is always True, hence it gets crashed whenever it is dereferenced since a bool object is a NULL pointer.
Conclusion
After root cause analysis of the bug, we raised the ticket on virtualbox.org below.
19579 - VBoxManage Segmentation Fault
Just after creating this ticket, we searched online (site:www.virtualbox.org “Segmentation fault” “VBoxManage”) and saw similar Segmentation fault reports.
Some of them are shared below.
Diff can be found on the following link.
EOF.