Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

History #9

Merged
merged 6 commits into from
May 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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