ronin-post_ex 0.1.0.beta1

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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.document +6 -0
  3. data/.github/workflows/ruby.yml +31 -0
  4. data/.gitignore +13 -0
  5. data/.rspec +1 -0
  6. data/.ruby-version +1 -0
  7. data/.yardopts +1 -0
  8. data/API_SPEC.md +235 -0
  9. data/COPYING.txt +165 -0
  10. data/ChangeLog.md +23 -0
  11. data/Gemfile +36 -0
  12. data/README.md +245 -0
  13. data/Rakefile +34 -0
  14. data/examples/bind_shell.rb +19 -0
  15. data/gemspec.yml +25 -0
  16. data/lib/ronin/post_ex/cli/shell_shell.rb +66 -0
  17. data/lib/ronin/post_ex/cli/system_shell.rb +811 -0
  18. data/lib/ronin/post_ex/remote_dir.rb +190 -0
  19. data/lib/ronin/post_ex/remote_file/stat.rb +174 -0
  20. data/lib/ronin/post_ex/remote_file.rb +417 -0
  21. data/lib/ronin/post_ex/remote_process.rb +170 -0
  22. data/lib/ronin/post_ex/resource.rb +144 -0
  23. data/lib/ronin/post_ex/sessions/bind_shell.rb +60 -0
  24. data/lib/ronin/post_ex/sessions/remote_shell_session.rb +48 -0
  25. data/lib/ronin/post_ex/sessions/reverse_shell.rb +67 -0
  26. data/lib/ronin/post_ex/sessions/rpc_session.rb +779 -0
  27. data/lib/ronin/post_ex/sessions/session.rb +73 -0
  28. data/lib/ronin/post_ex/sessions/shell_session.rb +618 -0
  29. data/lib/ronin/post_ex/system/fs.rb +650 -0
  30. data/lib/ronin/post_ex/system/process.rb +422 -0
  31. data/lib/ronin/post_ex/system/shell.rb +1037 -0
  32. data/lib/ronin/post_ex/system.rb +191 -0
  33. data/lib/ronin/post_ex/version.rb +26 -0
  34. data/lib/ronin/post_ex.rb +22 -0
  35. data/ronin-post_ex.gemspec +61 -0
  36. data/spec/sessions/bind_shell_spec.rb +31 -0
  37. data/spec/sessions/remote_shell_session_spec.rb +28 -0
  38. data/spec/sessions/reverse_shell_spec.rb +49 -0
  39. data/spec/sessions/rpc_session_spec.rb +500 -0
  40. data/spec/sessions/session_spec.rb +61 -0
  41. data/spec/sessions/shell_session_spec.rb +482 -0
  42. data/spec/spec_helper.rb +9 -0
  43. data/spec/system_spec.rb +66 -0
  44. metadata +155 -0
