October 22, 2026
PHP2Shell: Fixing GHSA-w8hw-w9fx-j79w
Web Exploitation | PHP | GHSA-w8hw-w9fx-j79w
By Hacktify
2 min read
The scenario
SkillBit's PHP2Shell challenge puts you in a familiar ops situation. A public exploit called php2shell targets the PHP build behind a fictional company's website (Inkline Print Co.). The proper fix is upgrading to PHP 8.5.12, but that has to wait for the next maintenance window.
The task: apply the interim configuration from the PHP security advisory (GHSA-w8hw-w9fx-j79w, a multipart request parsing out-of-memory issue), reload Apache with reload-apache, and keep artwork uploads working. The flag appears on the site once the mitigation is live.
This is defense work, not exploitation. The skill being tested is applying a vendor workaround correctly under real constraints.
Initial audit
The lab ran Apache with mod_php (PHP 8.5.11) on 127.0.0.1:8080, with the config at /etc/php/apache2/php.ini. The relevant baseline values:
max_multipart_body_parts = 110
memory_limit = 128Mmax_multipart_body_parts = 110
memory_limit = 128MThe Apache site config also set a request size cap:
<VirtualHost *:8080>
DocumentRoot /opt/lab/source
LimitRequestBody 131072
</VirtualHost><VirtualHost *:8080>
DocumentRoot /opt/lab/source
LimitRequestBody 131072
</VirtualHost>LimitRequestBody rejects anything over 128 KB with an HTTP 413 before PHP ever sees it. That's a useful extra layer, but it doesn't replace the advisory's fix, since smaller multipart requests still reach the parser.
The hurdle: sed -i fails
The advisory change was a one-line edit to memory_limit, so I tried the obvious approach:
sed -i 's/^memory_limit.*/memory_limit = -1/' /etc/php/apache2/php.ini
# sed: couldn't open temporary file /etc/php/apache2/sed...: Permission deniedsed -i 's/^memory_limit.*/memory_limit = -1/' /etc/php/apache2/php.ini
# sed: couldn't open temporary file /etc/php/apache2/sed...: Permission deniedThe permissions explain why:
ls -l /etc/php/apache2/php.ini
# -rw-rw----. 1 root lab 70322 Oct 22 11:52 /etc/php/apache2/php.inils -l /etc/php/apache2/php.ini
# -rw-rw----. 1 root lab 70322 Oct 22 11:52 /etc/php/apache2/php.iniMy user (group lab) can write to the file, but not to its parent directory. sed -i doesn't edit in place. It writes a temporary file in the same directory and renames it over the original, so it fails when the directory isn't writable.
The fix
Stage the edited copy in /tmp, then write it back into the existing file. Redirecting into the file only needs write access to the file itself, not the directory.
cat /etc/php/apache2/php.ini | sed 's/^memory_limit\s*=.*/memory_limit = -1/' > /tmp/php.ini
cat /tmp/php.ini > /etc/php/apache2/php.inicat /etc/php/apache2/php.ini | sed 's/^memory_limit\s*=.*/memory_limit = -1/' > /tmp/php.ini
cat /tmp/php.ini > /etc/php/apache2/php.iniThen reload Apache with the lab's helper:
reload-apache
# {"ok": true, "message": "Apache restarted."}reload-apache
# {"ok": true, "message": "Apache restarted."}And check the result with the challenge's validation endpoint:
curl -sS http://127.0.0.1:9000/result
# {"passed": true, "flag": "SkillBit{...}"}curl -sS http://127.0.0.1:9000/result
# {"passed": true, "flag": "SkillBit{...}"}The mitigation was accepted and uploads kept working.
Takeaways
- Config-level workarounds buy time. When a full upgrade has to wait, a vendor-recommended setting can reduce exposure in the meantime. Track it as temporary and schedule the real upgrade.
- File permissions and directory permissions are different. Write access to a file lets you change its contents with redirection, even when sed -i fails because the parent directory is locked down.
- Layered controls help. Apache's LimitRequestBody acts before PHP runs, so it blocks oversized requests early. It complements application-level fixes rather than replacing them.
- Verify after every change. Reload the service, then confirm the change actually took effect.