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.”
Slide 11
Slide 11 text
Real World Example #1
Type Juggling → WordPress Rest API content injection
https://blog.sucuri.net/2017/02/content-injection-
vulnerability-wordpress-rest-api.html
Slide 12
Slide 12 text
• 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
Slide 13
Slide 13 text
> update_item_permissions_check() {
$post = get_post( $request_id );
if($post && ) {
return WP_Error
}
return true;
}
• Our request_id, 456haxxed is nonsense
• So no $post is found
• Skip right through to return true;
✓ 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
Slide 20
Slide 20 text
• The language itself
• Dangerous stdlib & extensions
• PHP deployment & php.ini
Slide 21
Slide 21 text
> 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
Slide 22
Slide 22 text
> 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
Slide 23
Slide 23 text
> 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
Slide 24
Slide 24 text
> (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()
Slide 25
Slide 25 text
> 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
Slide 26
Slide 26 text
> 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
Slide 27
Slide 27 text
> include() / require()
A bit of a relic. There’s a better way™
✓ Use PHP’s autoloader
✓ Use PSR-1 or PSR-4 and Composer
Slide 28
Slide 28 text
> 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!
Slide 29
Slide 29 text
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/
Slide 30
Slide 30 text
• 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)
)
• 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)
)
Slide 34
Slide 34 text
No content
Slide 35
Slide 35 text
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/
Slide 36
Slide 36 text
• 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
)
Slide 37
Slide 37 text
• 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`;”;}}
Slide 38
Slide 38 text
• 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`;”;}}
Slide 39
Slide 39 text
Don’t roll your own
cryptography
Slide 40
Slide 40 text
• The language itself
• Dangerous stdlib & extensions
• PHP deployment & php.ini
Slide 41
Slide 41 text
> @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
Slide 42
Slide 42 text
• 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
Slide 43
Slide 43 text
✓ 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
Slide 44
Slide 44 text
–Porky Pig
“That’s all folks!”
Slide 45
Slide 45 text
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