### Cloud Computing v1
*A misc challenge that has nothing to do with the cloud, nor with computing.*
> Welcome to our new cloud function computing platform, enjoy here. http://pwnable.org:47780/
With those quoted words as the only description, a look at the website kindly gives us some of its source code:
```php So we upload a php file, it gets filtered by a WAF which we can't see, and then we can execute it?
We figured out what it does not like pretty quickly:
* dots  So no domains* spaces* underlines  So no `$_GET`* capital letters* Any input above 35 characters
> `"$()/;<=>?[\]{}~` all allowed, other ascii symbols banned> \- hyperreality [cr0wn]
We also noticed that some functions are disabled.
> I don't think scandir works. neither does exec, system or passthru.> \- Sansero [flagbot]
#### Digression
Let's make those things work. That was not a fruitful approach, but we did nonetheless consider some options.
In order to work around the character count limit, it would be nice if we could include a file from our own domain. For that, we'd need a short enough domain. But wait! domains contain dots!
To work around that problem, we can specify the IP address [without dots, as a decimal number](https://superuser.com/questions/857603/are-ip-addresses-with-and-without-leading-zeroes-the-same#comment1136605_857609).
Or we could also store the actual payload in a different GET parameter and eval that. But we don't have underscores. Since we can use eval though, we can use the character code.
> ` \- bazumo [flagbot]
#### Back On Track
At this point, bazumo made an important discovery: if we specify in the GET request `data` as an array - i.e. `?data[]=print("HELLO");` instead of `?data=print("HELLO");` - waf does not complain!
> that's sick, we can now get arbitrary length payloads> \- hyperreality [cr0wn]
> wrong.>> > Request-URI Too Long> > The requested URL's length exceeds the capacity limit for this server.> > *Apache/2.4.38 (Debian) Server at 172.23.0.2 Port 8000*>> pasting a complete urlencoded c99 shell is too much.>> \- LucidBrot [flagbot]
With `get_defined_functions(true)` we get a list of only the enabled functions. 
I soon came to believe that `readfile(index.php)` did not work despite being on that list whereas `highlight_file(index.php)` worked. In retrospect, that issue was probably just my browser which decided to put the output of `readfile` into a comment instead of printing it on the page, because I hadn't wrapped the call in `print()`.
First of all, we tried to activate all [error_reporting](https://www.php.net/manual/en/function.error-reporting.php).
> ````>  error_reporting ([ int$level ] ) : int> ````>> The **error_reporting()** function sets the [error_reporting](https://www.php.net/manual/en/errorfunc.configuration.php#ini.error-reporting) directive at runtime. PHP has many levels of errors, using this function sets that level for the duration (runtime) of your script. If the optional `level` is not set, **error_reporting()** will just return the current error reporting level.
The documentation sounds straightforward enough. But it is not, unless one reads closely. `$level` is a bitmask, so we actually needed to call `error_reporting(-1);`, which I find really counterintuitive.
Anyway, with error reporting enabled, we have something to work with:
> Warning: readfile(): **open_basedir restriction in effect.** File(/var/www/html/sandbox/index.php) is not within the allowed path(s): (/var/www/html/sandbox/1c8606cc4b48bc4fc247f31cdd94ceced948d144/) in /var/www/html/sandbox/1c8606cc4b48bc4fc247f31cdd94ceced948d144/index.php on line 1
At that point, tamas_dxw had a clue.
#### Phuck3
> as opposed to `easyphp`, we have a writable subdirectory and we can call `ini_set` / `ini_alter` so we could probably break out of open_basedir jail with `phuck3` (or something from [here](https://github.com/w181496/Web-CTF-Cheatsheet#open_basedir繞過))> \- tamas_dxw [emwtf]
And finally, we were able to read the file `function.php`!
The point of `phuck3` is to trick `open_basedir` using chdir, as advised by the docs:
> When a script tries to access the filesystem, for example using [include](https://www.php.net/manual/en/function.include.php), or [fopen()](https://www.php.net/manual/en/function.fopen.php), the location of the file is checked. When the file is outside the specified directory-tree, PHP will refuse to access it. All symbolic links are resolved, so it's not possible to avoid this restriction with a symlink. If the file doesn't exist then the symlink couldn't be resolved and the filename is compared to (a resolved) **open_basedir**.>> The special value `.` indicates that the working directory of the script will be used as the base-directory. This is, however, a little dangerous as the working directory of the script can easily be changed with [chdir()](https://www.php.net/manual/en/function.chdir.php).>> Source: [php.net](https://www.php.net/manual/en/ini.core.php#ini.open-basedir)
We are actually allowed to modify our `open_basedir` settings - as long as we are setting them to a directory we are allowed to access.
So let us do exactly that, in a totally innocent way.
1. Create a directory within our personal sandbox directory, because we have write access there.2. `chdir` into that new directory.3. `ini_set('open_basedir','..');`   That is totally legal, because we are allowed to access the parent directory anyway.4. Move into the parent directory. That's allowed by `open_basedir`.5. Do that again. And again. Until we're at the `/` directory.6. `ini_set('open_basedir','/')`;7. We now can access any directory we want - as long as the permissions of the directory allow us, of course. We only circumvented the `open_basedir` restriction.
#### Waf Sucks
Of course we can now read `function.php`:
```phpfunction waf($data=''){  if (strlen($data) > 35) {    return true;  }  if (preg_match("/[!#%&'*+,-.: \t@^_`|A-Z]/m", $data, $match) >= 1) {    return true;  }  return false;}
function initShellEnv($dir){  ini_set("open_basedir", "/var/www/html/$dir");}```
Now we can understand why that array bypass worked.
Firstly, `strlen` is applied to the data. If the data is an array, it will be implicitly converted to string. And `"Array"` is shorter than 35 characters.
Secondly, `preg_match` checks for violating data with a regex on a target *string*. > `preg_match()` returns `1` if the pattern matches given subject, `0` if it does not, or `FALSE` if an error occurred.> > **Warning**This function may return Boolean `FALSE`, but may also return a non-Boolean value which evaluates to `FALSE`. > Source: [php.net](https://www.php.net/manual/en/function.preg-match.php)
So if an error happens within `preg_match`, it will return something that can be interpreted as an integer of value `0`. Which is not `>= 1`.
#### Get The Flag
One `file_get_contents('/flag')` later and we had... *something*.
> wtf, there's a file at `/flag` but it's garbage> \- hyperreality [cr0wn]
> send it anyway>> \- LucidBrot [flagbot]
Downloading it in a [different way](https://stackoverflow.com/a/7263944/2550406) as an octet-stream gives us a gzip archive. And within that there's a `flag.img`.
```bash$ file flag.imgflag.img: Linux rev 1.0 ext2 filesystem data (mounted or unclean), UUID=d4d08581-e309-4c51-990b-6472ba249420 (large files)```
Obviously a mountable filesystem. But when we mounted it, all we got was an empty drive with only a `lost+found` dir which was empty as well.
There must be something hidden.
```bash$ binwalk file 
DECIMAL       HEXADECIMAL     DESCRIPTION--------------------------------------------------------------------------------0             0x0             Linux EXT filesystem, rev 1.0, ext2 filesystem data (mounted or unclean), UUID=d4d08581-e309-4c51-990b-6472ba24ba2446080         0xB400          PNG image, 728 x 100, 8-bit/color RGB, non-interlaced46121         0xB429          Zlib compressed data, default compression```
Right! There's some additional PNG image as well as some compressed data. Those files can be extracted using `binwalk --dd=".*" file`.
And in the PNG resides the flag!