Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
0da76e2b
Commit
0da76e2b
authored
Jun 07, 2017
by
Matthew Holt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mitm: Add experimental Tor support for interception detection
parent
8051c73c
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
142 additions
and
10 deletions
+142
-10
caddyhttp/httpserver/mitm.go
caddyhttp/httpserver/mitm.go
+103
-1
caddyhttp/httpserver/mitm_test.go
caddyhttp/httpserver/mitm_test.go
+39
-8
caddyhttp/httpserver/replacer.go
caddyhttp/httpserver/replacer.go
+0
-1
No files found.
caddyhttp/httpserver/mitm.go
View file @
0da76e2b
...
...
@@ -65,7 +65,16 @@ func (h *tlsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
mitm
=
!
info
.
looksLikeChrome
()
&&
!
info
.
looksLikeSafari
()
}
else
if
strings
.
Contains
(
ua
,
"Firefox"
)
{
checked
=
true
mitm
=
!
info
.
looksLikeFirefox
()
if
strings
.
Contains
(
ua
,
"Windows"
)
{
ver
:=
getVersion
(
ua
,
"Firefox"
)
if
ver
==
45.0
||
ver
==
52.0
{
mitm
=
!
info
.
looksLikeTor
()
}
else
{
mitm
=
!
info
.
looksLikeFirefox
()
}
}
else
{
mitm
=
!
info
.
looksLikeFirefox
()
}
}
else
if
strings
.
Contains
(
ua
,
"Safari"
)
{
checked
=
true
mitm
=
!
info
.
looksLikeSafari
()
...
...
@@ -87,6 +96,34 @@ func (h *tlsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h
.
next
.
ServeHTTP
(
w
,
r
)
}
// getVersion returns a (possibly simplified) representation of the version string
// from a UserAgent string. It returns a float, so it can represent major and minor
// versions; the rest of the version is just tacked on behind the decimal point.
// The purpose of this is to stay simple while allowing for basic, fast comparisons.
// If the version for softwareName is not found in ua, -1 is returned.
func
getVersion
(
ua
,
softwareName
string
)
float64
{
search
:=
softwareName
+
"/"
start
:=
strings
.
Index
(
ua
,
search
)
if
start
<
0
{
return
-
1
}
start
+=
len
(
search
)
end
:=
strings
.
Index
(
ua
[
start
:
],
" "
)
if
end
<
0
{
end
=
len
(
ua
)
}
strVer
:=
strings
.
Replace
(
ua
[
start
:
end
],
"-"
,
""
,
-
1
)
firstDot
:=
strings
.
Index
(
strVer
,
"."
)
if
firstDot
>=
0
{
strVer
=
strVer
[
:
firstDot
+
1
]
+
strings
.
Replace
(
strVer
[
firstDot
+
1
:
],
"."
,
""
,
-
1
)
}
ver
,
err
:=
strconv
.
ParseFloat
(
strVer
,
64
)
if
err
!=
nil
{
return
-
1
}
return
ver
}
// clientHelloConn reads the ClientHello
// and stores it in the attached listener.
type
clientHelloConn
struct
{
...
...
@@ -330,6 +367,7 @@ func (info rawHelloInfo) looksLikeFirefox() bool {
// Note: Firefox 51+ does not advertise 0x3374 (13172, NPN).
// Note: Firefox doesn't advertise 0x0 (0, SNI) when connecting to IP addresses.
// Note: Firefox 55+ doesn't appear to advertise 0xFF03 (65283, short headers). It used to be between 5 and 13.
// Note: Firefox on Fedora (or RedHat) doesn't include ECC suites because of patent liability.
requiredExtensionsOrder
:=
[]
uint16
{
23
,
65281
,
10
,
11
,
35
,
16
,
5
,
13
}
if
!
assertPresenceAndOrdering
(
requiredExtensionsOrder
,
info
.
extensions
,
true
)
{
return
false
...
...
@@ -543,6 +581,70 @@ func (info rawHelloInfo) looksLikeSafari() bool {
return
assertPresenceAndOrdering
(
expectedCipherSuiteOrder
,
info
.
cipherSuites
,
true
)
}
// looksLikeTor returns true if the info looks like a ClientHello from Tor browser
// (based on Firefox).
func
(
info
rawHelloInfo
)
looksLikeTor
()
bool
{
requiredExtensionsOrder
:=
[]
uint16
{
10
,
11
,
16
,
5
,
13
}
if
!
assertPresenceAndOrdering
(
requiredExtensionsOrder
,
info
.
extensions
,
true
)
{
return
false
}
// check for session tickets support; Tor doesn't support them to prevent tracking
for
_
,
ext
:=
range
info
.
extensions
{
if
ext
==
35
{
return
false
}
}
// We check for both presence of curves and their ordering, including
// an optional curve at the beginning (for Tor based on Firefox 52)
infoCurves
:=
info
.
curves
if
len
(
info
.
curves
)
==
4
{
if
info
.
curves
[
0
]
!=
29
{
return
false
}
infoCurves
=
info
.
curves
[
1
:
]
}
requiredCurves
:=
[]
tls
.
CurveID
{
23
,
24
,
25
}
if
len
(
infoCurves
)
<
len
(
requiredCurves
)
{
return
false
}
for
i
:=
range
requiredCurves
{
if
infoCurves
[
i
]
!=
requiredCurves
[
i
]
{
return
false
}
}
if
hasGreaseCiphers
(
info
.
cipherSuites
)
{
return
false
}
// We check for order of cipher suites but not presence, since
// according to the paper, cipher suites may be not be added
// or reordered by the user, but they may be disabled.
expectedCipherSuiteOrder
:=
[]
uint16
{
TLS_AES_128_GCM_SHA256
,
// 0x1301
TLS_CHACHA20_POLY1305_SHA256
,
// 0x1303
TLS_AES_256_GCM_SHA384
,
// 0x1302
tls
.
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
,
// 0xc02b
tls
.
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
,
// 0xc02f
tls
.
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
,
// 0xcca9
tls
.
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
,
// 0xcca8
tls
.
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
,
// 0xc02c
tls
.
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
,
// 0xc030
tls
.
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
,
// 0xc00a
tls
.
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
,
// 0xc009
tls
.
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
,
// 0xc013
tls
.
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
,
// 0xc014
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
,
// 0x33
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
,
// 0x39
tls
.
TLS_RSA_WITH_AES_128_CBC_SHA
,
// 0x2f
tls
.
TLS_RSA_WITH_AES_256_CBC_SHA
,
// 0x35
tls
.
TLS_RSA_WITH_3DES_EDE_CBC_SHA
,
// 0xa
}
return
assertPresenceAndOrdering
(
expectedCipherSuiteOrder
,
info
.
cipherSuites
,
false
)
}
// assertPresenceAndOrdering will return true if candidateList contains
// the items in requiredItems in the same order as requiredItems.
//
...
...
caddyhttp/httpserver/mitm_test.go
View file @
0da76e2b
...
...
@@ -132,6 +132,16 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
helloHex
:
`010001fc030383141d213d1bf069171843489faf808028d282c9828e1ba87637c863833c730720a67e76e152f4b704523b72317ef4587e231f02e2395e0ecac6be9f28c35e6ce600208a8ac02bc02fc02cc030cca9cca8cc14cc13c013c014009c009d002f0035000a010001931a1a0000ff0100010000000014001200000f66696e6572706978656c732e636f6d00170000002300785e85429bf1764f33111cd3ad5d1c56d765976fd962b49dbecbb6f7865e2a8d8536ad854f1fa99a8bbbf998814fee54a63a0bf162869d2bba37e9778304e7c4140825718e191b574c6246a0611de6447bdd80417f83ff9d9b7124069a9f74b90394ecb89bec5f6a1a67c1b89e50b8674782f53dd51807651a000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e3175500000000b00020100000a000a00081a1a001d001700182a2a0001000015009a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000`
,
interception
:
false
,
},
{
userAgent
:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
,
helloHex
:
`010000c203034166c97e2016046e0c88ad867c410d0aee470f4d9b4ec8fe41a751d2a6348e3100001c4a4ac02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a0100007dcaca0000ff0100010000000014001200000f66696e6572706978656c732e636f6d0017000000230000000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e3175500000000b00020100000a000a00086a6a001d001700187a7a000100`
,
interception
:
false
,
},
{
userAgent
:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
,
helloHex
:
`010000c203037741795e73cd5b4949f79a0dc9cccc8b006e4c0ec324f965c6fe9f0833909f0100001c7a7ac02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a0100007d7a7a0000ff0100010000000014001200000f66696e6572706978656c732e636f6d0017000000230000000d00140012040308040401050308050501080606010201000500050100000000001200000010000e000c02683208687474702f312e3175500000000b00020100000a000a00084a4a001d001700185a5a000100`
,
interception
:
false
,
},
},
"Firefox"
:
{
{
...
...
@@ -155,6 +165,12 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
helloHex
:
`010001fc030331e380b7d12018e1202ef3327607203df5c5732b4fa5ab5abaf0b60034c2fb662070c836b9b89123e37f4f1074d152df438fa8ee8a0f89b036fd952f4fcc0b994f001c130113031302c02bc02fcca9cca8c02cc030c013c014002f0035000a0100019700000014001200000f63616464797365727665722e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b0002010000230078c97e7716a041e2ea824571bef26a3dff2bf50a883cd15d904ab2d17deb514f6e0a079ee7c212c000178387ffafc2e530b6df6662f570aae134330f13c458a0eaad5a96a9696f572110918740b15db1143d19aaaa706942030b433a7e6150f62b443c0564e5b8f7ee9577bf3bf7faec8c67425b648ab54d880010000e000c02683208687474702f312e310005000501000000000028006b0069001d0020aee6e596155ee6f79f943e81ceabe0979d27fbbb8b9189ccb2ebc75226351f32001700410421875a44e510decac11ef1d7cfddd4dfe105d5cd3a2d42fba03ebde23e51e8ce65bda1b48be82d4848d1db2bfce68e94092e925a9ce0dbf5df35479558108489002b0009087f12030303020301000d0018001604030503060308040805080604010501060102030201002d000201010015002500000000000000000000000000000000000000000000000000000000000000000000000000`
,
interception
:
false
,
},
{
// Firefox on Fedora (RedHat) doesn't include ECC ciphers because of patent liabilities
userAgent
:
"Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0"
,
helloHex
:
`010000b70303f5280b74d617d42e39fd77b78a2b537b1d7787ce4fcbcf3604c9fbcd677c6c5500001ec02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100007000000014001200000f66696e6572706978656c732e636f6d00170000ff01000100000a000a0008001d001700180019000b00020100002300000010000e000c02683208687474702f312e31000500050100000000000d0018001604030503060308040805080604010501060102030201`
,
interception
:
false
,
},
},
"Edge"
:
{
{
...
...
@@ -170,6 +186,18 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
interception
:
false
,
},
},
"Tor"
:
{
{
userAgent
:
"Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0"
,
helloHex
:
`010000a40303137f05d4151f2d9095aee4254416d9dce73d6a1d857e8097ea20d021c04a7a81000016c02bc02fc00ac009c013c01400330039002f0035000a0100006500000014001200000f66696e6572706978656c732e636f6dff01000100000a00080006001700180019000b00020100337400000010000b000908687474702f312e31000500050100000000000d001600140401050106010201040305030603020304020202`
,
interception
:
false
,
},
{
userAgent
:
"Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0"
,
helloHex
:
`010000b4030322e1f3aff4c37caba303c2ce53ba1689b3e70117a46f413d44f70a74cb6a496100001ec02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100006d00000014001200000f66696e6572706978656c732e636f6d00170000ff01000100000a000a0008001d001700180019000b000201000010000b000908687474702f312e31000500050100000000ff030000000d0018001604030503060308040805080604010501060102030201`
,
interception
:
false
,
},
},
"Other"
:
{
// these are either non-browser clients or intercepted client hellos
{
// openssl s_client (OpenSSL 0.9.8zh 14 Jan 2016) - NOT an interception, but not a browser either
...
...
@@ -237,7 +265,7 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
interception
:
true
,
},
{
// IE 11 on Windows 10, intercepted by Fortigate (same firewallas above)
// IE 11 on Windows 10, intercepted by Fortigate (same firewall
as above)
userAgent
:
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"
,
helloHex
:
`010000e5030158ac634c5278d7b17421f23a64cc91d68c470c6b247322fe867ba035b373d05c000064003300320039003800160013c013c009c014c00ac012c008002f0035000a00150012003d003c00670040006b006ac011c0070096009a009900410084004500440088008700ba00be00bd00c000c400c3c03cc044c042c03dc045c04300090005000400ff01000058000a003600340000000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013001400150016001700180019000b0002010000000014001200000f66696e6572706978656c732e636f6d`
,
interception
:
true
,
...
...
@@ -270,6 +298,7 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
isFirefox
:=
parsed
.
looksLikeFirefox
()
isSafari
:=
parsed
.
looksLikeSafari
()
isEdge
:=
parsed
.
looksLikeEdge
()
isTor
:=
parsed
.
looksLikeTor
()
// we want each of the heuristic functions to be as
// exclusive but as low-maintenance as possible;
...
...
@@ -280,20 +309,22 @@ func TestHeuristicFunctionsAndHandler(t *testing.T) {
var
correct
bool
switch
client
{
case
"Chrome"
:
correct
=
isChrome
&&
!
isFirefox
&&
!
isSafari
&&
!
isEdge
correct
=
isChrome
&&
!
isFirefox
&&
!
isSafari
&&
!
isEdge
&&
!
isTor
case
"Firefox"
:
correct
=
!
isChrome
&&
isFirefox
&&
!
isSafari
&&
!
isEdge
correct
=
!
isChrome
&&
isFirefox
&&
!
isSafari
&&
!
isEdge
&&
!
isTor
case
"Safari"
:
correct
=
!
isChrome
&&
!
isFirefox
&&
isSafari
&&
!
isEdge
correct
=
!
isChrome
&&
!
isFirefox
&&
isSafari
&&
!
isEdge
&&
!
isTor
case
"Edge"
:
correct
=
!
isChrome
&&
!
isFirefox
&&
!
isSafari
&&
isEdge
correct
=
!
isChrome
&&
!
isFirefox
&&
!
isSafari
&&
isEdge
&&
!
isTor
case
"Tor"
:
correct
=
!
isChrome
&&
!
isFirefox
&&
!
isSafari
&&
!
isEdge
&&
isTor
case
"Other"
:
correct
=
!
isChrome
&&
!
isFirefox
&&
!
isSafari
&&
!
isEdge
correct
=
!
isChrome
&&
!
isFirefox
&&
!
isSafari
&&
!
isEdge
&&
!
isTor
}
if
!
correct
{
t
.
Errorf
(
"[%s] Test %d: Chrome=%v
, Firefox=%v, Safari=%v, Edge=%v; parsed hello: %+v
"
,
client
,
i
,
isChrome
,
isFirefox
,
isSafari
,
isEdge
,
parsed
)
t
.
Errorf
(
"[%s] Test %d: Chrome=%v
Firefox=%v Safari=%v Edge=%v Tor=%v
\n\t
parsed hello dec: %+v
\n\t
parsed hello hex: %#x
\n
"
,
client
,
i
,
isChrome
,
isFirefox
,
isSafari
,
isEdge
,
isTor
,
parsed
,
parsed
)
}
// test the handler too
...
...
caddyhttp/httpserver/replacer.go
View file @
0da76e2b
...
...
@@ -312,7 +312,6 @@ func (r *replacer) getSubstitution(key string) string {
if
val
{
return
"likely"
}
return
"unlikely"
}
return
"unknown"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment