Linux (game) hacking with
LD_PRELOAD
Hackersuli
2020 March
Awesome LD_PRELOAD examples
•
libkeepalive
−enable TCP keepalive socket options
• libleakmydata
−disable SSL certificate verification
• libfaketime
−modifies the system time for a single application
Tux vs Timeskew
TIMESKEW="2 1"
LD_PRELOAD=./libtimeskew.so supertuxkart
TIMESKEW="1 2"
LD_PRELOAD=./libtimeskew.so supertux2
Log SSL/TLS
• rm -f hooklog.bin
• LD_PRELOAD=`pwd`/hook.so.1 wget
https://google.com
• ./print-hooklog hooklog.bin | head
Random, Debian style
#include <stdlib.h>
#include <stdio.h>
int main() {
srand(1);
int x = rand();
srand(2);
int y = rand();
puts(x == y ? "ok" : "fail");
return !(x == y);
}
LD_PRELOAD explained
man ld
#define _GNU_SOURCE - This is needed to be able to use
RTLD_NEXT, see later
#include - no need to explain these
void (*orig_srand)(unsigned int seed); - we define the original
srand here so we can use it later
void srand(unsigned int seed) { - override original srand function
if(!orig_srand) { - don't depend on a constructor to resolve libc's
function, and do it on demand when it's first needed.
LD_PRELOAD explained
man dlsym
orig_srand = dlsym(RTLD_NEXT, "srand"); - RTLD_NEXT finds
the next occurrence of a function in the search order after the
current library
dlsym - obtain address of a symbol in a shared object or
executable
assert(orig_srand); - abort program if we don’t have the original
srand
orig_srand(0); - call original srand with a fixed seed of 0
Random, Debian style
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdlib.h>
#include <assert.h>
void (*orig_srand)(unsigned int seed);
void srand(unsigned int seed) {
if(!orig_srand) {
orig_srand = dlsym(RTLD_NEXT, "srand");
assert(orig_srand);}
orig_srand(0);}
Compiling and linking
gcc -Wall -fPIC -shared -o myldpreload.so ldpreload.c -ldl
-Wall – show all warnings
-fPIC – all function calls will be made via the Procedure
Linkage Table – PLT. Otherwise symbol relocations are
internally are resolved at load time, not good.
-shared – create a shared library
-ldl - tells the linker to find and link libdl.so, this is needed
by dlsym
Hacking vulnerable webserver
curl -X POST --data-binary @payload.so
http://<IP>/cgi-bin/cgitest?LD_PRELOAD=/proc/self/fd/0 -i
https://www.elttam.com//blog/goahead/
Because CGI was so secure back in 1999
Especially when the executable uses the LD_PRELOAD variable
and accepts it from the GET request
Can I privesc with LD_PRELOAD on
setuid/setgid binaries?
No *
* except if Defaults env_keep += LD_PRELOAD
#in suoders
Ghidra time
Note to self
Close everything, Skype, Spotify, Ghidra, ...
Pwnadventure
sudo gdb -p $(pidof PwnAdventure3-Linux-
Shipping)
• p GameWorld
• p *GameWorld
• p *(ClientWorld *) GameWorld
Pwnadventure
Print class definition:
− ptype ClientWorld
− ptype Player
copy to libGameLogic.h
p *(Player*)((*(ClientWorld*)GameWorld).m_activePlayer.m_object)
set variable=value
Fixing things
std::string vs const char*
std::string is an object holding the string data
const char* is a pointer
health: public vs protected
-std=c++11
Hiding on top of the tree
Frida vs LD_PRELOAD
When to use Frida and when to LD_PRELOAD
Frida is better for quick one-time hacks
LD_PRELOAD is nice when you want to share the
love with everyone
LD_PRELOAD is better when it is used in
production, e.g. a code is fixed
Bonus macOS
DYLD_INSERT_LIBRARIES is the LD_PRELOAD
By default, when System Integrity Protection is
enabled and and the program has the
CS_RESTRICT flag (Apple shipped binaries),
DYLD_INSERT_LIBRARIES will not work
Sad Panda
Basic macOS syntax
#include <stdio.h>
#include <syslog.h>
__attribute__((constructor))
static void customConstructor(int argc, const char **argv)
{
printf("Hello from dylib!n");
syslog(LOG_ERR, "Dylib injection successful in %sn", argv[0]);
}
gcc -dynamiclib inject.c -o inject.dylib
DYLD_INSERT_LIBRARIES=inject.dylib ./test
Prevent LD_PRELOAD
• statically link your program
• setuid/setgid set
• check for the LD_PRELOAD environment
variable, and complain
○ the attacker could also LD_PRELOAD the
function that lets you read environment
variables… :)
Prevent
DYLD_INSERT_LIBRARIES
• setuid and/or setgid bits are set
• restricted by codes signed with entitlements
• restricted segment
https://theevilbit.github.io/posts/dyld_insert_libraries_dylib_injection_in_macos_osx_deep
_dive/
References
LiveOverFlow !!!!
https://github.com/LiveOverflow/PwnAdventure3/tree/master/tools/linux
https://github.com/gaul/awesome-ld-preload
https://theevilbit.github.io/posts/dyld_insert_librari
es_dylib_injection_in_macos_osx_deep_dive/
Thank you for your
attention

Hackersuli - Linux game hacking with LD_PRELOAD

  • 1.
    Linux (game) hackingwith LD_PRELOAD Hackersuli 2020 March
  • 3.
    Awesome LD_PRELOAD examples • libkeepalive −enableTCP keepalive socket options • libleakmydata −disable SSL certificate verification • libfaketime −modifies the system time for a single application
  • 4.
    Tux vs Timeskew TIMESKEW="21" LD_PRELOAD=./libtimeskew.so supertuxkart TIMESKEW="1 2" LD_PRELOAD=./libtimeskew.so supertux2
  • 5.
    Log SSL/TLS • rm-f hooklog.bin • LD_PRELOAD=`pwd`/hook.so.1 wget https://google.com • ./print-hooklog hooklog.bin | head
  • 6.
    Random, Debian style #include<stdlib.h> #include <stdio.h> int main() { srand(1); int x = rand(); srand(2); int y = rand(); puts(x == y ? "ok" : "fail"); return !(x == y); }
  • 7.
    LD_PRELOAD explained man ld #define_GNU_SOURCE - This is needed to be able to use RTLD_NEXT, see later #include - no need to explain these void (*orig_srand)(unsigned int seed); - we define the original srand here so we can use it later void srand(unsigned int seed) { - override original srand function if(!orig_srand) { - don't depend on a constructor to resolve libc's function, and do it on demand when it's first needed.
  • 8.
    LD_PRELOAD explained man dlsym orig_srand= dlsym(RTLD_NEXT, "srand"); - RTLD_NEXT finds the next occurrence of a function in the search order after the current library dlsym - obtain address of a symbol in a shared object or executable assert(orig_srand); - abort program if we don’t have the original srand orig_srand(0); - call original srand with a fixed seed of 0
  • 9.
    Random, Debian style #define_GNU_SOURCE #include <dlfcn.h> #include <stdlib.h> #include <assert.h> void (*orig_srand)(unsigned int seed); void srand(unsigned int seed) { if(!orig_srand) { orig_srand = dlsym(RTLD_NEXT, "srand"); assert(orig_srand);} orig_srand(0);}
  • 10.
    Compiling and linking gcc-Wall -fPIC -shared -o myldpreload.so ldpreload.c -ldl -Wall – show all warnings -fPIC – all function calls will be made via the Procedure Linkage Table – PLT. Otherwise symbol relocations are internally are resolved at load time, not good. -shared – create a shared library -ldl - tells the linker to find and link libdl.so, this is needed by dlsym
  • 11.
    Hacking vulnerable webserver curl-X POST --data-binary @payload.so http://<IP>/cgi-bin/cgitest?LD_PRELOAD=/proc/self/fd/0 -i https://www.elttam.com//blog/goahead/ Because CGI was so secure back in 1999 Especially when the executable uses the LD_PRELOAD variable and accepts it from the GET request
  • 12.
    Can I privescwith LD_PRELOAD on setuid/setgid binaries? No * * except if Defaults env_keep += LD_PRELOAD #in suoders
  • 13.
  • 14.
    Note to self Closeeverything, Skype, Spotify, Ghidra, ...
  • 15.
    Pwnadventure sudo gdb -p$(pidof PwnAdventure3-Linux- Shipping) • p GameWorld • p *GameWorld • p *(ClientWorld *) GameWorld
  • 16.
    Pwnadventure Print class definition: −ptype ClientWorld − ptype Player copy to libGameLogic.h p *(Player*)((*(ClientWorld*)GameWorld).m_activePlayer.m_object) set variable=value
  • 17.
    Fixing things std::string vsconst char* std::string is an object holding the string data const char* is a pointer health: public vs protected -std=c++11
  • 18.
    Hiding on topof the tree
  • 19.
    Frida vs LD_PRELOAD Whento use Frida and when to LD_PRELOAD Frida is better for quick one-time hacks LD_PRELOAD is nice when you want to share the love with everyone LD_PRELOAD is better when it is used in production, e.g. a code is fixed
  • 20.
    Bonus macOS DYLD_INSERT_LIBRARIES isthe LD_PRELOAD By default, when System Integrity Protection is enabled and and the program has the CS_RESTRICT flag (Apple shipped binaries), DYLD_INSERT_LIBRARIES will not work Sad Panda
  • 21.
    Basic macOS syntax #include<stdio.h> #include <syslog.h> __attribute__((constructor)) static void customConstructor(int argc, const char **argv) { printf("Hello from dylib!n"); syslog(LOG_ERR, "Dylib injection successful in %sn", argv[0]); } gcc -dynamiclib inject.c -o inject.dylib DYLD_INSERT_LIBRARIES=inject.dylib ./test
  • 22.
    Prevent LD_PRELOAD • staticallylink your program • setuid/setgid set • check for the LD_PRELOAD environment variable, and complain ○ the attacker could also LD_PRELOAD the function that lets you read environment variables… :)
  • 23.
    Prevent DYLD_INSERT_LIBRARIES • setuid and/orsetgid bits are set • restricted by codes signed with entitlements • restricted segment https://theevilbit.github.io/posts/dyld_insert_libraries_dylib_injection_in_macos_osx_deep _dive/
  • 24.
  • 25.
    Thank you foryour attention