Trading value was a pretty simple challenge, though we’re not sure our solution is the right one ;)
The Challenge
Trading Value consisted of a page with a graph that seemed to display random values. Looking at the page source we find that the page is sending a request to the server every so often asking for the next value to display (file truncated):
{
chart: {
type: "spline",
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function() {
// set up the updating of the chart each second
var series = this.series[0];
var formula =
"KHYxLm1wayt2MS5kcmYqKHYxLm1way8wLjUpLXYxLmRyZikvKHYxLmF2ZyowLjEpKyh2Mi5hdmcqKHYyLm1kcyt2Mi5kbXEpKS0odjMucGRpK3YzLnBkaSszLzIqKHYzLnJhciktdjMuZ2RwKSswLjI1Kih2NC5tdW0qdjQuZGFkKSp2NC5hdmc=";
setInterval(function() {
$.get("/default", {
formula: formula,
values: { v1: "STC", v2: "PLA", v3: "SDF", v4: "OCK" }
}).done(function(data) {
var x = new Date().getTime(), // current time
y = parseInt(data);
if (y < 1000)
formula =
"KHYxLm1wayt2MS5kcmYqKHYxLm1way8wLjUpLXYxLmRyZikvKHYxLmF2ZyowLjEpKyh2Mi5hdmcqKHYyLm1kcyt2Mi5kbXEpKS0odjMucGRpK3YzLnBkaSszLzIqKHYzLnJhciktdjMuZ2RwKSswLjI1Kih2NC5tdW0qdjQuZGFkKSp2NC5hdmc=";
else if (y > 1000 && y < 10000)
formula =
"KHYxLm1way12MS5kcmYqKHYxLm1way8xMDApLXYxLmRyZikvKHYxLmF2ZyowLjMpLSh2Mi5hdmcvKCg0LzMpKnYyLm1kcyt2Mi5kbXEqMTAwKSkrKHYzLnBkaSt2My5wZGkrMy8yKig1KnYzLnJhciktNjkqdjMuZ2RwKSsxLjcqKHY0Lm11bSp2NC5kYWQpKjE2LjUqdjQuYXZn";
else if (y > 10000 && y < 100000)
formula =
"KHYxLm1way12MS5kcmYqKHYxLm1way8wLjEpLXYxLmRyZikvKHYxLmF2ZyowLjgpLSh2Mi5hdmcvKCgxLzIpKnYyLm1kcy0yNC92Mi5kbXEqMTApKSsodjMucGRpLXYzLnBkaSszLzIqKDIvNSp2My5yYXIpLTY2KnYzLmdkcCkqNy41Lyh2NC5tdW0vdjQuZGFkKSo2LjUvdjQuYXZn";
else
formula =
"KHYxLm1way12MS5kcmYqKHYxLm1way8wLjA2KS12MS5kcmYpLyh2MS5hdmcqMC4yNSkrKHYyLmF2Zy8oKDMvMikvdjIubWRzLTg0L3YyLmRtcSoxOSkpLSh2My5wZGktdjMucGRpKzkvMiooMTIvNyp2My5yYXIpLTY2KnYzLmdkcCkqMC41Lyh2NC5tdW0qKnY0LmRhZCkqMC4zOS92NC5hdmcqKjI=";
series.addPoint([x, y], true, true);
});
}, 1000);
}
}
}
}
The base64 payloads appear to be some sort of math formulae, for example:
(v1.mpk+v1.drf*(v1.mpk/0.5)-v1.drf)/(v1.avg*0.1)+(v2.avg*(v2.mds+v2.dmq))-(v3.pdi+v3.pdi+3/2*(v3.rar)-v3.gdp)+0.25*(v4.mum*v4.dad)*v4.avg
So:
- v1, v2, v3 and v4 are sent to the server with the NAME of the object they refer to
- The client also chooses which formula the server uses on them
So first of all what happens if we encode v4
in base64 and send that? The server responds
object(App\Entity\OCK)#253 (4) {
["id":"App\Entity\OCK":private]=>
NULL
["avg"]=>
int(267)
["mum"]=>
int(21)
["dad"]=>
int(80)
}
So vX
values refer to actual variables in php (a Symfony server). We try to run some shell_exec commands but nothing works, and the errors tell us that the values we send are being inputted into a new Expression("...")
formula. I didn’t know much about Symfony or Expression so I poked around but couldn’t find much. I figured i could print any variable in the current scope with values I put in v1
. I tried flag
and other values but nothing worked…
Just for fun I decided to set v1
to this
. Burp responded with “This message is too large to display”. Woooow, ok, time for CURL:
curl 'https://web1.ctfsecurinets.com/default?formula=djE%3d&values%5Bv1%5D=this' | grep 'Securinets{
Gives:
string(47) "Securinets{T00_Ea5y_T0_U5e_This_Local_variable}"
And that’s it! We were the first team to flag this ;)