From c9eb6bcb38b9428a0c6f9359d46e708efae116a3 Mon Sep 17 00:00:00 2001 From: Russ Magee Date: Thu, 13 Sep 2018 23:51:49 -0700 Subject: [PATCH] Added -a authtoken feature for scripted use --- TODO.txt | 2 ++ hkexauth.go | 22 ++++++++++++++++++++- hkexsh/hkexsh.go | 36 ++++++++++++++++++++++++---------- hkexshd/hkexshd.go | 49 +++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 93 insertions(+), 16 deletions(-) diff --git a/TODO.txt b/TODO.txt index c8486e9..5b3a087 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,6 +5,8 @@ Chaff Improvements - Mimicry of hand-typed traffic for chaff on interactive sessions - Client-input chaff file data (ie., Moby Dick) +KEx: Look at ECIES: https://godoc.org/github.com/bitherhq/go-bither/crypto/ecies + Architecture (DONE) - Move hkexnet components other than key exchange into a proper hkex package (ie., hkexsh imports hkex) - hkex should be usable for other client/svr utils, diff --git a/hkexauth.go b/hkexauth.go index 5083bc6..6960249 100644 --- a/hkexauth.go +++ b/hkexauth.go @@ -11,15 +11,17 @@ package hkexsh import ( "bytes" "encoding/csv" + "fmt" "io" "io/ioutil" "log" + "os/user" "runtime" "github.com/jameskeane/bcrypt" ) -func AuthUser(username string, auth string, fname string) (valid bool, allowedCmds string) { +func AuthUserByPasswd(username string, auth string, fname string) (valid bool, allowedCmds string) { b, e := ioutil.ReadFile(fname) if e != nil { valid = false @@ -65,3 +67,21 @@ func AuthUser(username string, auth string, fname string) (valid bool, allowedCm return } + +func AuthUserByToken(username string, auth string) (valid bool) { + u, ue := user.Lookup(username) + if ue != nil { + return false + } + + b, e := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir)) + if e != nil { + log.Printf("INFO: Cannot read %s/.hkexsh_id\n", u.HomeDir) + return false + } + + if string(b) == auth { + return true + } + return +} diff --git a/hkexsh/hkexsh.go b/hkexsh/hkexsh.go index 6562e0c..1a1517b 100755 --- a/hkexsh/hkexsh.go +++ b/hkexsh/hkexsh.go @@ -320,6 +320,7 @@ func rejectUserMsg() string { func main() { version := "0.2pre (NO WARRANTY)" var vopt bool + var aopt bool var dbg bool var shellMode bool // if true act as shell, else file copier var cAlg string @@ -345,7 +346,7 @@ func main() { flag.StringVar(&cAlg, "c", "C_AES_256", "`cipher` [\"C_AES_256\" | \"C_TWOFISH_128\" | \"C_BLOWFISH_64\"]") flag.StringVar(&hAlg, "m", "H_SHA256", "`hmac` [\"H_SHA256\"]") flag.UintVar(&port, "p", 2000, "`port`") - flag.StringVar(&authCookie, "a", "", "auth cookie") + //flag.StringVar(&authCookie, "a", "", "auth cookie") flag.BoolVar(&chaffEnabled, "e", true, "enabled chaff pkts (default true)") flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt `freq` min (msecs)") flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt `freq` max (msecs)") @@ -357,6 +358,7 @@ func main() { // hkexsh accepts a command (-x) but not // a srcpath (-r) or dstpath (-t) flag.StringVar(&cmdStr, "x", "", "`command` to run (if not specified run interactive shell)") + flag.BoolVar(&aopt, "a", false, "return autologin token from server") shellMode = true flag.Usage = UsageShell } else { @@ -443,10 +445,24 @@ func main() { log.SetOutput(ioutil.Discard) } + // See if we can log in via an auth token + u, _ := user.Current() + ab, aerr := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir)) + if aerr == nil { + authCookie = string(ab) + // Security scrub + ab = nil + runtime.GC() + } + if shellMode { // We must make the decision about interactivity before Dial() // as it affects chaffing behaviour. 20180805 - if len(cmdStr) == 0 { + if aopt { + op = []byte{'A'} + chaffFreqMin = 2 + chaffFreqMax = 10 + } else if len(cmdStr) == 0 { op = []byte{'s'} isInteractive = true } else { @@ -467,20 +483,20 @@ func main() { // client->server file copy // src file list is in copySrc op = []byte{'D'} - fmt.Println("client->server copy:", string(copySrc), "->", copyDst) + //fmt.Println("client->server copy:", string(copySrc), "->", copyDst) cmdStr = copyDst } else { // server->client file copy // remote src file(s) in copyDsr op = []byte{'S'} - fmt.Println("server->client copy:", string(copySrc), "->", copyDst) + //fmt.Println("server->client copy:", string(copySrc), "->", copyDst) cmdStr = string(copySrc) } } - conn, err := hkexnet.Dial("tcp", server, /*[kexAlg eg. "KEX_HERRADURA"], */ cAlg, hAlg) + conn, err := hkexnet.Dial("tcp", server /*[kexAlg eg. "KEX_HERRADURA"], */, cAlg, hAlg) if err != nil { - fmt.Println("Err!") + fmt.Println(err) panic(err) } defer conn.Close() @@ -514,9 +530,9 @@ func main() { ab = nil runtime.GC() } - + // Set up session params and send over to server - rec := hkexsh.NewSession(op, []byte(uname), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie),0) + rec := hkexsh.NewSession(op, []byte(uname), []byte(os.Getenv("TERM")), []byte(cmdStr), []byte(authCookie), 0) _, err = fmt.Fprintf(conn, "%d %d %d %d %d\n", len(rec.Op()), len(rec.Who()), len(rec.TermType()), len(rec.Cmd()), len(rec.AuthCookie(true))) _, err = conn.Write(rec.Op()) @@ -544,8 +560,8 @@ func main() { if shellMode { doShellMode(isInteractive, conn, oldState, rec) } else { // copyMode - _, s := doCopyMode(conn, pathIsDest, fileArgs, rec) - rec.SetStatus(s) + _, s := doCopyMode(conn, pathIsDest, fileArgs, rec) + rec.SetStatus(s) } if rec.Status() != 0 { diff --git a/hkexshd/hkexshd.go b/hkexshd/hkexshd.go index 20ac792..a4e8836 100755 --- a/hkexshd/hkexshd.go +++ b/hkexshd/hkexshd.go @@ -9,7 +9,9 @@ package main import ( "bytes" + "crypto/rand" "encoding/binary" + "encoding/hex" "flag" "fmt" "io" @@ -48,6 +50,7 @@ func runClientToServerCopyAs(who, ttype string, conn hkexnet.Conn, fpath string, os.Clearenv() os.Setenv("HOME", u.HomeDir) os.Setenv("TERM", ttype) + os.Setenv("HKEXSH", "1") var c *exec.Cmd cmdName := "/bin/tar" @@ -130,6 +133,7 @@ func runServerToClientCopyAs(who, ttype string, conn hkexnet.Conn, srcPath strin os.Clearenv() os.Setenv("HOME", u.HomeDir) os.Setenv("TERM", ttype) + os.Setenv("HKEXSH", "1") var c *exec.Cmd cmdName := "/bin/tar" @@ -216,7 +220,8 @@ func runShellAs(who, ttype string, cmd string, interactive bool, conn hkexnet.Co os.Clearenv() os.Setenv("HOME", u.HomeDir) os.Setenv("TERM", ttype) - + os.Setenv("HKEXSH", "1") + var c *exec.Cmd if interactive { c = exec.Command("/bin/bash", "-i", "-l") @@ -311,6 +316,17 @@ func runShellAs(who, ttype string, cmd string, interactive bool, conn hkexnet.Co return } +func GenAuthToken(who string) string { + tokenA, e := os.Hostname() + if e != nil { + tokenA = "badhost" + } + + tokenB := make([]byte, 64) + _, _ = rand.Read(tokenB) + return fmt.Sprintf("%s:%s", tokenA, hex.EncodeToString(tokenB)) +} + // Demo of a simple server that listens and spawns goroutines for each // connecting client. Note this code is identical to standard tcp // server code, save for declaring 'hkex' rather than 'net' @@ -442,7 +458,13 @@ func main() { log.Printf("[hkexsh.Session: op:%c who:%s cmd:%s auth:****]\n", rec.Op()[0], string(rec.Who()), string(rec.Cmd())) - valid, allowedCmds := hkexsh.AuthUser(string(rec.Who()), string(rec.AuthCookie(true)), "/etc/hkexsh.passwd") + var valid bool + var allowedCmds string // Currently unused + if hkexsh.AuthUserByToken(string(rec.Who()), string(rec.AuthCookie(true))) { + valid = true + } else { + valid, allowedCmds = hkexsh.AuthUserByPasswd(string(rec.Who()), string(rec.AuthCookie(true)), "/etc/hkexsh.passwd") + } // Security scrub rec.ClearAuthCookie() @@ -458,7 +480,24 @@ func main() { log.Printf("[allowedCmds:%s]\n", allowedCmds) - if rec.Op()[0] == 'c' { + if rec.Op()[0] == 'A' { + // Generate automated login token + addr := hc.RemoteAddr() + hname := strings.Split(addr.String(), ":")[0] + log.Printf("[Generating autologin token for [%s@%s]]\n", rec.Who(), hname) + token := GenAuthToken(string(rec.Who())) + tokenCmd := fmt.Sprintf("echo \"%s\" | tee ~/.hkexsh_id", token) + runErr, cmdStatus := runShellAs(string(rec.Who()), string(rec.TermType()), tokenCmd, false, hc, chaffEnabled) + // Returned hopefully via an EOF or exit/logout; + // Clear current op so user can enter next, or EOF + rec.SetOp([]byte{0}) + if runErr != nil { + log.Printf("[Error generating autologin token for %s@%s]\n", rec.Who(), hname) + } else { + log.Printf("[Autologin token generation completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus) + hc.SetStatus(cmdStatus) + } + } else if rec.Op()[0] == 'c' { // Non-interactive command addr := hc.RemoteAddr() //hname := goutmp.GetHost(addr.String()) @@ -470,9 +509,9 @@ func main() { // Clear current op so user can enter next, or EOF rec.SetOp([]byte{0}) if runErr != nil { - log.Printf("[Error spawning cmd for %s@%s]\n", rec.Who, hname) + log.Printf("[Error spawning cmd for %s@%s]\n", rec.Who(), hname) } else { - log.Printf("[Command completed for %s@%s, status %d]\n", rec.Who, hname, cmdStatus) + log.Printf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus) hc.SetStatus(cmdStatus) } } else if rec.Op()[0] == 's' {