rmitm 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MDdkN2Y1MDM1NzdiMjU3NmY0Nzg1ZDkzOWIwMDZkODI5MTUxMjUzYQ==
5
+ data.tar.gz: !binary |-
6
+ ZWMwNmQxM2NhYjMwYjc5M2M0MDVhMTQ4MTczMjI5NGY1ZjI2OTdjMg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZTI3N2UxN2E3NzMyM2NkODhkMGMwYjljNGI5NjUxNzBlMzBiZmRlNTRhYTVi
10
+ MGUxZWUxZDI3N2UxYzRkYzYzYmRmNjMzNjRlNTg2NmNmZjc5Mjg1NDk0MjY0
11
+ NjU2NTIwYmE1ZWZlNmMwYWMwYzNjNTM0MGNkYmM1MjI2NTlhOTc=
12
+ data.tar.gz: !binary |-
13
+ ODBiMjUyNDNhNGRmMDU4MDkyYzNjNWU2YWJiMzgzZTNiZWI5NzFkMjlkNzYx
14
+ NzU4ZTM5MmU3NTFkMDAxYmI2ZDRkNThhYjk5MjIyN2EwN2NhZTc4YjkxYjBk
15
+ MzU1NDFjMjA5NGE0MDlkMzIxNDA3NTVhZDhhZGU1MDdiZTU0Njk=
data/bin/readFromFlow ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python
2
+
3
+ from libmproxy import flow
4
+ import json, sys, getopt
5
+
6
+ def parse_args(argv):
7
+ try:
8
+ opts, args = getopt.getopt(argv, "hqs", ["help", "request", "response"])
9
+ except getopt.GetoptError as err:
10
+ print "Error {0}".format(err.msg)
11
+ usage()
12
+
13
+ if len(args) == 0:
14
+ print "FILENAME not specified"
15
+ usage()
16
+ elif len(args) > 1:
17
+ print "Only one FILENAME can be specified"
18
+ usage()
19
+ else:
20
+ global f
21
+ f = args[0]
22
+
23
+ for opt, arg in opts:
24
+ global request, response
25
+ if opt in ('-q', '--request'):
26
+ response = False
27
+ elif opt in ('-s', '--response'):
28
+ request = False
29
+ elif opt in ('-h', '--help'):
30
+ usage()
31
+
32
+ if not (response or request):
33
+ response = True
34
+ request = True
35
+ # END parse_args
36
+
37
+ def usage():
38
+ print "--"
39
+ print "readFromFlow - read netlib flow from an mitmdump output file"
40
+ print ""
41
+ print "readFromFlow [OPTIONS] FILENAME"
42
+ print "-h, --help"
43
+ print "\tdisplay this help and exit"
44
+ print "-q, --request"
45
+ print "\tread requests only"
46
+ print "-s, --response"
47
+ print "\tread responses only"
48
+ print ""
49
+ print "Only one FILENAME can be specified. By default both requests and responses are read"
50
+ print "--"
51
+ sys.exit(2)
52
+ # END usage
53
+
54
+ def readFlowFile():
55
+ with open(f, "rb") as ff:
56
+ freader = flow.FlowReader(ff)
57
+ try:
58
+ for d in freader.stream():
59
+ print "START {0}".format(d)
60
+ print "{"
61
+ if request:
62
+ sys.stdout.write("\"request\" : ")
63
+ dump(d._get_state()['request'])
64
+ if response:
65
+ if request:
66
+ print ","
67
+ sys.stdout.write("\"response\" : ")
68
+ dump(d._get_state()['response'])
69
+ print "\n}"
70
+ print "END {0}".format(d)
71
+ except flow.FlowReadError, v:
72
+ print "FlowReadError reading from file {0}".format(f)
73
+ # END readFlowFile
74
+
75
+ def dump(content):
76
+ json.dump(content, sys.stdout, indent=4, ensure_ascii=False)
77
+ # END dump
78
+
79
+ if __name__ == '__main__':
80
+ global request, response
81
+ request = True
82
+ response = True
83
+ parse_args(sys.argv[1:])
84
+ readFlowFile()
85
+
@@ -0,0 +1,18 @@
1
+ import sys, getopt, re
2
+
3
+ def response(context, flow):
4
+ if path.search(flow.request.path) is not None:
5
+ flow.response.code = 404
6
+ flow.response.msg = "Not Found"
7
+ flow.response.content = "Oooops!!"
8
+
9
+ def start(context, argv):
10
+ try:
11
+ opts, args = getopt.getopt(argv[1:], "p:", [])
12
+ except getopt.GetoptError as err:
13
+ print "Error " + err.msg + " " + err.opt
14
+ sys.exit(2)
15
+ for opt, arg in opts:
16
+ global path
17
+ if opt == '-p':
18
+ path = re.compile(arg)
@@ -0,0 +1,31 @@
1
+ import sys, getopt, re
2
+
3
+ # #########
4
+ # p -> escaped regular expression to match against the request path
5
+ # f -> path to filename to replace contents with
6
+ # #########
7
+
8
+ path = ""
9
+ filename = ""
10
+
11
+ def response(context, flow):
12
+ if path.search(flow.request.path) is not None:
13
+ flow.response.content = contents()
14
+
15
+ def contents():
16
+ with open (filename, "r") as myfile:
17
+ return myfile.read().replace('\n', '')
18
+
19
+ def start(context, argv):
20
+ try:
21
+ opts, args = getopt.getopt(argv[1:], "p:f:", [])
22
+ except getopt.GetoptError as err:
23
+ print "Error " + err.msg + " " + err.opt
24
+ sys.exit(2)
25
+ for opt, arg in opts:
26
+ global path
27
+ global filename
28
+ if opt == '-p':
29
+ path = re.compile(arg)
30
+ elif opt == '-f':
31
+ filename = arg
@@ -0,0 +1,29 @@
1
+ import sys, getopt, re
2
+
3
+ regexp = ""
4
+ replace = ""
5
+ path = ""
6
+
7
+ def response(context, flow):
8
+ # if flow.request.path == path:
9
+ if path.search(flow.request.path) is not None:
10
+ flow.response.content = regexp.sub(replace, flow.response.content, 1)
11
+ flow.response.headers["Content-Length"] = [str(len(flow.response.content))]
12
+
13
+
14
+ def start(context, argv):
15
+ try:
16
+ opts, args = getopt.getopt(argv[1:], "p:x:r:", [])
17
+ except getopt.GetoptError as err:
18
+ print "Error " + err.msg + " " + err.opt
19
+ sys.exit(2)
20
+ for opt, arg in opts:
21
+ global path
22
+ global regexp
23
+ global replace
24
+ if opt == '-p':
25
+ path = re.compile(arg)
26
+ elif opt == '-x':
27
+ regexp = re.compile(arg)
28
+ elif opt == '-r':
29
+ replace = arg
@@ -0,0 +1,2 @@
1
+ def request(context, flow):
2
+ del flow.request.headers['Accept-Encoding']
@@ -0,0 +1,40 @@
1
+ =begin
2
+ RMITM - provides a Ruby interface to mitmdump
3
+ Copyright (C) 2014 Marc Bleeze (marcbleeze<at>gmail<dot>com)
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ require_relative './mitmdump_reader'
20
+ require 'jsonpath'
21
+
22
+ class MitmFlowArray < Array
23
+
24
+ def self.from_file(filename)
25
+ MitmFlowArray.new(MitmdumpReader.new(filename).get_flows_from_file)
26
+ end
27
+
28
+ def values_by_jpath(jpath, first=true)
29
+ self.map { |f| first ? JsonPath.new(jpath).first(f) : JsonPath.new(jpath).on(f) }
30
+ end
31
+
32
+ def filter(conditions)
33
+ result = self
34
+ conditions.each do |jpath, re|
35
+ result = result.select { |flow| JsonPath.new(jpath).first(flow).encode!('ISO-8859-1', 'UTF-8', :invalid => :replace, :undef => :replace, :replace => "") =~ re }
36
+ end
37
+ MitmFlowArray.new(result)
38
+ end
39
+
40
+ end
data/lib/mitmdump.rb ADDED
@@ -0,0 +1,154 @@
1
+ =begin
2
+ RMITM - provides a Ruby interface to mitmdump
3
+ Copyright (C) 2014 Marc Bleeze (marcbleeze<at>gmail<dot>com)
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ require 'fileutils'
20
+
21
+ class Mitmdump
22
+
23
+ attr_reader :scripts, :params
24
+
25
+ def initialize(name, &block)
26
+ @name = name.to_sym
27
+ @port = 8080
28
+ @output = 'dumps/mitm.dump'
29
+ @scripts = []
30
+ @params = {}
31
+
32
+ instance_eval &block
33
+ end
34
+ ### DSL
35
+ def inherit(name)
36
+ intersect = @params.keys & proxy(name.to_sym).params.keys
37
+ intersect.empty? or
38
+ raise "Duplicate parameters #{intersect} when inheriting proxy '#{name.to_sym}'"
39
+ @scripts = @scripts | proxy(name.to_sym).scripts
40
+ @params.merge! proxy(name.to_sym).params
41
+ end
42
+
43
+ def port(port)
44
+ @port = port
45
+ end
46
+
47
+ def output(filename)
48
+ @output = filename
49
+ end
50
+
51
+ def blacklist(path)
52
+ script "#{script_path}blacklist.py", '-p' => path
53
+ end
54
+
55
+ def map_local(path, args={})
56
+ unless args[:file].nil?
57
+ script "#{script_path}map_local.py", '-p' => path, '-f' => args[:file]
58
+ else
59
+ raise ArgumentError, 'No file name provided for maplocal'
60
+ end
61
+ end
62
+
63
+ def replace(path, args={})
64
+ unless args[:swap].nil? | args[:with].nil?
65
+ script "#{script_path}replace.py", '-p' => path, '-x' => args[:swap], '-r' => args[:with]
66
+ else
67
+ raise ArgumentError, "Expecting arguments ':swap' and ':with' for replace"
68
+ end
69
+ end
70
+
71
+ def strip_encoding
72
+ script "#{script_path}strip_encoding.py"
73
+ end
74
+
75
+ def script(file, args={})
76
+ args.each do |k,v|
77
+ unless (key = v[/%[a-zA-Z_]+/]).nil?
78
+ @params.has_key?(key) or
79
+ raise "Parameter '#{key}' referenced in proxy '#{@name}' but not declared"
80
+ end
81
+ end
82
+ @scripts << [file, args]
83
+ end
84
+
85
+ def param(name)
86
+ !@params.has_key?("%#{name.to_s}") and @params["%#{name.to_s}"] = '' or
87
+ raise "Parameter name '#{name}' already declared"
88
+ end
89
+ ### END DSL
90
+ def start(args={})
91
+ check_params(args)
92
+ if port_available?
93
+ manage_dumpfile
94
+ @pid = Process.spawn command
95
+ Process.detach @pid
96
+ connection_successful? or
97
+ raise "Failed to start mitmdump after 10 seconds\nCOMMAND LINE: <#{command}>"
98
+ else
99
+ raise "Cannot start mitmdump on port #{@port} - port unavailable"
100
+ end
101
+ end
102
+
103
+ def stop
104
+ system("kill -0 #{@pid} >& /dev/null") and (system("kill -9 #{@pid}") or
105
+ raise "Could not stop proxy '#{@name}' (pid: #{@pid})") if @pid
106
+ end
107
+
108
+ def dumpfile
109
+ @output
110
+ end
111
+
112
+ private
113
+
114
+ def port_available?
115
+ `nc -z 127.0.0.1 #{@port} >& /dev/null`
116
+ !$?.success?
117
+ end
118
+
119
+ def command
120
+ cmd = "mitmdump -q -p #{@port} -w #{@output}"
121
+ @scripts.each do | name, opts |
122
+ cmd << " -s \"#{name}"
123
+ opts.each { |k,v| cmd << " '#{k}' '#{interpolate(v)}'" } if opts
124
+ cmd << "\""
125
+ end
126
+ cmd
127
+ end
128
+
129
+ def connection_successful?(timeout=10)
130
+ timeout.times { port_available? and sleep 1 or return true }
131
+ false
132
+ end
133
+
134
+ def manage_dumpfile
135
+ FileUtils.rm_f @output if File.exists?(@output)
136
+ d = File.dirname(@output)
137
+ FileUtils.mkpath(d) unless File.directory?(d)
138
+ end
139
+
140
+ def script_path
141
+ "#{File.expand_path('../../bin/scripts', __FILE__)}/"
142
+ end
143
+
144
+ def check_params(args={})
145
+ @params.keys.each do |k|
146
+ @params[k] = args[k[1..-1]] if args.has_key? k[1..-1] or
147
+ raise "Parameter '#{k}' not specified"
148
+ end
149
+ end
150
+
151
+ def interpolate(str)
152
+ str.gsub(/%[a-zA-Z_]+/, @params)
153
+ end
154
+ end
@@ -0,0 +1,69 @@
1
+ =begin
2
+ RMITM - provides a Ruby interface to mitmdump
3
+ Copyright (C) 2014 Marc Bleeze (marcbleeze<at>gmail<dot>com)
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ require "open3"
20
+ require "json"
21
+
22
+ class MitmdumpReader
23
+
24
+ def initialize(filename)
25
+ @filename = filename
26
+ end
27
+
28
+ def get_requests_from_file
29
+ read_from_flow_dump("-q")
30
+ end
31
+
32
+ def get_responses_from_file
33
+ read_from_flow_dump("-s")
34
+ end
35
+
36
+ private
37
+
38
+ def read_from_flow_dump(options="")
39
+ cmd = "#{File.expand_path('../../bin', __FILE__)}/readFromFlow #{options << ' '}#{@filename}"
40
+ pp cmd if $DEBUG
41
+ Open3.popen3(cmd) do |_, o, e, w|
42
+ @stdout = o.read.encode!('ISO-8859-1', 'UTF-8', :invalid => :replace, :undef => :replace, :replace => "")
43
+ @stderr = e.read
44
+ @stderr == "" ? parse : error
45
+ end
46
+ end
47
+
48
+ def parse
49
+ json_objects = []
50
+ flows.each do |a|
51
+ json_objects << JSON.parse(a)
52
+ # JSONPath doesn't support symbolized names
53
+ # json_objects << JSON.parse(a, :symbolize_names => true)
54
+ end
55
+ json_objects
56
+ end
57
+
58
+ def error
59
+ puts "*"*60, "******* Error returned from readFromFlow python script:", "*"*60, @stderr, "*"*60
60
+ exit 2
61
+ end
62
+
63
+ def flows
64
+ @stdout.scan(/START <libmproxy.flow.Flow instance at [^{]+(.*?)\nEND <libmproxy.flow.Flow instance at /m).map { |e| e[0] }
65
+ end
66
+
67
+ alias_method :get_flows_from_file, :read_from_flow_dump
68
+ public :get_flows_from_file
69
+ end
data/lib/rmitm.rb ADDED
@@ -0,0 +1,35 @@
1
+ =begin
2
+ RMITM - provides a Ruby interface to mitmdump
3
+ Copyright (C) 2014 Marc Bleeze (marcbleeze<at>gmail<dot>com)
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ require 'mitmdump'
20
+ require 'mitmdump_reader'
21
+ require 'mitm_flow_array'
22
+
23
+ def load_proxies(glob)
24
+ Dir.glob(glob).each { |f| load f }
25
+ end
26
+
27
+ def proxy(name)
28
+ $proxies[name.to_sym] or
29
+ raise "Cannot find proxy '#{name}'"
30
+ end
31
+
32
+ def mitmdump(name, &block)
33
+ $proxies ||= {}
34
+ $proxies[name.to_sym] = Mitmdump.new(name, &block)
35
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rmitm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Marc Bleeze
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jsonpath
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: ! " rmitm provides a DSL and useful ruby classes and python scripts
42
+ for using mitmdump\n for automated testing.\n"
43
+ email: marcbleeze@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - bin/readFromFlow
49
+ - bin/scripts/blacklist.py
50
+ - bin/scripts/map_local.py
51
+ - bin/scripts/replace.py
52
+ - bin/scripts/strip_encoding.py
53
+ - lib/mitm_flow_array.rb
54
+ - lib/mitmdump.rb
55
+ - lib/mitmdump_reader.rb
56
+ - lib/rmitm.rb
57
+ homepage: https://github.com/marcyb/rmitm
58
+ licenses:
59
+ - GPL-3.0
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 2.3.0
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: rmitm provides a DSL and useful ruby classes and python scripts for mitmdump
81
+ test_files: []