Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHP Hurts Programmers (and other tales)

PHP Hurts Programmers (and other tales)

Find out some of the sneaky ways the web’s favourite language-to-hate can give unsuspecting users just enough rope to hang themselves with. Take a slightly deeper dive into a few real-world bugs, and see how to (hopefully) avoid them in your own code.

Links from the end:

PHP The Right Way - http://www.phptherightway.com/
PHP: A fractal of bad design

https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/
WordPress vulnerability discussed:
https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html
Simple Machine Forums vulnerability discussed:
https://www.alertlogic.com/blog/writing-exploits-for-exotic-bug-classes-php-type-juggling/
ExpressionEngine vulnerability
https://foxglovesecurity.com/2017/02/07/type-juggling-and-php-object-injection-and-sqli-oh-my/
OWASP Resources
Both of these are “work in progress” / drafts
https://www.owasp.org/index.php/PHP_Security_Cheat_Sheet
https://www.owasp.org/index.php/PHP_Configuration_Cheat_Sheet
https://www.owasp.org/index.php/PHP_Object_Injection
PHP Configuration Checker (php.ini)
https://github.com/sektioneins/pcc
All about shell escaping & php
https://gist.github.com/Zenexer/40d02da5e07f151adeaeeaa11af9ab36
List of static analysis tools for PHP
https://github.com/exakat/php-static-analysis-tools
Gary Bernhardt’s “WAT” talk
https://www.destroyallsoftware.com/talks/wat

Keith Humm

March 29, 2017
Tweet

Other Decks in Programming

