New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OPCache: Direct execution opcode file without php source code file #6146
Conversation
sync master
Any chance we can have something like |
The |
I'd suggest starting a discussion on the PHP internals mailing list for this feature. |
@nikic |
Anyone is welcome to join: https://www.php.net/mailing-lists.php |
sync master
IMHO the string |
Are opcodes designed to be cross versions compatible? |
@mvorisek can not cross-version. |
@Girgias I was not referring to the speed but to the FFI. And you completely missed my point about having to sell the actual source code. it is not about the obfuscation but protecting what's yours. It is all readable if you ask me. @IMSoP I will edit this one here cause your latest statement seems like a good closure to this discussion. @TysonAndre, thank you sir for spending the time to explain it in simple terms and examples. The only points I totally understand are:
I will back off now. |
If you're talking about licensing implications, then what you're selling is the legal permission to use it in certain ways. That's no different whether the code you're distributing it is PHP, opcodes, or your own custom programming language. For that matter, it's no different whether you're distributing C source code or native x86 machine code. As far as I'm aware, there's no legal definition of "the actual source code" that would make any difference whatsoever. |
There seem to still be some misconceptions about what opcodes are, and therefore what this feature would achieve. Firstly, PHP opcodes are not native machine code. To execute PHP opcodes, you need to run the Zend Engine. As far as I know, the only other things that can do anything with opcodes are debugging tools, most of which are compiled on top of Zend Engine anyway. So opcodes do not make embedding or linking to PHP from other languages or programs any easier. Whether you have PHP source code or opcodes, you will need the Zend Engine to execute them. Secondly, PHP opcodes are not equivalent to Java bytecode or .Net CIL. The JVM and .Net runtime were explicitly designed for portability - "compile once, run anywhere". They define a standardised intermediate language, with strong guarantees about compatibility between versions and environments. To repeat: these are not incidental features, they are at the very heart of the design of these technologies. The Zend Engine in general, and OpCache in particular, has almost the opposite aim: its job is to make code run fast on the current environment. It can and will generate different representations based on factors like:
So opcodes are not, and never will be, a good way to distribute code to multiple targets. If you put opcodes in a PHAR file, that PHAR file is going to be useless to 99.999% of other PHP users, whose environment won't match yours. Finally, opcodes are not a good way to obfuscate PHP. Like portability, obfuscation is not a design aim, and it wouldn't make sense to compromise on other aims for that purpose. If you want to obfuscate PHP code, a standalone tool that operates on the PHP source code will be better in almost every way:
|
Additionally, this might increase download sizes for end users because multiple versions may end up being distributed in the same package depending on the supported platforms (e.g. Include foo.php81_linux.opcache, foo.php82_linux.opcache, foo.php81_windows32.opcache, foo.php82_windows64.opcache, etc.
Open source tools to automate minifying code already exist - e.g. https://github.com/box-project/box/blob/master/doc/configuration.md#compactors-compactors for phars, and probably many others. Stripping out tokens of kind |
There's also security downsides - this makes it much easier for malicious users to obfuscate code (for antivirus products, intrusion detection software, etc) when exploiting vulnerabilities. It also allows attackers to manually craft files with opcodes that would access invalid memory locations or cause php to execute c functions in unexpected ways (currently, the correctness of opcache is the only thing that ensures that the PHP VM doesn't read or write out of bounds memory location, I don't expect that to change)
https://www.gosecure.net/blog/2016/04/27/binary-webshell-through-opcache-in-php-7/ is one example of that and the current things that make it harder for an attacker to overwrite opcache's opcodes (system id) (I've never used this tool and I'm unfamiliar with that blog/company, this was the first google result for vulnerabilities with opcache.file_cache) |
@TysonAndre I guess you did not visit the github of that page where there is a py script to scrape the system_id |
The link was intended to give background for readers unfamiliar with php's opcode cache and what kind of security drawbacks could be expected from systems running opcodes distributed with php source libraries. If the attacker that doesn't have an account on your system can run python scripts (as the same user as the web server), you already have an issue. No, but https://github.com/GoSecure/php7-opcache-override/blob/master/system_id_scraper.py - interesting, it's more predictable that I would have guessed without looking at the code. I would have hoped that immutable system information such as hardware ids could be used if available but I guess the main purpose is just to avoid conflicting with incompatible php opcode versions - hardware ids probably wouldn't work well with docker or virtualization anyway.
For that bug report, I don't think php's maintainers would have interest in exposing functionality that makes it easier to exploit; if a researcher really wanted it, they could publish an external PHP module duplicating the function in opcache to help other security researchers/hobbyists. But the system_id isn't really related to my concern about this RFC - "The RFC allows attackers to manually craft files with opcodes that would access invalid memory locations or cause php to execute c functions in unexpected ways (on systems where |
The goal back then was to have the opcode cache outside of the htroot and 0 byte php files in the htroot. so how is the attacker getting access to the opcode cache ? the attack scenario assumes you can upload a file outside of the htroot. |
Agreed. https://github.com/GoSecure/php7-opcache-override/blob/master/analysis_tools/opcache_disassembler.py exists, for an example (targeting an older php minor version, struct layout and opcodes change in every minor version) PHP itself comes with the facilities to dump opcodes while compiling through https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.opt_debug_level - I imagine the ability to dump opcodes loaded from a file would additionally be added (and would be simple to add) if it became necessary to understand user-submitted bug reports. And opcode editors/decompilers would be created and improved upon if obfuscation became common |
I'm not sure the concept of an "htroot" has much meaning in a lot of modern applications. Dynamic content is rewritten to run a single "front controller" script, and static content is served from a separate directory; the two could be anywhere on disk. Any ".php" files that somehow get into the static content directory can be configured to go nowhere near PHP, and just serve a |
|
If you mean obfuscation, I'd recommend using a php to php obfuscator as in #6146 (comment) If you mean security, then if a single person publishes a "phpo" opcode file that can be used as an exploit or proof of concept (or tool to generate those files), anyone with limited computer knowledge could just copy the exploit, or change that for their purposes. The effect would thankfully be limited due to the system ini setting only affecting people using libraries that would use that functionality, but it's a concern if it is widely used.
Java bytecode has a verifier, and PHP doesn't - there are no plans I know of to add that to php and there are far, far more developers working on Java than on PHP's opcache - https://www.oracle.com/java/technologies/security-in-java.html
|
Might I also recommend looking up prior requests for this feature? The motivations for it have been the same, and the reasons why it's not a good idea haven't changed either.
Agreed, however (as I already said), there are much simpler ways to do this which have much fewer compatibility implications.
Let me get this straight.
Does that describe what you just said?
This one is going to blow your mind. JAR files can be unassembled too. Five seconds of googling found me a handy web-based tool that I need zero technical skill to use.
This is a non-sequiter. Yes, you can use PHP for non-web tasks. That has nothing to do with the argument at hand. Nothing. |
OK so we agree on something and you did get my sample use case right. Thank you. |
Can someone summarize the outcome of the discussions here and on the mailing list? It's unclear to me where things ended up. Is this in or out? |
@ramsey The RFC was voted on and unanimously rejected: https://wiki.php.net/rfc/direct-execution-opcode#vote The most succinct summary is probably the one Sara Golemon put in the voting thread on the mailing list:
|
Closing this per above comments (RFC has been declined). |
Seems like this would primarily benefit someone who wants to hide malware on a compromised server. |
Why this RFC is not approved? We need this feature! |
@hegoku There is a summary of why it was rejected two comments above yours, and you scrolled past most of the detailed discussion right here on this page. Feel free to also read the mailing list archive of the RFC discussion thread and vote announcement thread, which basically cover the same reasons. |
Function change:
$opcode_file
.$opcode_file
, the function will prepend<?phpo
to php opcode file and save to$opcode_file
$opcode_file
is null be same as the current state.<?phpo
tag are prepend to file of opcode only when useingopcache_compile_file()
and provide second args<?phpo
, opcache can be executing it without php source file.opcache.file_cache_only
must be enable andopcache.file_cache
must be setOPCache change:
$opcode_file
that starts with<?phpo
, will remove below verify:Add
opcache.allow_direct_exec_opcode
configuration options1.if set 0, default value, same as the current state, can not direct exec any opcode file
2. if set 1, only when opcode file start is
<?phpo
, direct exec opcode file without php source fileAdd
opcache.prohibit_different_version_opcode
optionNotice:
if code directory tree change, the PHP magic constant associated with a path will invaild
Example
compile php file
myphp.php
, code similar to below:then can exec
php myphp.bin.php
orinclude 'myphp.bin.php'
withoutmyphp.php
myphp.bin.php
will similar to:implementation
compile to file:
php
--->load code
--->compile to opcode
--->store to cache system directory
-->same path file
php
--->load code
--->compile to opcode
--->save to cache system directory
-->same path file
-->copy cache file to the specified pathopcache exec process: