Glenn Lazarus- Why Your Observability Strategy Needs Security Observability
Time tested php with libtimemachine
1. Time Tested PHP
Advanced testing techniques
with libTimeMachine
Nick Galbreath nickg@client9.com @ngalbreath
Vince Tse vtonehundred@gmail.com @vtonehundred
2012-07-19
2. Follow along or get the latest version at:
http://slidesha.re/
NDc5mK
3. Time Happens
While it should be avoided as much as possible, sometimes
"time happens" and applications need testing based on
simulated time.
• Financial applications (e.g. simulating ad spending and
budgeting)
• Security features (what happens when the cookie or auth
token expires?)
• System testing (what happens on leap year? day light
savings time? 2038?)
• Anything that runs periodically ("on the hour")
4. PHP Time Sources
• $_SERVER['REQUEST_TIME']
• time()
• microtime()
• gettimeofday()
• Single argument of date(fmt)
(equivalent to date(fmt, time())
5. Using $_SERVER['REQUEST_TIME']
• Available in all SAPI contexts (mod_php, CLI, CGI, FPM...)
• Created once at time of request
• "Lowest Cost" -- array lookup
• Easy to spoof in unit tests
• Can't spoof for functional tests
• Can't use it for timing
6. Passing as Argument
• Don't call time() et al directly in a function
but instead pass current time in.
• Allows unit testing
• Follows dependency injection best-practice
7. But what if your code
isn't or can't be
structured that way?
8. Time Travel with
libtimemachine!
https://github.com/vtonehundred/libtimemachine
Changes the system calls that PHP uses to get the
current time
• time (defined in <time.h>)
• gettimeofday (defined in <sys/time.h>)
• clock_gettime (defined in <time.h>)
and allows you to change them backwards or
forward, relative or absolute.
9. LibTimeMachine
• Use some secret loader sexiness to change the
underlying system calls.
(see 'man ld-linux' for details)
• Works on Linux systems
• Works on Mac OS X (only tested on 10.7.4)
• Sorry Windows
• (not sure about FreeBSD)
10. Plug and Play
git clone
git://github.com/vtonehundred/libtimemachine.git
cd libtimemachine
make
sudo cp libtimemachine.so [ /lib64 or /lib ]
sudo ldconfig
11. To use!
• libtimemachine reads /tmp/libtimemachine.conf
(or whatever file you want using the
LIBTIMEMACHINE_CONF environment variable)
• Single number controls how to adjust time
• If starts with "-" or "+" then current time will be adjusted
by a relative amount.
• If "just numbers" then the time is fixed with this value
• If "0" or missing, then use current time
12. PHP CLI
Just add LD_PRELOAD=libtimemachine.so
before php on the command line
$ php -r 'echo date("rn");'
Mon, 28 May 2012 23:03:38 -0400
$ # go back one year
$ echo "-31536000" > /tmp/libtimemachine.conf
$ LD_PRELOAD=libtimemachine.so
php -r 'echo date("rn");'
Sun, 29 May 2011 23:03:49 -0400
$ #winning
13. PHP 5.4 Built-In WebServer
This is the easiest way to go!
$ date
Mon May 28 23:27:19 2012
$ echo "31536000" > /tmp/libtimemachine.conf
$ LD_PRELOAD=/lib64/libtimemachine.so
./php -t ~/root -S 127.0.0.1:80
PHP 5.4.3 Development Server started at Tue May 28 23:29:19 2013
Listening on 127.0.0.1:80
Document root is ~/root
Press Ctrl-C to quit.
[Tue May 28 23:29:22 2013] 127.0.0.1:34913 [200]: ~/time.php
Command line CGI works similarly
14. Apache mod_php
Debian / Ubuntu
• Install libtimemachine.so in /lib64 or /
lib depending on your OS.
• (for good measure also do "sudo ldconfig")
• /etc/apache2/envvars controls the
apache and workers environment. Add
export LD_PRELOAD=libtimemachine.so
• sudo /etc/init.d/apache2 restart
16. Back One Day!
$ date
Sun, 27 May 2012 19:35:41 +0000
$ echo "-86400" > /tmp/libtimemachine.conf
$ curl 'http://127.0.0.1/phptime.php'
REQUEST_TIME : Sat, 26 May 2012 19:35:54 +0000
time() : Sat, 26 May 2012 19:35:54 +0000
microtime() : Sat, 26 May 2012 19:35:54 +0000
date('r') : Sat, 26 May 2012 19:35:54 +0000
gettimeofday() : Sat, 26 May 2012 19:35:54 +0000
17. apache mod_php
RedHat/CentOS
• Disable SELinux: in /etc/selinux/config set
SELINUX=disabled
• put libtimemachine.so in /lib64 or /lib
depending on your OS.
• (for good measure also do "sudo ldconfig")
• add to /etc/sysconfig/httpd
export LD_PRELOAD=libtimemachine.so
• And then...
18. Fail on Apache +
mod_php + CentOS 6.2
• SELinux removes LD_PRELOAD
• Even though we disabled SELinux, it appears the linker
isn't getting LD_PRELOAD
• mod_php is an shared library that loads shared
libraries. hmmm
• I suspect a bug in the OS? Or maybe mod_php is
compiled differently.
• Use PHP 5.4's built-in web server instead for testing.
19. Future Work
• Apache + PHP CGI (does anyone do this?)
• nginx + PHP FPM (the new hotness)
• Figuring out what is going on with CentOS
• Testing on mysql server.
• Packaging
20. Detecting
libtimemachine
• Look for existence of
/tmp/libtimemachine.conf
• Shell out and use "date +%s"
and compare to time()
• Use Apache mod_env and add
PassEnv LD_PRELOAD
to let PHP see the environment variable
21. Evil
• Can this technique be used for evil?
• Oh yeah.
• type "LD_PRELOAD rootkit" in your favorite
search engine for details
22. Mac OS X Notes
• Only tested on 10.7.4
• Mac OS X uses dyld for linking and works different than
gnu ld. See 'man dyld' for details.
• Instead of LD_PRELOAD, use:
DYLD_INSERT_LIBRARIES=./libtimemachine.dylib
• If that doesn't work, add
DYLD_FORCE_FLAT_NAMESPACE=1
23. Gotchas
• if you globally set LD_PRELOAD,
export LD_PRELOAD=libtimemachine.so
then everything you do might be time shifted
(to undo 'unset LD_PRELOAD')
• Your application might run a bit slower since
every time lookup requires reading
/tmp/libtimemachine.conf