craftwa.re

A walk outside the sandbox

Home Blog Cheat Sheets MacOS Tips Area 51 About

Function Interposing

|

Overview

  • In the Linux world, the LD_PRELOAD environment variable can be set to the path of a shared object, that will be loaded at runtime before any other library (including the C runtime, libc.so).
  • This mechanism can be used both for good and bad purposes, from inspecting program memory and adding trace logs to inject nefarious code into applications.
  • To prevent privilege escalation using suid binaries, the loader ignores the LD_PRELOAD variable if real user id is different than the effective user id.
  • This blog has a working example showing how this trick works for Linux.
  • macOS loader supports a similar feature called function interposing.

How it works

DYLD_INSERT_LIBRARIES
This is a colon separated list of dynamic libraries to load before the ones specified in the program. This lets you test new modules of existing dynamic shared libraries that are used in flat-namespace images by loading a temporary dynamic shared library with just the new modules. Note that this has no effect on images built a two-level namespace images using a dynamic shared library unless DYLD_FORCE_FLAT_NAMESPACE is also used.

DYLD_FORCE_FLAT_NAMESPACE
Force all images in the program to be linked as flat-namespace images and ignore any two-level namespace bindings. This may cause programs to fail to execute with a multiply defined symbol error if two-level namespace images are used to allow the images to have multiply defined symbols.

dyld (1) man page

Example

  • The program below prints six random lucky numbers:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(){
    srand(time(NULL));
    
    int i = 0;
    while(i<6)
        printf("Lucky number %d: %d\n", ++i, rand() % 100);
    
    return 0;
}
$ ./genRand
Lucky number 1: 19
Lucky number 2: 53
Lucky number 3: 0
Lucky number 4: 22
Lucky number 5: 5
Lucky number 6: 43
  • The code for our library containing the function(s) to interpose:
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

/*
 * Compile with:
 * $ gcc -Wall -o myRandLib.dylib -dynamiclib myRandLib.c
 *
 * Test with:
 * $ gcc -o genRand genRand.c
 * $ DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=myRandLib.dylib ./genRand
 *
 */

// Override original rand() function
int rand(void)
{
    return 42;
}

  • Finally, let’s test using the two environment variables from above:
$ gcc -Wall -o myRandLib.dylib -dynamiclib myRandLib.c
$ DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=myRandLib.dylib ./genRand
Lucky number 1: 42
Lucky number 2: 42
Lucky number 3: 42
Lucky number 4: 42
Lucky number 5: 42
Lucky number 6: 42

References