Skip to content

Instantly share code, notes, and snippets.

@singe
Last active May 17, 2016 16:37
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save singe/f433c54f134a9390214e to your computer and use it in GitHub Desktop.
Save singe/f433c54f134a9390214e to your computer and use it in GitHub Desktop.
A scanner for new POODLE (affecting TLS versions). These are mods to Adam Langley's (@agl__) work.
diff -r f60b128afd41 src/crypto/tls/common.go
--- a/src/crypto/tls/common.go Tue Nov 04 10:20:35 2014 -0800
+++ b/src/crypto/tls/common.go Mon Dec 08 14:56:25 2014 -0800
@@ -343,6 +343,8 @@
// be used.
CurvePreferences []CurveID
+ BreakCBCPadding bool
+
serverInitOnce sync.Once // guards calling (*Config).serverInit
}
diff -r f60b128afd41 src/crypto/tls/conn.go
--- a/src/crypto/tls/conn.go Tue Nov 04 10:20:35 2014 -0800
+++ b/src/crypto/tls/conn.go Mon Dec 08 14:56:25 2014 -0800
@@ -109,6 +109,8 @@
// used to save allocating a new buffer for each MAC.
inDigestBuf, outDigestBuf []byte
+
+ brokenCBC bool
}
func (hc *halfConn) setErrorLocked(err error) error {
@@ -125,10 +127,11 @@
// prepareCipherSpec sets the encryption and MAC states
// that a subsequent changeCipherSpec will use.
-func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) {
+func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction, brokenCBC bool) {
hc.version = version
hc.nextCipher = cipher
hc.nextMac = mac
+ hc.brokenCBC = brokenCBC
}
// changeCipherSpec changes the encryption and MAC states
@@ -339,14 +342,21 @@
// block of payload. finalBlock is a fresh slice which contains the contents of
// any suffix of payload as well as the needed padding to make finalBlock a
// full block.
-func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) {
+func padToBlockSize(payload []byte, blockSize int, broken bool) (prefix, finalBlock []byte) {
overrun := len(payload) % blockSize
paddingLen := blockSize - overrun
prefix = payload[:len(payload)-overrun]
finalBlock = make([]byte, blockSize)
copy(finalBlock, payload[len(payload)-overrun:])
- for i := overrun; i < blockSize; i++ {
- finalBlock[i] = byte(paddingLen - 1)
+ if !broken {
+ for i := overrun; i < blockSize; i++ {
+ finalBlock[i] = byte(paddingLen - 1)
+ }
+ } else {
+ for i := overrun; i < blockSize; i++ {
+ finalBlock[i] = byte(66-i)
+ }
+ finalBlock[blockSize-1] = byte(paddingLen-1)
}
return
}
@@ -390,7 +400,7 @@
c.SetIV(payload[:explicitIVLen])
payload = payload[explicitIVLen:]
}
- prefix, finalBlock := padToBlockSize(payload, blockSize)
+ prefix, finalBlock := padToBlockSize(payload, blockSize, hc.brokenCBC)
b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock))
c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix)
c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock)
diff -r f60b128afd41 src/crypto/tls/handshake_client.go
--- a/src/crypto/tls/handshake_client.go Tue Nov 04 10:20:35 2014 -0800
+++ b/src/crypto/tls/handshake_client.go Mon Dec 08 14:56:25 2014 -0800
@@ -478,8 +478,8 @@
serverCipher = hs.suite.aead(serverKey, serverIV)
}
- c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
- c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
+ c.in.prepareCipherSpec(c.vers, serverCipher, serverHash, c.config.BreakCBCPadding)
+ c.out.prepareCipherSpec(c.vers, clientCipher, clientHash, c.config.BreakCBCPadding)
return nil
}
diff -r f60b128afd41 src/crypto/tls/handshake_server.go
--- a/src/crypto/tls/handshake_server.go Tue Nov 04 10:20:35 2014 -0800
+++ b/src/crypto/tls/handshake_server.go Mon Dec 08 14:56:25 2014 -0800
@@ -464,8 +464,8 @@
serverCipher = hs.suite.aead(serverKey, serverIV)
}
- c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
- c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
+ c.in.prepareCipherSpec(c.vers, clientCipher, clientHash, false)
+ c.out.prepareCipherSpec(c.vers, serverCipher, serverHash, false)
return nil
}
// This is a very simple tool for checking for POODLE issues in TLS servers.
// See https://www.imperialviolet.org/2014/12/08/poodleagain.html
// Some quick hacks addedd by @singe @sensepost
package main
import (
"bufio"
"crypto/tls"
"errors"
"flag"
"fmt"
"net"
"os"
"strings"
"sync"
"time"
)
var (
hostsFile *string = flag.String("hosts", "", "Filename containing hosts to query")
onlyAffected *bool = flag.Bool("only-affected", false, "Only show output for affected sites")
tlsDetails *bool = flag.Bool("tls-details", false, "Show TLS cipher and protocol negotiated")
debug *bool = flag.Bool("debug", false, "Show full server response")
)
func scanHost(hostname string) error {
dialer := net.Dialer{
Timeout: 15 * time.Second,
}
if !strings.Contains(hostname, ":") {
hostname = hostname + ":443"
}
conn, err := tls.DialWithDialer(&dialer, "tcp", hostname, &tls.Config{
InsecureSkipVerify: true,
BreakCBCPadding: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
},
})
if err != nil {
return err
}
defer conn.Close()
if *tlsDetails {
version := conn.ConnectionState().Version
if version == tls.VersionSSL30 {
return errors.New(hostname+" SSLv3 negotiated\n")
} else if version == tls.VersionTLS10 {
fmt.Printf(hostname+" TLSv1.0 negotiated\n")
} else if version == tls.VersionTLS11 {
fmt.Printf(hostname+" TLSv1.1 negotiated\n")
} else if version == tls.VersionTLS12 {
fmt.Printf(hostname+" TLSv1.2 negotiated\n")
}
cipher := conn.ConnectionState().CipherSuite
if cipher == tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA {
fmt.Printf(hostname+" TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA negotiated\n")
} else if cipher == tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA {
fmt.Printf(hostname+" TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA negotiated\n")
} else if cipher == tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA {
fmt.Printf(hostname+" TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA negotiated\n")
} else if cipher == tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA {
fmt.Printf(hostname+" TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA negotiated\n")
} else if cipher == tls.TLS_RSA_WITH_AES_128_CBC_SHA {
fmt.Printf(hostname+" TLS_RSA_WITH_AES_128_CBC_SHA negotiated\n")
} else if cipher == tls.TLS_RSA_WITH_AES_256_CBC_SHA {
fmt.Printf(hostname+" TLS_RSA_WITH_AES_256_CBC_SHA negotiated\n")
} else if cipher == tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA {
fmt.Printf(hostname+" TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA negotiated\n")
} else if cipher == tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA {
fmt.Printf(hostname+" TLS_RSA_WITH_3DES_EDE_CBC_SHA negotiated\n")
}
}
conn.Write([]byte("HEAD / HTTP/1.1\r\nHost: "+hostname+"\r\n\r\n"))
var buf [512]byte
n, err := conn.Read(buf[:])
if err != nil {
return err
}
if *debug {
fmt.Printf(hostname+" %d: %s\n", n, buf[:n])
}
return nil
}
func worker(hosts <-chan string, done *sync.WaitGroup) {
defer done.Done()
for hostname := range hosts {
if strings.Contains(hostname, "-hosts=hosts") {
continue
}
err := scanHost(hostname)
if err != nil {
if !*onlyAffected {
fmt.Fprintf(os.Stderr, "NOT VULNERABLE: %s: %s\n", hostname, err)
}
continue
}
fmt.Printf("VULNERABLE: %s\n", hostname)
}
}
func main() {
flag.Parse()
var wg sync.WaitGroup
const numWorkers = 512
hostnames := make(chan string, numWorkers)
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(hostnames, &wg)
}
/*for _, hostname := range os.Args[1:] {
hostnames <- hostname
}*/
if len(*hostsFile) > 0 {
hosts, err := os.Open(*hostsFile)
if err != nil {
panic(err)
}
defer hosts.Close()
inHosts := bufio.NewScanner(hosts)
for inHosts.Scan() {
hostnames <- inHosts.Text()
}
if err := inHosts.Err(); err != nil {
panic(err)
}
}
close(hostnames)
wg.Wait()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment