Skip to content

Commit a328675

Browse files
committed
Add simulated cowsay support to wall
1 parent 8d21a91 commit a328675

File tree

1 file changed

+48
-5
lines changed

1 file changed

+48
-5
lines changed

modules/post/multi/general/wall.rb

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,76 @@ def initialize(info = {})
1616
to send messages to users on the target system.
1717
},
1818
'License' => MSF_LICENSE,
19-
'Author' => [ 'Jon Hart <jon_hart[at]rapid7.com>' ],
19+
'Author' => [
20+
'Jon Hart <jon_hart[at]rapid7.com>' # original metasploit module
21+
],
2022
# TODO: is there a way to do this on Windows?
2123
'Platform' => %w(linux osx unix),
2224
'SessionTypes' => %w(shell meterpreter)
2325
)
2426
)
2527
register_options(
2628
[
27-
OptString.new('MESSAGE', [true, 'The message to send']),
29+
OptString.new('MESSAGE', [false, 'The message to send', '']),
2830
OptString.new('USERS', [false, 'List of users to write(1) to, separated by commas. ' \
29-
' wall(1)s to all users by default'])
31+
' wall(1)s to all users by default']),
32+
OptBool.new('COWSAY', [true, 'Display MESSAGE in a ~cowsay way', false])
3033
], self.class)
3134
end
3235

36+
def cowsay(text)
37+
# cowsay(1) chunks a message up into 39-byte chunks and wraps it in '| ' and ' |'
38+
# Rex::Text.wordwrap(text, 0, 39, ' |', '| ') almost does this, but won't
39+
# split a word that has > 39 characters in it which results in oddly formed
40+
# text in the cowsay banner, so just do it by hand
41+
text_lines = text.scan(/.{1,34}/)
42+
max_length = text_lines.map(&:size).sort.last
43+
cloud_parts = []
44+
cloud_parts << " #{'_' * (max_length + 2)} "
45+
if text_lines.size == 1
46+
cloud_parts << "< #{text} >"
47+
else
48+
cloud_parts << "/ #{text_lines.first.ljust(max_length, ' ')} \\"
49+
if text_lines.size > 2
50+
text_lines[1, text_lines.length - 2].each do |line|
51+
cloud_parts << "| #{line.ljust(max_length, ' ')} |"
52+
end
53+
end
54+
cloud_parts << "\\ #{text_lines.last.ljust(max_length, ' ')} /"
55+
end
56+
cloud_parts << " #{'-' * (max_length + 2)} "
57+
cloud_parts << <<EOS
58+
\\ ,__,
59+
\\ (oo)____
60+
(__) )\\
61+
||--|| *
62+
EOS
63+
cloud_parts.join("\n")
64+
end
65+
3366
def users
3467
datastore['USERS'] ? datastore['USERS'].split(/\s*,\s*/) : nil
3568
end
3669

3770
def message
38-
datastore['MESSAGE'] ? datastore['MESSAGE'] : "Hello metasploit session #{session.id}, the time is #{Time.now}"
71+
if datastore['MESSAGE'].blank?
72+
text = "Hello from a metasploit session at #{Time.now}"
73+
else
74+
text = datastore['MESSAGE']
75+
end
76+
77+
datastore['COWSAY'] ? cowsay(text) : text
3978
end
4079

4180
def run
4281
if users
82+
# this requires that the target user has write turned on
4383
users.map { |user| cmd_exec("echo '#{message}' | write #{user}") }
4484
else
45-
cmd_exec("echo '#{message}' | wall")
85+
# this will send the messages to all users, regardless of whether or
86+
# not they have write turned on. If the session is root, the -n will disable
87+
# the annoying banner
88+
cmd_exec("echo '#{message}' | wall -n")
4689
end
4790
end
4891
end

0 commit comments

Comments
 (0)