Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
T
Titanium-Web-Proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Titanium-Web-Proxy
Commits
20c5ed20
Commit
20c5ed20
authored
Jun 27, 2017
by
Jehonathan Thomas
Committed by
GitHub
Jun 27, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #287 from honfika/develop
Parse Ssl client hello
parents
e22839c6
122feb3b
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
257 additions
and
34 deletions
+257
-34
MainWindow.xaml.cs
Examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
+20
-19
SessionListItem.cs
Examples/Titanium.Web.Proxy.Examples.Wpf/SessionListItem.cs
+6
-2
CustomBufferedPeekStream.cs
Titanium.Web.Proxy/Helpers/CustomBufferedPeekStream.cs
+58
-0
CustomBufferedStream.cs
Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs
+10
-0
ConnectRequest.cs
Titanium.Web.Proxy/Http/ConnectRequest.cs
+63
-0
ConnectResponse.cs
Titanium.Web.Proxy/Http/ConnectResponse.cs
+1
-0
HeaderCollection.cs
Titanium.Web.Proxy/Http/HeaderCollection.cs
+9
-0
HttpsTools.cs
Titanium.Web.Proxy/Http/HttpsTools.cs
+88
-12
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+1
-1
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+1
-0
No files found.
Examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
View file @
20c5ed20
...
...
@@ -16,6 +16,7 @@ using System.Windows.Navigation;
using
System.Windows.Shapes
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy.Examples.Wpf
...
...
@@ -71,7 +72,10 @@ namespace Titanium.Web.Proxy.Examples.Wpf
proxyServer
.
TrustRootCertificate
=
true
;
proxyServer
.
ForwardToUpstreamGateway
=
true
;
var
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
8000
,
true
);
var
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
8000
,
true
)
{
//IncludedHttpsHostNameRegex = new string[0],
};
proxyServer
.
AddEndPoint
(
explicitEndPoint
);
proxyServer
.
BeforeRequest
+=
ProxyServer_BeforeRequest
;
proxyServer
.
BeforeResponse
+=
ProxyServer_BeforeResponse
;
...
...
@@ -101,10 +105,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
SessionListItem
item
;
if
(
sessionDictionary
.
TryGetValue
(
e
,
out
item
))
{
item
.
Response
.
ResponseStatusCode
=
e
.
WebSession
.
Response
.
ResponseStatusCode
;
item
.
Response
.
ResponseStatusDescription
=
e
.
WebSession
.
Response
.
ResponseStatusDescription
;
item
.
Response
.
HttpVersion
=
e
.
WebSession
.
Response
.
HttpVersion
;
item
.
Response
.
ResponseHeaders
.
AddHeaders
(
e
.
WebSession
.
Response
.
ResponseHeaders
);
item
.
Response
=
e
.
WebSession
.
Response
;
item
.
Update
();
}
});
...
...
@@ -132,10 +133,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
SessionListItem
item2
;
if
(
sessionDictionary
.
TryGetValue
(
e
,
out
item2
))
{
item2
.
Response
.
ResponseStatusCode
=
e
.
WebSession
.
Response
.
ResponseStatusCode
;
item2
.
Response
.
ResponseStatusDescription
=
e
.
WebSession
.
Response
.
ResponseStatusDescription
;
item2
.
Response
.
HttpVersion
=
e
.
WebSession
.
Response
.
HttpVersion
;
item2
.
Response
.
ResponseHeaders
.
AddHeaders
(
e
.
WebSession
.
Response
.
ResponseHeaders
);
item2
.
Response
=
e
.
WebSession
.
Response
;
item2
.
Update
();
item
=
item2
;
}
...
...
@@ -165,16 +163,13 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
Number
=
lastSessionNumber
,
SessionArgs
=
e
,
Request
=
{
Method
=
e
.
WebSession
.
Request
.
Method
,
RequestUri
=
e
.
WebSession
.
Request
.
RequestUri
,
HttpVersion
=
e
.
WebSession
.
Request
.
HttpVersion
,
},
// save the headers because TWP will set it to null in Dispose
RequestHeaders
=
e
.
WebSession
.
Request
.
RequestHeaders
,
ResponseHeaders
=
e
.
WebSession
.
Response
.
ResponseHeaders
,
Request
=
e
.
WebSession
.
Request
,
Response
=
e
.
WebSession
.
Response
,
};
item
.
Request
.
RequestHeaders
.
AddHeaders
(
e
.
WebSession
.
Request
.
RequestHeaders
);
if
(
e
is
TunnelConnectSessionEventArgs
||
e
.
WebSession
.
Request
.
UpgradeToWebSocket
)
{
e
.
DataReceived
+=
(
sender
,
args
)
=>
...
...
@@ -232,9 +227,14 @@ namespace Titanium.Web.Proxy.Examples.Wpf
data
=
data
.
Take
(
truncateLimit
).
ToArray
();
}
//restore the headers
typeof
(
Request
).
GetProperty
(
nameof
(
Request
.
RequestHeaders
)).
SetValue
(
session
.
Request
,
session
.
RequestHeaders
);
typeof
(
Response
).
GetProperty
(
nameof
(
Response
.
ResponseHeaders
)).
SetValue
(
session
.
Response
,
session
.
ResponseHeaders
);
//string hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxRequest
.
Text
=
session
.
Request
.
HeaderText
+
session
.
Request
.
Encoding
.
GetString
(
data
)
+
(
truncated
?
Environment
.
NewLine
+
$"Data is truncated after
{
truncateLimit
}
bytes"
:
null
);
(
truncated
?
Environment
.
NewLine
+
$"Data is truncated after
{
truncateLimit
}
bytes"
:
null
)
+
(
session
.
Request
as
ConnectRequest
)?.
ClientHelloInfo
;
data
=
session
.
ResponseBody
??
new
byte
[
0
];
truncated
=
data
.
Length
>
truncateLimit
;
...
...
@@ -245,7 +245,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
//hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxResponse
.
Text
=
session
.
Response
.
HeaderText
+
session
.
Response
.
Encoding
.
GetString
(
data
)
+
(
truncated
?
Environment
.
NewLine
+
$"Data is truncated after
{
truncateLimit
}
bytes"
:
null
);
(
truncated
?
Environment
.
NewLine
+
$"Data is truncated after
{
truncateLimit
}
bytes"
:
null
)
+
(
session
.
Response
as
ConnectResponse
)?.
ServerHelloInfo
;
}
}
}
Examples/Titanium.Web.Proxy.Examples.Wpf/SessionListItem.cs
View file @
20c5ed20
...
...
@@ -78,9 +78,13 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public
byte
[]
ResponseBody
{
get
;
set
;
}
public
Request
Request
{
get
;
set
;
}
=
new
Request
();
public
Request
Request
{
get
;
set
;
}
public
Response
Response
{
get
;
set
;
}
=
new
Response
();
public
Response
Response
{
get
;
set
;
}
public
HeaderCollection
RequestHeaders
{
get
;
set
;
}
public
HeaderCollection
ResponseHeaders
{
get
;
set
;
}
public
event
PropertyChangedEventHandler
PropertyChanged
;
...
...
Titanium.Web.Proxy/Helpers/CustomBufferedPeekStream.cs
0 → 100644
View file @
20c5ed20
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Helpers
{
class
CustomBufferedPeekStream
{
private
readonly
CustomBufferedStream
baseStream
;
private
int
position
;
public
CustomBufferedPeekStream
(
CustomBufferedStream
baseStream
,
int
startPosition
=
0
)
{
this
.
baseStream
=
baseStream
;
position
=
startPosition
;
}
public
async
Task
<
bool
>
EnsureBufferLength
(
int
length
)
{
var
val
=
await
baseStream
.
PeekByteAsync
(
length
-
1
);
return
val
!=
-
1
;
}
public
byte
ReadByte
()
{
return
baseStream
.
PeekByteFromBuffer
(
position
++);
}
public
int
ReadInt16
()
{
int
i1
=
ReadByte
();
int
i2
=
ReadByte
();
return
(
i1
<<
8
)
+
i2
;
}
public
int
ReadInt24
()
{
int
i1
=
ReadByte
();
int
i2
=
ReadByte
();
int
i3
=
ReadByte
();
return
(
i1
<<
16
)
+
(
i2
<<
8
)
+
i3
;
}
public
byte
[]
ReadBytes
(
int
length
)
{
var
buffer
=
new
byte
[
length
];
for
(
int
i
=
0
;
i
<
buffer
.
Length
;
i
++)
{
buffer
[
i
]
=
ReadByte
();
}
return
buffer
;
}
}
}
Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs
View file @
20c5ed20
...
...
@@ -305,6 +305,16 @@ namespace Titanium.Web.Proxy.Helpers
return
streamBuffer
[
bufferPos
+
index
];
}
public
byte
PeekByteFromBuffer
(
int
index
)
{
if
(
bufferLength
<=
index
)
{
throw
new
Exception
(
"Index is out of buffer size"
);
}
return
streamBuffer
[
bufferPos
+
index
];
}
public
byte
ReadByteFromBuffer
()
{
if
(
bufferLength
==
0
)
...
...
Titanium.Web.Proxy/Http/ConnectRequest.cs
View file @
20c5ed20
...
...
@@ -8,5 +8,68 @@ namespace Titanium.Web.Proxy.Http
{
public
class
ConnectRequest
:
Request
{
public
ClientHelloInfo
ClientHelloInfo
{
get
;
set
;
}
}
public
class
ClientHelloInfo
{
public
int
MajorVersion
{
get
;
set
;
}
public
int
MinorVersion
{
get
;
set
;
}
public
byte
[]
Random
{
get
;
set
;
}
public
DateTime
Time
{
get
{
DateTime
time
=
DateTime
.
MinValue
;
if
(
Random
.
Length
>
3
)
{
time
=
new
DateTime
(
1970
,
1
,
1
,
0
,
0
,
0
,
0
,
DateTimeKind
.
Utc
)
.
AddSeconds
(((
uint
)
Random
[
3
]
<<
24
)
+
((
uint
)
Random
[
2
]
<<
16
)
+
((
uint
)
Random
[
1
]
<<
8
)
+
(
uint
)
Random
[
0
]).
ToLocalTime
();
}
return
time
;
}
}
public
byte
[]
SessionId
{
get
;
set
;
}
private
static
string
SslVersionToString
(
int
major
,
int
minor
)
{
string
str
=
"Unknown"
;
if
(
major
==
3
&&
minor
==
3
)
str
=
"TLS/1.2"
;
else
if
(
major
==
3
&&
minor
==
2
)
str
=
"TLS/1.1"
;
else
if
(
major
==
3
&&
minor
==
1
)
str
=
"TLS/1.0"
;
else
if
(
major
==
3
&&
minor
==
0
)
str
=
"SSL/3.0"
;
else
if
(
major
==
2
&&
minor
==
0
)
str
=
"SSL/2.0"
;
return
$"
{
major
}
.
{
minor
}
(
{
str
}
)"
;
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public
override
string
ToString
()
{
var
sb
=
new
StringBuilder
();
sb
.
AppendLine
(
"A SSLv3-compatible ClientHello handshake was found. Titanium extracted the parameters below."
);
sb
.
AppendLine
();
sb
.
AppendLine
(
$"Version:
{
SslVersionToString
(
MajorVersion
,
MinorVersion
)}
"
);
sb
.
AppendLine
(
$"Random:
{
string
.
Join
(
" "
,
Random
.
Select
(
x
=>
x
.
ToString
(
"X2"
)))}
"
);
sb
.
AppendLine
(
$"\"Time\":
{
Time
}
"
);
sb
.
AppendLine
(
$"SessionID:
{
string
.
Join
(
" "
,
SessionId
.
Select
(
x
=>
x
.
ToString
(
"X2"
)))}
"
);
return
sb
.
ToString
();
}
}
}
Titanium.Web.Proxy/Http/ConnectResponse.cs
View file @
20c5ed20
...
...
@@ -8,5 +8,6 @@ namespace Titanium.Web.Proxy.Http
{
public
class
ConnectResponse
:
Response
{
public
string
ServerHelloInfo
{
get
;
set
;
}
}
}
Titanium.Web.Proxy/Http/HeaderCollection.cs
View file @
20c5ed20
...
...
@@ -215,6 +215,15 @@ namespace Titanium.Web.Proxy.Http
return
false
;
}
/// <summary>
/// Removes all the headers.
/// </summary>
public
void
Clear
()
{
Headers
.
Clear
();
NonUniqueHeaders
.
Clear
();
}
internal
string
GetHeaderValueOrNull
(
string
headerName
)
{
HttpHeader
header
;
...
...
Titanium.Web.Proxy/Http/HttpsTools.cs
View file @
20c5ed20
...
...
@@ -3,54 +3,130 @@ using System.Collections.Generic;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Helpers
;
namespace
Titanium.Web.Proxy.Http
{
class
HttpsTools
{
public
static
async
Task
<
bool
>
IsClientHello
(
CustomBufferedStream
clientStream
)
public
static
async
Task
<
bool
>
IsClientHello
(
CustomBufferedStream
clientStream
,
TunnelConnectSessionEventArgs
connectArgs
)
{
//detects the HTTPS ClientHello message as it is described in the following url:
//https://stackoverflow.com/questions/3897883/how-to-detect-an-incoming-ssl-https-handshake-ssl-wire-format
var
request
=
(
ConnectRequest
)
connectArgs
.
WebSession
.
Request
;
int
recordType
=
await
clientStream
.
PeekByteAsync
(
0
);
if
(
recordType
==
0x80
)
{
var
peekStream
=
new
CustomBufferedPeekStream
(
clientStream
,
1
);
//SSL 2
int
length
=
await
clientStream
.
PeekByteAsync
(
1
);
int
length
=
peekStream
.
ReadByte
(
);
if
(
length
<
9
)
{
// Message body too short.
return
false
;
}
if
(
await
clientStream
.
PeekByteAsync
(
2
)
!=
0x01
)
if
(
peekStream
.
ReadByte
(
)
!=
0x01
)
{
// should be ClientHello
return
false
;
}
int
majorVersion
=
await
clientStream
.
PeekByteAsync
(
3
);
int
minorVersion
=
await
clientStream
.
PeekByteAsync
(
4
);
int
majorVersion
=
clientStream
.
ReadByte
(
);
int
minorVersion
=
clientStream
.
ReadByte
(
);
return
true
;
}
else
if
(
recordType
==
0x16
)
{
//SSL 3.0 or TLS 1.0, 1.1 and 1.2
int
majorVersion
=
await
clientStream
.
PeekByteAsync
(
1
);
int
minorVersion
=
await
clientStream
.
PeekByteAsync
(
2
);
var
peekStream
=
new
CustomBufferedPeekStream
(
clientStream
,
1
);
//should contain at least 43 bytes
int
requiredLength
=
43
;
// 2 version + 2 length + 1 type + 3 length(?) + 2 version + 32 random + 1 sessionid length
if
(!
await
peekStream
.
EnsureBufferLength
(
requiredLength
))
{
return
false
;
}
int
length1
=
await
clientStream
.
PeekByteAsync
(
3
);
int
length2
=
await
clientStream
.
PeekByteAsync
(
4
);
int
length
=
(
length1
<<
8
)
+
length2
;
//SSL 3.0 or TLS 1.0, 1.1 and 1.2
int
majorVersion
=
peekStream
.
ReadByte
(
);
int
minorVersion
=
peekStream
.
ReadByte
()
;
if
(
await
clientStream
.
PeekByteAsync
(
5
)
!=
0x01
)
int
length
=
peekStream
.
ReadInt16
();
if
(
peekStream
.
ReadByte
()
!=
0x01
)
{
// should be ClientHello
return
false
;
}
length
=
peekStream
.
ReadInt24
();
majorVersion
=
peekStream
.
ReadByte
();
minorVersion
=
peekStream
.
ReadByte
();
byte
[]
random
=
peekStream
.
ReadBytes
(
32
);
length
=
peekStream
.
ReadByte
();
requiredLength
+=
length
+
2
;
// sessionid + 2 data length
if
(!
await
peekStream
.
EnsureBufferLength
(
requiredLength
))
{
return
false
;
}
byte
[]
sessionId
=
peekStream
.
ReadBytes
(
length
);
length
=
peekStream
.
ReadInt16
();
requiredLength
+=
length
+
1
;
// data + data2 length
if
(!
await
peekStream
.
EnsureBufferLength
(
requiredLength
))
{
return
false
;
}
byte
[]
data
=
peekStream
.
ReadBytes
(
length
);
length
=
peekStream
.
ReadByte
();
if
(
length
<
1
)
{
return
false
;
}
requiredLength
+=
length
;
// data2
if
(!
await
peekStream
.
EnsureBufferLength
(
requiredLength
))
{
return
false
;
}
byte
[]
data2
=
peekStream
.
ReadBytes
(
length
);
byte
[]
data3
=
null
;
if
(
majorVersion
>
3
||
majorVersion
==
3
&&
minorVersion
>=
1
)
{
requiredLength
+=
2
;
if
(
await
peekStream
.
EnsureBufferLength
(
requiredLength
))
{
length
=
peekStream
.
ReadInt16
();
requiredLength
+=
length
;
if
(
await
peekStream
.
EnsureBufferLength
(
requiredLength
))
{
data3
=
peekStream
.
ReadBytes
(
length
);
}
}
}
request
.
ClientHelloInfo
=
new
ClientHelloInfo
{
MajorVersion
=
majorVersion
,
MinorVersion
=
minorVersion
,
Random
=
random
,
SessionId
=
sessionId
,
};
return
true
;
}
...
...
Titanium.Web.Proxy/RequestHandler.cs
View file @
20c5ed20
...
...
@@ -116,7 +116,7 @@ namespace Titanium.Web.Proxy
connectArgs
.
WebSession
.
Response
=
CreateConnectResponse
(
version
);
await
WriteResponse
(
connectArgs
.
WebSession
.
Response
,
clientStreamWriter
);
bool
isClientHello
=
await
HttpsTools
.
IsClientHello
(
clientStream
);
bool
isClientHello
=
await
HttpsTools
.
IsClientHello
(
clientStream
,
connectArgs
);
if
(
TunnelConnectResponse
!=
null
)
{
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
20c5ed20
...
...
@@ -89,6 +89,7 @@
<Compile
Include=
"Extensions\TcpExtensions.cs"
/>
<Compile
Include=
"Helpers\BufferPool.cs"
/>
<Compile
Include=
"Helpers\CustomBinaryReader.cs"
/>
<Compile
Include=
"Helpers\CustomBufferedPeekStream.cs"
/>
<Compile
Include=
"Helpers\CustomBufferedStream.cs"
/>
<Compile
Include=
"Helpers\Firefox.cs"
/>
<Compile
Include=
"Helpers\HttpHelper.cs"
/>
...
...
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