Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Executing Byte Array in Go

I'm trying to execute shellcode within a Go program, similar to how you can do it with other languages.

Example 1 - Shellcode in C program

Example 2 - http://www.debasish.in/2012/04/execute-shellcode-using-python.html

All methods have broadly similar techniques - assign the shellcode to executable memory via the OS specific allocation (mmap, virtualalloc, etc) and then execute the code by creating a function pointer pointing to that location before executing.

Here is my horrible hacky example at performing the same thing in Go. The shellcode has manipulations performed on it before being passed to the function, so it's format as a []byte is fixed. Saying that mmap expects a file descriptor passed in which is why the horrible "writing to a tmp file" section exists.

func osxExec(shellcode []byte) {
    f, err := os.Create("data/shellcode.tmp")
    if err != nil {
        fmt.Println(err)
    }
    defer f.Close()
    _,_ = f.Write(shellcode)
    f.Sync()

    b, err := syscall.Mmap(int(f.Fd()), 0, len(shellcode), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_SHARED)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Printf("%p", b)
}

At the end of the code I've got a pointer (slice?) to the code in what I presume is executable memory - but I've got no idea how to cast this address into a function pointer for execution. I asked on some IRC channels but it was suggested that it may not be possible.

Any help is greatly appreciated.

Cheers.

like image 806
Peleus Avatar asked May 15 '15 15:05

Peleus


3 Answers

First, you don't need to (currently) use mmap at all, since go memory is executable. If you do need mmap, you can use anonymous memory and forgo the temp file:

b, e := syscall.Mmap(0, 0, len(shellcode), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_ANON)
copy(b, shellcode)

Otherwise, you can try to use shellcode directly, since it's already backed by a contiguous array.

As for converting the bytes in shellcode to a function, the analog from C would look like:

f := *(*func() int)(unsafe.Pointer(&d[0]))

which creates a function value named f which can then be called like a normal function.

If the shellcode isn't specifically crafted fro Go, and you need to call it from a C stack, it would be easier to do it directly in C using cgo.

/*
call(char *code) {
    int (*ret)() = (int(*)())code;
    ret();
}
*/
import "C"

func main() {
    ...
    // at your call site, you can send the shellcode directly to the C 
    // function by converting it to a pointer of the correct type.
    C.call((*C.char)(unsafe.Pointer(&shellcode[0])))
like image 139
JimB Avatar answered Nov 13 '22 01:11

JimB


There are multiple ways to do it, though I have not tested them myself.

You may want to have a look at https://github.com/nelhage/gojit/blob/master/jit.go It implements a solution based on cgo (which is safer, but slower), and a solution based on a direct call. Look at all the build related functions.

like image 40
Didier Spezia Avatar answered Nov 13 '22 01:11

Didier Spezia


I was trying to learn how mmap PROT_EXEC would work in Go, and came up with the same problem.

I had a hard time to get the []byte to be casted to a function correctly. It is not so obvious as some of the answers here state.

I've came up with an working example of how to do it using a higher level mmap library called mmap-go. In this example we have an executable code of a function that increments one to the passed argument.

code := []byte{
    0x48, 0xc7, 0x44, 0x24, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x48, 0x8b, 0x44, 0x24, 0x08,
    0x48, 0xff, 0xc0,
    0x48, 0x89, 0x44, 0x24, 0x10,
    0xc3,
}

memory, err := mmap.MapRegion(nil, len(code), mmap.EXEC|mmap.RDWR, mmap.ANON, 0)
if err != nil {
    panic(err)
}

copy(memory, code)

memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)
f := *(*func(int) int)(ptr)

fmt.Println(f(10)) // Prints 11

I thought unsafe.Pointer(&memory[0]) would solve my problem like one of the answers suggests. But by doing that you cannot cast the address to a callable function in the correct address. The trick was to go one step further and do this

memory_ptr := &memory
ptr := unsafe.Pointer(&memory_ptr)

instead of

f := *(*func(int) int)(&memory[0])
like image 1
Bruno Calza Avatar answered Nov 13 '22 00:11

Bruno Calza