mirror of
https://github.com/securego/gosec.git
synced 2026-06-20 00:15:59 +02:00
* Add G408: SSH PublicKeyCallback Authentication Bypass Analyzer Implements a new SSA-based security analyzer (G408) that detects stateful misuse of ssh.PublicKeyCallback in SSH server configurations. This vulnerability can lead to authentication bypass where a server authenticates one SSH key but performs authorization checks on a different key. This addresses a critical security vulnerability (CVE-2024-45337, CVSS 9.1) that has affected production systems including Kubernetes and other SSH-based services. The vulnerability occurs when developers incorrectly capture and modify state within PublicKeyCallback closures, enabling attackers to authenticate with one key while the server operates on another key's credentials. Signed-off-by: Cosmin Cojocar <cosmin@cojocar.ch> * Fix tests Signed-off-by: Cosmin Cojocar <cosmin@cojocar.ch> --------- Signed-off-by: Cosmin Cojocar <cosmin@cojocar.ch>
351 lines
6.5 KiB
Go
351 lines
6.5 KiB
Go
package testutils
|
|
|
|
import "github.com/securego/gosec/v2"
|
|
|
|
// SampleCodeG408 - SSH PublicKeyCallback stateful misuse
|
|
var SampleCodeG408 = []CodeSample{
|
|
// Vulnerable: Direct capture and write to outer variable
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
var lastKey PublicKey
|
|
|
|
func setupServer() {
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
lastKey = key
|
|
return &Permissions{}, nil
|
|
}
|
|
_ = config
|
|
}
|
|
`}, 1, gosec.NewConfig()},
|
|
|
|
// Vulnerable: Struct field write via captured struct
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
type Server struct {
|
|
currentKey PublicKey
|
|
}
|
|
|
|
func setupServer() {
|
|
srv := &Server{}
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
srv.currentKey = key
|
|
return &Permissions{}, nil
|
|
}
|
|
_ = config
|
|
}
|
|
`}, 1, gosec.NewConfig()},
|
|
|
|
// Vulnerable: Map update with captured map
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
func setupServer() {
|
|
keyMap := make(map[string]PublicKey)
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
keyMap[conn.User()] = key
|
|
return &Permissions{}, nil
|
|
}
|
|
_ = config
|
|
}
|
|
`}, 1, gosec.NewConfig()},
|
|
|
|
// Vulnerable: Slice modification
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
func setupServer() {
|
|
keys := make([]PublicKey, 10)
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
keys[0] = key
|
|
return &Permissions{}, nil
|
|
}
|
|
_ = config
|
|
}
|
|
`}, 1, gosec.NewConfig()},
|
|
|
|
// Vulnerable: Nested struct field modification
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
type Session struct {
|
|
Auth struct {
|
|
LastKey PublicKey
|
|
}
|
|
}
|
|
|
|
func setupServer() {
|
|
session := &Session{}
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
session.Auth.LastKey = key
|
|
return &Permissions{}, nil
|
|
}
|
|
_ = config
|
|
}
|
|
`}, 1, gosec.NewConfig()},
|
|
|
|
// Safe: No captured variables modified
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
func setupServer() {
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
if isAuthorized(key) {
|
|
return &Permissions{}, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
_ = config
|
|
}
|
|
|
|
func isAuthorized(key PublicKey) bool {
|
|
return true
|
|
}
|
|
`}, 0, gosec.NewConfig()},
|
|
|
|
// Safe: Storing key data in Permissions.Extensions (correct pattern)
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
func setupServer() {
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
return &Permissions{
|
|
Extensions: map[string]string{
|
|
"pubkey": string(key.Marshal()),
|
|
},
|
|
}, nil
|
|
}
|
|
_ = config
|
|
}
|
|
`}, 0, gosec.NewConfig()},
|
|
|
|
// Safe: Only reading captured variables
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
func setupServer() {
|
|
authorizedKeys := map[string]bool{
|
|
"ssh-rsa AAA...": true,
|
|
}
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
keyStr := string(key.Marshal())
|
|
if authorizedKeys[keyStr] {
|
|
return &Permissions{}, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
_ = config
|
|
}
|
|
`}, 0, gosec.NewConfig()},
|
|
|
|
// Safe: No closure captures at all
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
func setupServer() {
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = checkKey
|
|
_ = config
|
|
}
|
|
|
|
func checkKey(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
return nil, nil
|
|
}
|
|
`}, 0, gosec.NewConfig()},
|
|
|
|
// Safe: Module-level function (not closure)
|
|
{[]string{`
|
|
package main
|
|
|
|
// Mock ssh types for testing
|
|
type PublicKey interface {
|
|
Marshal() []byte
|
|
}
|
|
|
|
type ConnMetadata interface {
|
|
User() string
|
|
}
|
|
|
|
type Permissions struct {
|
|
Extensions map[string]string
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
PublicKeyCallback func(ConnMetadata, PublicKey) (*Permissions, error)
|
|
}
|
|
|
|
func authenticateKey(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
|
// This is a module-level function, not a closure
|
|
return &Permissions{}, nil
|
|
}
|
|
|
|
func setupServer() {
|
|
config := &ServerConfig{}
|
|
config.PublicKeyCallback = authenticateKey
|
|
_ = config
|
|
}
|
|
`}, 0, gosec.NewConfig()},
|
|
}
|