TIL about blind rop, a technique to write buffer overflows against targets that restart after a crash. As example, targets that fork and recovers when the child crashes. Since the child has the same memory layout as the parent the stack canary will be the same.

The idea here is that we can perform an operation similar to a blind SQLI with our binary, overring one byte at each time until we find all the bytes that form our canary. So suppose we have a layout like the one below with our canary being 11 22 33 44:

+------------------------------+------------+
| A A A A A A A A A A A A A A A| 11 22 33 44|
+------------------------------+------------+
<-------- buffer -------------><---canary--->

In order to figure out the right canary in a blind attack we would overflow up to the first byte and start our investigation:

+------------------------------+------------+
| A A A A A A A A A A A A A A A| 00 22 33 44| -> CRASH
+------------------------------+------------+
<-------- buffer -------------><---canary--->

+------------------------------+------------+
| A A A A A A A A A A A A A A A| 01 22 33 44| -> CRASH
+------------------------------+------------+
<-------- buffer -------------><---canary--->

+------------------------------+------------+
| A A A A A A A A A A A A A A A| 02 22 33 44| -> CRASH
+------------------------------+------------+
<-------- buffer -------------><---canary--->

...

+------------------------------+------------+
| A A A A A A A A A A A A A A A| 11 22 33 44| -> SUCCESS
+------------------------------+------------+
<-------- buffer -------------><---canary--->

So we found that the first byte is 11, after that we will move to the second byte and repeat the algorithm until we find all the required bytes.

A small python scrip representing the algorithm on a 32 bit binary:

def leak_canary_byte(canary_prefix):
    for i in range(256):
        p = 'A'* buffer_size_until_canary + canary_prefix + chr(i)
        # Send p to the binary and see if we get a crash or not
        # Return if a crash didn't happen
    return None


def leak_canary():
    canary = ""
    for i in range(4):
        b = leak_canary_byte(canary)
        if b is None:
            err("bail")
            exit(1)
        ok("Found canary[%d]=%.2x"%(i, ord(b)))
        canary += b
    return canary

TIL about the algorithm an interpreter uses in order to load libraries in which your ELF file depends on:

The interpreter locates the libraries in this order:

  1. LD_PRELOAD environment variable, and anything in /etc/ld.so.preload
  2. LD_LIBRARY_PATH environment variable
  3. DT_RUNPATH or DT_RPATH specified in the binary file (both can be modified with patchelf)
  4. system-wide configuration (/etc/ld.so.conf)
  5. /lib and /usr/lib

TIL about the Ruby one-liners cookbook.

It has tons of interesting examples about how to achieve things in Ruby that we would previously rely on awk or sed to tackle.

A small example of what you will find there:

$ ruby -rset -ane 'BEGIN{s=Set.new}; (s.add($F[0]); next) if ARGV.size==1;
                   print if s.include?($F[0])' dept.txt marks.txt

When we don’t want to run a shell with sudo but still want to redirect output to a location we don’t have permission to access we can use the sudo tee trick.

$ sudo cat /root/file | sudo tee /whatever/test.out > /dev/null

The redirect to /dev/null is there to stop tee from outputting to the screen.

We have to use sudo with tee because a normal redirection performed by the shell is running with our users permissions.

This could be further simplified with:

$ sudo cat /root/file | sudo dd of=/whatever/test.out

dd is great to write large files (usually images to disks), but also works equally well with small ones.

TIL about scheduling timed jobs on MacOS through launchd.

Each launchd job is described by a separate plist file, meaning you can manage launchd timed jobs by simply adding or removing a file. These files should be placed in certain folders according to the goal of the job:

  • System tasks go to:
/Library/LaunchDaemons/

if they need to run no matter if a user is logged in to the system or not. These tasks will be started with “root” privileges.

  • If they need to run if any user is logged in, they go to:
/Library/LaunchAgents/

and will be executed with the privileges of the user that just logged in.

  • If they need to run if only you are logged in, they go to:
~/Library/LaunchAgents/

The documentation can be found here. Examples of timed jobs can be found here.

TIL about Floyd’s Tortoise and Hare algorithm.

This algorithm is used to detect whether there is a cycle in a list, and given that a cycle exists its starting point and the length of the cycle.

The algorithm can also be used to prove the existence of duplicate numbers in a list.

The time complexity of this algorithm is linear: O(n).
The space complexity of this algorithm is constant: O(1).

TIL that we can disable gems when loading a Ruby script through --disable=gems.

$ ruby --disable=gems -e ''

TIL that we can get optimized assembly output from a Cargo project by running the following command:

cargo rustc --release -- --emit asm

This will output x86 by default, but say we wanted the intel syntax?

cargo rustc -- --emit asm -C "llvm-args=-x86-asm-syntax=intel"

TIL that Rust closures creates an anonymous struct in order to represent their environment. As an example:

let a = 42;
let b = 100;
let f = |v: i32| v + a + b;

would be represented as something like:

struct __anonymous_e3b0105<'a> {
    a: &'a i32,
    b: &'a i32,
}

Copied to clipboard