diff --git a/.travis.yml b/.travis.yml index 939d8c2..86a95b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ env: - CGO_CFLAGS="-I$HOME/hashcat/deps/OpenCL-Headers -I$HOME/hashcat/include" - CGO_LDFLAGS="-L$HOME/hashcat" - LD_LIBRARY_PATH="/home/travis/hashcat:$LD_LIBRARY_PATH" - - HASHCAT_VERSION="v3.6.0" + - HASHCAT_VERSION="v5.1.0" - GO111MODULE=on addons: @@ -19,7 +19,6 @@ before_install: go: - "1.12.x" - - tip script: - ./misc/codecov_tests.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 6514baf..3aec125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # GoCrack Changelog +## 2.1 + +New Features: + +* Update Go to 1.12.6 +* Support for Go Modules +* Updated all server dependencies +* Dropped support for Hashcat 3.6 for 5.1 + ## 2.0 New Features: diff --git a/README.md b/README.md index a892eea..6434dd1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ GoCrack provides APIs to manage password cracking tasks across supported crackin ## Supported Engines -* [hashcat 3.6.0+](https://github.com/hashcat/hashcat) +* [hashcat 5.X+](https://github.com/hashcat/hashcat) ## Documentation diff --git a/docker/Makefile b/docker/Makefile index 8d46a3a..8c41dd9 100755 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,6 +1,6 @@ ROOT_PROJECT := gocrack PKG_AUTHOR := Christopher Schmitt -HASHCAT_VERSION := v3.6.0 +HASHCAT_VERSION := v5.1.0 GOCRACK_PROJ := github.com/fireeye/gocrack GOCRACK_CODE_LOCAL := ${GOPATH}/src/$(GOCRACK_PROJ) diff --git a/docker/README.md b/docker/README.md index ecba43f..ba6fbb5 100755 --- a/docker/README.md +++ b/docker/README.md @@ -2,7 +2,7 @@ This repository includes docker containers that build the necessary components for a successful gocrack install. -* Current Hashcat Version: v3.6 +* Current Hashcat Version: v5.1 * Current Go Version: v1.9 **Note**: You should be using Docker 17.X and require NVIDIA Docker plugin to run the worker image diff --git a/go.mod b/go.mod index 9eb14ac..8132bef 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Sereal/Sereal v0.0.0-20190606082811-cf1bab6c7a3a // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/asdine/storm v0.0.0-20190418133842-e0f77eada154 + github.com/fireeye/gocat v0.0.0-20190613212338-ce97d32213d0 github.com/gin-contrib/cors v1.3.0 github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.4.0 diff --git a/go.sum b/go.sum index 873d7bf..595bf17 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fireeye/gocat v0.0.0-20190613212338-ce97d32213d0 h1:9Q627+zI86PEvW6swCZocEJKt9N2ALa+QF9RVhQX/nw= +github.com/fireeye/gocat v0.0.0-20190613212338-ce97d32213d0/go.mod h1:jiQe5RA0SrrZbYO2QvfWgQKzLBDTmJH9nwrfokC3Euo= github.com/gin-contrib/cors v0.0.0-20170708080947-567de1916927 h1:Asmg6n4CmGBOR6Lc6v7NFVB7rTqeiig/lcPKxwC/YXw= github.com/gin-contrib/cors v0.0.0-20170708080947-567de1916927/go.mod h1:cw+u9IsAkC16e42NtYYVCLsHYXE98nB3M7Dr9mLSeH4= github.com/gin-contrib/cors v1.3.0 h1:PolezCc89peu+NgkIWt9OB01Kbzt6IP0J/JvkG6xxlg= diff --git a/gocat/README.md b/gocat/README.md deleted file mode 100644 index 32f2f62..0000000 --- a/gocat/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# gocat - -**This is a work in progress.** *API's will most likely change.* - -gocat is a cgo library for interacting with libhashcat. gocat enables you to create purpose-built password cracking tools that leverage the capabilities of [hashcat](https://hashcat.net/hashcat/). - -## Installation - -gocat requires hashcat [v3.6.0](https://github.com/hashcat/hashcat/releases) or higher to be compiled as a shared library. This can be accomplished by modifying hashcat's `src/Makefile` and setting `SHARED` to `1` - -Installing the Go Library: - - go get github.com/fireeye/gocrack/gocat - -## Known Issues - -* Lack of Windows Support: This won't work on windows as I haven't figured out how to build hashcat on windows -* Memory Leaks: hashcat has several (small) memory leaks that could cause increase of process memory over time - -## Contributing - -Contributions are welcome via pull requests provided they meet the following criteria: - -1. One feature or bug fix per PR -1. Code should be properly formatted (using go fmt) -1. Tests coverage should rarely decrease. All new features should have proper coverage diff --git a/gocat/ctypes.go b/gocat/ctypes.go deleted file mode 100644 index cbef5a4..0000000 --- a/gocat/ctypes.go +++ /dev/null @@ -1,20 +0,0 @@ -package gocat - -// #include -import "C" -import "unsafe" - -var cChar *C.char - -// convertArgsToC converts go strings into a **C.char. -// The results of this MUST be free'd -func convertArgsToC(args ...string) (C.int, **C.char) { - ptrSize := unsafe.Sizeof(cChar) - ptr := C.malloc(C.size_t(len(args)) * C.size_t(ptrSize)) - - for i := 0; i < len(args); i++ { - element := (**C.char)(unsafe.Pointer(uintptr(ptr) + uintptr(i)*ptrSize)) - *element = C.CString(string(args[i])) - } - return C.int(len(args)), (**C.char)(ptr) -} diff --git a/gocat/doc.go b/gocat/doc.go deleted file mode 100644 index 4d65bb0..0000000 --- a/gocat/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -/* -Package gocat is a cgo interface around libhashcat. It's main purpose is to abstract hashcat and allow you to build tools in Go that leverage the hashcat engine. - -gocat should be used with libhashcat v3.6.0 as previous versions have known memory leaks and could affect long running processes running multiple cracking tasks. - -*/ -package gocat diff --git a/gocat/gocat.go b/gocat/gocat.go deleted file mode 100644 index 42f5100..0000000 --- a/gocat/gocat.go +++ /dev/null @@ -1,307 +0,0 @@ -package gocat - -/* -#cgo CFLAGS: -I/usr/local/include/hashcat -std=c99 -Wall -O0 -g -#cgo linux CFLAGS: -D_GNU_SOURCE -#cgo LDFLAGS: -L/usr/local/lib -lhashcat - -#include "wrapper.h" -*/ -import "C" -import ( - "errors" - "fmt" - "os" - "path/filepath" - "sync" - "time" - "unsafe" - - "github.com/fireeye/gocrack/gocat/hcargp" -) - -var ( - // CompileTime should be set using -ldflags "-X" during compilation. This value is passed into hashcat_session_init - CompileTime = time.Now().UTC().Unix() - // ErrUnableToStopAtCheckpoint is raised whenever hashcat is unable to stop at next checkpoint. This is caused when - // the device status != STATUS_RUNNING or --restore-disable is set - ErrUnableToStopAtCheckpoint = errors.New("gocat: unable to stop at next checkpoint") - // ErrUnableToStop is raised whenever we were unable to stop a hashcat session. At this time, hashcat's hashcat_session_quit - // always returns success so you'll most likely never see this. - ErrUnableToStop = errors.New("gocat: unable to stop task") -) - -const ( - // sessionCracked indicates all hashes were cracked - sessionCracked int = iota - // sessionExhausted indicates all possible permutations were reached in the session - sessionExhausted - // sessionQuit indicates the session was quit by the user - sessionQuit - // sessionAborted indicates the session was aborted by the user - sessionAborted - // sessionAbortedCheckpoint indicates the session was checkpointed by the user (usually due to an abort signal or temperature limit) - sessionAbortedCheckpoint - // sessionAbortedRuntime indicates the session was stopped by hashcat (usually due to a runtime limit) - sessionAbortedRuntime -) - -// EventCallback defines the callback that hashcat/gocat calls -type EventCallback func(Hashcat unsafe.Pointer, Payload interface{}) - -// Options defines all the configuration options for gocat/hashcat -type Options struct { - // SharedPath should be set to the location where OpenCL kernels and .hcstat/hctune files live - SharedPath string - // ExecutablePath should be set to the location of the binary and not the binary itself. - // Hashcat does some weird logic and will ignore the shared path if this is incorrectly set. - // If ExecutablePath is not set, we'll attempt to calculate it using os.Args[0] - ExecutablePath string - // PatchEventContext if true will patch hashcat's event_ctx with a reentrant lock which will allow - // you to call several hashcat APIs (which fire another callback) from within an event callback. - // This is supported on macOS, Linux, and Windows. - PatchEventContext bool -} - -// ErrNoSharedPath is raised whenever Options.SharedPath is not set -var ErrNoSharedPath = errors.New("shared path must be set") - -func (o *Options) validate() (err error) { - if o.SharedPath == "" { - return ErrNoSharedPath - } - - if o.ExecutablePath == "" { - if o.ExecutablePath, _ = filepath.Abs(filepath.Dir(os.Args[0])); err != nil { - return err - } - } else { - if _, err = os.Stat(o.ExecutablePath); err != nil { - return err - } - } - - return nil -} - -// Hashcat is the interface which interfaces with libhashcat to provide password cracking capabilities. -type Hashcat struct { - // unexported fields below - wrapper C.gocat_ctx_t - cb EventCallback - opts Options - isEventPatched bool - l sync.Mutex - - // these must be free'd - executablePath *C.char - sharedPath *C.char -} - -// New creates a context that can be reused to crack passwords. -func New(opts Options, cb EventCallback) (hc *Hashcat, err error) { - if err = opts.validate(); err != nil { - return nil, err - } - - hc = &Hashcat{ - executablePath: C.CString(opts.ExecutablePath), - sharedPath: C.CString(opts.SharedPath), - cb: cb, - opts: opts, - } - - hc.wrapper = C.gocat_ctx_t{ - ctx: C.hashcat_ctx_t{}, - gowrapper: unsafe.Pointer(hc), - bValidateHashes: false, - } - - if retval := C.hashcat_init(&hc.wrapper.ctx, (*[0]byte)(unsafe.Pointer(C.event))); retval != 0 { - return - } - - return -} - -// EventCallbackIsReentrant returns a boolean indicating if hashcat_ctx.event_ctx has been patched to allow an event to fire another event -func (hc *Hashcat) EventCallbackIsReentrant() bool { - return hc.isEventPatched -} - -// RunJob starts a hashcat session and blocks until it has been finished. -func (hc *Hashcat) RunJob(args ...string) (err error) { - hc.l.Lock() - defer hc.l.Unlock() - - // initialize the default options in hashcat_ctx->user_options - if retval := C.user_options_init(&hc.wrapper.ctx); retval != 0 { - return - } - - argc, argv := convertArgsToC(append([]string{hc.opts.ExecutablePath}, args...)...) - defer C.freeargv(argc, argv) - - if retval := C.user_options_getopt(&hc.wrapper.ctx, argc, argv); retval != 0 { - return getErrorFromCtx(hc.wrapper.ctx) - } - - if retval := C.user_options_sanity(&hc.wrapper.ctx); retval != 0 { - return getErrorFromCtx(hc.wrapper.ctx) - } - - if retval := C.hashcat_session_init(&hc.wrapper.ctx, hc.executablePath, hc.sharedPath, argc, argv, C.int(CompileTime)); retval != 0 { - return getErrorFromCtx(hc.wrapper.ctx) - } - defer C.hashcat_session_destroy(&hc.wrapper.ctx) - - if hc.opts.PatchEventContext { - isPatchSuccessful, err := patchEventMutex(hc.wrapper.ctx) - if err != nil { - return err - } - hc.isEventPatched = isPatchSuccessful - } - - rc := C.hashcat_session_execute(&hc.wrapper.ctx) - switch int(rc) { - case sessionCracked, sessionExhausted, sessionQuit, sessionAborted, - sessionAbortedCheckpoint, sessionAbortedRuntime: - err = nil - default: - return getErrorFromCtx(hc.wrapper.ctx) - } - - return -} - -// RunJobWithOptions is a convenience function to take a HashcatSessionOptions struct and craft the necessary argvs to use -// for the hashcat session. -// This is NOT goroutine safe. If you are needing to run multiple jobs, create a context for each one. -func (hc *Hashcat) RunJobWithOptions(opts hcargp.HashcatSessionOptions) error { - args, err := opts.MarshalArgs() - if err != nil { - return err - } - return hc.RunJob(args...) -} - -// StopAtCheckpoint instructs the running hashcat session to stop at the next available checkpoint -func (hc *Hashcat) StopAtCheckpoint() error { - if retval := C.hashcat_session_checkpoint(&hc.wrapper.ctx); retval != 0 { - return ErrUnableToStopAtCheckpoint - } - return nil -} - -// AbortRunningTask instructs hashcat to abruptly stop the running session -func (hc *Hashcat) AbortRunningTask() { - C.hashcat_session_quit(&hc.wrapper.ctx) -} - -func getErrorFromCtx(ctx C.hashcat_ctx_t) error { - msg := C.hashcat_get_log(&ctx) - return fmt.Errorf("gocat: %s", C.GoString(msg)) -} - -//export callback -func callback(id uint32, hcCtx *C.hashcat_ctx_t, wrapper unsafe.Pointer, buf unsafe.Pointer, len C.size_t) { - ctx := (*Hashcat)(wrapper) - - var payload interface{} - var err error - - switch id { - case C.EVENT_LOG_ERROR: - payload = logMessageCbFromEvent(hcCtx, InfoMessage) - case C.EVENT_LOG_INFO: - payload = logMessageCbFromEvent(hcCtx, InfoMessage) - case C.EVENT_LOG_WARNING: - payload = logMessageCbFromEvent(hcCtx, WarnMessage) - case C.EVENT_BITMAP_INIT_PRE: - payload = logHashcatAction(id, "Generating bitmap tables") - case C.EVENT_BITMAP_INIT_POST: - payload = logHashcatAction(id, "Generated bitmap tables") - case C.EVENT_CALCULATED_WORDS_BASE: - if hcCtx.user_options.keyspace { - payload = logHashcatAction(id, fmt.Sprintf("Calculated Words Base: %d", hcCtx.status_ctx.words_base)) - } - case C.EVENT_WEAK_HASH_PRE: - payload = logHashcatAction(id, "Checking for weak hashes") - case C.EVENT_WEAK_HASH_POST: - payload = logHashcatAction(id, "Checked for weak hashes") - case C.EVENT_HASHLIST_SORT_SALT_PRE: - payload = logHashcatAction(id, "Sorting salts...") - case C.EVENT_HASHLIST_SORT_SALT_POST: - payload = logHashcatAction(id, "Sorted salts...") - case C.EVENT_OPENCL_SESSION_PRE: - payload = logHashcatAction(id, "Initializing device kernels and memory") - case C.EVENT_OPENCL_SESSION_POST: - payload = logHashcatAction(id, "Initialized device kernels and memory") - case C.EVENT_AUTOTUNE_STARTING: - payload = logHashcatAction(id, "Starting Autotune threads") - case C.EVENT_AUTOTUNE_FINISHED: - payload = logHashcatAction(id, "Autotune threads have started..") - case C.EVENT_OUTERLOOP_MAINSCREEN: - hashes := ctx.wrapper.ctx.hashes - payload = TaskInformationPayload{ - NumHashes: uint32(hashes.hashes_cnt_orig), - NumHashesUnique: uint32(hashes.digests_cnt), - NumSalts: uint32(hashes.salts_cnt), - } - case C.EVENT_MONITOR_PERFORMANCE_HINT: - payload = logHashcatAction(id, "Device performance might be suffering due to a less than optimal configuration") - case C.EVENT_POTFILE_REMOVE_PARSE_PRE: - payload = logHashcatAction(id, "Comparing hashes with potfile entries...") - case C.EVENT_POTFILE_REMOVE_PARSE_POST: - payload = logHashcatAction(id, "Compared hashes with potfile entries") - case C.EVENT_POTFILE_ALL_CRACKED: - payload = logHashcatAction(id, "All hashes exist in potfile") - if ctx.isEventPatched { - C.potfile_handle_show(&ctx.wrapper.ctx) - } - payload = FinalStatusPayload{ - Status: nil, - EndedAt: time.Now().UTC(), - AllHashesCracked: true, - } - case C.EVENT_SET_KERNEL_POWER_FINAL: - payload = logHashcatAction(id, "Approaching final keyspace, workload adjusted") - case C.EVENT_POTFILE_NUM_CRACKED: - ctxHashes := hcCtx.hashes - if ctxHashes.digests_done > 0 { - payload = logHashcatAction(id, fmt.Sprintf("Removed %d hash(s) found in potfile", ctxHashes.digests_done)) - } - case C.EVENT_CRACKER_HASH_CRACKED, C.EVENT_POTFILE_HASH_SHOW: - // Grab the separator for this session out of user options - sepr := C.GoString(&hcCtx.user_options.separator) - msg := C.GoString((*C.char)(buf)) - if payload, err = getCrackedPassword(id, msg, sepr); err != nil { - payload = logMessageWithError(id, err) - } - case C.EVENT_OUTERLOOP_FINISHED: - payload = FinalStatusPayload{ - Status: ctx.GetStatus(), - EndedAt: time.Now().UTC(), - } - case C.EVENT_WEAK_HASH_ALL_CRACKED: - payload = FinalStatusPayload{ - Status: nil, - EndedAt: time.Now().UTC(), - AllHashesCracked: true, - } - } - - // Events we're ignoring: - // EVENT_CRACKER_STARTING - // EVENT_OUTERLOOP_MAINSCREEN - - ctx.cb(unsafe.Pointer(hcCtx), payload) -} - -// Free releases all allocations. Call this when you're done with hashcat or exiting the application -func (hc *Hashcat) Free() { - C.hashcat_destroy(&hc.wrapper.ctx) - C.free(unsafe.Pointer(hc.executablePath)) - C.free(unsafe.Pointer(hc.sharedPath)) -} diff --git a/gocat/gocat_posix_test.go b/gocat/gocat_posix_test.go deleted file mode 100644 index fe8598e..0000000 --- a/gocat/gocat_posix_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// +build linux,cgo darwin,cgo - -package gocat - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestReentrantPatchWithPotfile(t *testing.T) { - crackedHashes := map[string]*string{} - - hc, err := New(Options{ - SharedPath: DefaultSharedPath, - PatchEventContext: true, - }, callbackForTests(crackedHashes)) - defer hc.Free() - - if err != nil { - t.Fatal(err) - } - - assert.NotNil(t, hc) - assert.NoError(t, err) - - potfilePath, err := filepath.Abs("./testdata/two_md5.potfile") - if err != nil { - t.Fatal(err) - } - - hashfilePath, err := filepath.Abs("./testdata/two_md5.hashes") - if err != nil { - t.Fatal(err) - } - - err = hc.RunJob("-a", "0", "-m", "0", "--potfile-path", potfilePath, hashfilePath, "./testdata/test_dictionary.txt") - assert.NoError(t, err) - assert.Len(t, crackedHashes, 2) - assert.Equal(t, "hello", *crackedHashes["5d41402abc4b2a76b9719d911017c592"]) - assert.Equal(t, "world", *crackedHashes["7d793037a0760186574b0282f2f435e7"]) -} - -func TestReentrantPatchWithPotfileMixed(t *testing.T) { - crackedHashes := map[string]*string{} - - hc, err := New(Options{ - SharedPath: DefaultSharedPath, - PatchEventContext: true, - }, callbackForTests(crackedHashes)) - defer hc.Free() - - if err != nil { - t.Fatal(err) - } - - assert.NotNil(t, hc) - assert.NoError(t, err) - - potfilePath, err := filepath.Abs("./testdata/one_md5_in_potfile.potfile") - if err != nil { - t.Fatal(err) - } - - // We need to make a backup of this because once this cracks, hashcat will update the potfile - orignalPotfileContents, err := ioutil.ReadFile(potfilePath) - if err != nil { - t.Fatal(err) - } - - hashfilePath, err := filepath.Abs("./testdata/one_md5_in_potfile.hashes") - if err != nil { - t.Fatal(err) - } - - err = hc.RunJob("-a", "0", "-m", "0", "--potfile-path", potfilePath, hashfilePath, "./testdata/test_dictionary.txt") - assert.NoError(t, err) - assert.Len(t, crackedHashes, 1) - // Unfortunately, I haven't found a way to show partial potfile results so the hash for "hello" won't be returned by this test - assert.NotContains(t, "5d41402abc4b2a76b9719d911017c592", crackedHashes) - // ...this one should crack successfully though - assert.Equal(t, "chris", *crackedHashes["6b34fe24ac2ff8103f6fce1f0da2ef57"]) - - fd, err := os.OpenFile(potfilePath, os.O_WRONLY|os.O_TRUNC, os.ModePerm) - if err != nil { - t.Logf("Test will be corrupted as we were unable to open the potfile to restore the original contents: %s", err) - t.FailNow() - } - fd.WriteAt(orignalPotfileContents, 0) -} diff --git a/gocat/gocat_status.go b/gocat/gocat_status.go deleted file mode 100644 index 3ef68df..0000000 --- a/gocat/gocat_status.go +++ /dev/null @@ -1,163 +0,0 @@ -package gocat - -// #include "wrapper.h" -import "C" -import ( - "fmt" - "unsafe" -) - -// DeviceStatus contains information about the OpenCL device that is cracking -type DeviceStatus struct { - DeviceID int - HashesSec string - ExecDev float64 -} - -// Status contains data about the current cracking session -type Status struct { - Session string - Status string - HashType string - HashTarget string - TimeStarted string - TimeEstimated string - TimeEstimatedRelative string - DeviceStatus []DeviceStatus - TotalSpeed string - ProgressMode int - Candidates map[int]string // map[DeviceID]string - Progress string - Rejected string - Recovered string - RestorePoint string - GuessMode int - GuessMask string `json:",omitempty"` - GuessQueue string `json:",omitempty"` - GuessBase string `json:",omitempty"` - GuessMod string `json:",omitempty"` - GuessCharset string `json:",omitempty"` -} - -var szStatusStruct = unsafe.Sizeof(C.hashcat_status_t{}) - -// GetStatus returns the status of the cracking. -// This is an implementation of https://github.com/hashcat/hashcat/blob/master/src/terminal.c#L709 -func (hc *Hashcat) GetStatus() *Status { - ptr := C.malloc(C.size_t(szStatusStruct)) - hcStatus := (*C.hashcat_status_t)(unsafe.Pointer(ptr)) - defer C.free(unsafe.Pointer(hcStatus)) - - if retval := C.hashcat_get_status(&hc.wrapper.ctx, hcStatus); retval != 0 { - return nil - } - defer C.status_status_destroy(&hc.wrapper.ctx, hcStatus) - - stats := &Status{ - Session: C.GoString(hcStatus.session), - Status: C.GoString(hcStatus.status_string), - HashType: C.GoString(hcStatus.hash_type), - HashTarget: C.GoString(hcStatus.hash_target), - TimeStarted: C.GoString(hcStatus.time_started_absolute), - TimeEstimated: C.GoString(hcStatus.time_estimated_absolute), - TimeEstimatedRelative: C.GoString(hcStatus.time_estimated_relative), - // Instead of using hcStatus.device_info_cnt here, we'll let go manage this - // to avoid having empty DeviceStatus's - DeviceStatus: make([]DeviceStatus, 0), - TotalSpeed: C.GoString(hcStatus.speed_sec_all), - ProgressMode: int(hcStatus.progress_mode), - Candidates: make(map[int]string), - GuessMode: int(hcStatus.guess_mode), - } - - switch stats.ProgressMode { - case C.PROGRESS_MODE_KEYSPACE_KNOWN: - stats.Progress = fmt.Sprintf("%d/%d (%.02f%%)", hcStatus.progress_cur_relative_skip, - hcStatus.progress_end_relative_skip, - hcStatus.progress_finished_percent) - stats.Rejected = fmt.Sprintf("%d/%d (%.02f%%)", hcStatus.progress_rejected, - hcStatus.progress_cur_relative_skip, - hcStatus.progress_rejected_percent) - stats.RestorePoint = fmt.Sprintf("%d/%d (%.02f%%)", hcStatus.restore_point, - hcStatus.restore_total, - hcStatus.restore_percent) - case C.PROGRESS_MODE_KEYSPACE_UNKNOWN: - stats.Progress = fmt.Sprintf("%d", hcStatus.progress_cur_relative_skip) - stats.Rejected = fmt.Sprintf("%d", hcStatus.progress_rejected) - stats.RestorePoint = fmt.Sprintf("%d", hcStatus.restore_point) - } - - switch stats.GuessMode { - case C.GUESS_MODE_STRAIGHT_FILE: - stats.GuessBase = C.GoString(hcStatus.guess_base) - case C.GUESS_MODE_STRAIGHT_FILE_RULES_FILE: - stats.GuessBase = C.GoString(hcStatus.guess_base) - stats.GuessMod = C.GoString(hcStatus.guess_mod) - case C.GUESS_MODE_STRAIGHT_FILE_RULES_GEN: - stats.GuessBase = C.GoString(hcStatus.guess_base) - stats.GuessMod = "Rules (Generated)" - case C.GUESS_MODE_STRAIGHT_STDIN: - stats.GuessBase = "Pipe" - case C.GUESS_MODE_STRAIGHT_STDIN_RULES_FILE: - stats.GuessBase = "Pipe" - stats.GuessMod = C.GoString(hcStatus.guess_mod) - case C.GUESS_MODE_STRAIGHT_STDIN_RULES_GEN: - stats.GuessBase = "Pipe" - stats.GuessMod = "Rules (Generated)" - case C.GUESS_MODE_COMBINATOR_BASE_LEFT: - stats.GuessBase = fmt.Sprintf("File (%s), Left Side", C.GoString(hcStatus.guess_base)) - stats.GuessMod = fmt.Sprintf("File (%s), Right Side", C.GoString(hcStatus.guess_mod)) - case C.GUESS_MODE_COMBINATOR_BASE_RIGHT: - stats.GuessBase = fmt.Sprintf("File (%s), Right Side", C.GoString(hcStatus.guess_base)) - stats.GuessMod = fmt.Sprintf("File (%s), Left Side", C.GoString(hcStatus.guess_mod)) - case C.GUESS_MODE_MASK: - stats.GuessMask = fmt.Sprintf("%s [%d]", C.GoString(hcStatus.guess_base), int(hcStatus.guess_mask_length)) - case C.GUESS_MODE_MASK_CS: - stats.GuessMask = fmt.Sprintf("%s [%d]", C.GoString(hcStatus.guess_base), int(hcStatus.guess_mask_length)) - stats.GuessCharset = C.GoString(hcStatus.guess_charset) - case C.GUESS_MODE_HYBRID1_CS: - stats.GuessCharset = C.GoString(hcStatus.guess_charset) - fallthrough // grab GuessBase/GuessMod from below as it's the same - case C.GUESS_MODE_HYBRID1: - stats.GuessBase = fmt.Sprintf("File (%s), Left Side", C.GoString(hcStatus.guess_base)) - stats.GuessMod = fmt.Sprintf("Mask (%s) [%d], Right Side", C.GoString(hcStatus.guess_base), int(hcStatus.guess_mask_length)) - } - - switch stats.GuessMode { - case C.GUESS_MODE_STRAIGHT_FILE, C.GUESS_MODE_STRAIGHT_FILE_RULES_FILE, - C.GUESS_MODE_STRAIGHT_FILE_RULES_GEN, C.GUESS_MODE_MASK: - stats.GuessQueue = fmt.Sprintf("%d/%d (%.02f%%)", hcStatus.guess_base_offset, - hcStatus.guess_base_count, hcStatus.guess_base_percent) - case C.GUESS_MODE_HYBRID1, C.GUESS_MODE_HYBRID2: - stats.GuessQueue = fmt.Sprintf("%d/%d (%.02f%%)", hcStatus.guess_base_offset, - hcStatus.guess_base_count, hcStatus.guess_base_percent) - } - - stats.Recovered = fmt.Sprintf("%d/%d (%.2f%%) Digests, %d/%d (%.2f%%) Salts", - hcStatus.digests_done, - hcStatus.digests_cnt, - hcStatus.digests_percent, - hcStatus.salts_done, - hcStatus.salts_cnt, - hcStatus.salts_percent, - ) - - for i := 0; i < int(hcStatus.device_info_cnt); i++ { - deviceInfo := hcStatus.device_info_buf[i] - if deviceInfo.skipped_dev { - continue - } - - stats.DeviceStatus = append(stats.DeviceStatus, DeviceStatus{ - DeviceID: i + 1, - HashesSec: C.GoString(deviceInfo.speed_sec_dev), - ExecDev: float64(deviceInfo.exec_msec_dev), - }) - - if deviceInfo.guess_candidates_dev != nil { - stats.Candidates[i+1] = C.GoString(deviceInfo.guess_candidates_dev) - } - } - - return stats -} diff --git a/gocat/gocat_test.go b/gocat/gocat_test.go deleted file mode 100644 index 352c472..0000000 --- a/gocat/gocat_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package gocat - -import ( - "fmt" - "log" - "strings" - "testing" - "unsafe" - - "github.com/fireeye/gocrack/gocat/hcargp" - - "github.com/stretchr/testify/assert" -) - -const ( - // Set this to true if you want the gocat callbacks used in the tests to print out - DebugTest bool = true - DefaultSharedPath string = "/usr/local/share/hashcat" -) - -type testStruct struct { - opts hcargp.HashcatSessionOptions - expectedError error -} - -func emptyCallback(hc unsafe.Pointer, payload interface{}) {} - -func callbackForTests(resultsmap map[string]*string) EventCallback { - return func(hc unsafe.Pointer, payload interface{}) { - switch pl := payload.(type) { - case LogPayload: - if DebugTest { - fmt.Printf("LOG [%s] %s\n", pl.Level, pl.Message) - } - case ActionPayload: - if DebugTest { - fmt.Printf("ACTION [%d] %s\n", pl.HashcatEvent, pl.Message) - } - case CrackedPayload: - if DebugTest { - fmt.Printf("CRACKED %s -> %s\n", pl.Hash, pl.Value) - } - if resultsmap != nil { - resultsmap[pl.Hash] = hcargp.GetStringPtr(pl.Value) - } - case FinalStatusPayload: - if DebugTest { - fmt.Printf("FINAL STATUS -> %v\n", pl.Status) - } - case TaskInformationPayload: - if DebugTest { - fmt.Printf("TASK INFO -> %v\n", pl) - } - } - } -} - -func TestOptionsExecPath(t *testing.T) { - // Valid - opts := Options{ - ExecutablePath: "", - SharedPath: "/tmp", - } - - err := opts.validate() - assert.Nil(t, err) - assert.True(t, strings.HasSuffix(opts.ExecutablePath, "_test")) - - // Not valid because executable path was incorrectly set by the user - opts.ExecutablePath = "/nope" - err = opts.validate() - assert.Error(t, err) -} - -func TestGoCatOptionsValidatorErrors(t *testing.T) { - for _, test := range []struct { - opts Options - expectedError error - expectedOpts map[string]interface{} - }{ - { - opts: Options{ - SharedPath: "", - }, - expectedError: ErrNoSharedPath, - }, - { - opts: Options{ - SharedPath: "/deadbeef", - ExecutablePath: "", - }, - }, - } { - err := test.opts.validate() - assert.Equal(t, test.expectedError, err) - } -} - -func TestGoCatCrackingMD5(t *testing.T) { - crackedHashes := map[string]*string{} - - hc, err := New(Options{ - SharedPath: DefaultSharedPath, - }, callbackForTests(crackedHashes)) - defer hc.Free() - - assert.NotNil(t, hc) - assert.NoError(t, err) - - err = hc.RunJob("-a", "0", "-m", "0", "--potfile-disable", "5d41402abc4b2a76b9719d911017c592", "./testdata/test_dictionary.txt") - assert.NoError(t, err) - assert.Len(t, crackedHashes, 1) - assert.Equal(t, "hello", *crackedHashes["5d41402abc4b2a76b9719d911017c592"]) -} - -func TestGoCatReusingContext(t *testing.T) { - crackedHashes := map[string]*string{} - - hc, err := New(Options{ - SharedPath: DefaultSharedPath, - }, callbackForTests(crackedHashes)) - defer hc.Free() - - assert.NotNil(t, hc) - assert.NoError(t, err) - - err = hc.RunJob("-a", "0", "-m", "0", "--potfile-disable", "5d41402abc4b2a76b9719d911017c592", "./testdata/test_dictionary.txt") - assert.NoError(t, err) - assert.Len(t, crackedHashes, 1) - assert.Equal(t, "hello", *crackedHashes["5d41402abc4b2a76b9719d911017c592"]) - - err = hc.RunJob("-a", "0", "-m", "0", "--potfile-disable", "9f9d51bc70ef21ca5c14f307980a29d8", "./testdata/test_dictionary.txt") - assert.NoError(t, err) - assert.Len(t, crackedHashes, 2) // the previous run will still exist in this map - assert.Equal(t, "bob", *crackedHashes["9f9d51bc70ef21ca5c14f307980a29d8"]) -} - -func TestGoCatRunJobWithOptions(t *testing.T) { - crackedHashes := map[string]*string{} - - hc, err := New(Options{ - SharedPath: DefaultSharedPath, - }, callbackForTests(crackedHashes)) - defer hc.Free() - - assert.NotNil(t, hc) - assert.NoError(t, err) - - err = hc.RunJobWithOptions(hcargp.HashcatSessionOptions{ - AttackMode: hcargp.GetIntPtr(0), - HashType: hcargp.GetIntPtr(0), - PotfileDisable: hcargp.GetBoolPtr(true), - InputFile: "9f9d51bc70ef21ca5c14f307980a29d8", - DictionaryMaskDirectoryInput: hcargp.GetStringPtr("./testdata/test_dictionary.txt"), - }) - - assert.NoError(t, err) - assert.Len(t, crackedHashes, 1) // the previous run will still exist in this map - assert.Equal(t, "bob", *crackedHashes["9f9d51bc70ef21ca5c14f307980a29d8"]) -} - -func TestGocatRussianHashes(t *testing.T) { - crackedHashes := map[string]*string{} - - hc, err := New(Options{ - SharedPath: DefaultSharedPath, - }, callbackForTests(crackedHashes)) - defer hc.Free() - - assert.NotNil(t, hc) - assert.NoError(t, err) - - err = hc.RunJobWithOptions(hcargp.HashcatSessionOptions{ - AttackMode: hcargp.GetIntPtr(0), - HashType: hcargp.GetIntPtr(0), - PotfileDisable: hcargp.GetBoolPtr(true), - InputFile: "./testdata/russian_test.hashes", - DictionaryMaskDirectoryInput: hcargp.GetStringPtr("./testdata/russian_test.dictionary"), - }) - - assert.NoError(t, err) - assert.Len(t, crackedHashes, 4) // the previous run will still exist in this map - fmt.Println("HI", crackedHashes) - fmt.Println(crackedHashes) -} - -func TestGoCatStopAtCheckpointWithNoRunningSession(t *testing.T) { - hc, err := New(Options{ - SharedPath: DefaultSharedPath, - }, emptyCallback) - defer hc.Free() - - assert.NotNil(t, hc) - assert.NoError(t, err) - - err = hc.StopAtCheckpoint() - assert.Equal(t, ErrUnableToStopAtCheckpoint, err) -} - -func ExampleHashcat_RunJobWithOptions() { - - eventCallback := func(hc unsafe.Pointer, payload interface{}) { - switch pl := payload.(type) { - case LogPayload: - if DebugTest { - fmt.Printf("LOG [%s] %s\n", pl.Level, pl.Message) - } - case ActionPayload: - if DebugTest { - fmt.Printf("ACTION [%d] %s\n", pl.HashcatEvent, pl.Message) - } - case CrackedPayload: - if DebugTest { - fmt.Printf("CRACKED %s -> %s\n", pl.Hash, pl.Value) - } - case FinalStatusPayload: - if DebugTest { - fmt.Printf("FINAL STATUS -> %v\n", pl.Status) - } - case TaskInformationPayload: - if DebugTest { - fmt.Printf("TASK INFO -> %v\n", pl) - } - } - } - - hc, err := New(Options{ - SharedPath: "/usr/local/share/hashcat", - }, eventCallback) - defer hc.Free() - - if err != nil { - log.Fatal(err) - } - - err = hc.RunJobWithOptions(hcargp.HashcatSessionOptions{ - AttackMode: hcargp.GetIntPtr(0), - HashType: hcargp.GetIntPtr(0), - PotfileDisable: hcargp.GetBoolPtr(true), - InputFile: "9f9d51bc70ef21ca5c14f307980a29d8", - DictionaryMaskDirectoryInput: hcargp.GetStringPtr("./testdata/test_dictionary.txt"), - }) - - if err != nil { - log.Fatal(err) - } -} diff --git a/gocat/handlers.go b/gocat/handlers.go deleted file mode 100644 index 230582f..0000000 --- a/gocat/handlers.go +++ /dev/null @@ -1,134 +0,0 @@ -package gocat - -// #include "wrapper.h" -import "C" -import ( - "fmt" - "strings" - "time" -) - -const ( - // InfoMessage is a log message from hashcat with the id of EVENT_LOG_INFO - InfoMessage LogLevel = iota - // WarnMessage is a log message from hashcat with the id of EVENT_LOG_WARNING - WarnMessage - // ErrorMessage is a log message from hashcat with the id of EVENT_LOG_ERROR - ErrorMessage - // AdviceMessage is a log message from hashcat with the id of EVENT_LOG_ADVICE - AdviceMessage -) - -// LogPayload defines the structure of an event log message from hashcat and sent to the user via the callback -type LogPayload struct { - Level LogLevel - Message string - Error error -} - -// TaskInformationPayload includes information about the task that hashcat is getting ready to process. This includes deduplicated hashes, etc. -type TaskInformationPayload struct { - NumHashes uint32 - NumHashesUnique uint32 - NumSalts uint32 -} - -// ActionPayload defines the structure of a generic hashcat event and sent to the user via the callback. -// An example of this would be the numerous PRE/POST events. -type ActionPayload struct { - HashcatEvent uint32 - LogPayload -} - -// CrackedPayload defines the structure of a cracked message from hashcat and sent to the user via the callback -type CrackedPayload struct { - IsPotfile bool - Hash string - Value string - CrackedAt time.Time -} - -// FinalStatusPayload is returned at the end of the cracking session -type FinalStatusPayload struct { - Status *Status - EndedAt time.Time - // AllHashesCracked is set when all hashes either exist in a potfile or are considered "weak" - AllHashesCracked bool -} - -// ErrCrackedPayload is raised whenever we get a cracked password callback but was unable to parse the message from hashcat -type ErrCrackedPayload struct { - Separator string - CrackedMsg string -} - -func (e ErrCrackedPayload) Error() string { - return fmt.Sprintf("Could not locate separator `%s` in msg", e.Separator) -} - -// LogLevel indicates the type of log message from hashcat -type LogLevel int8 - -func (s LogLevel) String() string { - switch s { - case InfoMessage: - return "INFO" - case WarnMessage: - return "WARN" - case ErrorMessage: - return "ERROR" - case AdviceMessage: - return "ADVICE" - default: - return "UNKNOWN" - } -} - -// logMessageCbFromEvent is called whenever hashcat sends a INFO/WARN/ERROR message -func logMessageCbFromEvent(ctx *C.hashcat_ctx_t, lvl LogLevel) LogPayload { - ectx := ctx.event_ctx - - return LogPayload{ - Level: lvl, - Message: C.GoStringN(&ectx.msg_buf[0], ectx.msg_len), - } -} - -func logMessageWithError(id uint32, err error) LogPayload { - return LogPayload{ - Level: ErrorMessage, - Message: err.Error(), - Error: err, - } -} - -func logHashcatAction(id uint32, msg string) ActionPayload { - return ActionPayload{ - LogPayload: LogPayload{ - Level: InfoMessage, - Message: msg, - }, - HashcatEvent: id, - } -} - -func getCrackedPassword(id uint32, msg string, sep string) (pl CrackedPayload, err error) { - // Some messages can have multiple variations of the separator (example: kerberos 13100) - // so we find the last one and use that to separate the original hash and it's value - idx := strings.LastIndex(msg, sep) - if idx == -1 { - err = ErrCrackedPayload{ - Separator: sep, - CrackedMsg: msg, - } - return - } - - pl = CrackedPayload{ - Hash: msg[:idx], - Value: msg[idx+1:], - IsPotfile: id == C.EVENT_POTFILE_HASH_SHOW, - CrackedAt: time.Now().UTC(), - } - return -} diff --git a/gocat/handlers_test.go b/gocat/handlers_test.go deleted file mode 100644 index bf85be3..0000000 --- a/gocat/handlers_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package gocat - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestLogLevelString(t *testing.T) { - for _, test := range []struct { - ll LogLevel - expected string - }{ - { - ll: InfoMessage, - expected: "INFO", - }, - { - ll: WarnMessage, - expected: "WARN", - }, - { - ll: ErrorMessage, - expected: "ERROR", - }, - { - ll: AdviceMessage, - expected: "ADVICE", - }, - { - ll: LogLevel(9), - expected: "UNKNOWN", - }, - } { - assert.Equal(t, test.expected, test.ll.String()) - } -} - -func TestGetCrackedPassword(t *testing.T) { - pl, err := getCrackedPassword(1, "deadbeefdeadbeefdeadbeefdeadbeef:chris", ":") - assert.Nilf(t, err, "expected err to be nil") - assert.Equalf(t, "chris", pl.Value, "expected the value to be chris") - assert.Equal(t, "deadbeefdeadbeefdeadbeefdeadbeef", pl.Hash, "expected the hash to be deadbeef yo!") - - pl, err = getCrackedPassword(1, "deadbeefdeadbeefdeadbeefdeadbeef:chris", ";") - assert.Equalf(t, err, ErrCrackedPayload{ - Separator: ";", - CrackedMsg: "deadbeefdeadbeefdeadbeefdeadbeef:chris", - }, "err payload is not correct") - - ecperr, ok := err.(ErrCrackedPayload) - assert.True(t, ok) - assert.NotNil(t, ecperr) - assert.Equal(t, "Could not locate separator `;` in msg", err.Error()) -} diff --git a/gocat/hcargp/gocat_hashcat_options.go b/gocat/hcargp/gocat_hashcat_options.go deleted file mode 100644 index 5caa0cb..0000000 --- a/gocat/hcargp/gocat_hashcat_options.go +++ /dev/null @@ -1,188 +0,0 @@ -package hcargp - -import ( - "fmt" - "reflect" - "runtime" - "strings" -) - -// GetStringPtr returns the pointer of s -func GetStringPtr(s string) *string { - return &s -} - -// GetIntPtr returns the pointer of i -func GetIntPtr(i int) *int { - return &i -} - -// GetBoolPtr returns the pointer of b -func GetBoolPtr(b bool) *bool { - return &b -} - -/* -We skip the following arguments because they are not needed: -- version -- help -- quiet -- status -- status-timer -- machine-readable -- stdout -- show -- left -- benchmark -- speed-only (todo?) -- progress-only (todo?) -- opencl-info -- keyspace -*/ - -// HashcatSessionOptions represents all the available hashcat options. The values here should always follow the latest version of hashcat -type HashcatSessionOptions struct { - HashType *int `hashcat:"--hash-type,omitempty"` - AttackMode *int `hashcat:"--attack-mode,omitempty"` - IsHexCharset *bool `hashcat:"--hex-charset,omitempty"` - IsHexSalt *bool `hashcat:"--hex-salt,omitempty"` - IsHexWordlist *bool `hashcat:"--hex-wordlist,omitempty"` - KeepGuessing *bool `hashcat:"--keep-guessing,omitempty"` - Loopback *bool `hashcat:"--loopback,omitempty"` - WeakHashThreshold *int `hashcat:"--weak-hash-threshold,omitempty"` - MarkovHCStat *string `hashcat:"--markov-hcstat,omitempty"` - DisableMarkov *bool `hashcat:"--markov-disable,omitempty"` - EnableClassicMarkov *bool `hashcat:"--markov-classic,omitempty"` - MarkovThreshold *int `hashcat:"--markov-threshold,omitempty"` - Force *bool `hashcat:"--force,omitempty"` - MaxRuntimeSeconds *int `hashcat:"--runtime,omitempty"` - SessionName *string `hashcat:"--session,omitempty"` - RestoreSession *bool `hashcat:"--restore,omitempty"` - DisableRestore *bool `hashcat:"--restore-disable,omitempty"` - RestoreFilePath *string `hashcat:"--restore-file-path,omitempty"` - OutfilePath *string `hashcat:"--outfile,omitempty"` - OutfileFormat *int `hashcat:"--outfile-format,omitempty"` - OutfileDisableAutoHex *bool `hashcat:"--outfile-autohex-disable,omitempty"` - OutfileCheckTimer *int `hashcat:"--outfile-check-timer,omitempty"` - Separator *string `hashcat:"--separator,omitempty"` - IgnoreUsername *bool `hashcat:"--username,omitempty"` - RemoveCrackedHash *bool `hashcat:"--remove,omitempty"` - RemoveCrackedHashTimer *int `hashcat:"--remove-timer,omitempty"` - PotfileDisable *bool `hashcat:"--potfile-disable,omitempty"` - PotfilePath *string `hashcat:"--potfile-path,omitempty"` - DebugMode *int `hashcat:"--debug-mode,omitempty"` - DebugFile *string `hashcat:"--debug-file,omitempty"` - InductionDir *string `hashcat:"--induction-dir,omitempty"` - LogfileDisable *bool `hashcat:"--logfile-disable,omitempty"` - HccapxMessagePair *string `hashcat:"--hccapx-message-pair,omitempty"` - NonceErrorCorrections *int `hashcat:"--nonce-error-corrections,omitempty"` - TrueCryptKeyFiles *string `hashcat:"--truecrypt-keyfiles,omitempty"` - VeraCryptKeyFiles *string `hashcat:"--veracrypt-keyfiles,omitempty"` - VeraCryptPIM *int `hashcat:"--veracrypt-pim,omitempty"` - SegmentSize *int `hashcat:"--segment-size,omitempty"` - BitmapMin *int `hashcat:"--bitmap-min,omitempty"` - BitmapMax *int `hashcat:"--bitmap-max,omitempty"` - CPUAffinity *string `hashcat:"--cpu-affinity,omitempty"` - OpenCLPlatforms *string `hashcat:"--opencl-platforms,omitempty"` - OpenCLDevices *string `hashcat:"--opencl-devices,omitempty"` - OpenCLDeviceTypes *string `hashcat:"--opencl-device-types,omitempty"` - OpenCLVectorWidth *string `hashcat:"--opencl-vector-width,omitempty"` - WorkloadProfile *int `hashcat:"--workload-profile,omitempty"` - KernelAccel *int `hashcat:"--kernel-accel,omitempty"` - KernelLoops *int `hashcat:"--kernel-loops,omitempty"` - NVIDIASpinDamp *int `hashcat:"--nvidia-spin-damp,omitempty"` - GPUTempDisable *bool `hashcat:"--gpu-temp-disable,omitempty"` - GPUTempAbort *int `hashcat:"--gpu-temp-abort,omitempty"` - GPUTempRetain *int `hashcat:"--gpu-temp-retain,omitempty"` - PowertuneEnable *bool `hashcat:"--powertune-enable,omitempty"` - ScryptTMTO *int `hashcat:"--scrypt-tmto,omitempty"` - Skip *int `hashcat:"--skip,omitempty"` - Limit *int `hashcat:"--limit,omitempty"` - RuleLeft *string `hashcat:"--rule-left,omitempty"` - RuleRight *string `hashcat:"--rule-right,omitempty"` - RulesFile *string `hashcat:"--rules-file,omitempty"` - GenerateRules *int `hashcat:"--generate-rules,omitempty"` - GenerateRulesFuncMin *int `hashcat:"--generate-rules-func-min,omitempty"` - GenerateRulesFuncMax *int `hashcat:"--generate-rules-func-max,omitempty"` - GenerateRulesSeed *int `hashcat:"--generate-rules-seed,omitempty"` - CustomCharset1 *string `hashcat:"--custom-charset1,omitempty"` - CustomCharset2 *string `hashcat:"--custom-charset2,omitempty"` - CustomCharset3 *string `hashcat:"--custom-charset3,omitempty"` - CustomCharset4 *string `hashcat:"--custom-charset4,omitempty"` - IncrementMask *bool `hashcat:"--increment,omitempty"` - IncrementMaskMin *int `hashcat:"--increment-min,omitempty"` - IncrementMaskMax *int `hashcat:"--increment-max,omitempty"` - - // InputFile can be a single hash or multiple hashes via a hashfile or hccapx - InputFile string `hashcat:","` - DictionaryMaskDirectoryInput *string `hashcat:",omitempty"` -} - -func parseTag(t string) (tag, options string) { - if idx := strings.Index(t, ","); idx != -1 { - return t[:idx], t[idx+1:] - } - return tag, "" -} - -// MarshalArgs returns a list of arguments set by the user to be passed into hashcat's session for execution -func (o HashcatSessionOptions) MarshalArgs() (args []string, err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - if s, ok := r.(string); ok { - panic(s) - } - - err = r.(error) - } - }() - - v := reflect.ValueOf(o) - for i := 0; i < v.NumField(); i++ { - tag := v.Type().Field(i).Tag.Get("hashcat") - if tag == "" { - continue - } - - name, opts := parseTag(tag) - val := v.Field(i) - - hasOmitEmpty := strings.Contains(opts, "omitempty") - if (val.Type().Kind() == reflect.Ptr && val.IsNil()) && hasOmitEmpty { - continue - } - - if val.Type().Kind() == reflect.Ptr { - val = reflect.Indirect(val) - } - - switch val.Type().Kind() { - case reflect.Bool: - if val.Bool() { - args = append(args, name) - } - case reflect.Int: - // Int's should always have a name... - if name != "" { - args = append(args, fmt.Sprintf("%s=%d", name, val.Int())) - } - case reflect.String: - if val.String() == "" { - continue - } - - if name != "" { - args = append(args, fmt.Sprintf("%s=%s", name, val.String())) - } else { - args = append(args, val.String()) - } - default: - err = fmt.Errorf("unknown type %s", val.Type().Kind()) - return - } - } - return -} diff --git a/gocat/hcargp/gocat_hashcat_options_test.go b/gocat/hcargp/gocat_hashcat_options_test.go deleted file mode 100644 index 0f07515..0000000 --- a/gocat/hcargp/gocat_hashcat_options_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package hcargp - -import ( - "fmt" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func ExampleHashcatSessionOptions_MarshalArgs() { - opts := HashcatSessionOptions{ - AttackMode: GetIntPtr(0), - HashType: GetIntPtr(0), - SessionName: GetStringPtr("example_args_session"), - PotfileDisable: GetBoolPtr(true), - InputFile: "deadbeefdeadbeefdeadbeefdeadbeef", - } - - args, err := opts.MarshalArgs() - if err != nil { - fmt.Printf("Failed to marshal args: %s\n", err) - return - } - - fmt.Println(strings.Join(args, " ")) - // Output: --hash-type=0 --attack-mode=0 --session=example_args_session --potfile-disable deadbeefdeadbeefdeadbeefdeadbeef -} - -func TestInternalParseTag(t *testing.T) { - tag, opts := parseTag("--example,omitempty,required") - - assert.Equal(t, "--example", tag) - assert.Equal(t, "omitempty,required", opts) - - tag, opts = parseTag("--example2,") - assert.Equal(t, "--example2", tag) - assert.Equal(t, "", opts) -} - -func TestHashcatSessionOptionsMarshalArgs(t *testing.T) { - for _, test := range []struct { - opts HashcatSessionOptions - expectedError error - expectedArgs []string - }{ - { - opts: HashcatSessionOptions{ - AttackMode: GetIntPtr(0), - HashType: nil, - InputFile: "deadbeefdeadbeefdeadbeefdeadbeef", - }, - expectedError: nil, - expectedArgs: []string{"--attack-mode=0", "deadbeefdeadbeefdeadbeefdeadbeef"}, - }, - { - opts: HashcatSessionOptions{ - IsHexCharset: GetBoolPtr(true), - InputFile: "deadbeefdeadbeefdeadbeefdeadbeef", - }, - expectedError: nil, - expectedArgs: []string{"--hex-charset", "deadbeefdeadbeefdeadbeefdeadbeef"}, - }, - { - opts: HashcatSessionOptions{ - IsHexCharset: GetBoolPtr(false), - InputFile: "deadbeefdeadbeefdeadbeefdeadbeef", - }, - expectedError: nil, - expectedArgs: []string{"deadbeefdeadbeefdeadbeefdeadbeef"}, - }, - { - opts: HashcatSessionOptions{ - AttackMode: GetIntPtr(0), - InputFile: "deadbeefdeadbeefdeadbeefdeadbeef", - DictionaryMaskDirectoryInput: GetStringPtr("./testdata/test_dictionary.txt"), - }, - expectedError: nil, - expectedArgs: []string{"--attack-mode=0", "deadbeefdeadbeefdeadbeefdeadbeef", "./testdata/test_dictionary.txt"}, - }, - } { - args, err := test.opts.MarshalArgs() - - assert.Equal(t, test.expectedError, err) - assert.Equal(t, test.expectedArgs, args) - } -} diff --git a/gocat/reentrant_patch_posix.go b/gocat/reentrant_patch_posix.go deleted file mode 100644 index 1211f9d..0000000 --- a/gocat/reentrant_patch_posix.go +++ /dev/null @@ -1,48 +0,0 @@ -// +build linux,cgo darwin,cgo - -package gocat - -/* -#include -#include "wrapper.h" - -extern int patch_event_ctx(hashcat_ctx_t *hashcat_ctx) -{ - int rc = -1; - - pthread_mutex_t pMutex; - pthread_mutexattr_t pAttr; - - event_ctx_t *event_ctx = hashcat_ctx->event_ctx; - - hc_thread_mutex_delete(event_ctx->mux_event); - rc = pthread_mutexattr_init(&pAttr); - if (rc != 0) - goto finished; - - pthread_mutexattr_settype(&pAttr, PTHREAD_MUTEX_RECURSIVE); - - rc = pthread_mutex_init(&pMutex, &pAttr); - if (rc != 0) - goto finished; - - event_ctx->mux_event = pMutex; - -finished: - return rc; -} -*/ -import "C" -import "errors" - -var errReentrantPatch = errors.New("failed to patch hashcat_ctx->event_ctx mutex") - -// patchEventMutex frees and updates hashcat's event mutex with a recursive one -// that allows an event callback to call another event callback without a deadlock condition -// NOTE: this only works on posix systems (linux, darwin) -func patchEventMutex(ctx C.hashcat_ctx_t) (patched bool, err error) { - if retval := C.patch_event_ctx(&ctx); retval != 0 { - return false, errReentrantPatch - } - return true, nil -} diff --git a/gocat/reentrant_patch_windows.go b/gocat/reentrant_patch_windows.go deleted file mode 100644 index a0113c1..0000000 --- a/gocat/reentrant_patch_windows.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build windows - -package gocat - -// patchEventMutex frees and updates hashcat's event mutex with a recursive one -// that allows an event callback to call another event callback without a deadlock condition. -func patchEventMutex(ctx interface{}) (patched bool, err error) { - // EnterCriticalSection on windows already allows for reentry into a critical section if called by the same thread - // see remarks @ https://msdn.microsoft.com/en-us/library/windows/desktop/ms682608(v=vs.85).aspx - return true, nil -} diff --git a/gocat/restoreutil/restoreutil.go b/gocat/restoreutil/restoreutil.go deleted file mode 100644 index ae0d670..0000000 --- a/gocat/restoreutil/restoreutil.go +++ /dev/null @@ -1,161 +0,0 @@ -package restoreutil - -import ( - "bufio" - "bytes" - "encoding/binary" - "io" - "os" - "strings" -) - -// StructSize is the size of the hashcat restore structure as defined here -// https://hashcat.net/wiki/doku.php?id=restore -const StructSize = 283 - -// RestoreData defines hashcat's .restore file -type RestoreData struct { - // Version is the hashcat version used to create the file - Version uint32 - // WorkingDirectory is the current working directory that hashcat was in at the time of this restore point. - // Hashcat will change to this directory. - WorkingDirectory string - // DictionaryPosition is the current poisition within the dictionary - DictionaryPosition uint32 - // MasksPosition is the current position within the list of masks - MasksPosition uint32 - // WordsPosition is the position within the dictionary/mask - WordsPosition uint64 - // ArgCount is the number of command line arguments passed into hashcat - ArgCount uint32 - - ArgvPointer uint64 - // Args contains the command line arguments - Args []string -} - -func (s *RestoreData) Write(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, &s.Version); err != nil { - return err - } - - bwd := make([]byte, 256) - for i, r := range s.WorkingDirectory { - bwd[i] = byte(r) - } - - if _, err := w.Write(bwd); err != nil { - return err - } - - if err := binary.Write(w, binary.LittleEndian, &s.DictionaryPosition); err != nil { - return err - } - - if err := binary.Write(w, binary.LittleEndian, &s.MasksPosition); err != nil { - return err - } - - // padding - if _, err := w.Write(make([]byte, 0x4)); err != nil { - return err - } - - if err := binary.Write(w, binary.LittleEndian, &s.WordsPosition); err != nil { - return err - } - - if err := binary.Write(w, binary.LittleEndian, &s.ArgCount); err != nil { - return err - } - - // padding - if _, err := w.Write(make([]byte, 0x4)); err != nil { - return err - } - - if err := binary.Write(w, binary.LittleEndian, &s.ArgvPointer); err != nil { - return err - } - - for _, arg := range s.Args { - barg := []byte(arg) - if !bytes.HasSuffix(barg, []byte("\n")) { - barg = append(barg, 10) - } - if _, err := w.Write(barg); err != nil { - return err - } - } - - return nil -} - -func restoreParser(f io.ReadSeeker, rd *RestoreData) error { - if err := binary.Read(f, binary.LittleEndian, &rd.Version); err != nil { - return err - } - - buf := make([]byte, 256) - if err := binary.Read(f, binary.LittleEndian, &buf); err != nil { - return err - } - rd.WorkingDirectory = string(bytes.Trim(buf, "\x00")) - - if err := binary.Read(f, binary.LittleEndian, &rd.DictionaryPosition); err != nil { - return err - } - - if err := binary.Read(f, binary.LittleEndian, &rd.MasksPosition); err != nil { - return err - } - - // there's 4 bytes of padding here in the struct - if _, err := f.Seek(0x4, os.SEEK_CUR); err != nil { - return err - } - - if err := binary.Read(f, binary.LittleEndian, &rd.WordsPosition); err != nil { - return err - } - - if err := binary.Read(f, binary.LittleEndian, &rd.ArgCount); err != nil { - return err - } - - // more padding... - if _, err := f.Seek(0x4, os.SEEK_CUR); err != nil { - return err - } - - if err := binary.Read(f, binary.LittleEndian, &rd.ArgvPointer); err != nil { - return err - } - - rdr := bufio.NewReader(f) - arg, err := rdr.ReadString('\n') - for err != io.EOF { - rd.Args = append(rd.Args, strings.TrimSpace(arg)) - arg, err = rdr.ReadString('\n') - } - return nil -} - -// ReadRestoreFile reads the restore file from fp -func ReadRestoreFile(fp string) (rd RestoreData, err error) { - f, err := os.Open(fp) - if err != nil { - return rd, err - } - defer f.Close() - - err = restoreParser(f, &rd) - return -} - -// ReadRestoreBytes reads the restore file from the bytes passed in -func ReadRestoreBytes(b []byte) (rd RestoreData, err error) { - rdr := bytes.NewReader(b) - err = restoreParser(rdr, &rd) - return -} diff --git a/gocat/restoreutil/restoreutil_test.go b/gocat/restoreutil/restoreutil_test.go deleted file mode 100644 index 5547f77..0000000 --- a/gocat/restoreutil/restoreutil_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package restoreutil - -import ( - "bytes" - "crypto/md5" - "io/ioutil" - "testing" - - "github.com/stretchr/testify/assert" -) - -func testRestoreFileContents(rd RestoreData, t *testing.T) { - assert.Equal(t, uint32(0x15e), rd.Version) - assert.Equal(t, "/Users/cschmitt/Desktop", rd.WorkingDirectory) - assert.Equal(t, uint32(0x0), rd.DictionaryPosition) - assert.Equal(t, uint32(0x4), rd.MasksPosition) - assert.Equal(t, uint64(0xaf0000), rd.WordsPosition) - assert.Equal(t, uint32(8), rd.ArgCount) - assert.Equal(t, 8, len(rd.Args)) -} - -func TestReadRestoreFile(t *testing.T) { - rd, err := ReadRestoreFile("./testdata/unittest_example.restore") - assert.Nil(t, err) - testRestoreFileContents(rd, t) -} - -func TestRestoreBytes(t *testing.T) { - b, err := ioutil.ReadFile("./testdata/unittest_example.restore") - assert.Nil(t, err) - - rd, err := ReadRestoreBytes(b) - assert.Nil(t, err) - testRestoreFileContents(rd, t) -} - -func TestWrite(t *testing.T) { - b, err := ioutil.ReadFile("./testdata/unittest_example.restore") - assert.Nil(t, err) - - beforeXsum := md5.Sum(b) - - rd, err := ReadRestoreBytes(b) - assert.Nil(t, err) - - buf := new(bytes.Buffer) - // Write the restore file to a byte Buffer - err = rd.Write(buf) - assert.Nil(t, err) - - afterXsum := md5.Sum(buf.Bytes()) - assert.Equalf(t, beforeXsum, afterXsum, "checksum mismatch") -} diff --git a/gocat/restoreutil/testdata/unittest_example.restore b/gocat/restoreutil/testdata/unittest_example.restore deleted file mode 100644 index 35c5e0c..0000000 Binary files a/gocat/restoreutil/testdata/unittest_example.restore and /dev/null differ diff --git a/gocat/testdata/mix_of_invalid_and_valid.hashes b/gocat/testdata/mix_of_invalid_and_valid.hashes deleted file mode 100644 index b055d47..0000000 --- a/gocat/testdata/mix_of_invalid_and_valid.hashes +++ /dev/null @@ -1,3 +0,0 @@ -5d41402abc4b2a76b9719d911017c592 -lolnope -golangbestlang \ No newline at end of file diff --git a/gocat/testdata/one_md5_in_potfile.hashes b/gocat/testdata/one_md5_in_potfile.hashes deleted file mode 100644 index a4f6cab..0000000 --- a/gocat/testdata/one_md5_in_potfile.hashes +++ /dev/null @@ -1,2 +0,0 @@ -5d41402abc4b2a76b9719d911017c592 -6b34fe24ac2ff8103f6fce1f0da2ef57 \ No newline at end of file diff --git a/gocat/testdata/one_md5_in_potfile.potfile b/gocat/testdata/one_md5_in_potfile.potfile deleted file mode 100644 index 4f66b24..0000000 --- a/gocat/testdata/one_md5_in_potfile.potfile +++ /dev/null @@ -1 +0,0 @@ -5d41402abc4b2a76b9719d911017c592:hello \ No newline at end of file diff --git a/gocat/testdata/russian_test.dictionary b/gocat/testdata/russian_test.dictionary deleted file mode 100644 index 6715aa8..0000000 --- a/gocat/testdata/russian_test.dictionary +++ /dev/null @@ -1,4 +0,0 @@ -фыва -фыв -фы -ф \ No newline at end of file diff --git a/gocat/testdata/russian_test.hashes b/gocat/testdata/russian_test.hashes deleted file mode 100644 index 1324ad7..0000000 --- a/gocat/testdata/russian_test.hashes +++ /dev/null @@ -1,4 +0,0 @@ -7b903cd2cb84bf0df76f133ade2b1d09 -809336c0a3882d1f9865b50eaa4b6f9b -056f15cdde7ff06bdcdcdb44feadcd9e -2bfe4581ac6cf8ce4c3e7ee8f07f518b diff --git a/gocat/testdata/test_dictionary.txt b/gocat/testdata/test_dictionary.txt deleted file mode 100644 index 2f37a40..0000000 --- a/gocat/testdata/test_dictionary.txt +++ /dev/null @@ -1,4 +0,0 @@ -hello -world -chris -bob \ No newline at end of file diff --git a/gocat/testdata/two_md5.hashes b/gocat/testdata/two_md5.hashes deleted file mode 100644 index f7f65f6..0000000 --- a/gocat/testdata/two_md5.hashes +++ /dev/null @@ -1,2 +0,0 @@ -5d41402abc4b2a76b9719d911017c592 -7d793037a0760186574b0282f2f435e7 \ No newline at end of file diff --git a/gocat/testdata/two_md5.potfile b/gocat/testdata/two_md5.potfile deleted file mode 100644 index 590c972..0000000 --- a/gocat/testdata/two_md5.potfile +++ /dev/null @@ -1,2 +0,0 @@ -5d41402abc4b2a76b9719d911017c592:hello -7d793037a0760186574b0282f2f435e7:world \ No newline at end of file diff --git a/gocat/validator.go b/gocat/validator.go deleted file mode 100644 index 47a165e..0000000 --- a/gocat/validator.go +++ /dev/null @@ -1,106 +0,0 @@ -package gocat - -// #include "wrapper.h" -import "C" -import ( - "fmt" - "strings" - "unsafe" -) - -// ValidationResult is the output from ValidateHashes and includes information about the hash file -type ValidationResult struct { - Valid bool - Errors []string - NumHashes uint32 - NumHashesUnique uint32 - NumSalts uint32 -} - -const errStr = "linter: %s failed with rv %d" - -// ValidateHashes is a linter that validates hashes before creating and executing a hashcat session -func ValidateHashes(pathToHashes string, hashType uint32) (*ValidationResult, error) { - var err error - var hashes *C.hashes_t - vr := &ValidationResult{ - Valid: true, - } - - hashpath := C.CString(pathToHashes) - defer C.free(unsafe.Pointer(hashpath)) - - validator := C.gocat_ctx_t{ - ctx: C.hashcat_ctx_t{}, - gowrapper: unsafe.Pointer(vr), - bValidateHashes: true, - } - - if retval := C.hashcat_init(&validator.ctx, (*[0]byte)(unsafe.Pointer(C.event))); retval != 0 { - err = fmt.Errorf(errStr, "hashcat_init", retval) - goto cleanup - } - - if retval := C.user_options_init(&validator.ctx); retval != 0 { - err = fmt.Errorf(errStr, "user_options_init", retval) - goto cleanup - } - - validator.ctx.user_options.hash_mode = C.u32(hashType) - validator.ctx.user_options_extra.hc_hash = hashpath - - if retval := C.hashconfig_init(&validator.ctx); retval != 0 { - err = fmt.Errorf(errStr, "hashconfig_init", retval) - goto cleanup - } - - hashes = validator.ctx.hashes - // Load hashes - if retval := C.hashes_init_stage1(&validator.ctx); retval != 0 { - err = fmt.Errorf(errStr, "hashes_init_stage1", retval) - goto cleanup - } - - // Removes duplicates - hashes.hashes_cnt_orig = hashes.hashes_cnt - if retval := C.hashes_init_stage2(&validator.ctx); retval != 0 { - err = fmt.Errorf(errStr, "hashes_init_stage2", retval) - goto cleanup - } - -cleanup: - if validator.ctx.hashes != nil { - vr.NumHashes = uint32(validator.ctx.hashes.hashes_cnt_orig) - vr.NumHashesUnique = uint32(validator.ctx.hashes.digests_cnt) - vr.NumSalts = uint32(validator.ctx.hashes.salts_cnt) - } - - if &validator.ctx != nil { - C.hashcat_destroy(&validator.ctx) - } - - return vr, err -} - -//export validatorCallback -func validatorCallback(id uint32, hcCtx *C.hashcat_ctx_t, results unsafe.Pointer, buf unsafe.Pointer, len C.size_t) { - var r = (*ValidationResult)(results) - - switch id { - case C.EVENT_LOG_WARNING: - ectx := hcCtx.event_ctx - msg := C.GoStringN(&ectx.msg_buf[0], ectx.msg_len) - if strings.Contains(msg, "kernel not found") || strings.Contains(msg, "falling back to") { - return - } - - if strings.Contains(msg, "Hashfile") && strings.Contains(msg, "on ") { - // strip out the filename in the warning as it's unnecessary for our purposes - onIndex := strings.Index(msg, "on ") - msg = msg[onIndex+3:] - } - - r.Valid = false - r.Errors = append(r.Errors, msg) - } -} diff --git a/gocat/validator_test.go b/gocat/validator_test.go deleted file mode 100644 index 191bde0..0000000 --- a/gocat/validator_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package gocat - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestValidateHashes(t *testing.T) { - for i, test := range []struct { - HashPathToTest string - HashType uint32 - IsValid bool - ExpectedHashes uint32 - ExpectedUniqueHashes uint32 - ExpectedSalts uint32 - ExpectedNumErrors int - }{ - { - HashPathToTest: "./testdata/mix_of_invalid_and_valid.hashes", - HashType: 0, - IsValid: false, - ExpectedHashes: 1, - ExpectedUniqueHashes: 1, - ExpectedSalts: 1, - ExpectedNumErrors: 2, - }, - { - HashPathToTest: "./testdata/two_md5.hashes", - HashType: 0, - IsValid: true, - ExpectedHashes: 2, - ExpectedUniqueHashes: 2, - ExpectedSalts: 1, - }, - } { - vr, err := ValidateHashes(test.HashPathToTest, test.HashType) - if err != nil { - assert.FailNow(t, "failed to initialize the validator") - } - - assert.Equalf(t, test.IsValid, vr.Valid, "failed equality check in test %d", i) - assert.Equalf(t, test.ExpectedHashes, vr.NumHashes, "failed equality check in test %d", i) - assert.Equalf(t, test.ExpectedUniqueHashes, vr.NumHashesUnique, "failed equality check in test %d", i) - assert.Equalf(t, test.ExpectedSalts, vr.NumSalts, "failed equality check in test %d", i) - assert.Equalf(t, test.ExpectedNumErrors, len(vr.Errors), "failed equality check in test %d", i) - } -} diff --git a/gocat/wrapper.c b/gocat/wrapper.c deleted file mode 100644 index cb3e0ae..0000000 --- a/gocat/wrapper.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "wrapper.h" - -void event(const u32 id, hashcat_ctx_t *hashcat_ctx, const void *buf, const size_t len) -{ - gocat_ctx_t *worker_tuple = (gocat_ctx_t*)hashcat_ctx; - // call the validator callback if we're in the hash validation mode - if (worker_tuple->bValidateHashes) - { - validatorCallback(id, &worker_tuple->ctx, worker_tuple->gowrapper, (void*)buf, (size_t)len); - } - else - { - callback(id, &worker_tuple->ctx, worker_tuple->gowrapper, (void*)buf, (size_t)len); - } -} - -void freeargv(int argc, char **argv) -{ - for (int i = 0; i < argc; i++) - { - free(argv[i]); - } - free(argv); -} diff --git a/gocat/wrapper.h b/gocat/wrapper.h deleted file mode 100644 index 5ebeff0..0000000 --- a/gocat/wrapper.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef GOHASHCAT_H_ -#define GOHASHCAT_H_ - -#include "common.h" -#include "types.h" -#include "memory.h" -#include "status.h" -#include "user_options.h" -#include "hashcat.h" -#include "potfile.h" -#include "thread.h" -#include "hashes.h" -#include "interface.h" - -typedef struct -{ - hashcat_ctx_t ctx; - void *gowrapper; - bool bValidateHashes; -} gocat_ctx_t; - -void callback(u32 id, hashcat_ctx_t *hashcat_ctx, void *wrapper, void *buf, size_t len); -void validatorCallback(u32 id, hashcat_ctx_t *hashcat_ctx, void *wrapper, void *buf, size_t len); -void event(const u32 id, hashcat_ctx_t *hashcat_ctx, const void *buf, const size_t len); -void freeargv(int argc, char **argv); - -#endif \ No newline at end of file diff --git a/misc/setup_travis.sh b/misc/setup_travis.sh index d5eb7b6..c9f2346 100755 --- a/misc/setup_travis.sh +++ b/misc/setup_travis.sh @@ -3,4 +3,5 @@ cd $HOME git clone https://github.com/hashcat/hashcat.git && cd $HOME/hashcat && git submodule update --init --recursive && git checkout $HASHCAT_VERSION -make SHARED=1 +make SHARED=1 ENABLE_BRAIN=0 +sudo make install \ No newline at end of file diff --git a/server/rpc/rpc_tasks.go b/server/rpc/rpc_tasks.go index 94cca4e..0276de7 100644 --- a/server/rpc/rpc_tasks.go +++ b/server/rpc/rpc_tasks.go @@ -115,6 +115,7 @@ func (s *RPCServer) getTaskPayload(c *gin.Context) *RPCError { Err: err, } } + c.JSON(http.StatusOK, &NewTaskPayloadResponse{ TaskID: task.TaskID, FileID: task.FileID, @@ -124,7 +125,6 @@ func (s *RPCServer) getTaskPayload(c *gin.Context) *RPCError { TaskDuration: task.TaskDuration, }) - c.JSON(http.StatusOK, task) return nil } diff --git a/server/web/file_management_task.go b/server/web/file_management_task.go index 45f3847..7872106 100644 --- a/server/web/file_management_task.go +++ b/server/web/file_management_task.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/fireeye/gocrack/gocat" + "github.com/fireeye/gocat" "github.com/fireeye/gocrack/server/storage" "github.com/gin-gonic/gin" diff --git a/server/web/server.go b/server/web/server.go index 2f8ac80..9f14bdb 100644 --- a/server/web/server.go +++ b/server/web/server.go @@ -180,10 +180,9 @@ func newHTTPServer(cfg Config, s *Server) *http.Server { c.File(filepath.Join(cfg.UserInterface.StaticPath, "index.html")) }) - engine.StaticFS("/static/js/", http.Dir(filepath.Join(cfg.UserInterface.StaticPath, "static", "js"))) - engine.StaticFS("/static/css/", http.Dir(filepath.Join(cfg.UserInterface.StaticPath, "static", "css"))) - engine.StaticFS("/static/fonts/", http.Dir(filepath.Join(cfg.UserInterface.StaticPath, "static", "fonts"))) - engine.StaticFS("/static/img/", http.Dir(filepath.Join(cfg.UserInterface.StaticPath, "static", "img"))) + engine.StaticFS("/js/", http.Dir(filepath.Join(cfg.UserInterface.StaticPath, "js"))) + engine.StaticFS("/css/", http.Dir(filepath.Join(cfg.UserInterface.StaticPath, "css"))) + engine.StaticFS("/img/", http.Dir(filepath.Join(cfg.UserInterface.StaticPath, "img"))) engine.StaticFile("/favicon.ico", filepath.Join(cfg.UserInterface.StaticPath, "favicon.ico")) engine.GET("/gocrack-config.json", func(c *gin.Context) { c.JSON(http.StatusOK, map[string]interface{}{ diff --git a/server/web/task_management.go b/server/web/task_management.go index 2ac65c3..188f7e2 100644 --- a/server/web/task_management.go +++ b/server/web/task_management.go @@ -65,11 +65,12 @@ type CreateTaskResponse struct { // HashcatEnginePayload defines the structure of task.EnginePayload for jobs created for the hashcat engine type HashcatEnginePayload struct { - HashType string `json:"hash_type"` - AttackMode string `json:"attack_mode"` - Masks *EngineFileItem `json:"masks,omitempty"` - DictionaryFile *EngineFileItem `json:"dictionary_file,omitempty"` - ManglingRuleFile *EngineFileItem `json:"mangling_file,omitempty"` + HashType string `json:"hash_type"` + AttackMode string `json:"attack_mode"` + Masks *EngineFileItem `json:"masks,omitempty"` + DictionaryFile *EngineFileItem `json:"dictionary_file,omitempty"` + ManglingRuleFile *EngineFileItem `json:"mangling_file,omitempty"` + DisableOptimizedEngine bool `json:"disable_optimizations"` } // TaskInfoResponseItem defines the response for all the information possible about a given task @@ -300,8 +301,8 @@ NoAccess: } BadRequest: return &WebAPIError{ - StatusCode: http.StatusBadRequest, - Err: err, + StatusCode: http.StatusBadRequest, + Err: err, CanErrorBeShownToUser: true, UserError: "Your request is malformed", } @@ -370,24 +371,24 @@ func (s *Server) getAvailableTasks(c *gin.Context) *WebAPIError { if pageNum, err = strconv.Atoi(c.DefaultQuery("page", "0")); err != nil { return &WebAPIError{ - StatusCode: http.StatusBadRequest, - Err: errors.New("page must be an integer"), + StatusCode: http.StatusBadRequest, + Err: errors.New("page must be an integer"), CanErrorBeShownToUser: true, } } if limit, err = strconv.Atoi(c.DefaultQuery("limit", "20")); err != nil { return &WebAPIError{ - StatusCode: http.StatusBadRequest, - Err: errors.New("limit must be an integer"), + StatusCode: http.StatusBadRequest, + Err: errors.New("limit must be an integer"), CanErrorBeShownToUser: true, } } if ascendingOrder, err = strconv.ParseBool(c.DefaultQuery("ascending", "0")); err != nil { return &WebAPIError{ - StatusCode: http.StatusBadRequest, - Err: errors.New("ascending must be a boolean"), + StatusCode: http.StatusBadRequest, + Err: errors.New("ascending must be a boolean"), CanErrorBeShownToUser: true, } } @@ -497,8 +498,8 @@ func (s *Server) webChangeTaskStatus(c *gin.Context) *WebAPIError { if err := c.BindJSON(&req); err != nil { return &WebAPIError{ - StatusCode: http.StatusBadRequest, - Err: err, + StatusCode: http.StatusBadRequest, + Err: err, CanErrorBeShownToUser: true, } } @@ -588,8 +589,8 @@ func (s *Server) webModifyTask(c *gin.Context) *WebAPIError { if err := c.BindJSON(&request); err != nil { return &WebAPIError{ - StatusCode: http.StatusBadRequest, - Err: err, + StatusCode: http.StatusBadRequest, + Err: err, CanErrorBeShownToUser: true, } } diff --git a/shared/hashcat_types.go b/shared/hashcat_types.go index 5662c59..066c38a 100644 --- a/shared/hashcat_types.go +++ b/shared/hashcat_types.go @@ -73,8 +73,48 @@ var SupportedHashcatModes = []HModeInfo{ Category: "Raw Hash", }, { - Number: 5000, - Name: "SHA-3(Keccak)", + Number: 17300, + Name: "SHA3-224", + Category: "Raw Hash", + }, + { + Number: 17400, + Name: "SHA3-256", + Category: "Raw Hash", + }, + { + Number: 17500, + Name: "SHA3-384", + Category: "Raw Hash", + }, + { + Number: 17600, + Name: "SHA3-512", + Category: "Raw Hash", + }, + { + Number: 17700, + Name: "Keccak-224", + Category: "Raw Hash", + }, + { + Number: 17800, + Name: "Keccak-256", + Category: "Raw Hash", + }, + { + Number: 17900, + Name: "Keccak-384", + Category: "Raw Hash", + }, + { + Number: 18000, + Name: "Keccak-512", + Category: "Raw Hash", + }, + { + Number: 600, + Name: "BLAKE2b-512", Category: "Raw Hash", }, { @@ -99,12 +139,12 @@ var SupportedHashcatModes = []HModeInfo{ }, { Number: 11700, - Name: "GOST R 34.11-2012 (Streebog) 256-bit", + Name: "GOST R 34.11-2012 (Streebog) 256-bit, big-endian", Category: "Raw Hash", }, { Number: 11800, - Name: "GOST R 34.11-2012 (Streebog) 512-bit", + Name: "GOST R 34.11-2012 (Streebog) 512-bit, big-endian", Category: "Raw Hash", }, { @@ -293,6 +333,26 @@ var SupportedHashcatModes = []HModeInfo{ Name: "HMAC-SHA512 (key = $salt)", Category: "Raw Hash, Authenticated", }, + { + Number: 11750, + Name: "HMAC-Streebog-256 (key = $pass), big-endian", + Category: "Raw Hash, Authenticated", + }, + { + Number: 11760, + Name: "HMAC-Streebog-256 (key = $salt), big-endian", + Category: "Raw Hash, Authenticated", + }, + { + Number: 11850, + Name: "HMAC-Streebog-512 (key = $pass), big-endian", + Category: "Raw Hash, Authenticated", + }, + { + Number: 11860, + Name: "HMAC-Streebog-512 (key = $salt), big-endian", + Category: "Raw Hash, Authenticated", + }, { Number: 14000, Name: "DES (PT = $salt, key = $pass)", @@ -345,7 +405,22 @@ var SupportedHashcatModes = []HModeInfo{ }, { Number: 2500, - Name: "WPA/WPA2", + Name: "WPA-EAPOL-PBKDF2", + Category: "Network protocols", + }, + { + Number: 2501, + Name: "WPA-EAPOL-PMK", + Category: "Network protocols", + }, + { + Number: 16800, + Name: "WPA-PMKID-PBKDF2", + Category: "Network protocols", + }, + { + Number: 16801, + Name: "WPA-PMKID-PMK", Category: "Network protocols", }, { @@ -416,6 +491,21 @@ var SupportedHashcatModes = []HModeInfo{ Category: "Network protocols", Example: "$krb5tgs$23$*user$realm$test/spn*$63386d22d359fe42230300d56852c9eb$891ad31d09ab89c6b3b8c5e5de6c06a7f49fd559d7a9a3c32576c8fedf705376cea582ab5938f7fc8bc741acf05c5990741b36ef4311fe3562a41b70a4ec6ecba849905f2385bb3799d92499909658c7287c49160276bca0006c350b0db4fd387adc27c01e9e9ad0c20ed53a7e6356dee2452e35eca2a6a1d1432796fc5c19d068978df74d3d0baf35c77de12456bf1144b6a750d11f55805f5a16ece2975246e2d026dce997fba34ac8757312e9e4e6272de35e20d52fb668c5ed", }, + { + Number: 16100, + Name: "TACACS+", + Category: "Network protocols", + }, + { + Number: 16500, + Name: "JWT (JSON Web Token)", + Category: "Network protocols", + }, + { + Number: 18200, + Name: "Kerberos 5 AS-REP etype 23", + Category: "Network protocols", + }, { Number: 121, Name: "SMF (Simple Machines Forum)", @@ -501,6 +591,11 @@ var SupportedHashcatModes = []HModeInfo{ Name: "Django (PBKDF2-SHA256)", Category: "Forums, CMS, E-Commerce, Frameworks", }, + { + Number: 16000, + Name: "Tripcode", + Category: "Forums, CMS, E-Commerce, Frameworks", + }, { Number: 3711, Name: "Mediawiki B type", @@ -626,6 +721,11 @@ var SupportedHashcatModes = []HModeInfo{ Name: "SSHA-512(Base64), LDAP {SSHA512}", Category: "HTTP, SMTP, LDAP Server", }, + { + Number: 16400, + Name: "CRAM-MD5 Dovecot", + Category: "HTTP, SMTP, LDAP Server", + }, { Number: 15000, Name: "FileZilla Server >= 0.9.55", @@ -696,17 +796,17 @@ var SupportedHashcatModes = []HModeInfo{ }, { Number: 122, - Name: "OSX v10.4, OSX v10.5, OSX v10.6", + Name: "macOS v10.4, MacOS v10.5, MacOS v10.6", Category: "Operating-Systems", }, { Number: 1722, - Name: "OSX v10.7", + Name: "macOS v10.7", Category: "Operating-Systems", }, { Number: 7100, - Name: "OSX v10.8, OSX v10.9, OSX v10.10", + Name: "macOS v10.8+ (PBKDF2-SHA512)", Category: "Operating-Systems", }, { @@ -814,11 +914,21 @@ var SupportedHashcatModes = []HModeInfo{ Name: "SAP CODVN B (BCODE)", Category: "Enterprise Application Software (EAS)", }, + { + Number: 7701, + Name: "SAP CODVN B (BCODE) via RFC_READ_TABLE", + Category: "Enterprise Application Software (EAS)", + }, { Number: 7800, Name: "SAP CODVN F/G (PASSCODE)", Category: "Enterprise Application Software (EAS)", }, + { + Number: 7801, + Name: "SAP CODVN F/G (PASSCODE) via RFC_READ_TABLE", + Category: "Enterprise Application Software (EAS)", + }, { Number: 10300, Name: "SAP CODVN H (PWDSALTEDHASH) iSSHA-1", @@ -909,6 +1019,16 @@ var SupportedHashcatModes = []HModeInfo{ Name: "LUKS", Category: "Full-Disk encryptions (FDE)", }, + { + Number: 16700, + Name: "FileVault 2", + Category: "Full-Disk encryptions (FDE)", + }, + { + Number: 18300, + Name: "Apple File System (APFS)", + Category: "Full-Disk encryptions (FDE)", + }, { Number: 9700, Name: "MS Office <= 2003 $0", @@ -984,6 +1104,11 @@ var SupportedHashcatModes = []HModeInfo{ Name: "PDF 1.7 Level 8 (Acrobat 10 - 11)", Category: "Documents", }, + { + Number: 16200, + Name: "Apple Secure Notes", + Category: "Documents", + }, { Number: 9000, Name: "Password Safe v2", @@ -1019,11 +1144,36 @@ var SupportedHashcatModes = []HModeInfo{ Name: "Blockchain, My Wallet", Category: "Password Managers", }, + { + Number: 15200, + Name: "Blockchain, My Wallet V2", + Category: "Password Managers", + }, + { + Number: 16600, + Name: "Electrum Wallet (Salt-Type 1-3)", + Category: "Password Managers", + }, { Number: 13400, Name: "Keepass 1 (AES/Twofish) and Keepass 2 (AES)", Category: "Password Managers", }, + { + Number: 16300, + Name: "Ethereum Pre-Sale Wallet, PBKDF2-HMAC-SHA256", + Category: "Password Managers", + }, + { + Number: 16900, + Name: "Ansible Vault", + Category: "Password Managers", + }, + { + Number: 18100, + Name: "TOTP (HMAC-SHA1)", + Category: "One-Time Passwords", + }, { Number: 99999, Name: "Plaintext", @@ -1231,7 +1381,12 @@ var SupportedHashcatModes = []HModeInfo{ }, { Number: 15300, - Name: "DPAPI masterkey file v1 and v2", + Name: "DPAPI masterkey file v1", + Category: "Operating Systems", + }, + { + Number: 15900, + Name: "DPAPI masterkey file v2", Category: "Operating Systems", }, } diff --git a/worker/engines/hashcat/hashcat_engine.go b/worker/engines/hashcat/hashcat_engine.go index 007ca20..c490ee2 100644 --- a/worker/engines/hashcat/hashcat_engine.go +++ b/worker/engines/hashcat/hashcat_engine.go @@ -4,11 +4,12 @@ import ( "fmt" "os" "path/filepath" + "runtime" "unsafe" - "github.com/fireeye/gocrack/gocat" - "github.com/fireeye/gocrack/gocat/hcargp" - "github.com/fireeye/gocrack/gocat/restoreutil" + "github.com/fireeye/gocat" + "github.com/fireeye/gocat/hcargp" + "github.com/fireeye/gocat/restoreutil" "github.com/fireeye/gocrack/opencl" "github.com/fireeye/gocrack/server/rpc" "github.com/fireeye/gocrack/server/storage" @@ -141,9 +142,10 @@ func (s *HashcatEngine) Start() error { if _, err := os.Stat(restoreFilePath); !os.IsNotExist(err) { // When restoring, we can really only set restore related options opts = hcargp.HashcatSessionOptions{ - RestoreSession: hcargp.GetBoolPtr(true), - SessionName: hcargp.GetStringPtr(s.TaskID), - RestoreFilePath: hcargp.GetStringPtr(restoreFilePath), + RestoreSession: hcargp.GetBoolPtr(true), + SessionName: hcargp.GetStringPtr(s.TaskID), + RestoreFilePath: hcargp.GetStringPtr(restoreFilePath), + OptimizedKernelEnabled: hcargp.GetBoolPtr(true), } checkpoint, err := restoreutil.ReadRestoreFile(restoreFilePath) @@ -170,13 +172,14 @@ func (s *HashcatEngine) Start() error { } else { // Not a restore opts = hcargp.HashcatSessionOptions{ - AttackMode: hcargp.GetIntPtr(int(s.Options.AttackMode)), - HashType: hcargp.GetIntPtr(s.Options.HashType), - PotfileDisable: hcargp.GetBoolPtr(true), - InputFile: s.TaskFilePath, - SessionName: hcargp.GetStringPtr(s.TaskID), - RestoreFilePath: hcargp.GetStringPtr(restoreFilePath), - OutfilePath: hcargp.GetStringPtr(outFilePath), + AttackMode: hcargp.GetIntPtr(int(s.Options.AttackMode)), + HashType: hcargp.GetIntPtr(s.Options.HashType), + PotfileDisable: hcargp.GetBoolPtr(true), + InputFile: s.TaskFilePath, + SessionName: hcargp.GetStringPtr(s.TaskID), + RestoreFilePath: hcargp.GetStringPtr(restoreFilePath), + OutfilePath: hcargp.GetStringPtr(outFilePath), + OptimizedKernelEnabled: hcargp.GetBoolPtr(true), } if s.MasksFile != "" && s.Options.AttackMode == shared.AttackModeBruteForce { @@ -221,6 +224,9 @@ func (s *HashcatEngine) Start() error { } } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + defer saveCheckpoint(s.Upstream, s.TaskID, restoreFilePath) return s.engine.RunJobWithOptions(opts) } diff --git a/worker/engines/hashcat/helpers.go b/worker/engines/hashcat/helpers.go index a54d37d..8c9a5d4 100644 --- a/worker/engines/hashcat/helpers.go +++ b/worker/engines/hashcat/helpers.go @@ -4,7 +4,7 @@ import ( "io/ioutil" "os" - "github.com/fireeye/gocrack/gocat/restoreutil" + "github.com/fireeye/gocat/restoreutil" "github.com/fireeye/gocrack/server/rpc" "github.com/rs/zerolog/log" diff --git a/worker/engines/hashcat/restore_modifer.go b/worker/engines/hashcat/restore_modifer.go index b15204e..6142aee 100644 --- a/worker/engines/hashcat/restore_modifer.go +++ b/worker/engines/hashcat/restore_modifer.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "github.com/fireeye/gocrack/gocat/restoreutil" + "github.com/fireeye/gocat/restoreutil" "github.com/fireeye/gocrack/opencl" "github.com/fireeye/gocrack/server/storage" "github.com/fireeye/gocrack/shared" diff --git a/worker/engines/hashcat/restore_modifier_test.go b/worker/engines/hashcat/restore_modifier_test.go index 56fadc5..44b301f 100644 --- a/worker/engines/hashcat/restore_modifier_test.go +++ b/worker/engines/hashcat/restore_modifier_test.go @@ -3,7 +3,7 @@ package hashcat import ( "testing" - "github.com/fireeye/gocrack/gocat/restoreutil" + "github.com/fireeye/gocat/restoreutil" "github.com/fireeye/gocrack/opencl" "github.com/fireeye/gocrack/server/storage" "github.com/fireeye/gocrack/shared"