Skip to content

Commit d3a759d

Browse files
committed
Make changes for initial linux railgun support
1 parent 89e8125 commit d3a759d

File tree

5 files changed

+153
-33
lines changed

5 files changed

+153
-33
lines changed

lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def process_function_call(function, args, client)
221221
# it's not a pointer (LPVOID is a pointer but is not backed by railgun memory, ala PBLOB)
222222
buffer = [0].pack(native)
223223
case param_desc[0]
224-
when "LPVOID", "HANDLE"
224+
when "LPVOID", "HANDLE", "SIZE_T"
225225
num = param_to_number(args[param_idx])
226226
buffer += [num].pack(native)
227227
when "DWORD"

lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_function.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ class DLLFunction
4040
"DWORD" => ["in", "return"],
4141
"WORD" => ["in", "return"],
4242
"BYTE" => ["in", "return"],
43-
"LPVOID" => ["in", "return"], # sf: for specifying a memory address (e.g. VirtualAlloc/HeapAlloc/...) where we dont want ot back it up with actuall mem ala PBLOB
43+
"LPVOID" => ["in", "return"], # sf: for specifying a memory address (e.g. VirtualAlloc/HeapAlloc/...) where we don't want to back it up with actual mem ala PBLOB
4444
"HANDLE" => ["in", "return"],
45+
"SIZE_T" => ["in", "return"],
4546
"PDWORD" => ["in", "out", "inout"], # todo: support for functions that return pointers to strings
4647
"PWCHAR" => ["in", "out", "inout"],
4748
"PCHAR" => ["in", "out", "inout"],

lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def call(functions)
170170
# it's not a pointer
171171
buffer = [0].pack(@native)
172172
case param_desc[0]
173-
when "LPVOID", "HANDLE"
173+
when "LPVOID", "HANDLE", "SIZE_T"
174174
num = param_to_number(args[param_idx])
175175
buffer += [num].pack(@native)
176176
when "DWORD"
@@ -209,7 +209,7 @@ def call(functions)
209209
group.add_tlv(TLV_TYPE_RAILGUN_STACKBLOB, literal_pairs_blob)
210210
group.add_tlv(TLV_TYPE_RAILGUN_BUFFERBLOB_IN, in_only_buffer)
211211
group.add_tlv(TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT, inout_buffer)
212-
group.add_tlv(TLV_TYPE_RAILGUN_DLLNAME, dll_name )
212+
group.add_tlv(TLV_TYPE_RAILGUN_DLLNAME, dll_host.dll_path)
213213
group.add_tlv(TLV_TYPE_RAILGUN_FUNCNAME, function.windows_name)
214214
request.tlvs << group
215215

lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,13 @@ def add_function(dll_name, function_name, return_type, params, windows_name=nil,
212212
# Raises an exception if a dll with the given name has already been
213213
# defined.
214214
#
215-
def add_dll(dll_name, windows_name=dll_name)
215+
def add_dll(dll_name, remote_name=dll_name)
216216

217217
if dlls.has_key? dll_name
218218
raise "A DLL of name #{dll_name} has already been loaded."
219219
end
220220

221-
dlls[dll_name] = DLL.new(windows_name, constant_manager)
221+
dlls[dll_name] = DLL.new(remote_name, constant_manager)
222222
end
223223

224224

test/modules/post/test/railgun.rb

Lines changed: 146 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,139 @@ class MetasploitModule < Msf::Post
1111

1212
def initialize(info={})
1313
super( update_info( info,
14-
'Name' => 'Railgun API Tests',
15-
'Description' => %q{ This module will test railgun api functions},
16-
'License' => MSF_LICENSE,
17-
'Author' => [ 'Spencer McIntyre'],
18-
'Platform' => [ 'windows' ]
19-
))
14+
'Name' => 'Railgun API Tests',
15+
'Description' => %q{ This module will test railgun api functions },
16+
'License' => MSF_LICENSE,
17+
'Author' => [ 'Spencer McIntyre' ],
18+
'Platform' => [ 'linux', 'windows' ]
19+
))
2020
end
2121

22-
def test_api_function_calls
22+
def init_railgun_defs
23+
unless session.railgun.dlls.has_key?('libc')
24+
session.railgun.add_dll('libc', 'libc.so.6')
25+
end
26+
session.railgun.add_function(
27+
'libc',
28+
'calloc',
29+
'LPVOID',
30+
[
31+
['SIZE_T', 'nmemb', 'in'],
32+
['SIZE_T', 'size', 'in']
33+
],
34+
nil,
35+
'cdecl'
36+
)
37+
session.railgun.add_function(
38+
'libc',
39+
'getpid',
40+
'DWORD',
41+
[],
42+
nil,
43+
'cdecl'
44+
)
45+
session.railgun.add_function(
46+
'libc',
47+
'inet_ntop',
48+
'LPVOID',
49+
[
50+
['DWORD', 'af', 'in'],
51+
['PBLOB', 'src', 'in'],
52+
['PBLOB', 'dst', 'out'],
53+
['DWORD', 'size', 'in']
54+
],
55+
nil,
56+
'cdecl'
57+
)
58+
session.railgun.add_function(
59+
'libc',
60+
'malloc',
61+
'LPVOID',
62+
[['SIZE_T', 'size', 'in']],
63+
nil,
64+
'cdecl'
65+
)
66+
session.railgun.add_function(
67+
'libc',
68+
'memfrob',
69+
'LPVOID',
70+
[
71+
['PBLOB', 'mem', 'inout'],
72+
['SIZE_T', 'length', 'in']
73+
],
74+
nil,
75+
'cdecl'
76+
)
77+
end
2378