Transcript

  1. “you pull out the hammer, but to your dismay, it

    has the claw part on both sides”
  2. > $x = “2 plus 3”; string(8)“2 plus 3” >

    $x += 1; int(3) > $x *= 0.333; double(0.999) > $x = $x && true; bool(true)
  3. > “foo” == TRUE true > “foo” == 0 true

    > TRUE == 0 false > “55” == “055” true > 55 != 055 true *https://www.destroyallsoftware.com/talks/wat WAT.
  4. C. A. R. Hoare Invented quicksort … and the null

    pointer “The most important property of a program is whether it accomplishes the intention of its user.”
  5. Real World Example #1 Type Juggling → WordPress Rest API

    content injection https://blog.sucuri.net/2017/02/content-injection- vulnerability-wordpress-rest-api.html
  6. • Posts have an integer id property that’s populated by

    URL router • WordPress sometimes prioritises HTTP GET and POST vars instead • WordPress inconsistent in usage of router id vs request id /wp-json/wp/v2/posts/123 /wp-json/wp/v2/posts/123?id=456haxxed
  7. > update_item_permissions_check() {
 $post = get_post( $request_id );
 
 if($post

    && <no auth>) {
 return WP_Error 
 }
 
 return true;
 } • Our request_id, 456haxxed is nonsense • So no $post is found • Skip right through to return true;
  8. > update_item() {
 $id = (int) $request_id;
 $post = get_post(

    $id );
 if($post) do_update_stuff();
 }
  9. > update_item() {
 $id = (int) $request_id;
 $post = get_post(

    $id );
 if($post) do_update_stuff();
 } /wp-json/wp/v2/posts/123?id=456haxxed hax0red j00r bl0g
  10. ✓ Avoid type juggling wherever possible
 Use strict comparison operators

    e.g. === ✓ Use the right tools for the job
 i.e. hash_equals for timing-safe string comparison ✓ … don’t forget to use them for things like CSRF tokens ✓ Consider promoting primitives to objects
 i.e. function verify(ResetToken $t) : boolean ✓ Consider phpdoc annotations & static analysis
 /** @returns boolean **/
 https://github.com/exakat/php-static-analysis-tools ✓ Read www.phptherightway.com
  11. > array_filter($input, $callback)
 array_map($callback, $input)
 mostly just annoying ✓ Use

    the newer OO APIs where available ✓ Consider third party libs e.g. underscore-php for fnprog
  12. > strip_tags() > strip_tags($str, $allowable_tags)
 welcome to the land of

    XSS ✓ Use HTMLPurifier or OWASP AntiSammy ✓ Check that your template engine does too
  13. > addslashes()
 when all you have is a hammer, 


    everything looks like a nail. ✓ Use an escaping technique fit for purpose - whether you are escaping for the shell, or SQL
  14. > (pre 7.0) mysql_escape_string()
 totally insecure. deprecated in 2002 (!)


    still works in PHP 5.6 (!?!) > (mysql/mysqli)_real_escape_string()
 be very careful—don’t trust this with your life ✓ Use PDO, and PDO::quote ✓ Use prepared statements / parameterised queries ✓ Try not to build SQL with _printf()
  15. > escapeshellarg()
 Sometimes OK in controlled env
 But it’s impossible

    to use safely in all envs > escapeshellcmd()
 Completely insecure
 Actually undermines previous sanitisation ✓ Never use escapeshellcmd ✓ Avoid functions that use it internally - mail(), system() etc. ✓ Avoid shelling out if you can ✓ Use pcntl extension with pcntl_fork and pcntl_exec
  16. > unserialize()
 Not for user input, ever
 Potentially lets users

    control code execution ✓ Never unserialise() user input ✓ Use an interchange format instead ✓ JSON, Protobufs, Msgpack, Thrift are all good choices
  17. > include() / require()
 A bit of a relic. There’s

    a better way™ ✓ Use PHP’s autoloader ✓ Use PSR-1 or PSR-4 and Composer
  18. > mcrypt
 Most popular* crypto extension. Unmaintained since 2007. ✓

    Use libsodium (+Halite), or OpenSSL instead ✓ Use random_bytes or ircmaxell\RandomLib for csprng ✓ Don’t roll your own crypto!
  19. Real World Example #2 Type Juggling → Bypass authentication in

    “Simple Machine Forums” https://www.alertlogic.com/blog/writing-exploits-for- exotic-bug-classes-php-type-juggling/
  20. • Code verifying a password recovery token • Tokens generated

    randomly and emailed to user • User input hashed, then first 10 chars are compared if ( 
 substr(md5($input), 0, 10)
 == 
 substr($storedHash, 0, 10)
 )
  21. > substr($storedHash,0,10) string “0e12345678” > $input = “190539”; > substr(md5($input),0,10)

    string “0e25261622” > (“0e12345678” == “0e25261622”) → bool(?)
  22. > substr($storedHash,0,10) string “0e12345678” > $input = “190539”; > substr(md5($input),0,10)

    string “0e25261622” > (“0e12345678” == “0e25261622”) → bool(true) gained admin access
  23. • Hashes can look like scientific notation “0e12345678” • ==

    operator may coerce either/both sides to ints • …if they “look like numbers” if ( 
 substr(md5($input), 0, 10)
 == 
 substr($storedHash, 0, 10)
 )
  24. Real World Example #3 Type Juggling → Object Injection →

    SQLi
 in Expression Engine https://foxglovesecurity.com/2017/02/07/type-juggling- and-php-object-injection-and-sqli-oh-my/
  25. • Cookie $payload storing serialised PHP objects • Cookie signed

    with $signature, supplied in cookie itself • Valid signature in theory shows cookie untampered • sess_crypt_key is private • But $payload is entirely user controlled if ( md5( $payload . $this->sess_crypt_key ) == $signature )
  26. • unserialize() sets username to an actual instance of EE’s

    Token\Variable class, instead of a plain string • System passes username from the new object to EE’s SQL engine where() a:1:{s:13:”:new:username”;O:67:"EllisLab\\\ \\ExpressionEngine\\\\\Library\\\\\Parser\\ \\\Conditional\\\\\Token\\\\Variable":1:{s: 6:"lexeme";s:1:"'; DROP TABLE `users`;”;}}
  27. • EE’s SQL escape routine allows __toString() of this object

    to be included in query verbatim • Instance’s state can be crafted as needed by adjusting serialised data a:1:{s:13:”:new:username”;O:67:"EllisLab\\\ \\ExpressionEngine\\\\\Library\\\\\Parser\\ \\\Conditional\\\\\Token\\\\Variable":1:{s: 6:"lexeme";s:1:"'; DROP TABLE `users`;”;}}
  28. > @fopen(‘http://non-existant.file', ‘r’); • allow_url_fopen controls whether this works at

    all • … unless it’s in disable_functions • @ operator suppresses the does-not-exist warning • … unless scream.enabled is set in php.ini • … or it was set with ini_set() • error_reporting setting can also alter this
  29. • display_errors
 Whether errors are output to the browser •

    allow_url_include
 Whether you can include() code from a remote URL • allow_url_fopen
 Whether fopen() can load remote files (use cURL!) • session.use_cookies
 Whether sessions use cookies or URL parameters • zend.multibyte
 Allows encodings like SJIS/BIG5 to be used for source files
  30. ✓ You must control your environment ✓ …or trust whoever

    does ✓ Make environment config part of your deployment ✓ Consider Suhosin if running third party apps ✓ Be careful with Apache mod_php
 What if a user uploads dodgy_script.php7 ? ✓ Use php.ini security analysis tools
 e.g. https://github.com/sektioneins/pcc 

  31. Link-fu • PHP The Right Way - http://www.phptherightway.com/ • PHP:

    A fractal of bad design
 https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/ • WordPress vulnerability discussed:
 https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html • Simple Machine Forums vulnerability discussed:
 https://www.alertlogic.com/blog/writing-exploits-for-exotic-bug-classes-php-type-juggling/ • ExpressionEngine vulnerability
 https://foxglovesecurity.com/2017/02/07/type-juggling-and-php-object-injection-and-sqli-oh-my/ • OWASP Resources
 Both of these are “work in progress” / drafts
 https://www.owasp.org/index.php/PHP_Security_Cheat_Sheet 
 https://www.owasp.org/index.php/PHP_Configuration_Cheat_Sheet 
 https://www.owasp.org/index.php/PHP_Object_Injection • PHP Configuration Checker (php.ini)
 https://github.com/sektioneins/pcc • All about shell escaping & php
 https://gist.github.com/Zenexer/40d02da5e07f151adeaeeaa11af9ab36 • List of static analysis tools for PHP
 https://github.com/exakat/php-static-analysis-tools • Gary Bernhardt’s “WAT” talk
 https://www.destroyallsoftware.com/talks/wat