zoho

// TL;DR


How the idea came up

This one wasn’t a planned hunt. I was busy with exams and had only opened my laptop to email some work to a friend. After sending it, I went through my other inboxes — Yahoo, Zoho, Outlook — reading mail, nothing important.

I was about to close the laptop when a thought just clicked: “there’s no image proxy on Zoho Mail — that’d be a bug.” I have no idea why it surfaced. So I went back, opened a random email with images in it, and checked — and there was an image proxy. Slightly deflating. But I copied the image address and opened it in a new tab anyway, and that’s where the actual bug was waiting.

What an image proxy does

When an email contains an image, loading it directly from the sender’s server leaks information about you — your IP, when you opened the mail, your client. To prevent that, webmail services route images through their own image proxy: the proxy fetches the image server-side and serves it to you from the mail provider’s domain, so the original host only ever talks to the proxy.

Zoho’s proxy URL looked like this:

https://mail.zoho.com/zm/ImageProxy?source=https://site.com/pic.jpg&uid=...&mode=mailview

The source parameter is the image the proxy goes and fetches.

The finding: SVG through the proxy

The first thing I tried was simply changing source to a different site’s image:

...ImageProxy?source=https://anothersite.com/pic.jpg&...

Zoho happily fetched and displayed it. So the proxy would retrieve arbitrary URLs, not just images from the original email. That alone was interesting — but the real question was what kind of file it would serve back.

I pointed it at an SVG instead of a JPG:

...ImageProxy?source=https://anothersite.com/pic.svg&...

Zoho displayed that too. So I pointed it at my own SVG crafted to carry a script — mysite.com/xss.svg — and boom: XSS executed, running in the context of mail.zoho.com.

zoho-xss

Why SVG is the key

The whole bug hinges on the difference between a JPG and an SVG.

A JPEG or PNG is just pixel data — harmless to render. SVG is different: it’s XML, and it can contain <script>. When a browser renders an SVG as an actual document (rather than as a flat <img>), that script runs. So if a proxy that’s meant to serve images will fetch and serve an SVG with a content type the browser treats as a renderable document, it becomes an XSS delivery mechanism — the attacker’s script executes under the mail provider’s trusted domain.

That’s the trap: “image proxy” sounds safe because images are inert, but SVG is an image format that is also a scriptable document. Treating all “images” as equally harmless is the mistake.

The fix

The remediation is about how the proxy serves what it fetches:

  • Don’t serve SVG as a renderable document. Force a non-executable content type (e.g. serve as image/png after rasterising, or with Content-Type that the browser won’t execute) and send Content-Disposition: attachment / X-Content-Type-Options: nosniff so it can’t be rendered inline as active content.
  • Restrict what the proxy will fetch and return — validate that the response is a genuine raster image, not arbitrary XML/SVG.
  • Serve proxied content from a sandboxed, cookieless domain, so that even if something does render, it isn’t in the trusted mail.zoho.com origin.

So, I reported the issue quickly to Zoho and they triaged my report very fast after some minutes and rewarded me a $200 bounty.

zoho-reward

Takeaways

  • “Image” is not a synonym for “safe.” SVG is an image and a scriptable XML document. Any feature that accepts, stores, or proxies “images” should be tested with an SVG payload specifically.
  • Image proxies are a rich target. They fetch URLs server-side and serve the result from a trusted origin — test what else they’ll fetch (SSRF angle) and what content types they’ll hand back (XSS angle).
  • Follow the stray thought. This bug started as an idle “I bet there’s no image proxy” as I was closing the laptop. The hunch was wrong, but chasing it anyway led straight to the real issue. Curiosity beats methodology more often than it should.
  • Check the content type, not just the URL. The proxy serving a different site was mildly interesting; the proxy serving a different file type that the browser executes is the actual vulnerability.

Thanks for reading — hope you liked this one. Have a nice day, and happy hacking!

Anas Mahmood

Web & API penetration tester and bug bounty hunter. I write up the bugs I find and the methods that found them.

Leave a Reply

Your email address will not be published. Required fields are marked *