rmitm 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []