Position Independent Code (PIC) and Shellcode: An Introduction

Yua Mikanana
3 min readSep 23, 2023

--

In the realm of cybersecurity, certain terminologies and concepts form the very fabric of various attack vectors and defense strategies. Position Independent Code (PIC), commonly known as Shellcode, is one such concept. But what is Shellcode, and how does it differ from other code paradigms? Let’s delve in.

What is Position Independent Code (Shellcode)?

Position Independent Code is code that is designed to execute regardless of where it resides in memory. This is in contrast to most programs, which might need to be loaded at a specific address to function correctly. Shellcode is a type of PIC, often written for exploits and payloads in hacking.

The term “Shellcode” originally referred to code that, when executed, would spawn a command shell — hence the name. However, today, it generally refers to any payload used for exploitation.

Uses of Shellcode:

  1. Exploitation: The primary use of shellcode is to exploit vulnerabilities in software, where the attacker can inject and execute their code.
  2. Payloads: Once a vulnerability is exploited, shellcode can be used to deliver payloads, which can range from spawning a reverse shell to injecting ransomware or establishing persistence.
  3. Bypassing Restrictions: Shellcode can be crafted to bypass security mechanisms, making detection and mitigation difficult.

Shellcode vs. Compiled Code vs. Interpreted Code:

  1. Shellcode: As mentioned, shellcode is position-independent, designed to run from any location in memory. It doesn’t rely on external libraries or functions and should be small and efficient to work within the constraints of an exploit.
  2. Compiled Code: This is code written in languages like C or C++ that is then compiled into machine code by a compiler. The result is a binary executable that the system can run. Unlike shellcode, compiled programs often rely on fixed memory addresses and external libraries.
  3. Interpreted Code: This is code written in languages like Python, Ruby, or JavaScript that is executed line-by-line by an interpreter. It’s not converted into machine code; instead, the interpreter reads and executes it directly. This makes it generally slower than compiled code, but it’s more flexible and platform-independent.

Example of Shellcode:

A basic example of shellcode might be a sequence of bytes that, when injected into a vulnerable application, spawns a system shell.

Assembly example:

xor eax, eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push eax
mov edx, esp
push ebx
mov ecx, esp
mov al, 11
int 0x80

This is a basic example for spawning a /bin/sh shell on Linux.

Introducing Donut.exe:

Donut is a tool that can generate shellcode from .NET assemblies, making it easier for penetration testers and cyber-attackers to use .NET-based payloads in their exploits. Given the popularity and capabilities of the .NET framework, this is a significant development.

With Donut, you can convert a .NET assembly (either EXE or DLL) into position-independent code that can be executed in memory from a wide range of applications.

Interested in a more practical hands-on demonstration?

Check out this video posted on Gemini Cyber Security Youtube channel, whereby Donut.exe is demonstrated, generating a shellcode from the infamous Quasar RAT.

https://youtu.be/jklQ00ZFWzo

Conclusion:

Position Independent Code, especially shellcode, is a testament to the intricate dance between attackers trying to exploit systems and defenders trying to secure them. It represents both the ingenuity of cyber-attackers and the challenges faced by cybersecurity professionals. By understanding tools like shellcode and Donut, defenders can better prepare for the evolving cyber threat landscape.

--

--