@@ -0,0 +1,650 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-post_ex - a Ruby API for Post-Exploitation.
4
+ #
5
+ # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-post_ex is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-post_ex is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-post_ex. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/post_ex/resource'
22
+ require 'ronin/post_ex/remote_file'
23
+ require 'ronin/post_ex/remote_file/stat'
24
+ require 'ronin/post_ex/remote_dir'
25
+
26
+ require 'hexdump'
27
+
28
+ module Ronin
29
+ module PostEx
30
+ class System < Resource
31
+ #
32
+ # Provides access to a system's a File System (FS).
33
+ #
34
+ # # Supported API Methods
35
+ #
36
+ # The File System resource uses the following post-exploitation methods,
37
+ # defined by the {#session} object:
38
+ #
39
+ # * `fs_getcwd() -> String`
40
+ # * `fs_chdir(path : String)`
41
+ # * `fs_readlink(path : String) -> String`
42
+ # * `fs_readdir(path : String) -> Array[String]`
43
+ # * `fs_glob(pattern : String) -> Array[String]`
44
+ # * `fs_mktemp(basename : String) -> String`
45
+ # * `fs_mkdir(new_path : String)`
46
+ # * `fs_copy(src : String, dest : String)`
47
+ # * `fs_unlink(path : String)`
48
+ # * `fs_rmdir(path : String)`
49
+ # * `fs_move(src : String, dest : String)`
50
+ # * `fs_link(src : String, dest : String)`
51
+ # * `fs_chgrp(group : String, path : String)`
52
+ # * `fs_chown(user : String, path : String)`
53
+ # * `fs_chmod(mode : Integer, path : String)`
54
+ # * `fs_stat(path : String) => Hash[Symbol, Object] | nil`
55
+ #
56
+ class FS < Resource
57
+
58
+ #
59
+ # Gets the current working directory.
60
+ #
61
+ # @return [String]
62
+ # The path of the current working directory.
63
+ #
64
+ # @note
65
+ # May call the `fs_getcwd` method, if defined by the {#session}
66
+ # object.
67
+ #
68
+ def getcwd
69
+ if @session.respond_to?(:fs_getcwd)
70
+ @cwd = @session.fs_getcwd
71
+ end
72
+
73
+ return @cwd
74
+ end
75
+ resource_method :getcwd, [:fs_getcwd]
76
+
77
+ alias getwd getcwd
78
+ alias pwd getcwd
79
+
80
+ #
81
+ # Changes the current working directory.
82
+ #
83
+ # @param [String] path
84
+ # The path to use as the new current working directory.
85
+ #
86
+ # @return [String]
87
+ # The new current working directory.
88
+ #
89
+ # @note
90
+ # May call the `fs_chdir` method, if defined by the {#session}
91
+ # object.
92
+ #
93
+ def chdir(path)
94
+ path = expand_path(path)
95
+ old_cwd = @cwd
96
+
97
+ @cwd = if @session.respond_to?(:fs_chdir)
98
+ @session.fs_chdir(path)
99
+ else
100
+ path
101
+ end
102
+
103
+ if block_given?
104
+ yield @cwd
105
+ chdir(old_cwd)
106
+ end
107
+
108
+ return @cwd
109
+ end
110
+ resource_method :chdir
111
+
112
+ #
113
+ # Joins the path with the current working directory.
114
+ #
115
+ # @param [String] path
116
+ # The path to join.
117
+ #
118
+ # @return [String]
119
+ # The absolute path.
120
+ #
121
+ def expand_path(path)
122
+ if (@cwd && path[0,1] != '/')
123
+ ::File.expand_path(::File.join(@cwd,path))
124
+ else
125
+ path
126
+ end
127
+ end
128
+
129
+ #
130
+ # Reads the full contents of a file.
131
+ #
132
+ # @param [String] path
133
+ # The path to the file.
134
+ #
135
+ # @return [String]
136
+ # The contents of the file.
137
+ #
138
+ # @note
139
+ # Requires the `fs_readfile` method be defined by the {#session}
140
+ # object.
141
+ #
142
+ def readfile(path)
143
+ @session.fs_readfile(path)
144
+ end
145
+ resource_method :readfile, [:fs_readfile]
146
+
147
+ #
148
+ # Reads the destination of a link.
149
+ #
150
+ # @param [String] path
151
+ # The path to the link.
152
+ #
153
+ # @return [String]
154
+ # The destination of the link.
155
+ #
156
+ # @note
157
+ # Requires the `fs_readlink` method be defined by the {#session}
158
+ # object.
159
+ #
160
+ def readlink(path)
161
+ @session.fs_readlink(path)
162
+ end
163
+ resource_method :readlink, [:fs_readlink]
164
+
165
+ #
166
+ # Opens a directory for reading.
167
+ #
168
+ # @param [String] path
169
+ # The path to the directory.
170
+ #
171
+ # @return [RemoteDir]
172
+ # The opened directory.
173
+ #
174
+ # @note
175
+ # Requires the `fs_readdir` method be defined by the {#session}
176
+ # object.
177
+ #
178
+ def readdir(path)
179
+ path = expand_path(path)
180
+ entries = @session.fs_readdir(path)
181
+
182
+ return RemoteDir.new(path,entries)
183
+ end
184
+ resource_method :readdir, [:fs_readdir]
185
+
186
+ #
187
+ # Searches the file-system for matching paths.
188
+ #
189
+ # @param [String] pattern
190
+ # A path-glob pattern.
191
+ #
192
+ # @yield [path]
193
+ # The given block, will be passed each matching path.
194
+ #
195
+ # @return [String] path
196
+ # A path in the file-system that matches the pattern.
197
+ #
198
+ # @return [Array<String>]
199
+ # If no block is given, the matching paths will be returned.
200
+ #
201
+ # @example
202
+ # exploit.fs.glob('*.txt')
203
+ # # => [...]
204
+ #
205
+ # @example
206
+ # exploit.fs.glob('**/*.xml') do |path|
207
+ # # ...
208
+ # end
209
+ #
210
+ # @note
211
+ # Requires the `fs_glob` method be defined by the {#session} object.
212
+ #
213
+ def glob(pattern,&block)
214
+ path = expand_path(pattern)
215
+ paths = @session.fs_glob(pattern)
216
+
217
+ paths.each(&block) if block
218
+ return paths
219
+ end
220
+ resource_method :glob, [:fs_glob]
221
+
222
+ #
223
+ # Opens a file for reading.
224
+ #
225
+ # @param [String] path
226
+ # The path to file.
227
+ #
228
+ # @param [String] mode
229
+ # The mode to open the file in.
230
+ #
231
+ # @yield [file]
232
+ # If a block is given, it will be passed the newly opened file.
233
+ # After the block has returned, the file will be closed and
234
+ # `nil` will be returned.
235
+ #
236
+ # @yieldparam [RemoteFile] file
237
+ # The temporarily opened remote file.
238
+ #
239
+ # @return [RemoteFile, nil]
240
+ # If no block was given, then the newly opened remote file will be
241
+ # returned.
242
+ #
243
+ # @see RemoteFile.open
244
+ #
245
+ def open(path,mode='r',&block)
246
+ RemoteFile.open(@session,expand_path(path),mode,&block)
247
+ end
248
+ resource_method :open
249
+
250
+ #
251
+ # Hexdumps the contents of a file.
252
+ #
253
+ # @param [String] path
254
+ # The path of the file.
255
+ #
256
+ # @param [IO] output
257
+ # The output stream to write the hexdump to.
258
+ #
259
+ # @return [nil]
260
+ #
261
+ def hexdump(path,output=STDOUT)
262
+ open(path) { |file| Hexdump.dump(file,output: output) }
263
+ end
264
+ resource_method :hexdump, [:fs_read]
265
+
266
+ #
267
+ # Writes data to a file.
268
+ #
269
+ # @param [String] path
270
+ # The path to the file.
271
+ #
272
+ # @param [String] data
273
+ # The data to write.
274
+ #
275
+ # @return [nil]
276
+ #
277
+ def write(path,data)
278
+ open(path) { |file| file.write(data) }
279
+ end
280
+ resource_method :write, [:fs_write]
281
+
282
+ #
283
+ # Touches a file.
284
+ #
285
+ # @param [String] path
286
+ # The path of the file.
287
+ #
288
+ # @return [nil]
289
+ #
290
+ def touch(path)
291
+ open(path) { |file| file << '' }
292
+ end
293
+ resource_method :touch, [:fs_write]
294
+
295
+ #
296
+ # Opens a tempfile.
297
+ #
298
+ # @param [String] basename
299
+ # The base-name to use in the tempfile.
300
+ #
301
+ # @yield [tempfile]
302
+ # The given block will be passed the newly opened tempfile.
303
+ # After the block has returned, the tempfile will be closed
304
+ # and `nil` will be returned.
305
+ #
306
+ # @yieldparam [RemoteFile] tempfile
307
+ # The temporarily opened tempfile.
308
+ #
309
+ # @return [RemoteFile, nil]
310
+ # The newly opened tempfile.
311
+ #
312
+ # @note
313
+ # Requires the `fs_mktemp` method be defined by the {#session} object.
314
+ #
315
+ def tmpfile(basename,&block)
316
+ open(@session.fs_mktemp(basename),&block)
317
+ end
318
+ resource_method :tmpfile, [:fs_mktemp]
319
+
320
+ #
321
+ # Creates a directory.
322
+ #
323
+ # @param [String] path
324
+ # The path of the directory.
325
+ #
326
+ # @return [true]
327
+ # Specifies that the directory was successfully created.
328
+ #
329
+ # @note
330
+ # Requires the `fs_mkdir` method be defined by the {#session} object.
331
+ #
332
+ def mkdir(path)
333
+ @session.fs_mkdir(path)
334
+ return true
335
+ end
336
+ resource_method :mkdir, [:fs_mkdir]
337
+
338
+ #
339
+ # Copies a file.
340
+ #
341
+ # @param [String] path
342
+ # The path of the file to copy.
343
+ #
344
+ # @param [String] new_path
345
+ # The destination path to copy to.
346
+ #
347
+ # @return [true]
348
+ # Specifies that the file was successfully copied.
349
+ #
350
+ # @note
351
+ # Requires the `fs_copy` method be defined by the {#session} object.
352
+ #
353
+ def copy(path,new_path)
354
+ @session.fs_copy(expand_path(path),expand_path(new_path))
355
+ return true
356
+ end
357
+ resource_method :copy, [:fs_copy]
358
+
359
+ #
360
+ # Unlinks a file.
361
+ #
362
+ # @param [String] path
363
+ # The path of the file.
364
+ #
365
+ # @return [true]
366
+ # Specifies that the file was successfully removed.
367
+ #
368
+ # @note
369
+ # Requires the `fs_unlink` method be defined by the {#session} object.
370
+ #
371
+ def unlink(path)
372
+ @session.fs_unlink(expand_path(path))
373
+ return true
374
+ end
375
+ resource_method :unlink, [:fs_unlink]
376
+
377
+ alias rm unlink
378
+
379
+ #
380
+ # Removes a directory.
381
+ #
382
+ # @param [String] path
383
+ # The path of the directory.
384
+ #
385
+ # @return [true]
386
+ # Specifies that the directory was successfully removed.
387
+ #
388
+ # @note
389
+ # Requires the `fs_rmdir` method be defined by the {#session} object.
390
+ #
391
+ def rmdir(path)
392
+ @session.fs_rmdir(expand_path(path))
393
+ return true
394
+ end
395
+ resource_method :rmdir, [:fs_rmdir]
396
+
397
+ #
398
+ # Moves a file or directory.
399
+ #
400
+ # @param [String] path
401
+ # The path of the file or directory to be moved.
402
+ #
403
+ # @param [String] new_path
404
+ # The destination path for the file or directory to be moved to.
405
+ #
406
+ # @return [true]
407
+ # Specifies that the file or directory was successfully moved.
408
+ #
409
+ # @note
410
+ # Requires the `fs_move` method be defined by the {#session} object.
411
+ #
412
+ def move(path,new_path)
413
+ @session.fs_move(expand_path(path),expand_path(new_path))
414
+ return true
415
+ end
416
+ resource_method :move, [:fs_move]
417
+
418
+ alias rename move
419
+
420
+ #
421
+ # Creates a symbolic link.
422
+ #
423
+ # @param [String] path
424
+ # The path that the link will point to.
425
+ #
426
+ # @param [String] new_path
427
+ # The path of the link.
428
+ #
429
+ # @return [true]
430
+ # Specifies that the symbolic link was successfully created.
431
+ #
432
+ # @note
433
+ # Requires the `fs_link` method be defined by the {#session} object.
434
+ #
435
+ def link(path,new_path)
436
+ @session.fs_link(path,new_path)
437
+ return true
438
+ end
439
+ resource_method :link, [:fs_link]
440
+
441
+ #
442
+ # Changes ownership of a file or directory.
443
+ #
444
+ # @param [String, (String,String)] owner
445
+ # the user and/or group that will own the file or directory.
446
+ #
447
+ # @param [String] path
448
+ # The path of the file or directory.
449
+ #
450
+ # @return [true]
451
+ # Specifies that the ownership was successfully changed.
452
+ #
453
+ # @example
454
+ # exploit.fs.chown('www', 'one.html')
455
+ #
456
+ # @example
457
+ # exploit.fs.chown(['alice', 'users'], 'one.html')
458
+ #
459
+ # @note
460
+ # Requires the `fs_chown` method be defined by the {#session} object.
461
+ #
462
+ def chown(owner,path)
463
+ user, group = owner
464
+
465
+ chgrp(group,path) if group
466
+
467
+ @session.fs_chown(user,expand_path(path))
468
+ return true
469
+ end
470
+ resource_method :chown, [:fs_chown]
471
+
472
+ #
473
+ # Changes group ownership on one or more files or directories.
474
+ #
475
+ # @param [String] group
476
+ # The group that will own the file or directory.
477
+ #
478
+ # @param [String] path
479
+ # The path of the file or directory.
480
+ #
481
+ # @return [true]
482
+ # Specifies that the group ownership was successfully changed.
483
+ #
484
+ # @example
485
+ # exploit.fs.chgrp('www', 'one.html')
486
+ #
487
+ # @note
488
+ # Requires the `fs_chgrp` method be defined by the {#session} object.
489
+ #
490
+ def chgrp(group,path)
491
+ @session.fs_chgrp(group,expand_path(path))
492
+ return true
493
+ end
494
+ resource_method :chgrp, [:fs_chgrp]
495
+
496
+ #
497
+ # Changes permissions on one or more file or directorie.
498
+ #
499
+ # @param [Integer] mode
500
+ # The new mode for the file or directory.
501
+ #
502
+ # @param [String] path
503
+ # The path of the file or directory.
504
+ #
505
+ # @return [true]
506
+ # Specifies that the permissions were successfully changed.
507
+ #
508
+ # @example
509
+ # exploit.fs.chmod(0665, 'one.html')
510
+ #
511
+ # @note
512
+ # Requires the `fs_chmod` method be defined by the {#session} object.
513
+ #
514
+ def chmod(mode,path)
515
+ @session.fs_chmod(mode,expand_path(path))
516
+ return true
517
+ end
518
+ resource_method :chmod, [:fs_chmod]
519
+
520
+ #
521
+ # Gathers statistics on a file or directory.
522
+ #
523
+ # @param [String] path
524
+ # The path of the file or directory.
525
+ #
526
+ # @return [RemoteFile::Stat]
527
+ # The statistics on the file or directory.
528
+ #
529
+ # @see RemoteFile::Stat#initialize
530
+ #
531
+ def stat(path)
532
+ RemoteFile::Stat.new(@session,expand_path(path))
533
+ end
534
+ resource_method :stat, [:fs_stat]
535
+
536
+ #
537
+ # Tests whether a file or directory exists.
538
+ #
539
+ # @param [String] path
540
+ # The path of the file or directory in question.
541
+ #
542
+ # @return [Boolean]
543
+ # Specifies whether the file or directory exists.
544
+ #
545
+ def exists?(path)
546
+ begin
547
+ stat(path)
548
+ return true
549
+ rescue Errno::ENOENT
550
+ return false
551
+ end
552
+ end
553
+ resource_method :exists?, [:fs_stat]
554
+
555
+ #
556
+ # Tests whether a file exists.
557
+ #
558
+ # @param [String] path
559
+ # The path of the file in question.
560
+ #
561
+ # @return [Boolean]
562
+ # Specifies whether the file exists.
563
+ #
564
+ def file?(path)
565
+ begin
566
+ stat(path).file?
567
+ rescue Errno::ENOENT
568
+ return false
569
+ end
570
+ end
571
+ resource_method :file?, [:fs_stat]
572
+
573
+ #
574
+ # Tests whether a directory exists.
575
+ #
576
+ # @param [String] path
577
+ # The path of the directory in question.
578
+ #
579
+ # @return [Boolean]
580
+ # Specifies whether the directory exists.
581
+ #
582
+ def directory?(path)
583
+ begin
584
+ stat(path).directory?
585
+ rescue Errno::ENOENT
586
+ return false
587
+ end
588
+ end
589
+ resource_method :directory?, [:fs_stat]
590
+
591
+ #
592
+ # Tests whether a FIFO pipe exists.
593
+ #
594
+ # @param [String] path
595
+ # The path of the FIFO pipe in question.
596
+ #
597
+ # @return [Boolean]
598
+ # Specifies whether the FIFO pipe exists.
599
+ #
600
+ def pipe?(path)
601
+ begin
602
+ stat(path).pipe?
603
+ rescue Errno::ENOENT
604
+ return false
605
+ end
606
+ end
607
+ resource_method :pipe?, [:fs_stat]
608
+
609
+ #
610
+ # Tests whether a UNIX socket exists.
611
+ #
612
+ # @param [String] path
613
+ # The path of the UNIX socket in question.
614
+ #
615
+ # @return [Boolean]
616
+ # Specifies whether the UNIX socket exists.
617
+ #
618
+ def socket?(path)
619
+ begin
620
+ stat(path).socket?
621
+ rescue Errno::ENOENT
622
+ return false
623
+ end
624
+ end
625
+ resource_method :socket?, [:fs_stat]
626
+
627
+ #
628
+ # Tests whether a file is empty.
629
+ #
630
+ # @param [String] path
631
+ # The path of the file in question.
632
+ #
633
+ # @return [Boolean]
634
+ # Specifies whether the file is empty.
635
+ #
636
+ def zero?(path)
637
+ begin
638
+ stat(path).zero?
639
+ rescue Errno::ENOENT
640
+ return false
641
+ end
642
+ end
643
+ resource_method :zero?, [:fs_stat]
644
+
645
+ alias empty? zero?
646
+
647
+ end
648
+ end
649
+ end
650
+ end