Skip to content

Commit

Permalink
v0.1.5-2024-11-30; fix #6
Browse files Browse the repository at this point in the history
  • Loading branch information
cyclone-github authored Nov 30, 2024
1 parent f3db64f commit 6d715f0
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 169 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,21 @@ _**This toolset is proudly the first publicly released Phantom Vault Extractor a
```
### Decryptor usage example:
```
./phantom_decryptor.bin -h phantom.txt -w wordlist.txt
-----------------------------------------------
| Cyclone's Phantom Vault Decryptor |
| https://github.com/cyclone-github/phantom_pwn |
-----------------------------------------------
Vault file: phantom.txt
Vault file: hash.txt
Valid Vaults: 1
CPU Threads: 16
Wordlist: wordlist.txt
Working...
2024/11/30 14:11:35 Working...
{"encryptedKey":{"digest":"sha256","encrypted":"5pLvA3bCjNGYBbSjjFY3mdPknwFfp3cz9dCBv6izyyrqEhYCBkKwo3zZUzBP44KtY3","iterations":10000,"kdf":"pbkdf2","nonce":"NZT6kw5Cd5VeZu5yJGJcFcP24tnmg4xsR","salt":"A43vTZnm9c5CiQ6FLTdV9v"},"version":1}:password
2024/11/30 14:11:39 Decrypted: 1/1 6181.36 h/s 00h:00m:03s
2024/11/30 14:11:39 Finished
Decrypted: 0/1 6360.82 h/s 00h:01m:00s
```
### Decryptor supported options:
```
Expand Down
4 changes: 2 additions & 2 deletions phantom_decryptor/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.22.4

require (
github.com/btcsuite/btcutil v1.0.2
golang.org/x/crypto v0.26.0
golang.org/x/crypto v0.29.0
)

require golang.org/x/sys v0.23.0 // indirect
require golang.org/x/sys v0.27.0 // indirect
8 changes: 4 additions & 4 deletions phantom_decryptor/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
Expand Down
25 changes: 18 additions & 7 deletions phantom_decryptor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ v0.1.3-2024-07-06-1100;
fixed https://github.com/cyclone-github/phantom_pwn/issues/3
v0.1.4-2024-08-31-1630;
finished implementing flag -o {output file}
v0.1.5-2024-11-30-1415;
fix https://github.com/cyclone-github/phantom_pwn/issues/6
swapped crackedCount and lineProcessed channels for atomic int32 for better performance
multiple performance optimizations in process.go
print vault:password when vault is cracked
*/

// main func
Expand Down Expand Up @@ -80,11 +85,15 @@ func main() {
// set CPU threads
numThreads := setNumThreads(*threadFlag)

// channels / variables
crackedCountCh := make(chan int, 10) // buffer of 10 to reduce blocking
linesProcessedCh := make(chan int, 1000) // buffer of 1000 to reduce blocking
// variables
var (
crackedCount int32
linesProcessed int32
wg sync.WaitGroup
)

// channels
stopChan := make(chan struct{})
var wg sync.WaitGroup

// goroutine to watch for ctrl+c
handleGracefulShutdown(stopChan)
Expand All @@ -102,14 +111,16 @@ func main() {

// monitor status of workers
wg.Add(1)
go monitorPrintStats(crackedCountCh, linesProcessedCh, stopChan, startTime, validVaultCount, &wg, *statsIntervalFlag)
go monitorPrintStats(&crackedCount, &linesProcessed, stopChan, startTime, validVaultCount, &wg, *statsIntervalFlag)

// start the processing logic
startProc(*wordlistFileFlag, *outputFile, numThreads, stopChan, vaults, crackedCountCh, linesProcessedCh)
startProc(*wordlistFileFlag, *outputFile, numThreads, vaults, &crackedCount, &linesProcessed, stopChan)

// close stop channel to signal all workers to stop
time.Sleep(10 * time.Millisecond)
closeStopChannel(stopChan)

// wait for monitorPrintStats to finish
wg.Wait()
}

// end code
2 changes: 1 addition & 1 deletion phantom_decryptor/print_welcome.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// version func
func versionFunc() {
fmt.Fprintln(os.Stderr, "Cyclone's Phantom Vault Decryptor v0.1.4-2024-08-31-1630\nhttps://github.com/cyclone-github/phantom_pwn\n")
fmt.Fprintln(os.Stderr, "Cyclone's Phantom Vault Decryptor v0.1.5-2024-11-30-1415\nhttps://github.com/cyclone-github/phantom_pwn\n")
}

// help func
Expand Down
200 changes: 62 additions & 138 deletions phantom_decryptor/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,185 +2,109 @@ package main

import (
"bufio"
"bytes"
"fmt"
"io"
"log"
"os"
"sync"
"sync/atomic"
"time"
)

// process logic
func startProc(wordlistFileFlag string, outputPath string, numGoroutines int, stopChan chan struct{}, vaults []Vault, crackedCountCh chan int, linesProcessedCh chan int) {
const readBufferSize = 1024 * 1024 // read buffer
const writeBufferSize = 128 * 1024 // write buffer

var linesHashed int64 = 0
var procWg sync.WaitGroup
var readWg sync.WaitGroup
var writeWg sync.WaitGroup
var hexDecodeErrors int64 = 0 // hex error counter

readChunks := make(chan []byte, 500) // channel for reading chunks of data
writeData := make(chan []byte, 10) // channel for writing processed data

func startProc(wordlistFileFlag string, outputPath string, numGoroutines int, vaults []Vault, crackedCount *int32, linesProcessed *int32, stopChan chan struct{}) {
var file *os.File
var err error

if wordlistFileFlag == "" {
file = os.Stdin // default to stdin if no input flag is provided
file = os.Stdin
} else {
file, err = os.Open(wordlistFileFlag)
if err != nil {
log.Printf("Error opening file: %v\n", err)
return
log.Fatalf("Error opening file: %v\n", err)
}
defer file.Close()
}

startTime := time.Now()

readWg.Add(1)
go func() {
defer readWg.Done()
var remainder []byte
reader := bufio.NewReaderSize(file, readBufferSize)
for {
chunk := make([]byte, readBufferSize)
n, err := reader.Read(chunk)
if err == io.EOF {
break
}
if err != nil {
fmt.Println(os.Stderr, "Error reading chunk:", err)
return
}
var outputFile *os.File
if outputPath != "" {
outputFile, err = os.OpenFile(outputPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("Error opening output file: %v", err)
}
defer outputFile.Close()
}

chunk = chunk[:n]
chunk = append(remainder, chunk...)
var writer *bufio.Writer
if outputPath != "" {
writer = bufio.NewWriter(outputFile)
} else {
writer = bufio.NewWriter(os.Stdout)
}
defer writer.Flush()

lastNewline := bytes.LastIndexByte(chunk, '\n')
if lastNewline == -1 {
remainder = chunk
} else {
readChunks <- chunk[:lastNewline+1]
remainder = chunk[lastNewline+1:]
}
}
if len(remainder) > 0 {
readChunks <- remainder
}
close(readChunks)
}()
var (
writerMu sync.Mutex
wg sync.WaitGroup
)

// start worker goroutines
linesCh := make(chan []byte, 1000)
for i := 0; i < numGoroutines; i++ {
procWg.Add(1)
wg.Add(1)
go func() {
defer procWg.Done()
for chunk := range readChunks {
localBuffer := bytes.NewBuffer(nil)
writer := bufio.NewWriterSize(localBuffer, writeBufferSize)
processChunk(chunk, &linesHashed, &hexDecodeErrors, writer, stopChan, vaults, crackedCountCh, linesProcessedCh)
writer.Flush()
if localBuffer.Len() > 0 {
writeData <- localBuffer.Bytes()
}
defer wg.Done()
for password := range linesCh {
processPassword(password, vaults, &writerMu, writer, crackedCount, linesProcessed, stopChan)
}
}()
}

writeWg.Add(1)
go func() {
defer writeWg.Done()
var writer *bufio.Writer
if outputPath != "" {
outFile, err := os.OpenFile(outputPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println(os.Stderr, "Error creating output file:", err)
return
}
defer outFile.Close()
writer = bufio.NewWriterSize(outFile, writeBufferSize)
} else {
writer = bufio.NewWriterSize(os.Stdout, writeBufferSize)
}

for data := range writeData {
writer.Write(data)
}
writer.Flush()
}()

procWg.Wait()
readWg.Wait()
close(writeData)
writeWg.Wait()

elapsedTime := time.Since(startTime)
runTime := float64(elapsedTime.Seconds())
linesPerSecond := float64(linesHashed) / elapsedTime.Seconds()
if hexDecodeErrors > 0 {
log.Printf("HEX decode errors: %d\n", hexDecodeErrors)
// read lines from file and send them to workers
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Bytes()
password := make([]byte, len(line))
copy(password, line)
linesCh <- password
}
log.Printf("Finished processing %d lines in %.3f sec (%.3f lines/sec)\n", linesHashed, runTime, linesPerSecond)
}
close(linesCh)

// process wordlist chunks
func processChunk(chunk []byte, count *int64, hexErrorCount *int64, writer *bufio.Writer, stopChan chan struct{}, vaults []Vault, crackedCountCh chan int, linesProcessedCh chan int) {
lineStart := 0
for i := 0; i < len(chunk); i++ {
if chunk[i] == '\n' {
password := chunk[lineStart:i]
decodedBytes, _, hexErrCount := checkForHexBytes(password)
startCracker(stopChan, decodedBytes, vaults, crackedCountCh, linesProcessedCh, writer)
atomic.AddInt64(count, 1)
atomic.AddInt64(hexErrorCount, int64(hexErrCount))
lineStart = i + 1 // move start index past the newline
}
if err := scanner.Err(); err != nil {
log.Fatalf("Error reading file: %v\n", err)
}

// handle cases where there is no newline at the end of the chunk
if lineStart < len(chunk) {
password := chunk[lineStart:]
decodedBytes, _, hexErrCount := checkForHexBytes(password)
startCracker(stopChan, decodedBytes, vaults, crackedCountCh, linesProcessedCh, writer)
atomic.AddInt64(count, 1)
atomic.AddInt64(hexErrorCount, int64(hexErrCount))
}
wg.Wait()

writer.Flush()
log.Println("Finished")
}

// hash cracking worker
func startCracker(stopChan chan struct{}, password []byte, vaults []Vault, crackedCountCh chan int, linesProcessedCh chan int, writer *bufio.Writer) {
allDecrypted := true
func processPassword(password []byte, vaults []Vault, writerMu *sync.Mutex, writer *bufio.Writer, crackedCount *int32, linesProcessed *int32, stopChan chan struct{}) {
atomic.AddInt32(linesProcessed, 1)
// check for hex, ignore hexErrCount
decodedPassword, _, _ := checkForHexBytes(password)

for i := range vaults {
if !vaults[i].Decrypted { // check only undecrypted vaults
decryptedData, err := decryptVault(vaults[i].EncryptedData, password, vaults[i].Salt, vaults[i].Nonce, vaults[i].Iterations, vaults[i].Kdf)
if err != nil {
allDecrypted = false
continue // skip to next vault if decryption fails
if atomic.LoadInt32(&vaults[i].Decrypted) == 0 {
decryptedData, err := decryptVault(vaults[i].EncryptedData, decodedPassword, vaults[i].Salt, vaults[i].Nonce, vaults[i].Iterations, vaults[i].Kdf)
if err != nil || !isValid(decryptedData) {
continue
}
if isValid(decryptedData) {
crackedCountCh <- 1
vaults[i].Decrypted = true

if atomic.CompareAndSwapInt32(&vaults[i].Decrypted, 0, 1) {
output := fmt.Sprintf("%s:%s\n", vaults[i].VaultText, string(decodedPassword))
if writer != nil {
writer.WriteString(fmt.Sprintf("\nPassword: '%s'\n", password))
writerMu.Lock()
atomic.AddInt32(crackedCount, 1)
writer.WriteString(output)
writer.Flush()
} else {
fmt.Printf("\nPassword: '%s'\n", password)
writerMu.Unlock()
}

// exit if all vaults are cracked
if isAllVaultsCracked(vaults) {
closeStopChannel(stopChan)
}
} else {
allDecrypted = false
return
}
}
}

linesProcessedCh <- 1

if allDecrypted {
closeStopChannel(stopChan)
}
}
Loading

0 comments on commit 6d715f0

Please sign in to comment.