Skip to content

Commit

Permalink
Merge pull request #8 from jfjallid/interactive
Browse files Browse the repository at this point in the history
New interactive mode
  • Loading branch information
jfjallid authored Mar 4, 2024
2 parents c2dddb2 + 85e8282 commit 4201abd
Show file tree
Hide file tree
Showing 7 changed files with 1,233 additions and 36 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ options:
-u, --user Username
-p, --pass Password
-n, --no-pass Disable password prompt and send no credentials
-i, --interactive Start an interactive session
--hash Hex encoded NT Hash for user password
--local Authenticate as a local user instead of domain user
--null Attempt null session authentication
Expand Down Expand Up @@ -73,3 +74,9 @@ options:
```
./go-shareenum --host server001 --user Administrator --pass adminPass123 --enum --recurse --list --exclude "ADMIN$,C$"
```

### Start an interactive session

```
./go-shareenum --host server001 --user Administrator --pass adminPass123 --interactive
```
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/jfjallid/go-shareenum
go 1.19

require (
github.com/jfjallid/go-smb v0.3.0
github.com/jfjallid/go-smb v0.3.8
github.com/jfjallid/golog v0.3.3
golang.org/x/net v0.6.0
golang.org/x/term v0.5.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/jfjallid/ber v1.1.0 h1:hVaxa1K4vBwh5Arvc70uhatZephEgJpRTHKQCf4u7OA=
github.com/jfjallid/ber v1.1.0/go.mod h1:EJC7yARA25HsUp0G498XpiJahwGx+T+HKh2LHZ/QslA=
github.com/jfjallid/go-smb v0.3.0 h1:PBm0FMtZ4tkDe41DdXrgOt8QXgKUvfcWUcQkKQku2K4=
github.com/jfjallid/go-smb v0.3.0/go.mod h1:ImsLHX4xjKkSbKottJO9+qyhDo/b72QiaXh2n6E6ZTY=
github.com/jfjallid/go-smb v0.3.8 h1:G+N2AcVqi6GiF1KoL9QDIKCfLHITXDYR71czkFCMhF0=
github.com/jfjallid/go-smb v0.3.8/go.mod h1:ImsLHX4xjKkSbKottJO9+qyhDo/b72QiaXh2n6E6ZTY=
github.com/jfjallid/golog v0.3.3 h1:dY6qf8wTxJ9OwBPVTadVRDmt/6MVXSWwXrxaGMMyXsU=
github.com/jfjallid/golog v0.3.3/go.mod h1:19Q/zg5OgPPd0xhFllokPnMzthzhFPZmiAGAokE7k58=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
Expand Down
150 changes: 117 additions & 33 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import (
)

var log = golog.Get("")
var release string = "0.1.7"
var release string = "0.1.8"
var includedExts map[string]interface{}
var excludedExts map[string]interface{}
var excludedFolders map[string]interface{}
Expand All @@ -61,7 +61,7 @@ func isFlagSet(name string) bool {
return found
}

func printFiles(files []smb.SharedFile) {
func printFilesExt(files []smb.SharedFile) {
if len(files) > 0 {
for _, file := range files {
fileType := "file"
Expand Down Expand Up @@ -124,7 +124,63 @@ func filterFiles(input []smb.SharedFile) []smb.SharedFile {
return files
}

func downloadFiles(session *smb.Connection, share string, files []smb.SharedFile) {
func getShares(options *localOptions, host string) (shares []string, err error) {
share := "IPC$"
err = options.c.TreeConnect(share)
if err != nil {
return
}
f, err := options.c.OpenFile(share, "srvsvc")
if err != nil {
options.c.TreeDisconnect(share)
return
}

bind, err := dcerpc.Bind(f, dcerpc.MSRPCUuidSrvSvc, 3, 0, dcerpc.MSRPCUuidNdr)
if err != nil {
if !options.interactive {
log.Errorln("Failed to bind to service")
}
f.CloseFile()
options.c.TreeDisconnect(share)
return
}
if !options.interactive {
log.Infoln("Successfully performed Bind to service")
}

result, err := bind.NetShareEnumAll(host)
if err != nil {
f.CloseFile()
options.c.TreeDisconnect(share)
return
}

for _, netshare := range result {
name := netshare.Name[:len(netshare.Name)-1]
if (netshare.TypeId == dcerpc.StypeDisktree) || (netshare.TypeId == dcerpc.StypeIPC) {
shares = append(shares, name)
}
}
f.CloseFile()
options.c.TreeDisconnect(share)

return
}

func listShares(options *localOptions, host string) {
shares, err := getShares(options, host)
if err != nil {
log.Errorln(err)
return
}
log.Debugf("Retrieved list of %d shares\n", len(shares))
for _, share := range shares {
fmt.Println(share)
}
}

func downloadFiles(session *smb.Connection, share string, files []smb.SharedFile, createDirectories bool) {
if len(files) > 0 {
for _, file := range files {
if file.IsDir || file.IsJunction {
Expand Down Expand Up @@ -164,14 +220,18 @@ func downloadFiles(session *smb.Connection, share string, files []smb.SharedFile
fulldir = downloadDir
}

err := os.MkdirAll(fulldir, 0755)
if err != nil {
log.Errorf("Failed to create dir %s with error: %v\n", err)
continue
localFile := filename
if createDirectories {
err := os.MkdirAll(fulldir, 0755)
if err != nil {
log.Errorf("Failed to create dir %s with error: %v\n", err)
continue
}
localFile = fulldir + string(os.PathSeparator) + filename
}

// Open local file in the subdir and start downloading the file
f, err := os.OpenFile(fulldir+string(os.PathSeparator)+filename, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0640)
f, err := os.OpenFile(localFile, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0640)
if err != nil {
log.Errorln(err)
continue
Expand Down Expand Up @@ -204,9 +264,9 @@ func listFilesRecursively(session *smb.Connection, share, parent, dir string) er
}

fmt.Printf("%s:\n", parent)
printFiles(files)
printFilesExt(files)
if download {
downloadFiles(session, share, files)
downloadFiles(session, share, files, true)
}

for _, file := range files {
Expand Down Expand Up @@ -257,9 +317,9 @@ func listFiles(session *smb.Connection, shares []string, recurse bool) error {
files = filterFiles(files)

fmt.Printf("\n#### Listing files for share (%s) ####\n", share)
printFiles(files)
printFilesExt(files)
if download {
downloadFiles(session, share, files)
downloadFiles(session, share, files, true)
}
if recurse {
for _, file := range files {
Expand Down Expand Up @@ -293,6 +353,7 @@ var helpMsg = `
-u, --user Username
-p, --pass Password
-n, --no-pass Disable password prompt and send no credentials
-i, --interactive Start an interactive session
--hash Hex encoded NT Hash for user password
--local Authenticate as a local user instead of domain user
--null Attempt null session authentication
Expand Down Expand Up @@ -324,10 +385,16 @@ var helpMsg = `
-v, --version Show version
`

type localOptions struct {
c *smb.Connection
interactive bool
smbOptions *smb.Options
}

func main() {
var host, username, password, hash, domain, shareFlag, excludeShareFlag, includeName, includeExt, excludeExt, excludeFolder, socksIP string
var port, dialTimeout, socksPort, relayPort int
var debug, dirList, recurse, shareEnumFlag, noEnc, forceSMB2, localUser, nullSession, version, verbose, relay, noPass bool
var debug, dirList, recurse, shareEnumFlag, noEnc, forceSMB2, localUser, nullSession, version, verbose, relay, noPass, interactive bool
var err error

flag.Usage = func() {
Expand Down Expand Up @@ -373,6 +440,8 @@ func main() {
flag.IntVar(&socksPort, "socks-port", 1080, "")
flag.BoolVar(&noPass, "no-pass", false, "")
flag.BoolVar(&noPass, "n", false, "")
flag.BoolVar(&interactive, "i", false, "")
flag.BoolVar(&interactive, "interactive", false, "")

flag.Parse()

Expand Down Expand Up @@ -462,7 +531,7 @@ func main() {
return
}

if !shareEnumFlag {
if !shareEnumFlag && !interactive {
if shareFlag == "" {
log.Errorln("Please specify a share name or the share enumeration flag.")
return
Expand Down Expand Up @@ -523,7 +592,7 @@ func main() {
excludedShares[part] = true
}

options := smb.Options{
smbOptions := smb.Options{
Host: host,
Port: port,
Initiator: &smb.NTLMInitiator{
Expand All @@ -540,61 +609,76 @@ func main() {

// Only if not using SOCKS
if socksIP == "" {
options.DialTimeout, err = time.ParseDuration(fmt.Sprintf("%ds", dialTimeout))
smbOptions.DialTimeout, err = time.ParseDuration(fmt.Sprintf("%ds", dialTimeout))
if err != nil {
log.Errorln(err)
return
}
}

var session *smb.Connection
var opts localOptions
opts.smbOptions = &smbOptions // Useful if we want to establish new connections in the shell

if socksIP != "" {
dialSocksProxy, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", socksIP, socksPort), nil, proxy.Direct)
if err != nil {
log.Errorln(err)
return
}
options.ProxyDialer = dialSocksProxy
smbOptions.ProxyDialer = dialSocksProxy
}

if relay {
options.RelayPort = relayPort
session, err = smb.NewRelayConnection(options)
smbOptions.RelayPort = relayPort
opts.c, err = smb.NewRelayConnection(smbOptions)
} else {
session, err = smb.NewConnection(options)
opts.c, err = smb.NewConnection(smbOptions)
}
if err != nil {
log.Criticalln(err)
return
}

defer session.Close()
defer func() {
if opts.c != nil {
opts.c.Close()
}
}()

if session.IsSigningRequired.Load() {
if opts.c.IsSigningRequired.Load() {
log.Noticeln("[-] Signing is required")
} else {
log.Noticeln("[+] Signing is NOT required")
}

if session.IsAuthenticated {
log.Noticef("[+] Login successful as %s\n", session.GetAuthUsername())
if opts.c.IsAuthenticated {
log.Noticef("[+] Login successful as %s\n", opts.c.GetAuthUsername())
} else {
log.Noticeln("[-] Login failed")
return
}

if interactive {
shell := newShell(&opts)
if shell == nil {
log.Errorln("Failed to start an interactive shell")
return
}
shell.cmdloop()
return
}

if shareEnumFlag {
share := "IPC$"
err := session.TreeConnect(share)
err := opts.c.TreeConnect(share)
if err != nil {
log.Errorln(err)
return
}
f, err := session.OpenFile(share, "srvsvc")
f, err := opts.c.OpenFile(share, "srvsvc")
if err != nil {
log.Errorln(err)
session.TreeDisconnect(share)
opts.c.TreeDisconnect(share)
return
}

Expand All @@ -603,7 +687,7 @@ func main() {
log.Errorln("Failed to bind to service")
log.Errorln(err)
f.CloseFile()
session.TreeDisconnect(share)
opts.c.TreeDisconnect(share)
return
}
log.Infoln("Successfully performed Bind to service")
Expand All @@ -612,7 +696,7 @@ func main() {
if err != nil {
log.Errorln(err)
f.CloseFile()
session.TreeDisconnect(share)
opts.c.TreeDisconnect(share)
return
}

Expand All @@ -630,13 +714,13 @@ func main() {
}
}
f.CloseFile()
session.TreeDisconnect(share)
opts.c.TreeDisconnect(share)

log.Debugf("Retrieved list of %d shares\n", len(shares))

fmt.Printf("\n#### %s ####\n", host)
if dirList {
err = listFiles(session, shares, recurse)
err = listFiles(opts.c, shares, recurse)
if err != nil {
log.Errorln(err)
return
Expand All @@ -650,7 +734,7 @@ func main() {
} else {
fmt.Printf("#### %s ####\n", host)
// Use specified list of shares
err = listFiles(session, shares, recurse)
err = listFiles(opts.c, shares, recurse)
if err != nil {
log.Errorln(err)
return
Expand Down
17 changes: 17 additions & 0 deletions misc_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import "fmt"

const (
useRawTerminal bool = true
)

func (self *shell) printf(format string, a ...any) (n int, err error) {
output := fmt.Sprintf(format, a...)
return self.t.Write([]byte(output))
}

func (self *shell) println(a ...any) (n int, err error) {
output := fmt.Sprintln(a...)
return self.t.Write([]byte(output))
}
15 changes: 15 additions & 0 deletions misc_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import "fmt"

const (
useRawTerminal bool = false
)

func (self *shell) printf(format string, a ...any) (n int, err error) {
return fmt.Printf(format, a...)
}

func (self *shell) println(a ...any) (n int, err error) {
return fmt.Println(a...)
}
Loading

0 comments on commit 4201abd

Please sign in to comment.