In the last blog post of the X86 Linux assembly series, we focused on how to make our Hello World payload friendly for use as a payload in exploits. However, we didn’t cover how to extract the payload itself for use in exploits. Sure you could view the Objdump output and copy each hex byte out by hand, but that would be tedious and time consuming. Today I want to cover a method for extracting our custom payload from an object file created with GAS using Objcopy.
In covering this method, we will showcase how to do this manually by hand, as well as via a script I wrote for automating the process of compiling and extracting the payload then printing it in a hex-encoded format that is compatible with C or Python code. This new script has been added to the Secure Ideas Professionally Evil x86_asm GitHub repository and can be found under the newly added utils folder.
The Method: GAS + Objcopy
Using these two tools makes it possible to build a binary object from the source code that isn’t yet an executable (it still requires linking to work), but still has the respective sections. Since our entire payload was in the .text section of the binary, Objcopy can extract that entire section to its own file by itself, and that would be a binary file containing just our payload.
Using GAS to Make an Object File
In our previous example we were using the Makefile to build our project, but the code files also contained comments that explained how to compile and link the code to produce a working standalone ELF binary. The part we are interested in here is the compile part. We can skip the linking part. We just need GAS to compile our code into its binary equivalent, which is what it does when it compiles it into an object file. Consider the comment on compiling found in our hello_world_gas_solution_3.s source code file:
# Compile: as --march=i386 --32 ./hello_world_gas_solution_3.s -o hello_world_gas_solution_3.o
This invokes the as command to compile the source code file ./hello_world_gas_solution_3.s into the object file hello_world_gas_solution_3.o. The next comment after it would use that object file and compile it, which we can skip in this method. Even as an object file this still shows up as an ELF file.
Since it still shows up as an ELF, even though it won’t run as it is right now, objdump will still show that our binary code is present if we run objdump -d ./hello_world_gas_solution_3.o as shown below.
The output from the Objdump command shows that this is the .text section of the object file and that’s really all we need to move forward to carve out our payload with Objcopy.
Extracting the Raw Binary of .text with Objcopy
Now that we have the object file, we will use Objcopy to carve the .text section out of the file and place it into another file as raw binary. This would get rid of all the information outside of the section, such as ELF headers and other sections that we don’t need.
To accomplish this, let’s review the man page on Objcopy and review a few switches that might be useful to us for this cause. First, we need to find a switch that will allow us to target a section for extraction which appears to be the -j switch. The -j switch takes a parameter of a search pattern to match against the sections, which means we can provide it with .text and it should extract just that section of the pattern.
Next, we need to specify the output format that we want. We wanted to output the second file as a raw binary file. If we run Objcopy without specifying this, then we will end up with a second ELF file.
This won’t work for our payload extraction since we want just the raw binary bytes from the .text section. Again reviewing the man page for Objcopy we can find the -O switch which allows us to specify the output format. This switch takes a parameter called bdfname, however the man page doesn’t say what values would be accepted there.
That is because what’s supported in terms of bdfname depends on the build of Objcopy itself. In order to see which values *YOUR* version of Objcopy supports, you will need to run:
The list will be at the bottom of the help screen. Here we can see an option for binary.
Now that we have the switches we can make a command that will perform the extraction we are after. We can test this out with the following command:
objcopy -j .text -O binary ./hello_world_gas_solution_3.o new1.file
The following screenshot shows this in action, a dump of the file and a disassembly of the original object so you can compare bytes and that it’s exactly our payload.
Using the Raw Binary File for Payload Use
With the use of GAS to make an object file and Objcopy to carve the payload out of the object file, we now have a raw binary file that contains our payload by itself. This unfortunately isn’t copy-and-paste friendly, but is easy enough to use in Python or C. Simply open the file for binary read and copy it to your buffer and use it. However we can do better than this, and we should!
Building a Script To Automatically Build & Extract Payloads from Source
If we think about what we’ve done up to this point, it’s a logical step-by-step process. That’s perfect for automation! With a little time effort today we are able to produce a Python script that can automate this process of compiling and extracting the payload. On top of that, we can also have this format the payload so it’s hex-encoded and nicely formatted for use in C or Python code if we need to copy-and-paste it into PoC exploit code for those two languages, or just dump it out as a raw hex encoded string.
Furthermore, we can also have it perform some sanity checks on the payload and alert us to possible limitations of the payload that we might want to be aware of, such as signed chars, carriage returns, space, line feeds, or null bytes that are often restrictions in several use cases.
Due to the size of this script, it will not be pasted here. However this script has been built and uploaded to the Secure Ideas Professionally Evil x86_asm GitHub repository. It can be found in the new created utils folder as build_and_extract_x86_payload.py
Using the Script
This script has a help menu that can be brought up by providing no arguments or the –help menu.
This script, in its most basic usage syntax would be:
Using it this way will perform the compiling and extraction of the payload, collect and print stats about the payload, then print the hex-encoded payload using the default style of raw. The screenshot below shows what this would produce.
There is also support for styling via the -s or –style parameter which will modify the formatting of the payload dump. Currently the script supports C or Python formatting for the payloads. The results of the payload dump would be as follows.
I hope you’ve enjoyed this blog post and learned something new today about extracting your shellcode and find the new tool useful. The code for this post will be added to the Secure Ideas Professionally Evil x86_asm GitHub repository. In future posts, we will:
- How to build a C shellcode tester stub and use it to test our shellcode standalone
- Other Syscalls and Uses.
Ready for a challenge? We post Mystery Challenges on Facebook, Linkedin, and Twitter. If you’re interested in security fundamentals, we have a Professionally Evil Fundamentals (PEF) channel that covers a variety of technology topics. We also answer general basic questions in our Knowledge Center. Finally, if you’re looking for a penetration test, professional training for your organization, or just have general security questions please Contact Us.
Linux X86 Assembly Series Blog Post
Interested in more information about the X86 architecture and Linux shellcode/assembly? This blog is a part of a series and the full list of blogs in this series can be found below:
- A Hacker's Tour of the X86 CPU Architecture
- Linux X86 Assembly – How to Build a Hello World Program in NASM
- Linux X86 Assembly – How to Build a Hello World Program in GAS
- Linux X86 Assembly – How to Make Our Hello World Usable as an Exploit Payload
- Linux X86 Assembly – How To Make Payload Extraction Easier
- Linux X86 Assembly – How To Test Custom Shellcode Using a C Payload Tester