Create a discord bot for minecraft bedrock docker

Purpose

Explain how I create a discord bot for Minecraft bedrock on docker.
No explanation on docker while be done here but there is a lot of tutorial for installing docker on server.

Prerequisite

  • a Linux server (at home or on Internet)
  • python3
  • linux screen (sudo apt-get install screen)
  • create a dev account on discord https://discord.com/developers and create a bot

Minecraft docker

After you fully installed docker on the server start the Minecraft bedrock container from dockerhub:

1
2
3
4
5
6
7
8
9
10
11
docker run -d -it --name Minecraft \
-e EULA=TRUE \
-p 19132:19132/udp \
-e LEVEL_NAME='NightMare' \
-e LEVEL_SEED=789905374 \
-e SERVER_NAME='MyServer' \
-e MAX_PLAYERS=5 \
-e DEFAULT_PLAYER_PERMISSION_LEVEL=member \
-e GAMEMODE=survival \
-e DIFFICULTY=hard \
itzg/minecraft-bedrock-server

The full documentation of this container is here https://hub.docker.com/r/itzg/minecraft-bedrock-server

Know you will have something like :

1
2
3
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5246641c80da itzg/minecraft-bedrock-server "/usr/local/bin/entr…" 3 hours ago Up 39 minutes (healthy) 0.0.0.0:19132->19132/udp Minecraft

to interact with the server you need to do:

1
2
3
4
5
$ docker attach Minecraft
DEBU[2562] Forwarding signal signal="window changed"
DEBU[2562] Forwarding signal signal="window changed"
list
There are 0/5 players online:

Okay, now we have a Minecraft server and we can send command to it \o/.

To exit (docker) CTRL+P CTRL+Q. If you run CTRL+C you exit the docker and he stop :/

Now we are going to use “screen” to keep the “docker attach” open even we close our term/ssh.

1
2
3
$ screen -S Minecraft
$ docker attach Minecraft
CTRL+A CTRL+D
  • screen -S Minecraft: to create a screen with name “Minecraft”
  • docker attach : to open console mode of the container
  • CTRL+A CTRL+D : to exit the screen and keep running the code inside

Now we have a screen session open (screen -ls to list), but how can we send Minecraft command on it !!!

1
$ screen -S Minecraft -p 0 -X stuff "weather clear^M"
  • -S : session name
  • -p : preselect window
  • -X : send command
  • ^M : Enter

Here we send the command “weather clear” to the server.

Extract some log from docker

If we want to extract user connection and disconnection we can trace the log of the server.

For that we can create a job with bash to extract last log

1
$ docker logs --since=1m Minecraft > /var/log/dockerMinecraftLast.log

With this code we can extract last log from 1 min.

If you add it to crontab you can schedule the task every minute

1
2
$ crontab -e
* * * * * /PathToTheScript/lastDockerMinecraftLog.sh

The bot

code not fully optimized :D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import discord
import asyncio

import os
from bdsPing import bdsPing
import re
import datetime

server='YourServerIP'
port=19132

class MyClient(discord.Client):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# create the background task and run it in the background
self.bg_task = self.loop.create_task(self.my_background_task())

async def on_ready(self):
print('Logged in as')
print(self.user.name)
print(self.user.id)
print('------')

async def on_message(self, message):

if message.content.startswith('!server'):
datas = bdsPing(server,port)
await message.channel.send("Server: {}:{}\nServer Name: {}\nPlayer: {}/{}\nGame Mode: {}".format(server,port,datas['ServerName'],datas['PlayerCount'],datas['PlayerLimit'],datas['GameMode']))

if message.content.startswith('!stats'):
stream = os.popen('docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" Minecraft')
out = stream.read()
await message.channel.send(out)

if message.content.startswith('!setDay'):
os.popen('screen -S Minecraft -p 0 -X stuff "time set day^M"')
await message.channel.send('set time to [day]')

if message.content.startswith('!setNight'):
os.popen('screen -S Minecraft -p 0 -X stuff "time set night^M"')
await message.channel.send('set time to [night]')

if message.content.startswith('!normal'):
os.popen('screen -S Minecraft -p 0 -X stuff "difficulty normal^M"')
await message.channel.send('set difficulty to [normal]')

if message.content.startswith('!hard'):
os.popen('screen -S Minecraft -p 0 -X stuff "difficulty hard^M"')
await message.channel.send('set difficulty to [hard]')

if message.content.startswith('!thunder'):
os.popen('screen -S Minecraft -p 0 -X stuff "weather thunder^M"')
await message.channel.send('set weather to [thunder]')

if message.content.startswith('!sun'):
os.popen('screen -S Minecraft -p 0 -X stuff "weather clear^M"')
await message.channel.send('set weather to [clear]')

if message.content.startswith('!help'):
msg = "Prefix cmd with <!> \nserver : show server info \nstats : show server stat \nsetDay : set day \nsetNight : set night \nnormal : set difficulty to normal \nhard : set difficulty to hard \nthunder : set weather to thunder \nsun : set weather to sun\nopAdmin : set User operator \ndeOpAdmin : remove User operator \ncreativeAdmin : set creative mode to User \nsurvieAdmin : set survie mode to User"
await message.channel.send(msg)

if message.content.startswith('!opAdmin'):
os.popen('screen -S Minecraft -p 0 -X stuff "op MicrosoftGameName^M"')
await message.channel.send('set operator [MicrosoftGameName]')