79+
def test_api_function_calls_linux
80+
return unless session.platform == 'linux'
81+
init_railgun_defs
82+
buffer = nil
83+
buffer_size = 128
84+
buffer_value = nil
85+
it "Should include error information in the results" do
86+
ret = true
87+
result = session.railgun.libc.malloc(128)
88+
ret &&= result['GetLastError'] == 0
89+
ret &&= result['ErrorMessage'].is_a? String
90+
end
91+
92+
it "Should support functions with no parameters" do
93+
ret = true
94+
result = session.railgun.libc.getpid()
95+
ret &&= result['GetLastError'] == 0
96+
ret &&= result['return'] == session.sys.process.getpid
97+
end
98+
99+
it "Should support functions with literal parameters" do
100+
ret = true
101+
result = session.railgun.libc.calloc(buffer_size, 1)
102+
ret &&= result['GetLastError'] == 0
103+
buffer = result['return']
104+
ret &&= buffer != 0
105+
end
106+
107+
it "Should support functions with in/out/inout parameter types" do
108+
ret = true
109+
# first test in/out parameter types
110+
result = session.railgun.libc.inet_ntop(2, "\x0a\x00\x00\x01", 128, 128)
111+
ret &&= result['GetLastError'] == 0
112+
ret &&= result['return'] != 0
113+
ret &&= result['dst'][0...8] == '10.0.0.1'
114+
# then test the inout parameter type
115+
result = session.railgun.libc.memfrob('metasploit', 10)
116+
ret &&= result['GetLastError'] == 0
117+
ret &&= result['return'] != 0
118+
ret &&= result['mem'] == 'GO^KYZFEC^'
119+
end
120+
121+
it "Should support calling multiple functions at once" do
122+
ret = true
123+
multi_rail = [
124+
['libc', 'getpid', []],
125+
['libc', 'memfrob', ['metasploit', 10]]
126+
]
127+
results = session.railgun.multi(multi_rail)
128+
ret &&= results.length == multi_rail.length
129+
ret &&= results[0]['return'] == session.sys.process.getpid
130+
ret &&= results[1]['mem'] == 'GO^KYZFEC^'
131+
end
132+
133+
it "Should support writing memory" do
134+
ret = true
135+
buffer_value = Rex::Text.rand_text_alphanumeric(buffer_size)
136+
ret &&= session.railgun.memwrite(buffer, buffer_value, buffer_size)
137+
end
138+
139+
it "Should support reading memory" do
140+
ret = true
141+
ret &&= session.railgun.memread(buffer, buffer_size) == buffer_value
142+
end
143+
end
144+
145+
def test_api_function_calls_windows
146+
return unless session.platform == 'windows'
24147
it "Should include error information in the results" do
25148
ret = true
26149
result = session.railgun.kernel32.GetCurrentProcess()
@@ -70,43 +193,39 @@ def test_api_function_calls
70193
ret &&= results[2]['return'] == session.sys.process.getpid
71194
end
72195

73-
it "Should support reading memory" do
74-
ret = true
75-
result = client.railgun.kernel32.GetModuleHandleA('kernel32')
76-
ret &&= result['GetLastError'] == 0
77-
ret &&= result['return'] != 0
78-
return false unless ret
79-
80-
handle = result['return']
81-
mz_header = client.railgun.memread(handle, 4)
82-
ret &&= mz_header == "MZ\x90\x00"
83-
end
84-
85196
it "Should support writing memory" do
86197
ret = true
87-
result = client.railgun.kernel32.GetProcessHeap()
198+
result = session.railgun.kernel32.GetProcessHeap()
88199
ret &&= result['GetLastError'] == 0
89200
ret &&= result['return'] != 0
90201
return false unless ret
91202

92203
buffer_size = 32
93204
handle = result['return']
94-
result = client.railgun.kernel32.HeapAlloc(handle, 0, buffer_size)
205+
result = session.railgun.kernel32.HeapAlloc(handle, 0, buffer_size)
95206
ret &&= result['GetLastError'] == 0
96207
ret &&= result['return'] != 0
97208
return false unless ret
98209

99210
buffer_value = Rex::Text.rand_text_alphanumeric(buffer_size)
100211
buffer = result['return']
101-
ret &&= client.railgun.memwrite(buffer, buffer_value, buffer_size)
102-
ret &&= client.railgun.memread(buffer, buffer_size) == buffer_value
212+
ret &&= session.railgun.memwrite(buffer, buffer_value, buffer_size)
213+
ret &&= session.railgun.memread(buffer, buffer_size) == buffer_value
103214

104-
client.railgun.kernel32.HeapFree(handle, 0, buffer)
215+
session.railgun.kernel32.HeapFree(handle, 0, buffer)
105216
ret
106217
end
107218

108-
end
219+
it "Should support reading memory" do
220+
ret = true
221+
result = session.railgun.kernel32.GetModuleHandleA('kernel32')
222+
ret &&= result['GetLastError'] == 0
223+
ret &&= result['return'] != 0
224+
return false unless ret
109225

226+
handle = result['return']
227+
mz_header = session.railgun.memread(handle, 4)
228+
ret &&= mz_header == "MZ\x90\x00"
229+
end
230+
end
110231
end
111-
112-

0 commit comments

Comments
 (0)