Skip to content

Commit

Permalink
Merge pull request #9 from C-Sto/history
Browse files Browse the repository at this point in the history
Add History feature
  • Loading branch information
C-Sto authored May 2, 2020
2 parents 408e629 + fc202c2 commit 091b4e6
Show file tree
Hide file tree
Showing 16 changed files with 280 additions and 53 deletions.
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ venv*
*.7z
*.pprof
*.exe
*.dit

*SECURITY
*SYSTEM
/lib/*
*.test
*.prof
*.out
/test/python/*
16 changes: 11 additions & 5 deletions cmd/dumpSecrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Settings struct {
Outfile string
NoPrint bool
Stream bool
History bool
}

func GoSecretsDump(s Settings) error {
Expand All @@ -28,14 +29,14 @@ func GoSecretsDump(s Settings) error {
if s.Outfile != "" {
fmt.Println("Writing to file ", s.Outfile)
if s.Stream {
fileStreamWriter(dataChan, s)
go fileStreamWriter(dataChan, s)
} else {
fileWriter(dataChan, s)
go fileWriter(dataChan, s)
}
} else {
consoleWriter(dataChan, s)
go consoleWriter(dataChan, s)
}
return nil
return dr.Dump()
}

func consoleWriter(val <-chan ditreader.DumpedHash, s Settings) {
Expand Down Expand Up @@ -68,6 +69,9 @@ func consoleWriter(val <-chan ditreader.DumpedHash, s Settings) {
hs.WriteString(append.String())
}
hs.WriteString("\n")
if s.History {
hs.WriteString(dh.HistoryString())
}
//pts = dh.Supp.HashString() + "\n"
}
fmt.Print(hs.String())
Expand Down Expand Up @@ -114,7 +118,9 @@ func fileWriter(val <-chan ditreader.DumpedHash, s Settings) {
kerbs.WriteString(append.String())
kerbs.WriteString("\n")
}

if s.History {
hs.WriteString(dh.HistoryString())
}
//pts = dh.Supp.HashString() + "\n"
plaintext.WriteString(pts.String())
}
Expand Down
8 changes: 6 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/C-Sto/gosecretsdump/cmd"
)

const version = "0.1.0"
const version = "0.2.0"

func main() {

Expand All @@ -26,6 +26,7 @@ func main() {
flag.BoolVar(&s.NoPrint, "noprint", false, "Don't print output to screen (probably use this with the -out flag)")
flag.BoolVar(&s.Stream, "stream", false, "Stream to files rather than writing in a block. Can be much slower.")
flag.BoolVar(&vers, "version", false, "Print version and exit")
flag.BoolVar(&s.History, "history", false, "Include Password History")
flag.Parse()

if vers {
Expand All @@ -35,7 +36,10 @@ func main() {
flag.Usage()
os.Exit(1)
}
cmd.GoSecretsDump(s)
e := cmd.GoSecretsDump(s)
if e != nil {
panic(e)
}
}

//info dumped out of https://github.com/SecureAuthCorp/impacket/blob/master/impacket/examples/secretsdump.py
3 changes: 3 additions & 0 deletions pkg/ditreader/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,6 @@ var accTypes = map[int32]string{
0x30000001: "SAM_MACHINE_ACCOUNT",
0x30000002: "SAM_TRUST_ACCOUNT",
}

var emptyNT = []byte{0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31, 0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0}
var emptyLM = []byte{0xaa, 0xd3, 0xb4, 0x35, 0xb5, 0x14, 0x04, 0xee, 0xaa, 0xd3, 0xb4, 0x35, 0xb5, 0x14, 0x04, 0xee}
34 changes: 29 additions & 5 deletions pkg/ditreader/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,17 @@ func decryptAES(key, value, iv []byte) ([]byte, error) {
}

type CryptedHashW16 struct {
Header [8]byte
KeyMaterial [16]byte
Unknown uint32
EncrypedHash [32]byte
Header [8]byte
KeyMaterial [16]byte
Unknown uint32
EncryptedHash [32]byte
}

type CryptedHashW16History struct {
Header [8]byte
KeyMaterial [16]byte
Unknown uint32
EncryptedHash []byte
}

func NewCryptedHashW16(data []byte) CryptedHashW16 {
Expand All @@ -129,7 +136,24 @@ func NewCryptedHashW16(data []byte) CryptedHashW16 {
r.Unknown = binary.LittleEndian.Uint32(data[:4])
data = data[4:]

copy(r.EncrypedHash[:], data[:32])
copy(r.EncryptedHash[:], data[:32])

return r
}

func NewCryptedHashW16History(data []byte) CryptedHashW16History {
r := CryptedHashW16History{}
//data := make([]byte, len(inData))
//copy(data, inData)
copy(r.Header[:], data[:8])
data = data[8:]
copy(r.KeyMaterial[:], data[:16])
data = data[16:]

r.Unknown = binary.LittleEndian.Uint32(data[:4])
data = data[4:]
r.EncryptedHash = make([]byte, len(data))
copy(r.EncryptedHash[:], data[:])

return r
}
17 changes: 7 additions & 10 deletions pkg/ditreader/ditreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"crypto/rc4"
"fmt"
"os"
"runtime"
"sync"

"github.com/C-Sto/gosecretsdump/pkg/systemreader"
Expand All @@ -16,7 +15,6 @@ import (

//New Creates a new dit dumper
func New(system, ntds string) (DitReader, error) {
parallel := false
r := DitReader{
isRemote: false,
history: false,
Expand All @@ -31,21 +29,20 @@ func New(system, ntds string) (DitReader, error) {
printUserStatus: false,
systemHiveLocation: system,
ntdsFileLocation: ntds,
db: esent.Esedb{}.Init(ntds),
userData: make(chan DumpedHash, 500),
//db: esent.Esedb{}.Init(ntds),
userData: make(chan DumpedHash, 500),
}

if parallel {
for i := 0; i < runtime.NumCPU()-1; i++ {
go r.decryptWorker()
}
}
var err error
r.db, err = esent.Esedb{}.Init(ntds)
if err != nil {
return r, err
}
r.cursor, err = r.db.OpenTable("datatable")
if err != nil {
return r, err
}
go r.dump() //start dumping the file immediately output will be put into the output channel as it comes
//go r.dump() //start dumping the file immediately output will be put into the output channel as it comes

return r, err
}
Expand Down
52 changes: 52 additions & 0 deletions pkg/ditreader/dumpedInfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,58 @@ type DumpedHash struct {
Enabled bool
UAC uacFlags
Supp SuppInfo
History PwdHistory
}

type PwdHistory struct {
LmHist [][]byte
NTHist [][]byte
}

func (d DumpedHash) HistoryStrings() []string {
r := make([]string, 0, len(d.History.NTHist))
for i, v := range d.History.LmHist {
r = append(r, fmt.Sprintf("%s_history%d:%s:%s:%s:::",
d.Username,
i,
d.Rid,
hex.EncodeToString(v),
hex.EncodeToString(emptyNT),
))
}
for i, v := range d.History.NTHist {
r = append(r, fmt.Sprintf("%s_history%d:%s:%s:%s:::",
d.Username,
i,
d.Rid,
hex.EncodeToString(emptyLM),
hex.EncodeToString(v),
))
}
return r
}

func (d DumpedHash) HistoryString() string {
r := strings.Builder{}
for i, v := range d.History.LmHist {
r.WriteString(fmt.Sprintf("%s_history%d:%s:%s:%s:::\n",
d.Username,
i,
d.Rid,
hex.EncodeToString(v),
hex.EncodeToString(emptyNT),
))
}
for i, v := range d.History.NTHist {
r.WriteString(fmt.Sprintf("%s_history%d:%s:%s:%s:::\n",
d.Username,
i,
d.Rid,
hex.EncodeToString(emptyLM),
hex.EncodeToString(v),
))
}
return r.String()
}

func (d DumpedHash) HashString() string {
Expand Down
64 changes: 60 additions & 4 deletions pkg/ditreader/records.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (d *DitReader) DecryptRecord(record esent.Esent_record) (DumpedHash, error)
if bytes.Compare(encryptedLM.Header[:4], []byte("\x13\x00\x00\x00")) == 0 {
encryptedLMW := NewCryptedHashW16(b)
pekIndex := encryptedLMW.Header
tmpLM, err = decryptAES(d.pek[pekIndex[4]], encryptedLMW.EncrypedHash[:16], encryptedLMW.KeyMaterial[:])
tmpLM, err = decryptAES(d.pek[pekIndex[4]], encryptedLMW.EncryptedHash[:16], encryptedLMW.KeyMaterial[:])
if err != nil {
return dh, err
}
Expand All @@ -48,7 +48,7 @@ func (d *DitReader) DecryptRecord(record esent.Esent_record) (DumpedHash, error)
}
} else {
//hard coded empty lm hash
dh.LMHash, _ = hex.DecodeString("aad3b435b51404eeaad3b435b51404ee")
dh.LMHash = emptyLM //, _ = hex.DecodeString("aad3b435b51404eeaad3b435b51404ee")
}

//nt hash
Expand All @@ -61,7 +61,7 @@ func (d *DitReader) DecryptRecord(record esent.Esent_record) (DumpedHash, error)
if bytes.Compare(encryptedNT.Header[:4], []byte("\x13\x00\x00\x00")) == 0 {
encryptedNTW := NewCryptedHashW16(v)
pekIndex := encryptedNTW.Header
tmpNT, err = decryptAES(d.pek[pekIndex[4]], encryptedNTW.EncrypedHash[:16], encryptedNTW.KeyMaterial[:])
tmpNT, err = decryptAES(d.pek[pekIndex[4]], encryptedNTW.EncryptedHash[:16], encryptedNTW.KeyMaterial[:])
if err != nil {
return dh, err
}
Expand All @@ -77,7 +77,7 @@ func (d *DitReader) DecryptRecord(record esent.Esent_record) (DumpedHash, error)
}
} else {
//hard coded empty NTLM hash
dh.NTHash, _ = hex.DecodeString("31D6CFE0D16AE931B73C59D7E0C089C0")
dh.NTHash = emptyNT //, _ = hex.DecodeString("31D6CFE0D16AE931B73C59D7E0C089C0")
}

//username
Expand All @@ -90,10 +90,66 @@ func (d *DitReader) DecryptRecord(record esent.Esent_record) (DumpedHash, error)
dh.Username = fmt.Sprintf("%s", v)
}

//Password history LM
if !d.noLMHash {
if v, _ := record.GetBytVal(nlmPwdHistory); v != nil && len(v) > 0 { //&& len(v) > 0 {
ch, err := NewCryptedHash(v)
if err != nil {
return dh, err
}
tmphst := []byte{}
tmphst, err = d.removeRC4(ch)
if err != nil {
return dh, err
}

for i := 16; i < len(tmphst); i += 16 {
hst1 := tmphst[i : i+16]
hst2, err := removeDES(hst1, dh.Rid)
dh.History.LmHist = append(dh.History.LmHist, hst2)
if err != nil {
return dh, err
}
}
}
}

//password history NT
if v, _ := record.GetBytVal(nntPwdHistory); v != nil && len(v) > 0 { //&& len(v) > 0 {
ch, err := NewCryptedHash(v)
if err != nil {
return dh, err
}
tmphst := []byte{}
if bytes.Compare(ch.Header[:4], []byte("\x13\x00\x00\x00")) == 0 {
encryptedNTW := NewCryptedHashW16History(v)
pekIndex := encryptedNTW.Header
tmphst, err = decryptAES(d.pek[pekIndex[4]], encryptedNTW.EncryptedHash[:], encryptedNTW.KeyMaterial[:])
if err != nil {
return dh, err
}
} else {
tmphst, err = d.removeRC4(ch)
if err != nil {
return dh, err
}
}
for i := 16; i < len(tmphst); i += 16 {
hst1 := tmphst[i : i+16]
hst2, err := removeDES(hst1, dh.Rid)
if err != nil {
return dh, err
}
dh.History.NTHist = append(dh.History.NTHist, hst2)
}

}
//check if account is enabled
if v, _ := record.GetLongVal(nuserAccountControl); v != 0 { // record.Column[nuserAccountControl"]].Long; v != 0 {
dh.UAC = decodeUAC(int(v))
}

//check if cleartext exists
if val, _ := record.GetBytVal(nsupplementalCredentials); len(val) > 24 {
//if val := record.Column[nsupplementalCredentials"]]; len(val.BytVal) > 24 {
var err error
Expand Down
17 changes: 10 additions & 7 deletions pkg/esent/ese.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var stringCodePages = map[uint32]string{
1252: "cp1252",
} //standin for const lookup/enum thing

func (e Esedb) Init(fn string) Esedb {
func (e Esedb) Init(fn string) (Esedb, error) {
//create the esedb structure
r := Esedb{
filename: fn,
Expand All @@ -45,8 +45,8 @@ func (e Esedb) Init(fn string) Esedb {
}

//'mount' the database (parse the file)
r.mountDb(fn)
return r
err := r.mountDb(fn)
return r, err
}

//OpenTable opens a table, and returns a cursor pointing to the current parsing state
Expand Down Expand Up @@ -108,14 +108,17 @@ func (e *Esedb) OpenTable(s string) (*Cursor, error) {
return &r, nil
}

func (e *Esedb) mountDb(filename string) {
func (e *Esedb) mountDb(filename string) (err error) {
//the first page is the dbheader
e.loadPages(filename)

err = e.loadPages(filename)
if err != nil {
return
}
// this was a gross way of working out how many pages the file has...

//this is where everything actually gets parsed out
e.parseCatalog(CATALOG_PAGE_NUMBER) //4 ?

return
}

func (e *Esedb) parseCatalog(pagenum uint32) error {
Expand Down
Loading

0 comments on commit 091b4e6

Please sign in to comment.