-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmproxy.rb
180 lines (161 loc) · 5.09 KB
/
mproxy.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# XXX: Doesn't log non-ssl (plugins not getting called for non-ssl connections)
MITM_ROOT = File.expand_path(File.dirname(__FILE__))
LOG_DIR = "#{MITM_ROOT}/logs"
Dir.mkdir(LOG_DIR) unless File.exists?(LOG_DIR)
require 'rubygems'
require 'logger'
require 'net/http'
require 'webrick'
require 'webrick/https'
require 'webrick/httpproxy'
require 'webrick/log'
require "#{MITM_ROOT}/lib/quickcert"
require "#{MITM_ROOT}/lib/util"
require "#{MITM_ROOT}/lib/webrick_ssl_serialno"
begin
require 'mechanize'
rescue LoadError
puts "Please install the mechanize gem"
exit
end
begin
require 'ruby-debug'
rescue LoadError
end
require 'plugin'
$DEBUG = false # sets debugging in webrick
$verbose = true # verbose debugging for mproxy
$count = -1
$plugins = load_plugins("#{MITM_ROOT}/plugins")
# enable modifications to unparsed_uri
class WEBrick::HTTPRequest
def unparsed_uri=(str)
@unparsed_uri = str
end
end
# The proxy class, allocates a new HTTPServer for each requested server:port combo
# When an request arrives, makes a connection to the real server with Mechanize
# @spoofed_hosts is a hash of existing mitm server objects indexed by name
class SSLMITM < WEBrick::HTTPProxyServer
def initialize(config)
# XXX: agent should be per client
@agent = WWW::Mechanize.new
@agent.keep_alive = false
@agent.verify_callback = Proc.new{ true }
@spoofed_hosts = {}
@mitm_port = 4433
@retry_count = 0
config[:Logger] = WEBrick::Log::new("#{LOG_DIR}/error.log", 5)
super
end
def ssl_mitm(server, port)
dest = "#{server}:#{port}"
if @spoofed_hosts[dest]
return @spoofed_hosts[dest].config[:Port]
else
begin
# XXX: ask system for an unused port
mitm = WEBrick::HTTPServer.new(:Port => @mitm_port,
:HTTPVersion => "1.0",
:SSLEnable => true,
:SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
:SSLCertName => [["C", "US"], ["O", server], ["CN", server] ])
rescue
@retry_count += 1
@mitm_port += 1
if @retry_count < 10
retry
else
puts "Couldn't allocate port in SSLMITM, not retrying, too many tries already"
exit
end
end
@spoofed_hosts[dest] = mitm
mitm.mount_proc('/') { |req,res|
puts "Request: #{req.request_line}"
$plugins.each { |plug|
plug.request(req.request_line, req.header, req.body);
}
meth, url, ver = req.request_line.split(" ")
# XXX: validate meth and ver
agent = WWW::Mechanize.new
agent.keep_alive = false
puts " doing #{meth.upcase}" if $verbose
case meth.upcase
when 'GET'
r = agent.get("https://#{server}:#{port}#{url}", req.body, req.header)
when 'HEAD'
# FIXME: 2nd param should be QS params
r = agent.head("https://#{server}:#{port}#{url}", {}, :headers => req.header)
when 'POST'
# FIXME: need to handle headers
r = agent.post("https://#{server}:#{port}#{url}", req.body)
else
puts "Not handling #{meth} yet"
end
status_line = "HTTP/1.1 #{r.code} OK\r\n"
puts "Response: #{status_line}"
$plugins.each { |plug|
# FIXME: get real HTTP ver and response msg
plug.response(status_line, r.response, r.body);
}
puts " Body:\n#{hexdump(r.body[0..31])}"
res.body = r.body
puts " Header.keys: #{r.header.keys.sort.join(", ")}" if $verbose
puts " Connection #{r.header['connection']}" if $verbose
r.header.keys.each { |k|
unless k == 'content-encoding'
res.header[k] = r.header[k]
end
}
}
st = Thread.new { mitm.start }
return mitm.config[:Port]
end
end
def proxy_connect(req, res)
puts "SSLMITM.proxy_connect" if $verbose
puts " req.request_line: #{req.request_line}" if $verbose
host, port = req.unparsed_uri.split(":")
unless port then port = 443; end
mitm_port = ssl_mitm(host, port)
req.unparsed_uri = "127.0.0.1:#{mitm_port}"
super
end
end
s = SSLMITM.new(
:Port => 9999,
:HTTPVersion => "1.0",
:MaxClients => 1,
:ServerSoftware => "MalignProxy/0.0.1",
:RequestCallback => Proc.new { |req,res|
# Called on request data
$count += 1
puts "\n--- #{$count} #{'-'*40}"
if $verbose
puts "sslmitm.RequestCallback"
puts " #{req.request_line}"
end
meth, url, ver = req.request_line.split(" ")
unless meth == 'CONNECT'
$plugins.each { |plug|
plug.request(req.request_line, req.header, req.body);
}
end
},
:ProxyContentHandler => Proc.new { |req,res|
# Called for response data
if $verbose
puts "sslmitm.ProxyContentHandler"
puts " #{req.request_line}"
end
meth, url, ver = req.request_line.split(" ")
unless meth == 'CONNECT'
$plugins.each { |plug|
plug.response(res.status_line, res.header, res.body);
}
end
}
);
trap("INT"){ s.shutdown }
s.start