# 0CTF/TCTF 2020 Quals

I played this CTF with the UK and Swiss based "Organizers" team.

Wechat Generator  
\---  
> Come and check my latest innovation! It's normal to encounter some bugs and
> glitches since it is still in beta development.  
> http://pwnable.org:5000/

I worked on this challenge together with bazumo, Robin and Titus.

We are presented with a website where we can customise a WeChat conversation:

![](images/index.png)

Clicking "Preview" here POSTs the data
`[{"type":0,"message":"Love+you!"},{"type":1,"message":"Me+too!!!"}]` to the
server. The server returns both a "previewid" UUID to identify the image, and
the rendered image itself in `data:image/svg+xml;base64` format, replacing the
picture on the right.

Clicking "Share" POSTs the "previewid" to the server, which sends back a URL
to view the image you created. Looking more closely, the image has a path of
the form `/image/PwVYJO/png`, and is not stored in a static directory. In
fact, the image is rendered each time it is requested, and by changing `png`
to other common image formats, it will render in those formats instead. Even
PDF is supported!

At this point we know it's almost certainly ImageMagick processing our SVG
data on the backend, which opens up a whole can of potential security issues.
However, there are a couple limitations – `/image` returns an error if the
chosen format is not three letters long, plus some incredibly dangerous
formats that [ImageMagick
supports](https://imagemagick.org/script/formats.php) such as Magick Scripting
Language (MSL) are not available.

The WeChat generator has emojis, which are encoded in square brackets e.g.
`[smile]`. By looking at the generated SVG, we can see that this gets replaced
with an `<image>` tag that has a `link:href` attribute pointing to a static
image. We can close the image tag ourselves and add arbitrary SVG data which
will get processed by ImageMagick.

For instance, the following triggers a HTTP request from the server to our
[requestbin](https://requestbin.com/) when viewing the rendered PNG:

```  
[{"type":1,"message":"[lmfao.png"/> <image height="1600" width="1000"
xlink:href="http://dn3sdwb1d1noc.x.pipedream.net/" /> ] "}]  
```

If we link to an SVG on our server, ImageMagick will additionally parse the
`xlink:href` inside that. At this point we spent a long time trying common SVG
payloads hoping to get LFI or even RCE (ImageTragick). Finally, we discovered
an [obscure feature of ImageMagick](http://en3srwbud1noc.x.pipedream.net/),
the `text:` input format. It gives us LFI that we can see get rendered in our
image! For example if we use `xlink:href="text:/etc/passwd"` in the above
payload, we get:

![](images/lfi.png)

Visiting `/flag` prints "Good job! Now what's the next step?". A single step
would of course be too easy for this CTF.

We use the LFI to further explore the filesystem and eventually discover the
source code for the application in `/app/app.py`. The `text:` trick only
renders the first "page", and there seems to be no way to view more. It's
enough to see a good amount of the source though:

![](images/lfi2.png)

This reveals a new route, `/SUp3r_S3cret_URL/0Nly_4dM1n_Kn0ws`. We can post a
URL path from the same website to the Chrome WebDriver "admin". The aim is now
to find an XSS vulnerability to exploit him with.

![](images/admin.png)

We can't just send a link to an SVG with `<script>alert(1)</script>` in it,
due to the restrictive Content Security Policy which blocks inline scripts:

```  
img-src * data:; default-src 'self'; style-src 'self' 'unsafe-inline';
connect-src 'self'; object-src 'none'; base-uri 'self'  
```

We need to find a way of sourcing a script that includes XSS, from the same
origin.

The other notable part of the source is an additional `callback` parameter for
the error handler. This allows us to add three characters then an equals
before any JSON error thrown by the website. This appears to give us so few
characters that it's useless, on the other hand it doesn't make sense unless
it's part of the solution.

After trying many, many image formats and payloads, finally bazumo spotted
something we should have noticed much earlier. There's an injection into the
`previewid` parameter of `/share`, where the error reflects our input back.
For instance an input of `' + alert(1); b = {'c': "` results in:

```  
{"error": "Convert exception: unable to open image `previews/' + alert(1); b =
{'c': " ': No such file or directory @ error/blob.c/OpenBlob/2874"}  
```

By using the weird `callback` parameter we can introduce a single quote in
front of the JSON, and turn this into valid Javascript where our injection
gets executed. As this response is coming from the website itself, it passes
the CSP protection, and can be included in a script tag in an SVG we send the
admin to win. `src` is blocked but we can use `xlink:href` on the script tag –
this is valid SVG. After sending this to the admin, he gives us the flag,
which is indeed what we wanted as a reward for hours of reading about the SVG
format.

Included is a script [wechat_solve.py](wechat_solve.py) which automates the
whole process.  

Original writeup (https://github.com/hyperreality/ctf-
writeups/blob/master/2020_tctf/README.md).# 0CTF/TCTF 2020 Quals

I played this CTF with the UK and Swiss based "Organizers" team.

Wechat Generator  
\---  
> Come and check my latest innovation! It's normal to encounter some bugs and
> glitches since it is still in beta development.  
> http://pwnable.org:5000/

I worked on this challenge together with bazumo, Robin and Titus.

We are presented with a website where we can customise a WeChat conversation:

![](images/index.png)

Clicking "Preview" here POSTs the data
`[{"type":0,"message":"Love+you!"},{"type":1,"message":"Me+too!!!"}]` to the
server. The server returns both a "previewid" UUID to identify the image, and
the rendered image itself in `data:image/svg+xml;base64` format, replacing the
picture on the right.

Clicking "Share" POSTs the "previewid" to the server, which sends back a URL
to view the image you created. Looking more closely, the image has a path of
the form `/image/PwVYJO/png`, and is not stored in a static directory. In
fact, the image is rendered each time it is requested, and by changing `png`
to other common image formats, it will render in those formats instead. Even
PDF is supported!

At this point we know it's almost certainly ImageMagick processing our SVG
data on the backend, which opens up a whole can of potential security issues.
However, there are a couple limitations – `/image` returns an error if the
chosen format is not three letters long, plus some incredibly dangerous
formats that [ImageMagick
supports](https://imagemagick.org/script/formats.php) such as Magick Scripting
Language (MSL) are not available.

The WeChat generator has emojis, which are encoded in square brackets e.g.
`[smile]`. By looking at the generated SVG, we can see that this gets replaced
with an `<image>` tag that has a `link:href` attribute pointing to a static
image. We can close the image tag ourselves and add arbitrary SVG data which
will get processed by ImageMagick.

For instance, the following triggers a HTTP request from the server to our
[requestbin](https://requestbin.com/) when viewing the rendered PNG:

```  
[{"type":1,"message":"[lmfao.png"/> <image height="1600" width="1000"
xlink:href="http://dn3sdwb1d1noc.x.pipedream.net/" /> ] "}]  
```

If we link to an SVG on our server, ImageMagick will additionally parse the
`xlink:href` inside that. At this point we spent a long time trying common SVG
payloads hoping to get LFI or even RCE (ImageTragick). Finally, we discovered
an [obscure feature of ImageMagick](http://en3srwbud1noc.x.pipedream.net/),
the `text:` input format. It gives us LFI that we can see get rendered in our
image! For example if we use `xlink:href="text:/etc/passwd"` in the above
payload, we get:

![](images/lfi.png)

Visiting `/flag` prints "Good job! Now what's the next step?". A single step
would of course be too easy for this CTF.

We use the LFI to further explore the filesystem and eventually discover the
source code for the application in `/app/app.py`. The `text:` trick only
renders the first "page", and there seems to be no way to view more. It's
enough to see a good amount of the source though:

![](images/lfi2.png)

This reveals a new route, `/SUp3r_S3cret_URL/0Nly_4dM1n_Kn0ws`. We can post a
URL path from the same website to the Chrome WebDriver "admin". The aim is now
to find an XSS vulnerability to exploit him with.

![](images/admin.png)

We can't just send a link to an SVG with `<script>alert(1)</script>` in it,
due to the restrictive Content Security Policy which blocks inline scripts:

```  
img-src * data:; default-src 'self'; style-src 'self' 'unsafe-inline';
connect-src 'self'; object-src 'none'; base-uri 'self'  
```

We need to find a way of sourcing a script that includes XSS, from the same
origin.

The other notable part of the source is an additional `callback` parameter for
the error handler. This allows us to add three characters then an equals
before any JSON error thrown by the website. This appears to give us so few
characters that it's useless, on the other hand it doesn't make sense unless
it's part of the solution.

After trying many, many image formats and payloads, finally bazumo spotted
something we should have noticed much earlier. There's an injection into the
`previewid` parameter of `/share`, where the error reflects our input back.
For instance an input of `' + alert(1); b = {'c': "` results in:

```  
{"error": "Convert exception: unable to open image `previews/' + alert(1); b =
{'c': " ': No such file or directory @ error/blob.c/OpenBlob/2874"}  
```

By using the weird `callback` parameter we can introduce a single quote in
front of the JSON, and turn this into valid Javascript where our injection
gets executed. As this response is coming from the website itself, it passes
the CSP protection, and can be included in a script tag in an SVG we send the
admin to win. `src` is blocked but we can use `xlink:href` on the script tag –
this is valid SVG. After sending this to the admin, he gives us the flag,
which is indeed what we wanted as a reward for hours of reading about the SVG
format.

Included is a script [wechat_solve.py](wechat_solve.py) which automates the
whole process.  

Original writeup (https://github.com/hyperreality/ctf-
writeups/blob/master/2020_tctf/README.md).# 0CTF/TCTF 2020 Quals

I played this CTF with the UK and Swiss based "Organizers" team.

Wechat Generator  
\---  
> Come and check my latest innovation! It's normal to encounter some bugs and
> glitches since it is still in beta development.  
> http://pwnable.org:5000/

I worked on this challenge together with bazumo, Robin and Titus.

We are presented with a website where we can customise a WeChat conversation:

![](images/index.png)

Clicking "Preview" here POSTs the data
`[{"type":0,"message":"Love+you!"},{"type":1,"message":"Me+too!!!"}]` to the
server. The server returns both a "previewid" UUID to identify the image, and
the rendered image itself in `data:image/svg+xml;base64` format, replacing the
picture on the right.

Clicking "Share" POSTs the "previewid" to the server, which sends back a URL
to view the image you created. Looking more closely, the image has a path of
the form `/image/PwVYJO/png`, and is not stored in a static directory. In
fact, the image is rendered each time it is requested, and by changing `png`
to other common image formats, it will render in those formats instead. Even
PDF is supported!

At this point we know it's almost certainly ImageMagick processing our SVG
data on the backend, which opens up a whole can of potential security issues.
However, there are a couple limitations – `/image` returns an error if the
chosen format is not three letters long, plus some incredibly dangerous
formats that [ImageMagick
supports](https://imagemagick.org/script/formats.php) such as Magick Scripting
Language (MSL) are not available.

The WeChat generator has emojis, which are encoded in square brackets e.g.
`[smile]`. By looking at the generated SVG, we can see that this gets replaced
with an `<image>` tag that has a `link:href` attribute pointing to a static
image. We can close the image tag ourselves and add arbitrary SVG data which
will get processed by ImageMagick.

For instance, the following triggers a HTTP request from the server to our
[requestbin](https://requestbin.com/) when viewing the rendered PNG:

```  
[{"type":1,"message":"[lmfao.png"/> <image height="1600" width="1000"
xlink:href="http://dn3sdwb1d1noc.x.pipedream.net/" /> ] "}]  
```

If we link to an SVG on our server, ImageMagick will additionally parse the
`xlink:href` inside that. At this point we spent a long time trying common SVG
payloads hoping to get LFI or even RCE (ImageTragick). Finally, we discovered
an [obscure feature of ImageMagick](http://en3srwbud1noc.x.pipedream.net/),
the `text:` input format. It gives us LFI that we can see get rendered in our
image! For example if we use `xlink:href="text:/etc/passwd"` in the above
payload, we get:

![](images/lfi.png)

Visiting `/flag` prints "Good job! Now what's the next step?". A single step
would of course be too easy for this CTF.

We use the LFI to further explore the filesystem and eventually discover the
source code for the application in `/app/app.py`. The `text:` trick only
renders the first "page", and there seems to be no way to view more. It's
enough to see a good amount of the source though:

![](images/lfi2.png)

This reveals a new route, `/SUp3r_S3cret_URL/0Nly_4dM1n_Kn0ws`. We can post a
URL path from the same website to the Chrome WebDriver "admin". The aim is now
to find an XSS vulnerability to exploit him with.

![](images/admin.png)

We can't just send a link to an SVG with `<script>alert(1)</script>` in it,
due to the restrictive Content Security Policy which blocks inline scripts:

```  
img-src * data:; default-src 'self'; style-src 'self' 'unsafe-inline';
connect-src 'self'; object-src 'none'; base-uri 'self'  
```

We need to find a way of sourcing a script that includes XSS, from the same
origin.

The other notable part of the source is an additional `callback` parameter for
the error handler. This allows us to add three characters then an equals
before any JSON error thrown by the website. This appears to give us so few
characters that it's useless, on the other hand it doesn't make sense unless
it's part of the solution.

After trying many, many image formats and payloads, finally bazumo spotted
something we should have noticed much earlier. There's an injection into the
`previewid` parameter of `/share`, where the error reflects our input back.
For instance an input of `' + alert(1); b = {'c': "` results in:

```  
{"error": "Convert exception: unable to open image `previews/' + alert(1); b =
{'c': " ': No such file or directory @ error/blob.c/OpenBlob/2874"}  
```

By using the weird `callback` parameter we can introduce a single quote in
front of the JSON, and turn this into valid Javascript where our injection
gets executed. As this response is coming from the website itself, it passes
the CSP protection, and can be included in a script tag in an SVG we send the
admin to win. `src` is blocked but we can use `xlink:href` on the script tag –
this is valid SVG. After sending this to the admin, he gives us the flag,
which is indeed what we wanted as a reward for hours of reading about the SVG
format.

Included is a script [wechat_solve.py](wechat_solve.py) which automates the
whole process.  

Original writeup (https://github.com/hyperreality/ctf-
writeups/blob/master/2020_tctf/README.md).