Nexus
10df69722d
All checks were successful
Build and Publish ipserv / build_and_publish (push) Successful in 1m12s
185 lines
5.2 KiB
JavaScript
185 lines
5.2 KiB
JavaScript
const VALID_KEYS = {
|
|
ip: {
|
|
required: true,
|
|
match: /^(\d{1,3}\.){3}\d{1,3}$/
|
|
},
|
|
city: {
|
|
required: false,
|
|
match: /^[a-zA-Z ]+$/
|
|
},
|
|
country: {
|
|
required: false,
|
|
match: /^[a-zA-Z ]+$/
|
|
},
|
|
countryCode: {
|
|
required: false,
|
|
match: /^[A-Z]{2}$/
|
|
},
|
|
asn: {
|
|
required: false,
|
|
match: /^\d+$/
|
|
},
|
|
isp: {
|
|
required: false,
|
|
match: /^.+$/ // ISP names can be anything really
|
|
},
|
|
lat: {
|
|
required: false,
|
|
match: /^-?\d+(\.\d+)?$/
|
|
},
|
|
lon: {
|
|
required: false,
|
|
match: /^-?\d+(\.\d+)?$/
|
|
},
|
|
hostname: {
|
|
required: false,
|
|
match: /^.+$/
|
|
},
|
|
timezone: {
|
|
required: false,
|
|
match: /^[^\/]\/.+$/
|
|
},
|
|
locale: {
|
|
required: false,
|
|
match: /^[a-z]{2}([_-][A-Z]{2})?$/
|
|
},
|
|
subnet: {
|
|
required: false,
|
|
match: /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/
|
|
},
|
|
abuse: {
|
|
required: false,
|
|
match: /^.+$/
|
|
}
|
|
}
|
|
const BOBS = [
|
|
"|",
|
|
"/",
|
|
"-",
|
|
"\\"
|
|
]
|
|
|
|
async function isCORSEnabled(url) {
|
|
try {
|
|
const response = await fetch(url, { method: "GET", mode: "cors"});
|
|
return response.ok;
|
|
} catch {
|
|
return false;
|
|
};
|
|
}
|
|
|
|
|
|
async function getRealIP() {
|
|
const response = await fetch("https://api.ipify.org?format=json");
|
|
const data = await response.json();
|
|
return data.ip;
|
|
|
|
}
|
|
|
|
|
|
async function checkRaw(url, real) {
|
|
const response = await fetch(url);
|
|
const text = await response.text();
|
|
return text === real;
|
|
}
|
|
|
|
async function checkRoot(url) {
|
|
const response = await fetch(url);
|
|
const data = await response.json();
|
|
return data
|
|
}
|
|
|
|
async function checkLookup(url) {
|
|
const response = await fetch(url);
|
|
const data = await response.json();
|
|
return data
|
|
}
|
|
|
|
async function checkImFeelingLucky(url) {
|
|
const response = await fetch(url);
|
|
const data = await response.json();
|
|
return data;
|
|
}
|
|
|
|
function checkReturnedJSON(data) {
|
|
let dataIncluded = 0;
|
|
for (let key in VALID_KEYS) {
|
|
if (data[key]) {
|
|
if(VALID_KEYS[key].match.test(data[key])) {
|
|
console.debug("Server included key %s (value: '%s')", key, data[key]);
|
|
dataIncluded++;
|
|
} else {
|
|
console.warn("Server included key %s (value: '%s'), but it's not valid", key, data[key]);
|
|
}
|
|
} else {
|
|
if (VALID_KEYS[key].required) {
|
|
console.warn("Server didn't include required key %s", key);
|
|
} else {
|
|
console.log("Server didn't include optional key %s", key)
|
|
}
|
|
}
|
|
}
|
|
return dataIncluded;
|
|
}
|
|
|
|
|
|
async function main(e) {
|
|
e.preventDefault();
|
|
const base_url = document.querySelector("input").value;
|
|
const log = document.getElementById("results");
|
|
log.textContent = "Checking: " + base_url + "\n";
|
|
log.textContent += "Checking if CORS is enabled on the target... ";
|
|
const corsEnabled = await isCORSEnabled(base_url);
|
|
log.textContent += corsEnabled ? "Yes\n" : "No\n";
|
|
if (!corsEnabled) {
|
|
log.textContent += "CORS is not enabled, the script will not work\n";
|
|
return;
|
|
}
|
|
|
|
log.textContent += "Getting external IP from trusted source... ";
|
|
const realIP = await getRealIP();
|
|
log.textContent += realIP + "\n";
|
|
|
|
log.textContent += "Checking /... ";
|
|
const root = await checkRoot(base_url);
|
|
const rootFeatures = checkReturnedJSON(root)
|
|
let rootOK = rootFeatures >= 1 && root.ip === realIP;
|
|
log.textContent += rootOK ? `OK (${rootFeatures}/${Object.keys(VALID_KEYS).length} features)\n` : "FAILED\n";
|
|
|
|
|
|
log.textContent += "Checking /lookup... ";
|
|
const lookup = await checkLookup(base_url + "/lookup?ip=8.8.8.8");
|
|
const lookupFeatures = checkReturnedJSON(lookup)
|
|
let lookupOK = lookupFeatures >= 1 && lookup.ip != realIP;
|
|
log.textContent += lookupOK ? `OK (${rootFeatures}/${Object.keys(VALID_KEYS).length} features)\n` : "FAILED\n";
|
|
|
|
log.textContent += "Checking /imfeelinglucky (this may take a while) ";
|
|
let changed = false;
|
|
for(let i=0; i<512; i++) {
|
|
console.debug("Checking /imfeelinglucky iteration %d/512", i);
|
|
const lucky = await checkImFeelingLucky(base_url + "/imfeelinglucky");
|
|
const luckyFeatures = checkReturnedJSON(lucky)
|
|
let luckyOK = luckyFeatures >= 1 && lucky.ip != realIP;
|
|
if (luckyOK) {
|
|
console.debug("Server modified IP, OK")
|
|
log.textContent = log.textContent.slice(0, -1)
|
|
log.textContent += `OK (${luckyFeatures}/${Object.keys(VALID_KEYS).length} features, took ${i} attempts for a modified IP)\n`;
|
|
changed = true;
|
|
break;
|
|
}
|
|
else {
|
|
console.debug("Server did not modify IP, retrying")
|
|
log.textContent = log.textContent.slice(0, -1)
|
|
log.textContent += BOBS[i % 4];
|
|
}
|
|
}
|
|
if(!changed) {
|
|
log.textContent += "FAILED (did not modify returned IP within 512 tries)\n";
|
|
}
|
|
|
|
log.textContent += "Checking /raw... ";
|
|
const raw = await checkRaw(base_url + "/raw", realIP);
|
|
log.textContent += raw ? "OK\n" : "FAILED\n";
|
|
}
|
|
|
|
document.querySelector("form").addEventListener("submit", main);
|