Rating:

[中文](./readme_zh.md) [English](./readme.md)

[Docker](./docker) [Exp](./exp.py)

# CloudMusic_rev WriteUp

Here is 1.0 version writeup:

[impakho/ciscn2019_final_web1](https://github.com/impakho/ciscn2019_final_web1)

This challenge is 2.0 version.

Read the webpage source code, find out `#firmware` in `index` page.

![1](./img/1.png)

`#firmware` page, only admin permitted.

![2](./img/2.png)

And then reg and login. In my share page, you can see a english song, the others are chinese songs.

This english song is the default song in web music player.

So go to see the source code of `#share`. You can see `/media/share.php?` and `btoa`. It's not difficult to find an arbitrary file read exploit here.

![3](./img/3.png)

Try to read `../index.php` and visit `http://127.0.0.1/media/share.php?Li4vaW5kZXgucGhw`.

![4](./img/4.png)

It blocks `.php` file. But it gives us a hint, we can use `urlencode` to bypass.

![5](./img/5.png)

Read the `../index.php` file successfully. We can also read other file.

We have to read the admin password to use `#firmware`.

The exploit is in `/include/upload.php`. It use `/lib/parser.so` to parse the mp3 file we uploaded. It checks the admin password inside `.so`.

![6](./img/6.png)

We use `IDA` to decompile `/lib/parser.so` file. The exploit is in `read_title` / `read_artist` / `read_album` function which use `strcpy`. We can `off by null` to set `mframe_data` first byte to `0x00`. Then we can read `mem_mpasswd` aka admin password.

![7](./img/7.png)

Comparing to `1.0 version`, this is a wrong `parser.so`. It uses `strlen` to get the length. `unicode` will not work anymore.

We can make a frame (length: `0x70`), and then upload the mp3 file to get admin password.

The `mp3` file is in `exp.py` script.

We use admin password to login, and visit `#firmware`.

![8](./img/8.png)

![9](./img/9.png)

Leak the source code, and read `#firmware` source code.

We can upload a `.so` file here. And guess the filename based on server time.

Finally, load the `.so` file to `rce`.

We can use `__attribute__ ((constructor))` to `rce`.

Just like this:

```
#include <stdio.h>
#include <string.h>

char _version[0x130];
char * version = &_version;

__attribute__ ((constructor)) void fun(){
memset(version,0,0x130);
FILE * fp=popen("/usr/bin/tac /flag", "r");
if (fp==NULL) return;
fread(version, 1, 0x100, fp);
pclose(fp);
}
```

![10](./img/10.png)

Comparing with `1.0` version, there is no `rce` result here.

So we can write the result to `/uploads/firmware/` or `/uploads/music/`.

Our user `www-data` have no permission to read the `/flag` file.

We need to find `suid` program to read the flag. `/usr/bin/tac` have `suid` permission. So we can use it to read the flag.

The payload is `/usr/bin/tac /flag > /var/www/html/uploads/firmware/xxxxx`.

Script to `getflag`, see the `exp.py` file.

Flag:`de1ctf{W3b_ANND_PWNNN_C1ou9mus1c_revvvv11}`