0

I was wondering how can I execute bytes (shellcode, basically) in GoLang. Anyway I found something that helped me getting started, check the code below:

package main

import (
    "fmt"
    "log"
    "syscall"
    "unsafe"
)

const (
    MEM_COMMIT  = 0x1000
    MEM_RESERVE = 0x2000

    PAGE_EXECUTE_READWRITE = 0x40
)

var (
    kernel32     = syscall.MustLoadDLL("kernel32.dll")
    VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
)

func SysAlloc(n uintptr) (uintptr, error) {
    addr, _, err := VirtualAlloc.Call(0, n, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE)
    if addr == 0 {
        return 0, err
    }
    return addr, nil
}

func mkprog() error {
    const size = 64 * 1024
    addr, err := SysAlloc(size)
    if err != nil {
        return err
    }
    b := (*[size]byte)(unsafe.Pointer(addr))
    b[0] = 0xc3 // RET 
    b[1] = 0x90 // NOP
    syscall.Syscall(addr, 0, 0, 0, 0)
    return nil
}

func main() {
    err := mkprog()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("HELLO\n")
}

It works, it executes the NOP and the RET and the program exits sucessfully. The thing is: if I replace b[] with a shellcode (WinExec calc.exe) like this

b[0] = 0x33
b[1] = 0xc0                          
b[2] = 0x50                              
b[3] = 0x68
b[4] = 0x2E
b[5] = 0x65
b[6] = 0x78
b[7] = 0x65              
b[8] = 0x68
b[9] = 0x63
b[10] = 0x61
b[11] = 0x6C
b[12] = 0x63              
b[13] = 0x8B
b[14] = 0xC4                          
b[15] = 0x6A
b[16] = 0x01                          
b[17] = 0x50                              
b[18] = 0xBB
b[19] = 0xED
b[20] = 0x2A
b[21] = 0x86
b[22] = 0x7C              
b[23] = 0xFF
b[24] = 0xD3

It does not run anymore. Shouldnt it run properly or am I missing something here?

This is the shellcode in C/Python, for reference:

"\x33\xc0"                          # XOR EAX,EAX
"\x50"                              # PUSH EAX      => padding for lpCmdLine
"\x68\x2E\x65\x78\x65"              # PUSH ".exe"
"\x68\x63\x61\x6C\x63"              # PUSH "calc"
"\x8B\xC4"                          # MOV EAX,ESP
"\x6A\x01"                          # PUSH 1
"\x50"                              # PUSH EAX
"\xBB\xED\x2A\x86\x7C"              # MOV EBX,kernel32.WinExec
"\xFF\xD3"                          # CALL EBX

And the error

Exception 0xc0000005 0x8 0x7c862aed 0x7c862aed
PC=0x7c862aed
signal arrived during cgo execution

main.mkprog(0x0, 0x0)
    C:/Users/guitmz/Documents/Go_test4/test_4.go:64 +0xfe
main.main()
    C:/Users/guitmz/Documents/Go_test4/test_4.go:69 +0x2e

goroutine 2 [runnable]:
runtime.forcegchelper()
    c:/go/src/runtime/proc.go:90
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1

goroutine 3 [runnable]:
runtime.bgsweep()
    c:/go/src/runtime/mgc0.go:82
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1

goroutine 4 [runnable]:
runtime.runfinq()
    c:/go/src/runtime/malloc.go:712
runtime.goexit()
    c:/go/src/runtime/asm_amd64.s:2232 +0x1
rax     0x7fe10
rbx     0x7c862aed
rcx     0x0
rdx     0x0
rdi     0x7ff5ffffd000
rsi     0xc082021ec0
rbp     0x569ae0
rsp     0x7fdf8
r8      0x0
r9      0x50
r10     0x8
r11     0x4d5520
r12     0x3d
r13     0x0
r14     0x0
r15     0x0
rip     0x7c862aed
rflags  0x10246
cs      0x33
fs      0x53
gs      0x2b
Error: process exited with code 2.

Thanks

  • 2
    "does not run anymore" is not an error message. -1 –  May 26 '15 at 20:48
  • added the error, sorry! – Guilherme Thomazi May 26 '15 at 20:51
  • There are multiple issues here - the biggest being that dump seems to be 64 bit - yet you appear to have grabbed some ASM that is a 32-bit sample. You're using the stack to pass parameters which 64 bit Windows does not do (its ABI is "fastcall" which is register based). The code also uses some 32-bit registers where 64 bit ones should probably be used. – Simon Whitehead May 26 '15 at 22:17
  • 1
    similar to: [Executing Byte Array in Go](http://stackoverflow.com/questions/30263520/executing-byte-array-in-go) – Dave C May 26 '15 at 22:32
  • Dave, yes, that's where I got my initial idea from. Hmmm x64 systems can run 32bit code so I don't know if that's the problem, Simon. It didnt worked with a 64bit shellcode either so I have no clue. – Guilherme Thomazi May 27 '15 at 14:01

1 Answers1

1

syscall.Syscall don't do what you think.

It make a System Call, a call to OS kernel function, not an arbitrary jump to a location

Moreover the shellcode expect the C calling conventions, for example the stack pointer to be to a C stack etc. this conditions are not fulfilled in go runtime

Xavier Combelle
  • 10,968
  • 5
  • 28
  • 52
  • Well I understand that. Check http://pastebin.com/eHb6zMyD please, a more elaborated example. This is also working and does not make sense to me why the shellcode I put in it wont run. The shellcode was tested in C, works fine. – Guilherme Thomazi May 27 '15 at 17:14
  • What you pass to SystemCall give a random result. Did you test the way I given ? – Xavier Combelle May 27 '15 at 20:02
  • Yes, no luck. Anyway, so here: https://github.com/debasishm89/C-Codes/blob/master/shellcode-exp.c we have a working C example. I did ported it (except the shellcode itself, which im using other - tested and working fine): http://pastebin.com/mRjGL6G3 is not working either.. i believe is a pretty decent port and it should work but nothing so far.. Windows 8.1 x64 – Guilherme Thomazi May 27 '15 at 21:16
  • This is still not answered! – Guilherme Thomazi Jun 03 '15 at 20:09
  • It is still not answered, maybe it's because it's impossible to answer in one of your satisfying way. Because a C shell code is not suitable to a go program, because (at least) the stack layout are all different – Xavier Combelle Jun 08 '15 at 18:45
  • I dont think that is the case. I have presented a few examples of working code, just wanted to know why some instruction run and others doesnt in Go. – Guilherme Thomazi Jun 09 '15 at 19:50
  • the instructions you use use PUSH and CALL so the C stack. and C call convention – Xavier Combelle Jun 10 '15 at 15:01