if message.content.startswith('!deOpAdmin'):
os.popen('screen -S Minecraft -p 0 -X stuff "deop MicrosoftGameName^M"')
await message.channel.send('set deoperator [MicrosoftGameName]')

if message.content.startswith('!creativeAdmin'):
os.popen('screen -S Minecraft -p 0 -X stuff "gamemode 1 MicrosoftGameName^M"')
await message.channel.send('set gamemode [creative MicrosoftGameName]')

if message.content.startswith('!survieAdmin'):
os.popen('screen -S Minecraft -p 0 -X stuff "gamemode 0 MicrosoftGameName^M"')
await message.channel.send('set gamemode [survie MicrosoftGameName]')


async def my_background_task(self):
await self.wait_until_ready()
oldLines = []
channel = self.get_channel(YourChannelID) # channel ID goes here
while not self.is_closed():
currentLines = []
f = open("/var/log/dockerMinecraftLast.log", "r")
lines = f.readlines()
for line in lines:
m = re.search(r"Player (connected|disconnected): ([a-zA-Z0-9]{1,}), xuid: ([0-9]{1,})",line)
if m is not None:
currentTime = datetime.datetime.now() + datetime.timedelta(hours=2)
prettyTime = "{}:{}".format(currentTime.hour,currentTime.minute)
log = "[{}] {} {}".format(prettyTime,m.group(2),m.group(1))
currentLines.append(log)

for current in currentLines:
if current not in oldLines:
await channel.send(current)

await asyncio.sleep(60) # task runs every 60 seconds
oldLines = currentLines

client = MyClient()
client.run('YOUR DISCORD KEY')

bdsPing.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import socket
import json

server='YourServerIp'
port=19132

def bdsPing(server,port):
#Ping
bytesToSend = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78\x00\x00\x00\x00\x00\x00\x00\x00'

serverAddressPort = (server,port)
bufferSize = 1024

# Create a UDP socket at client side
UDPClientSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

# Send to server using created UDP socket
UDPClientSocket.sendto(bytesToSend, serverAddressPort)

msgFromServer = UDPClientSocket.recvfrom(bufferSize)

#msg = "Message from Server {}".format(msgFromServer[0])
data = msgFromServer[0][35:].decode("utf-8")
indexes = ['Game','ServerName','ProtocolVersion','ServerVersion','PlayerCount','PlayerLimit','ServerId','WorldName','GameMode','NintendoLimited','Ipv4Port','Ipv6Port']
datas = data[:-1].split(';')
out = {}
for i in range(len(indexes)):
out[indexes[i]] = datas[i]

return out
#return json.dumps(out)


if __name__ == '__main__':

print(bdsPing(server,port))

To run the bot in background start it in a screen session.

1
2
3
screen -S BdsBot
python3 bot.py
CTRL+A CTRL+D

bds bot screen example

enjoy ;)

HackTheBox challenge web Emdee five for life

Purpose

Solving web challenge (Emdee five for life) from Hackthebox.eu
It is not really a hacking challenge but a coding challenge ;)

Reconnaissance

Access the web page give us this page

Screen

We need to encrypt hash the text very fast.

Get information of the query with Burp:
Burp
We see that the page have a session. So we need to store this information before we send the query back.

Step to solve the challenge:

  • grep the page and the session
  • extract the value to hash
  • hash the value
  • send it back to the server with the session ID

Lets code this

It is possible to code this in several languages.
Try it in python3

Spoiler warning
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import requests
import hashlib
import re


url = "http://134.209.29.219:31621/"
pattern = r'<h3 align=\'center\'>([0-9a-zA-Z]{1,})</h3><center>'

s = requests.session()
r = s.get(url)
if r.status_code == 200:
#print("# HTML code page:")
#print(r.text)
m = re.search(pattern,r.text)

if m.group(1):
#print("# Extract value [{}]".format(m.group(1)))
md5_value = hashlib.md5(m.group(1)).hexdigest()
#print("# MD5 value [{}]".format(md5_value))

x = s.post(url, data = {'hash': md5_value, 'submit': 'Submit'})
print(x.text)

else:
print("regex error")

else:
print("http error")

output
Shell output

Bash online

Just for the fun, and to show that bash is often helpfull :)

Spoiler warning
1
curl -s -q -b /tmp/cookies.txt -d hash=$(echo -ne $(curl -q -s -c /tmp/cookies.txt http://134.209.29.219:31637/ | grep -oE "[0-9a-zA-Z7]{20}") | md5sum | awk '{print $1}') http://134.209.29.219:31637 | grep -oE "HTB{.*}" 

Configure proxy Socks in Burp

Purpose

Using an external server (VPS…) to output your burp traffic.

Burp Socks

SSH

First open a socks connection between your laptop and the remote server

[shell]
1
ssh -D 1080 -q -C -N -f -i /home/user/.ssh/rsa.pkey user@189.10.201.210 -p 22

This will open a local connection at 127.0.0.1:1080.

  • -D open a SOCKS proxy on local port
  • -C compress data
  • -q quiet mode
  • -N do not execute remote commands (forward only)
  • -i path to ssh key
  • user@IP user and IP (to connect to)
  • -p remote SSH port

you can use “netstat” to check if port is listen
Netstat Socks

Burp

configure burp to use the socks proxy

in Project options tab

Netstat Socks