I recently came across an interesting Local File Inclusion vulnerability in a private bug bounty program which I was able to upgrade to a Remote Code Execution. The interesting fact about this and what makes it different is that the underlying operating system was pretty hardened and almost all usual ways to upgrade your LFI were blocked or failed silently.
So here’s a short write-up about a handy way to upgrade your LFI, for which I’d also like to credit my fellow bug bounty hunter @smiegles for his tip which finalised my exploit!
Bonus point: It was an unauthenticated Remote Code Execution on a login form ;-)
A Simple Local File Inclusion Vulnerability
The vulnerable web application basically required some form of authentication before giving access to an administrative upload interface. However the application did also use one smelly parameter called “lang” to specify the language for its UI:
So this was quite obvious to exploit - at the very first glance - you only had to use path traversal sequences:
This simply fetched the /etc/passwd file and echoed it back in the HTTP response.
Some common ways of upgrading from LFI to RCE
Now usually when I find a Local File Inclusion, I first try to turn it into a Remote Code Execution before reporting it since they are usually better paid ;-).
So there’s a variety of different tricks to turn your LFI into RCE, just like:
- Using file upload forms/functions
- Using the PHP wrapper expect://command
- Using the PHP wrapper php://file
- Using the PHP wrapper php://filter
- Using PHP input:// stream
- Using data://text/plain;base64,command
- Using /proc/self/environ
- Using /proc/self/fd
- Using log files with controllable input like:
Unfortunately the system was quite hardened and the Apache process did neither have the necessary wrappers enabled nor had it access to any log files or the /proc tree - so any of the above mentioned ways was silently failing with the application just returning its normal “authentication failed” response.
RCE Using Control over PHP Session Values
While trying to find a way to get some arbitrary code executed without having access to the administrative interface itself, I noticed a quite interesting behaviour of the web application. When trying to authenticate with invalid credentials such as admin/admin:
The application issued a couple of Set-Cookie instructions like the following, which strongly indicates that the values from the authentication requests might be stored within the PHP session on the server side:
As you might know PHP5 stores it’s session files by default under /var/lib/php5/sess_[PHPSESSID] - so the above issued session “i56kgbsq9rm8ndg3qbarhsbm27” would be stored under /var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
When having a look at the session file itself using the previously discovered LFI vulnerability:
indicators even got stronger:
So as a consequence out of this, using some arbitrary PHP code as the username:
Resulted in an arbitrary value being set in the Set-Cookie directive (and therefore in the session file):
The session file could again afterwards be included using the LFI (note that you need to remove the cookie from the request, otherwise it would get overwritten again and the payload would fail):
Which resulted in the final Remote Code Execution:
Take aways: Never forget to sanitize your session variables! ;-)