Skip to content

Commit

Permalink
Misc
Browse files Browse the repository at this point in the history
  • Loading branch information
hupe1980 committed Aug 21, 2021
1 parent 992c3ca commit 8bc1076
Show file tree
Hide file tree
Showing 12 changed files with 404 additions and 20 deletions.
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# gopwn ![Build Status](https://github.com/hupe1980/gopwn/workflows/build/badge.svg)
# gopwn
![Build Status](https://github.com/hupe1980/gopwn/workflows/build/badge.svg)
[![Go Reference](https://pkg.go.dev/badge/github.com/hupe1980/gopwn.svg)](https://pkg.go.dev/github.com/hupe1980/gopwn)
> Golang CTF framework and exploit development module
This module is strictly for educational purposes only. Usage of the methods and tools for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable laws. Developers assume no liability and are not responsible for any misuse or damage caused by this module.
Expand All @@ -13,14 +15,14 @@ import (
"bytes"
"fmt"

"github.com/hupe1980/gopwn"
"github.com/hupe1980/gopwn"
)

func main() {
p, _ := gopwn.NewProcess([]string{"/binary"})
p.SendLine(append(bytes.Repeat([]byte("A"), 200), gopwn.P32L(0xdeadbeef)...))
out, _ := p.RecvLine()
fmt.Println(string(out))
p, _ := gopwn.NewProcess([]string{"./ctfbinary"})
p.SendLine(append(bytes.Repeat([]byte("A"), 200), gopwn.P32L(0xdeadbeef)...))
out, _ := p.RecvLine()
fmt.Println(string(out))
}
```

Expand All @@ -33,8 +35,10 @@ i := U32L([]byte("\xef\xbe\xad\xde"))
assert.Equal(t, uint32(0xdeadbeef), i) // true
```

### Assembly and Disassembly

### Documentation
See http://godoc.org/github.com/hupe1980/gopwn
See [godoc](https://pkg.go.dev/github.com/hupe1980/gopwn).

### Examples
See more complete [examples](https://github.com/hupe1980/exploit-exercises/tree/main/exploits/go).
Expand All @@ -47,6 +51,7 @@ Usage:
gopwn [command]

Available Commands:
cave Search for code caves
checksec Check binary security settings
completion Prints shell autocompletion scripts for gopwn
cyclic Generation of unique sequences
Expand Down
7 changes: 5 additions & 2 deletions asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,11 @@ func (d *Disassembler) Disam(data []byte, vma uint64) (string, error) {
return "", err
}
var output string
for _, insn := range insns {
output += fmt.Sprintf("0x%x:\t%s\t\t%s\n", insn.Address, insn.Mnemonic, insn.OpStr)
for i, insn := range insns {
output += fmt.Sprintf("0x%-12x% -30x%s %s", insn.Address, insn.Bytes, insn.Mnemonic, insn.OpStr)
if i < (len(insns) - 1) {
output += "\n"
}
}
return output, nil
}
Expand Down
4 changes: 2 additions & 2 deletions asm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ func TestDISASM(t *testing.T) {
t.Run("x86_64", func(t *testing.T) {
assembly, err := DisamX86_64([]byte("\x48\xc7\xc0\x17\x00\x00\x00"), 0)
assert.NoError(t, err)
assert.Equal(t, "0x0:\tmov\t\trax, 0x17\n", assembly)
assert.Equal(t, "0x0 48 c7 c0 17 00 00 00 mov rax, 0x17", assembly)
})

t.Run("i386", func(t *testing.T) {
assembly, err := DisamI386([]byte("\xb8\x5d\x00\x00\x00"), 0)
assert.NoError(t, err)
assert.Equal(t, "0x0:\tmov\t\teax, 0x5d\n", assembly)
assert.Equal(t, "0x0 b8 5d 00 00 00 mov eax, 0x5d", assembly)
})
}
43 changes: 43 additions & 0 deletions binary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package gopwn

import "bytes"

type BinaryReader interface {
Read(p []byte) (n int, err error)
ReadAt(b []byte, off int64) (n int, err error)
Seek(offset int64, whence int) (int64, error)
}

type Cave struct {
SectionName string
Begin int
End int
Size int
Addr int
Infos string
}

func searchCaves(name string, body []byte, offset, addr uint64, infos string, caveSize int) []Cave {
caveBytes := []byte("\x00")
var caves []Cave
caveCount := 0
for currentOffset := 0; currentOffset < len(body); currentOffset++ {
currentByte := body[currentOffset]
if bytes.Contains([]byte{currentByte}, caveBytes) {
caveCount++
} else {
if caveCount >= caveSize {
caves = append(caves, Cave{
SectionName: name,
Size: caveCount,
Addr: int(addr) + currentOffset - caveCount,
Begin: int(offset) + currentOffset - caveCount,
End: int(offset) + currentOffset,
Infos: infos,
})
}
caveCount = 0
}
}
return caves
}
82 changes: 82 additions & 0 deletions cmd/cave.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

import (
"errors"
"fmt"
"strconv"

"github.com/hupe1980/gopwn"
"github.com/spf13/cobra"
)

func newCaveCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "cave [file] [size]",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return errors.New("requires a file and a size argument")
}
if _, err := strconv.Atoi(args[1]); err != nil {
return err
}
return nil
},
Short: "Search for code caves",
SilenceUsage: true,
SilenceErrors: true,
Example: "gopwn cave /usr/bin/ping 200",
RunE: func(cmd *cobra.Command, args []string) error {
size, err := strconv.Atoi(args[1])
if err != nil {
return err
}
fh, bt, err := gopwn.OpenFile(args[0])
if err != nil {
return err
}
var caves []gopwn.Cave
switch bt {
case gopwn.BINTYPE_ELF:
elf, err := gopwn.NewELFFromReader(fh)
if err != nil {
return err
}
defer elf.Close()
caves = elf.Caves(size)
case gopwn.BINTYPE_PE:
pe, err := gopwn.NewPEFromReader(fh)
if err != nil {
return err
}
defer pe.Close()
caves = pe.Caves(size)
case gopwn.BINTYPE_MACHO:
macho, err := gopwn.NewMACHOFromReader(fh)
if err != nil {
return err
}
defer macho.Close()
caves = macho.Caves(size)
}

if len(caves) == 0 {
fmt.Println("\n[-] NO CAVE DETECTED!")
return nil
}

for _, cave := range caves {
fmt.Println("\n[+] CAVE DETECTED!")
fmt.Printf("[!] Section Name: %s\n", cave.SectionName)
fmt.Printf("[!] Section Flags: %s\n", cave.Infos)
fmt.Printf("[!] Virtual Address: %#x\n", cave.Addr)
fmt.Printf("[!] Cave Begin: %#x\n", cave.Begin)
fmt.Printf("[!] Cave End: %#x\n", cave.End)
fmt.Printf("[!] Cave Size: %#x (%d bytes)\n", cave.Size, cave.Size)
}

return nil
},
}

return cmd
}
1 change: 1 addition & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func newRootCmd(version string) *cobra.Command {
SilenceErrors: true,
}
cmd.AddCommand(
newCaveCmd(),
newCyclicCmd(),
newChecksecCmd(),
newCompletionCmd(),
Expand Down
129 changes: 126 additions & 3 deletions elf.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
package gopwn

import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"text/tabwriter"

"github.com/fatih/color"
)

type ELF struct {
path string // Path to the file
file *elf.File
hdr interface{} // elf.Header32 or elf.Header64
arch Arch
endian Endian
symbols []elf.Symbol
raw []byte
}

func NewELF(path string) (*ELF, error) {
f, err := elf.Open(path)
fh, err := os.Open(path)
if err != nil {
return nil, err
}
return NewELFFromReader(fh)
}

func NewELFFromBytes(b []byte) (*ELF, error) {
r := bytes.NewReader(b)
return NewELFFromReader(r)
}

func NewELFFromReader(r BinaryReader) (*ELF, error) {
f, err := elf.NewFile(r)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -49,15 +68,64 @@ func NewELF(path string) (*ELF, error) {
return nil, fmt.Errorf("Unknown endianness %x.", f.Data)
}

if _, err := r.Seek(0, io.SeekStart); err != nil {
return nil, err
}
var hdr interface{}
switch f.Class {
case elf.ELFCLASS32:
hdr = new(elf.Header32)
if err := binary.Read(r, f.ByteOrder, hdr); err != nil {
return nil, err
}
case elf.ELFCLASS64:
hdr = new(elf.Header64)
if err := binary.Read(r, f.ByteOrder, hdr); err != nil {
return nil, err
}
}

rawData, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}

return &ELF{
path: path,
file: f,
hdr: hdr,
arch: arch,
endian: endian,
symbols: symbols,
raw: rawData,
}, nil
}

