Added -a authtoken feature for scripted use

This commit is contained in:
Russ Magee 2018-09-13 23:51:49 -07:00
parent 350f3f375e
commit c9eb6bcb38
4 changed files with 93 additions and 16 deletions

View file

@ -5,6 +5,8 @@ Chaff Improvements
- Mimicry of hand-typed traffic for chaff on interactive sessions - Mimicry of hand-typed traffic for chaff on interactive sessions
- Client-input chaff file data (ie., Moby Dick) - Client-input chaff file data (ie., Moby Dick)
KEx: Look at ECIES: https://godoc.org/github.com/bitherhq/go-bither/crypto/ecies
Architecture Architecture
(DONE) - Move hkexnet components other than key exchange into a proper hkex package (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, (ie., hkexsh imports hkex) - hkex should be usable for other client/svr utils,

View file

@ -11,15 +11,17 @@ package hkexsh
import ( import (
"bytes" "bytes"
"encoding/csv" "encoding/csv"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"os/user"
"runtime" "runtime"
"github.com/jameskeane/bcrypt" "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) b, e := ioutil.ReadFile(fname)
if e != nil { if e != nil {
valid = false valid = false
@ -65,3 +67,21 @@ func AuthUser(username string, auth string, fname string) (valid bool, allowedCm
return 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
}

View file

@ -320,6 +320,7 @@ func rejectUserMsg() string {
func main() { func main() {
version := "0.2pre (NO WARRANTY)" version := "0.2pre (NO WARRANTY)"
var vopt bool var vopt bool
var aopt bool
var dbg bool var dbg bool
var shellMode bool // if true act as shell, else file copier var shellMode bool // if true act as shell, else file copier
var cAlg string 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(&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.StringVar(&hAlg, "m", "H_SHA256", "`hmac` [\"H_SHA256\"]")
flag.UintVar(&port, "p", 2000, "`port`") 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.BoolVar(&chaffEnabled, "e", true, "enabled chaff pkts (default true)")
flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt `freq` min (msecs)") flag.UintVar(&chaffFreqMin, "f", 100, "chaff pkt `freq` min (msecs)")
flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt `freq` max (msecs)") flag.UintVar(&chaffFreqMax, "F", 5000, "chaff pkt `freq` max (msecs)")
@ -357,6 +358,7 @@ func main() {
// hkexsh accepts a command (-x) but not // hkexsh accepts a command (-x) but not
// a srcpath (-r) or dstpath (-t) // a srcpath (-r) or dstpath (-t)
flag.StringVar(&cmdStr, "x", "", "`command` to run (if not specified run interactive shell)") 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 shellMode = true
flag.Usage = UsageShell flag.Usage = UsageShell
} else { } else {
@ -443,10 +445,24 @@ func main() {
log.SetOutput(ioutil.Discard) 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 { if shellMode {
// We must make the decision about interactivity before Dial() // We must make the decision about interactivity before Dial()
// as it affects chaffing behaviour. 20180805 // 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'} op = []byte{'s'}
isInteractive = true isInteractive = true
} else { } else {
@ -467,20 +483,20 @@ func main() {
// client->server file copy // client->server file copy
// src file list is in copySrc // src file list is in copySrc
op = []byte{'D'} op = []byte{'D'}
fmt.Println("client->server copy:", string(copySrc), "->", copyDst) //fmt.Println("client->server copy:", string(copySrc), "->", copyDst)
cmdStr = copyDst cmdStr = copyDst
} else { } else {
// server->client file copy // server->client file copy
// remote src file(s) in copyDsr // remote src file(s) in copyDsr
op = []byte{'S'} op = []byte{'S'}
fmt.Println("server->client copy:", string(copySrc), "->", copyDst) //fmt.Println("server->client copy:", string(copySrc), "->", copyDst)
cmdStr = string(copySrc) 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 { if err != nil {
fmt.Println("Err!") fmt.Println(err)
panic(err) panic(err)
} }
defer conn.Close() defer conn.Close()
@ -516,7 +532,7 @@ func main() {
} }
// Set up session params and send over to server // 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", _, 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))) len(rec.Op()), len(rec.Who()), len(rec.TermType()), len(rec.Cmd()), len(rec.AuthCookie(true)))
_, err = conn.Write(rec.Op()) _, err = conn.Write(rec.Op())
@ -544,8 +560,8 @@ func main() {
if shellMode { if shellMode {
doShellMode(isInteractive, conn, oldState, rec) doShellMode(isInteractive, conn, oldState, rec)
} else { // copyMode } else { // copyMode
_, s := doCopyMode(conn, pathIsDest, fileArgs, rec) _, s := doCopyMode(conn, pathIsDest, fileArgs, rec)
rec.SetStatus(s) rec.SetStatus(s)
} }
if rec.Status() != 0 { if rec.Status() != 0 {

View file

@ -9,7 +9,9 @@ package main
import ( import (
"bytes" "bytes"
"crypto/rand"
"encoding/binary" "encoding/binary"
"encoding/hex"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@ -48,6 +50,7 @@ func runClientToServerCopyAs(who, ttype string, conn hkexnet.Conn, fpath string,
os.Clearenv() os.Clearenv()
os.Setenv("HOME", u.HomeDir) os.Setenv("HOME", u.HomeDir)
os.Setenv("TERM", ttype) os.Setenv("TERM", ttype)
os.Setenv("HKEXSH", "1")
var c *exec.Cmd var c *exec.Cmd
cmdName := "/bin/tar" cmdName := "/bin/tar"
@ -130,6 +133,7 @@ func runServerToClientCopyAs(who, ttype string, conn hkexnet.Conn, srcPath strin
os.Clearenv() os.Clearenv()
os.Setenv("HOME", u.HomeDir) os.Setenv("HOME", u.HomeDir)
os.Setenv("TERM", ttype) os.Setenv("TERM", ttype)
os.Setenv("HKEXSH", "1")
var c *exec.Cmd var c *exec.Cmd
cmdName := "/bin/tar" cmdName := "/bin/tar"
@ -216,6 +220,7 @@ func runShellAs(who, ttype string, cmd string, interactive bool, conn hkexnet.Co
os.Clearenv() os.Clearenv()
os.Setenv("HOME", u.HomeDir) os.Setenv("HOME", u.HomeDir)
os.Setenv("TERM", ttype) os.Setenv("TERM", ttype)
os.Setenv("HKEXSH", "1")
var c *exec.Cmd var c *exec.Cmd
if interactive { if interactive {
@ -311,6 +316,17 @@ func runShellAs(who, ttype string, cmd string, interactive bool, conn hkexnet.Co
return 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 // Demo of a simple server that listens and spawns goroutines for each
// connecting client. Note this code is identical to standard tcp // connecting client. Note this code is identical to standard tcp
// server code, save for declaring 'hkex' rather than 'net' // 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", log.Printf("[hkexsh.Session: op:%c who:%s cmd:%s auth:****]\n",
rec.Op()[0], string(rec.Who()), string(rec.Cmd())) 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 // Security scrub
rec.ClearAuthCookie() rec.ClearAuthCookie()
@ -458,7 +480,24 @@ func main() {
log.Printf("[allowedCmds:%s]\n", allowedCmds) 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 // Non-interactive command
addr := hc.RemoteAddr() addr := hc.RemoteAddr()
//hname := goutmp.GetHost(addr.String()) //hname := goutmp.GetHost(addr.String())
@ -470,9 +509,9 @@ func main() {
// Clear current op so user can enter next, or EOF // Clear current op so user can enter next, or EOF
rec.SetOp([]byte{0}) rec.SetOp([]byte{0})
if runErr != nil { 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 { } 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) hc.SetStatus(cmdStatus)
} }
} else if rec.Op()[0] == 's' { } else if rec.Op()[0] == 's' {