Sec Bug->Bug #81711 [Opn]: Private properties data leak by casting object to array
| From: | stas@php.net | Date: | Wed, 23 Feb 2022 18:15:33 +0000 |
| Subject: | Sec Bug->Bug #81711 [Opn]: Private properties data leak by casting object to array | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-239998@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=81711&edit=1
ID: 81711
Updated by: stas@php.net
Reported by: black at scene-si dot org
Summary: Private properties data leak by casting object to
array
Status: Open
-Type: Security
+Type: Bug
Package: Class/Object related
Operating System: Linux, web
PHP Version: 8.1.3
Block user comment: N
Private report: Y
New Comment:
Visibility is not a security feature.
Previous Comments:
------------------------------------------------------------------------
[2022-02-23 11:09:08] black at scene-si dot org
Description:
------------
In order to access private variables, one needs to reach the variable either with the scope (an
instance of the class and it's descendants, bound variables, etc.), or specific functions like
var_dump, reflection, zvar. These things can be disabled with the
disable_function
options for the paranoid among us, but the document behaviour of (array) casting
cannot.
[1] I started this with the wish to expand an object with each property corresponding to a named
argument. My idea was that ...$obj or ...(array)$obj should work.
Apparently, objects are not Traversable
[2] Casting an object to (array) exposes private variables, prefixed with the class name. This is
actually documented
[here](https://www.php.net/manual/en/language.types.array.php#language.types.array.casting)
[3] Case 3 is a successful output - ...get_object_vars($obj) behaves exactly as I would
like ...$obj or ...(array)$obj to behave.
[4] Just demonstrating that $a->b is unreachable from the current scope.
I am forced to conclude:
- casting objects to arrays with (array) is a data exfiltration vector, which can't be disabled
with disable_functions or other php.ini settings,
- it's a valid step in a digital hack: after gaining RCE, even with the strictest
disable_function settings, it's possible to escalate the hack by reading values which would
otherwise be protected by program scope, possibly exposing database connection details and other
credentials for private and public services.
My recommendation:
1. Support ...$obj for argument unpacking, don't expose private vars,
2. Modify behavior of (array)$obj, don't expose private vars,
3. Provide a std class for objects to implement Traversable over their properties (slightly related
to 1. as a way to implement).
Test script:
---------------
<?php
// Repro code: https://www.tehplayground.com/WL0AFQHHybPQI1St
class A {
public string $a = "hello world";
private string $b = "from hell";
}
function PrintGreeting($a = "", $b = "from croatia") {
echo "$a $b\n";
}
$a = new A;
foreach ($a as $k => $v) {
echo "k=$k v=$v\n";
}
echo "====\n";
var_dump((array)$a);
try {
PrintGreeting(...$a);
} catch (\TypeError $e) {
echo "[1] Caught TypeError: " . $e->getMessage() . "\n";
}
try {
PrintGreeting(...(array)$a);
} catch (\Error $e) {
echo "[2] Caught Error: " . $e->getMessage() . "\n";
}
$a->a = "[3] hello world";
PrintGreeting(...get_object_vars($a));
try {
echo $a->b;
} catch (\Error $e) {
echo "[4] " . $e->getMessage() . "\n";
}
Expected result:
----------------
k=a v=hello world
====
array(2) {
["a"]=>
string(11) "hello world"
}
[1] Caught TypeError: Only arrays and Traversables can be unpacked
[2] Caught Error: Unknown named parameter $
hello world from croatia
[4] Cannot access private property A::$b
- (array) cast should be equal to get_object_vars()
Actual result:
--------------
k=a v=hello world
====
array(2) {
["a"]=>
string(11) "hello world"
["Ab"]=>
string(9) "from hell"
}
[1] Caught TypeError: Only arrays and Traversables can be unpacked
[2] Caught Error: Unknown named parameter $
[3] hello world from croatia
[4] Cannot access private property A::$b
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=81711&edit=1