-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Common Metasploit Module Coding Mistakes
Please see CONTRIBUTING.md for an authoritative coding guide. This document has fallen out of date. We don't write bad code any more! Hooray!
This is a collection of all the bad code we often see in Metasploit modules. You should avoid them, too.
Note: Some of these examples use puts() for demo purposes, but you should always use print_status / print_error when writing a module.
- Not checking the return value of a Metasploit API
- Ruby 1.9.3 vs 1.8.7... gotcha!
- Not checking the return value when using match()
- Not checking nil before accessing a method
- Using exception handling to shut an error up
- Not taking advantage of the 'ensure' block
- Adding the 'VERBOSE' option
- Neglecting to use 'vars_post' for send_request_cgi() when crafting a POST request
- Bad variable naming style
- Using global variables
- Modifying the datastore during execution
1. Not checking the return value of a Metasploit API
res = send_request_cgi({
'method' => 'GET',
'uri' => '/app/index.php'
})
# There's a bug here, because res can return nil (due to a timeout or other reasons)
# If that happens, you will hit a "undefined method `code' for nil:NilClass" error.
# The correct way should be: if res && res.code == 200
if res.code == 200
print_status("Response looks good")
else
print_error("Unexpected response")
end
2. Ruby 1.9.3 vs 1.8.7... gotcha!
some_string = "ABC"
# This can cause unexpected results to your module.
# Better to always do: char = some_string[1, 1]
char = some_string[1]
if char == 'B'
puts "You will see this message in Ruby 1.9.3"
elsif char == 66
puts "You will see this message in Ruby 1.8.7"
end
# 1.9 allows a comma after the last argument when calling
# a method while 1.8 does not. The most common place to
# see this error is in the update_info() section in a
# module's constructor.
some_method(
"arg1",
"arg2", # <-- This comma is a syntax error on 1.8.x
)
3. Not checking the return value when using match()
str = "dragon! drag on! Not lizard, I don't do that tongue thing"
# This tries to print "Not snake", but it's not in the string,
# so you'll get this error: "undefined method `[]' for nil:NilClass"
puts str.match(/(Not snake)/)[0]
# The above is better written as:
if (str =~ /(Not snake)/)
puts $1
end
4. Not checking nil first before accessing a method
str = "These things are round and tasty, let's call them... tastycles!"
food = str.scan(/donut holes/)[0]
# food is nil, and nil has no method called "empty".
# This will throw an error: "undefined method `empty?' for nil:NilClass"
if food.empty? or food.nil?
puts "I don't know what it's called"
end
5. Using exception handling to shut an error up
begin
# This block has 2 issues:
# Issue #1: sample() is not a method in 1.8.7
# Issue #2: Divided by 0 (race condition)
n = [0, 1, 2, 3, 4, 5].sample
1/n
rescue
# If the user reports a bug saying this code isn't
# working, it can be hard to debug exactly what went
# wrong for the user without a backtrace.
# When you do this, the error also won't be logged in
# framework.log, either.
# Note that rescuing ::Exception is especially harmful
# because it can even hide syntax errors.
end
6. Not taking advantage of the 'ensure' block
# You should use the ensure block to make sure x always has a value,
# which also avoids repeating code
begin
n = [0, 1, 2].sample
x = 1/n
rescue ZeroDivisionError => e
puts "Are you smarter than a 5th grader? #{e.message}"
x = 0 # Can put this in the ensure block
rescue NoMethodError
puts "You must be using an older Ruby"
x = 0 # Can put this in the ensure block
end
puts "Value is #{x.to_s}"
7. Adding the 'VERBOSE' option
register_options(
[
# You already have this. Just type 'show advanced' and you'll see it.
# So no need to register again
OptBool.new("VERBOSE", [false, 'Enable detailed status messages', false])
], self.class)
8. Neglecting to use send_request_cgi()'s vars_get or vars_get when crafting a POST/GET request
data_post = 'user=jsmith&pass=hello123'
# You should use the 'vars_post' key instead of 'data',
# unless you're trying to avoid the API escaping your
# parameter names
send_request_cgi({
'method' => 'POST',
'uri' => '/',
'data' => data_post
})
9. Bad variable naming style
# What's this, Java?
# The proper naming style in this case should be: my_string
myString = "hello, world"
10. Using global variables
# $msg is a global variable that can be accessed anywhere within the program.
# This can induce bugs to other modules or mixins that are hard to debug.
# Use @instance variables instead.
# This is also mentioned in your HACKING file :-)
class Opinion
def initialize
# This variable shouldn't be shared with other classes
$msg = "It's called the Freedom of Information Act. The Hippies finally got something right."
end
end
class Metasploit3
def initialize
puts $msg
end
end
Opinion.new
Metasploit3.new
11. Modifying the datastore during execution
# https://github.com/rapid7/metasploit-framework/issues/3853
datastore['BAD'] = 'This is bad.'
- Home Welcome to Metasploit!
- Using Metasploit A collection of useful links for penetration testers.
-
Setting Up a Metasploit Development Environment From
apt-get install
togit push
. - CONTRIBUTING.md What should your contributions look like?
- Landing Pull Requests Working with other people's contributions.
- Using Git All about Git and GitHub.
- Contributing to Metasploit Be a part of our open source community.
- Meterpreter All about the Meterpreter payload.