// Address determines the virtual address for the specified file offset
func (e *ELF) Address(offset uint64) (uint64, error) {
for _, p := range e.file.Progs {
start := p.Off
end := p.Off + p.Filesz

if offset >= start && offset < end {
return offset - p.Off + p.Vaddr, nil
}
}
return 0, fmt.Errorf("Offset %x is not in range of an ELF segment", offset)
}

// Offset determines the offset for the specified virtual address
func (e *ELF) Offset(address uint64) (uint64, error) {
for _, p := range e.file.Progs {
start := p.Vaddr
end := p.Vaddr + p.Filesz

if address >= start && address < end {
return address - p.Vaddr + p.Off, nil
}
}
return 0, fmt.Errorf("Address %x is not in range of an ELF segment", address)
}

func (e *ELF) Architecture() Arch {
return e.arch
}
Expand Down Expand Up @@ -129,3 +197,58 @@ func (e *ELF) Checksec() string {
func (e *ELF) Close() error {
return e.file.Close()
}

func (e *ELF) DumpHeader(hdr interface{}) {
fmt.Println("-------------------------- Elf Header ------------------------")
switch e.file.Class {
case elf.ELFCLASS64:
h := e.hdr.(elf.Header64)
fmt.Printf("Magic: % x\n", h.Ident)
fmt.Printf("Class: %s\n", elf.Class(h.Ident[elf.EI_CLASS]))
fmt.Printf("Data: %s\n", elf.Data(h.Ident[elf.EI_DATA]))
fmt.Printf("Version: %s\n", elf.Version(h.Version))
fmt.Printf("OS/ABI: %s\n", elf.OSABI(h.Ident[elf.EI_OSABI]))
fmt.Printf("ABI Version: %d\n", h.Ident[elf.EI_ABIVERSION])
fmt.Printf("Elf Type: %s\n", elf.Type(h.Type))
fmt.Printf("Machine: %s\n", elf.Machine(h.Machine))
fmt.Printf("Entry: 0x%x\n", h.Entry)
fmt.Printf("Program Header Offset: 0x%x\n", h.Phoff)
fmt.Printf("Section Header Offset: 0x%x\n", h.Shoff)
fmt.Printf("Flags: 0x%x\n", h.Flags)
fmt.Printf("Elf Header Size (bytes): %d\n", h.Ehsize)
fmt.Printf("Program Header Entry Size (bytes): %d\n", h.Phentsize)
fmt.Printf("Number of Program Header Entries: %d\n", h.Phnum)
fmt.Printf("Size of Section Header Entry: %d\n", h.Shentsize)
fmt.Printf("Number of Section Header Entries: %d\n", h.Shnum)
fmt.Printf("Index of Section Header string table: %d\n", h.Shstrndx)
case elf.ELFCLASS32:
h := e.hdr.(elf.Header32)
fmt.Printf("Magic: % x\n", h.Ident)
fmt.Printf("Class: %s\n", elf.Class(h.Ident[elf.EI_CLASS]))
fmt.Printf("Data: %s\n", elf.Data(h.Ident[elf.EI_DATA]))
fmt.Printf("Version: %s\n", elf.Version(h.Version))
fmt.Printf("OS/ABI: %s\n", elf.OSABI(h.Ident[elf.EI_OSABI]))
fmt.Printf("ABI Version: %d\n", h.Ident[elf.EI_ABIVERSION])
fmt.Printf("Elf Type: %s\n", elf.Type(h.Type))
fmt.Printf("Machine: %s\n", elf.Machine(h.Machine))
fmt.Printf("Entry: 0x%x\n", h.Entry)
fmt.Printf("Program Header Offset: 0x%x\n", h.Phoff)
fmt.Printf("Section Header Offset: 0x%x\n", h.Shoff)
fmt.Printf("Flags: 0x%x\n", h.Flags)
fmt.Printf("Elf Header Size (bytes): %d\n", h.Ehsize)
fmt.Printf("Program Header Entry Size (bytes): %d\n", h.Phentsize)
fmt.Printf("Number of Program Header Entries: %d\n", h.Phnum)
fmt.Printf("Size of Section Header Entry: %d\n", h.Shentsize)
fmt.Printf("Number of Section Header Entries: %d\n", h.Shnum)
fmt.Printf("Index of Section Header string table: %d\n", h.Shstrndx)
}
}

func (e *ELF) Caves(caveSize int) []Cave {
var caves []Cave
for _, s := range e.file.Sections {
body, _ := s.Data()
caves = append(caves, searchCaves(s.Name, body, s.Offset, s.Addr, s.Flags.String(), caveSize)...)
}
return caves
}
Loading

0 comments on commit 8bc1076

Please sign in to comment.