mirror of
https://github.com/trufflesecurity/trufflehog.git
synced 2024-11-15 01:17:34 +00:00
0a3451a1ba
* Create a new context with timeout per request * match timeout * use context timeout * reduce timeout
141 lines
3.8 KiB
Go
141 lines
3.8 KiB
Go
package privatekey
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
|
|
var githubFingerprints = map[string]string{
|
|
"SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s": "RSA",
|
|
"SHA256:br9IjFspm1vxR3iA35FWE+4VTyz1hYVLIE2t1/CeyWQ": "DSA - deprecated",
|
|
"SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM": "ECDSA",
|
|
"SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU": "ED25519",
|
|
}
|
|
|
|
// https://docs.gitlab.com/ee/user/gitlab_com/index.html#ssh-host-keys-fingerprints
|
|
var gitlabFingerprints = map[string]string{
|
|
"SHA256:HbW3g8zUjNSksFbqTiUWPWg2Bq1x8xdGUrliXFzSnUw": "ECDSA",
|
|
"SHA256:eUXGGm1YGsMAS7vkcx6JOJdOGHPem5gQp4taiCfCLB8": "ED25519",
|
|
"SHA256:ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ": "RSA",
|
|
}
|
|
|
|
func firstResponseFromSSH(ctx context.Context, parsedKey any, username, hostport string) (string, error) {
|
|
signer, err := ssh.NewSignerFromKey(parsedKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Verify the server fingerprint to ensure that there is no MITM replay attack
|
|
config := &ssh.ClientConfig{
|
|
User: username,
|
|
Auth: []ssh.AuthMethod{
|
|
ssh.PublicKeys(signer),
|
|
},
|
|
HostKeyCallback: func(hostname string, _ net.Addr, key ssh.PublicKey) error {
|
|
switch hostname {
|
|
case "github.com:22":
|
|
fingerprint := fingerprintSSHPublicKey(key)
|
|
if _, ok := githubFingerprints[fingerprint]; !ok {
|
|
return fmt.Errorf("unknown host fingerprint for github.com, got %s", fingerprint)
|
|
}
|
|
case "gitlab.com:22":
|
|
fingerprint := fingerprintSSHPublicKey(key)
|
|
if _, ok := gitlabFingerprints[fingerprint]; !ok {
|
|
return fmt.Errorf("unknown host fingerprint for gitlab.com, got %s", fingerprint)
|
|
}
|
|
default:
|
|
return errors.New("unknown host in fingerprint db")
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
client, err := sshDialWithContext(ctx, "tcp", hostport, config)
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), "unable to authenticate") {
|
|
return "", errPermissionDenied
|
|
}
|
|
return "", err
|
|
}
|
|
defer client.Close()
|
|
|
|
session, err := client.NewSession()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer session.Close()
|
|
|
|
var output bytes.Buffer
|
|
session.Stderr = &output
|
|
|
|
err = session.Shell()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_ = session.Wait()
|
|
|
|
return output.String(), err
|
|
}
|
|
|
|
func sshDialWithContext(ctx context.Context, network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
|
|
d := net.Dialer{}
|
|
conn, err := d.DialContext(ctx, network, addr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error dialing %s: %w", addr, err)
|
|
}
|
|
|
|
ncc, chans, reqs, err := ssh.NewClientConn(conn, addr, config)
|
|
if err != nil {
|
|
conn.Close()
|
|
return nil, fmt.Errorf("error creating SSH connection to %s: %w", addr, err)
|
|
}
|
|
|
|
client := ssh.NewClient(ncc, chans, reqs)
|
|
return client, nil
|
|
}
|
|
|
|
var errPermissionDenied = errors.New("permission denied")
|
|
|
|
func verifyGitHubUser(ctx context.Context, parsedKey any) (*string, error) {
|
|
output, err := firstResponseFromSSH(ctx, parsedKey, "git", "github.com:22")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if strings.Contains(output, "Permission denied") {
|
|
return nil, errPermissionDenied
|
|
}
|
|
|
|
if strings.Contains(output, "successfully authenticated") {
|
|
username := strings.TrimSuffix(strings.Split(output, " ")[1], "!")
|
|
return &username, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func verifyGitLabUser(ctx context.Context, parsedKey any) (*string, error) {
|
|
output, err := firstResponseFromSSH(ctx, parsedKey, "git", "gitlab.com:22")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if strings.Contains(output, "Permission denied") {
|
|
return nil, errPermissionDenied
|
|
}
|
|
|
|
if strings.Contains(output, "Welcome to GitLab") {
|
|
split := strings.Split(output, " ")
|
|
username := strings.TrimPrefix(strings.TrimSuffix(split[len(split)-1], "!"), "@")
|
|
return &username, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|