-
Notifications
You must be signed in to change notification settings - Fork 14.4k
Polymorphic x86/x64 Block API #13832
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
Conversation
This changes the x86 version to the (10 bytes) larger variant that can handle full 32-bit jumps which is necesary for maximum compatibility within the framwork. Additionally, numeric literals are expressed in hex for compatibility with the keystone assembler allowing these files to be compatitble with external tools.
Unit tests appear to be failing due to my use of Ruby's |
Dude, YES. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes look sensible to me. I was able to reference Crimson Forge easily. I have yet to test this, of course.
The latest commit was strictly comment changes in the x64 source code, so nothing executable should have been changed. I think you can safely skip restarting all of the tests. |
Original Release Notes This PR adds the necessary infrastructure to load and process polymorphic assembly stubs from external data files and uses it to dynamically reorder the instructions for the Block API stub that powers all of the x86 and x64 native Windows payloads. The intention is to add polymorphic qualities to the payloads generated by Metasploit without either changing the size or requiring that the payloads be self modifying and thus reside in RWX memory which in and of itself is often identified as malicious. Now each time a payload is generated, the common Block API section is randomized making writing signatures to match it much more complicated. This is done independently of encoding, and therefor the benefits are present even without encoding. |
This is awesome stuff Spencer. Love ya work mate 👍 |
Release NotesImproved Metasploit-generated x86 and x64 native Windows payloads by adding polymorphic qualities without changing the size or requiring that the payloads be self modifying. |
This PR adds the necessary infrastructure to load and process polymorphic assembly stubs from external data files and uses it to dynamically reorder the instructions for the Block API stub that powers all of the x86 and x64 native Windows payloads. The intention is to add polymorphic qualities to the payloads generated by Metasploit without either changing the size or requiring that the payloads be self modifying and thus reside in RWX memory which in and of itself is often identified as malicious. Now each time a payload is generated, the common Block API section is randomized making writing signatures to match it much more complicated. This is done independently of encoding, and therefor the benefits are present even without encoding.
I also consolidated the x86 Block API stubs, removing the duplicates from
util/exe.rb
so they would reap the same benefits. These ones however used a slightly modified version that would allow for full 32-bit jumps, thus making the stub slightly larger. The x64 stub was left unmodified. If the size increase is unacceptable, I'll need to readd the older version in something likeblock_api_small.x86.graphml
. See the "x86 Small" column below for a metric comparison on this scenario.Known Limitations
Known limitations include the fact that the only changes that are made are in the ordering of instructions. New instructions are not added at this time to maintain the desirable small size, however if size restrictions could be propagated down, this could be an easy area for improvement. The second limitation is that instructions themselves are not altered to use different registers for storage or anything like that.
With these limitations in place however we'll still see:
1These are metrics for the hypothetical smaller version of the x86 Block API. Due to the nature of how the algorithm works, the more optimized the assembly code is, the few opportunities there are to shuffle the instructions while retaining the original functionality.
Future Work
With the data loaded in the graph, there's an opportunity for future work to inject NOPs and generally deoptimize instructions. By splitting instructions into two and injecting them into the graph with the appropriate constraints, the build process would allow them to be reconstructed without necessarily being placed sequentially. This would require working more with the assembly source code and thus coupling the logic more tightly with the x86 and AMD64 architectures.
GraphML?
Data is loaded from a GraphML file that is precalculated so it is unnecessary for Metasploit to perform the binary analysis to determine the positional constraints for the instructions. I chose to use GraphML because:
The new GraphML parser library I wrote follows the GraphML specification for all of the implemented elements. Hyperedges and ports are omitted from the current implementation because they are not necessary at this time.
It's not realistic to create the necessary GraphML data files by hand. I used the
tools/analysis/graph.py
utility included within my crimson-forge project to generate it. The gist of this process was:external/source/shellcode/windows/(x64|x86)/src/block/block_api.asm
into a binary file (either use nasm or thetools/assembler.py
tool in crimson-forge)tools/analysis/graph.py -a x86 block_api.bin graphml block_api.x86.graphml
For posterity, this is the build automation script I used:
GraphML Data Generation
Script
Note the source files in the home directory and the use of
amd64
instead ofx64
.Output
This approach uses data as opposed to the existing Rex::Polymorphic library like Shikata Ga Nai does because the Rex library targets binary code as opposed to assembly source. When I looked into update it to work with both x86 and x64 source code, the alterations seemed non-trivial. Furthermore, had the necessary alterations been made, we would then have needed to defined each stub independently in Ruby in such a way that updating it would be more complicated than generating a new data file.
Assembly Source Files
Since I consolidated the Block API usage within Metasploit as part of this work, I updated the original asm files because they are now the authoritative source. This mostly altered the x86 version to be the larger Block API variant that works in more scenarios. One self-serving change I had to make was to update all numeric literals to their hex representation. This doesn't change anything for nasm or metasm, however it was necessary since the keystone engine used by crimson-forge assumes that numeric literals are base-16 instead of decimal.
Testing
There are three primary things that need to be tested. Both x86 and x64 native Windows payloads and 32-bit EXEs generated using msfvenom /
generate -f exe ...
.msfconsole
use exploits/windows/smb/psexec
set PAYLOAD windows/x64/meterpreter/reverse_tcp
set PAYLOAD windows/meterpreter/reverse_tcp
use payload/windows/meterpreter/reverse_tcp
generate -f exe -o /path/to/your/output.exe
to_handler
Thanks for reading!