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

[WIP]add privilege escalation method of su #97

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ issues/
vendor/
log/
results/
config.toml
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,12 +453,12 @@ Prepare subcommand installs required packages on each server.
$ vuls prepare -help
prepare
[-config=/path/to/config.toml] [-debug]
[-ask-sudo-password]
[-ask-become-password]
[-ask-key-password]

-ask-key-password
Ask ssh privatekey password before scanning
-ask-sudo-password
-ask-become-password
Ask sudo password of target servers before scanning
-config string
/path/to/toml (default "$PWD/config.toml")
Expand Down Expand Up @@ -490,7 +490,7 @@ scan:
[-report-slack]
[-report-text]
[-http-proxy=http://192.168.0.1:8080]
[-ask-sudo-password]
[-ask-become-password]
[-ask-key-password]
[-debug]
[-debug-sql]
Expand All @@ -500,7 +500,7 @@ scan:

-ask-key-password
Ask ssh privatekey password before scanning
-ask-sudo-password
-ask-become-password
Ask sudo password of target servers before scanning
-aws-profile string
AWS Profile to use (default "default")
Expand Down Expand Up @@ -552,9 +552,9 @@ scan:
| empty password | - | |
| with password | required | or use ssh-agent |

## -ask-sudo-password option
## -ask-become-password option

| sudo password on target servers | -ask-sudo-password | |
| sudo password on target servers | -ask-become-password | |
|:-----------------|:-------|:------|
| NOPASSWORD | - | defined as NOPASSWORD in /etc/sudoers on target servers |
| with password | required | . |
Expand All @@ -568,7 +568,7 @@ all.(json|txt) includes the scan results of all servres and servername.(json|txt

### Scan all servers defined in config file
```
$ vuls scan --report-slack --report-mail --cvss-over=7 -ask-sudo-password -ask-key-password -cve-dictionary-dbpath=$PWD/cve.sqlite3
$ vuls scan --report-slack --report-mail --cvss-over=7 -ask-become-password -ask-key-password -cve-dictionary-dbpath=$PWD/cve.sqlite3
```
With this sample command, it will ..
- Ask sudo password and ssh key passsword before scanning
Expand All @@ -583,7 +583,7 @@ $ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3 server1 server2
```
With this sample command, it will ..
- Use SSH Key-Based authentication with empty password (without -ask-key-password option)
- Sudo with no password (without -ask-sudo-password option)
- Sudo with no password (without -ask-become-password option)
- Scan only 2 servers (server1, server2)
- Print scan result to terminal

Expand Down
35 changes: 34 additions & 1 deletion commands/cmdutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package commands
import (
"fmt"

log "github.com/Sirupsen/logrus"
"github.com/howeyc/gopass"
)

func getPasswd(prompt string) (string, error) {
func getPassword(prompt string) (string, error) {
for {
fmt.Print(prompt)
pass, err := gopass.GetPasswdMasked()
Expand All @@ -19,3 +20,35 @@ func getPasswd(prompt string) (string, error) {
}

}

func askKeyPassword() (keyPassword string, err error) {
prompt := "SSH key password: "

if keyPassword, err = getPassword(prompt); err != nil {
log.Error(err)
return "", err
}
return
}

func askBecomePassword(becomeMethod string) (becomePassword string, err error) {
var prompt string

switch becomeMethod {
case "su":
prompt = "su password: "
log.Warn("su is executed without password")
log.Warn("If you can not exececute su without password, ssh will hang. Please stop this process by Ctrl-C")
return "", nil
case "sudo", "":
prompt = "sudo password: "
default:
return "", fmt.Errorf("BecomeMethod: unsupported method %s", becomeMethod)
}

if becomePassword, err = getPassword(prompt); err != nil {
log.Error(err)
return "", err
}
return
}
28 changes: 10 additions & 18 deletions commands/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ type PrepareCmd struct {
debug bool
configPath string

askSudoPassword bool
askKeyPassword bool
askBecomePassword bool
askKeyPassword bool

useUnattendedUpgrades bool
}
Expand All @@ -61,7 +61,7 @@ func (*PrepareCmd) Usage() string {
return `prepare:
prepare
[-config=/path/to/config.toml]
[-ask-sudo-password]
[-ask-become-password]
[-ask-key-password]
[-debug]

Expand All @@ -86,8 +86,8 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
)

f.BoolVar(
&p.askSudoPassword,
"ask-sudo-password",
&p.askBecomePassword,
"ask-become-password",
false,
"Ask sudo password of target servers before scanning",
)
Expand All @@ -102,24 +102,16 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {

// Execute execute
func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
var keyPass, sudoPass string
var askFunc c.AskFunc
var err error
if p.askKeyPassword {
prompt := "SSH key password: "
if keyPass, err = getPasswd(prompt); err != nil {
logrus.Error(err)
return subcommands.ExitFailure
}
askFunc.KeyPassword = askKeyPassword
}
if p.askSudoPassword {
prompt := "sudo password: "
if sudoPass, err = getPasswd(prompt); err != nil {
logrus.Error(err)
return subcommands.ExitFailure
}
if p.askBecomePassword {
askFunc.BecomePassword = askBecomePassword
}

err = c.Load(p.configPath, keyPass, sudoPass)
err = c.Load(p.configPath, askFunc)
if err != nil {
logrus.Errorf("Error loading %s, %s", p.configPath, err)
return subcommands.ExitUsageError
Expand Down
28 changes: 10 additions & 18 deletions commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ type ScanCmd struct {
reportText bool
reportS3 bool

askSudoPassword bool
askKeyPassword bool
askBecomePassword bool
askKeyPassword bool

useYumPluginSecurity bool
useUnattendedUpgrades bool
Expand Down Expand Up @@ -92,7 +92,7 @@ func (*ScanCmd) Usage() string {
[-report-slack]
[-report-text]
[-http-proxy=http://192.168.0.1:8080]
[-ask-sudo-password]
[-ask-become-password]
[-ask-key-password]
[-debug]
[-debug-sql]
Expand Down Expand Up @@ -178,8 +178,8 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
)

f.BoolVar(
&p.askSudoPassword,
"ask-sudo-password",
&p.askBecomePassword,
"ask-become-password",
false,
"Ask sudo password of target servers before scanning",
)
Expand All @@ -202,24 +202,16 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {

// Execute execute
func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
var keyPass, sudoPass string
var askFunc c.AskFunc
var err error
if p.askKeyPassword {
prompt := "SSH key password: "
if keyPass, err = getPasswd(prompt); err != nil {
logrus.Error(err)
return subcommands.ExitFailure
}
askFunc.KeyPassword = askKeyPassword
}
if p.askSudoPassword {
prompt := "sudo password: "
if sudoPass, err = getPasswd(prompt); err != nil {
logrus.Error(err)
return subcommands.ExitFailure
}
if p.askBecomePassword {
askFunc.BecomePassword = askBecomePassword
}

err = c.Load(p.configPath, keyPass, sudoPass)
err = c.Load(p.configPath, askFunc)
if err != nil {
logrus.Errorf("Error loading %s, %s", p.configPath, err)
return subcommands.ExitUsageError
Expand Down
26 changes: 8 additions & 18 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,13 +208,14 @@ func (c *SlackConf) Validate() (errs []error) {

// ServerInfo has SSH Info, additional CPE packages to scan.
type ServerInfo struct {
ServerName string
User string
Password string
Host string
Port string
KeyPath string
KeyPassword string
ServerName string
User string
Password string
Host string
Port string
KeyPath string
KeyPassword string
BecomeMethod string

CpeNames []string

Expand All @@ -223,7 +224,6 @@ type ServerInfo struct {

// userd internal
LogMsgAnsiColor string // DebugLog Color
SudoOpt SudoOption
Container Container
}

Expand All @@ -243,13 +243,3 @@ type Container struct {
Name string
Type string
}

// SudoOption is flag of sudo option.
type SudoOption struct {

// echo pass | sudo -S ls
ExecBySudo bool

// echo pass | sudo sh -C 'ls'
ExecBySudoSh bool
}
6 changes: 3 additions & 3 deletions config/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package config

// Load loads configuration
func Load(path, keyPass, sudoPass string) error {
func Load(path string, ask AskFunc) error {
var loader Loader
loader = TOMLLoader{}

return loader.Load(path, keyPass, sudoPass)
return loader.Load(path, ask)
}

// Loader is interface of concrete loader
type Loader interface {
Load(string, string, string) error
Load(string, AskFunc) error
}
56 changes: 50 additions & 6 deletions config/tomlloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@ import (
type TOMLLoader struct {
}

type AskFunc struct {
KeyPassword func() (string, error)
BecomePassword func(string) (string, error)
}

// Load load the configuraiton TOML file specified by path arg.
func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
func (c TOMLLoader) Load(pathToToml string, ask AskFunc) (err error) {
var conf Config
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
log.Error("Load config failed", err)
Expand All @@ -45,12 +50,42 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
Conf.Default = d
servers := make(map[string]ServerInfo)

if keyPass != "" {
d.KeyPassword = keyPass
var keyPassword string
if ask.KeyPassword != nil {
if keyPassword, err = ask.KeyPassword(); err != nil {
return err
}
}

if sudoPass != "" {
d.Password = sudoPass
if keyPassword != "" {
d.KeyPassword = keyPassword
}

var suPassword, sudoPassword string
if ask.BecomePassword != nil {
for _, v := range conf.Servers {
becomeMethod := v.BecomeMethod
if becomeMethod == "" {
becomeMethod = d.BecomeMethod
}

switch becomeMethod {
case "su":
if suPassword == "" {
if suPassword, err = ask.BecomePassword(becomeMethod); err != nil {
return err
}
}
case "sudo", "":
if sudoPassword == "" {
if sudoPassword, err = ask.BecomePassword(becomeMethod); err != nil {
return err
}
}
default:
return fmt.Errorf("BecomeMethod: unsupported method %s", becomeMethod)
}
}
}

i := 0
Expand All @@ -68,8 +103,17 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {

// s.Password = sudoPass
s.Password = v.Password
s.BecomeMethod = v.BecomeMethod
if s.BecomeMethod == "" {
s.BecomeMethod = d.BecomeMethod
}
if s.Password == "" {
s.Password = d.Password
switch s.BecomeMethod {
case "su":
s.Password = suPassword
case "sudo", "":
s.Password = sudoPassword
}
}

s.Host = v.Host
Expand Down
2 changes: 0 additions & 2 deletions scan/debian.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface) {

deb = newDebian(c)

// set sudo option flag
c.SudoOpt = config.SudoOption{ExecBySudo: true}
deb.setServerInfo(c)

if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
Expand Down
2 changes: 0 additions & 2 deletions scan/freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ func newBsd(c config.ServerInfo) *bsd {
//https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/freebsd.rb
func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
bsd = newBsd(c)
//set sudo option flag
c.SudoOpt = config.SudoOption{ExecBySudo: true}
bsd.setServerInfo(c)

if r := sshExec(c, "uname", noSudo); r.isSuccess() {
Expand Down
Loading