Author: Marvyn Zalewski

Marvyn is a nerdy guy which is into Linux and everything connected to it. He also loves to automate his home and build up a home lab which includes e.G. a custom steam machine and backup automation. He loves to hear EDM music and try to become a gin enthusiast.
Multiprocessing using Python 3.7

Multiprocessing using Python 3.7

Hey Guys,
I’d like to tell you about Multiprocessing using Python 3.7 which is slightly different that Multithreading with Python 2.7. I already wrote something about Multithreading with Python 2.7 which you can read here.

Basics

By definition a process is a collection of one or more threads that shares memory, code segments and rights but do not share with another processes.

Accordingly to prior paragraph the default case of using multiprocessing is when your program can be divided into several tasks running concurrently and independent from each other. In addition, multithreading is normally used for shared data structure that is written and read by various threads within a process.

I prepared two examples to you how to treat a Multiprocessing pool synchronous or asynchronous.

Examples

The following code block shows the basic code I used in both examples. I only changed the way how to wait until all child processes are done.

#!/usr/bin/env python3
## Importing all needed modules
import multiprocessing
import random
import time
import timeit

print("### Starting Multiprocessing")

## Starting timer which keep track of the runtime
start_time = timeit.default_timer()

## Define the function which will be executed within the pool
def asyncProcess(processRuntime, processID):
    """
    Sleeps the amount of time as seconds from variable processRuntime
    returns: <STRING>
    """
    time.sleep(processRuntime)
    return("%i: This Process ran %i seconds" % (processID, processRuntime))


if __name__ == '__main__':
    ## Define Pool Size of maximal concurrent processes
    pool_size = 10

    ## Define an empty list where we store child processes
    processes = []

    ## Define an empty list to store finished processes
    finished_processes = []

    ## Define an empty pool whith maximal concurrent processes
    pool = multiprocessing.Pool(processes=pool_size)

    ## Firing in total of 10 processes
    for i in range(0, pool_size):
        ## Append the process variable with a process object
        processes.append(pool.apply_async(asyncProcess, args=(random.randint(0, 10), i,)))
    
    ## Closes the pool to stop accepting new processes
    pool.close()

Synchronous Pool

Sometimes you want to execute some tasks but want to wait until all child process are finished. To achieve this you need to execute pool.join() which simply waits until all child processes finished their job. This is similar to Multithreading in Python 2.7.

    pool.join()
    ## Iterate through the processes variable
    for process in processes:
        ## Print the process returned value
        print(process.get())
            
    print("Parent: this Process ran %s seconds"
          % str(timeit.default_timer() - start_time))

Demo

Asynchronous Pool

You are also able to e.g. print the result if a child process is done. Alternatively, you could do something further with the data until everything else is finished.

    ## Iterate through processes as long as a process is running
    while True:
        ## Iterate through the processes variable
        for process in processes:
            ## Check if process is done and not in finished_processes
            if(process.ready() and process not in finished_processes):
                ## Print the returned value
                print(process.get())
                ## Append the finished process to finished_processes
                finished_processes.append(process)
        ## Break while loop when finished_processes length equal processes length
        if len(finished_processes) == len(processes):
            break
    print("Parent: this Process ran %s seconds" % str(timeit.default_timer() - start_time))

Demo

Conclusion

I hope you enjoyed this small insight into Pythons Multiprocessing. If you have any questions or improvements just leave a comment below this post.

If you want to read more about Multiprocessing and Processes in general, take a look at these links:

Title Image via LINK

Rocketbeans Lamp – Update #1

Rocketbeans Lamp – Update #1

Hey guys,
recently Rocketbeans updated their Website and also updated the API Endpoint for our beautiful lamp.

If you haven’t read about the Smart RGB-LED Lamp before you better take a look at my older Post here.

Changes

  • The API endpoint changed to https://api.rocketbeans.tv/v1/frontend/init
  • Added all possible colors (Cyan, Yellow and Purple)
  • Added error signals
    • Cyan = If JSON parsing failed
    • Purple = If show type not matching [“rerun”, “preview”, “live”]
    • Yellow = If HTTP connection failed
  • Added a switch to enable or disable error signals
  • I needed to dig deeper into the JSON to get the current show type

Code

As usual you are able to watch or star my Github repository to keep updated.

#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

/*
* Coniguration
** CONNECTION_INDICATOR - blinking led during startup when defined
** ERROR_INDICATOR - blinking led sequence when parsing or http error occured
*/
#define CONNECTION_INDICATOR
#define ERROR_INDICATOR
#define LED_RED GPIO_NUM_14
#define LED_GREEN GPIO_NUM_27
#define LED_BLUE GPIO_NUM_26

#define WIFI_SSID "Your Wifi"
#define WIFI_PASSWORD "Your Wifi Password"
#define ROCKETBEANS_API "https://api.rocketbeans.tv/v1/frontend/init"

/*
* Information
*/
#define __product__ "Beanlight"
#define __version__ "1001DEV"
#define __author__ "Marvyn Zalewski <[email protected]>"
#define __copyright__ "(c) 2018 KeyboardInterrupt.org"

/*
* Global Variables
*/
int status = WL_IDLE_STATUS;

/*
* Functions
*/
void setLampToBlack()
{
    digitalWrite(LED_RED, LOW);
    digitalWrite(LED_GREEN, LOW);
    digitalWrite(LED_BLUE, LOW);
}

void setLampToBlue()
{
    digitalWrite(LED_RED, LOW);
    digitalWrite(LED_GREEN, LOW);
    digitalWrite(LED_BLUE, HIGH);
}

void setLampToGreen()
{
    digitalWrite(LED_RED, LOW);
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_BLUE, LOW);
}

void setLampToCyan()
{
    digitalWrite(LED_RED, LOW);
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_BLUE, HIGH);
}

void setLampToRed()
{
    digitalWrite(LED_RED, HIGH);
    digitalWrite(LED_GREEN, LOW);
    digitalWrite(LED_BLUE, LOW);
}

void setLampToPurple()
{
    digitalWrite(LED_RED, HIGH);
    digitalWrite(LED_GREEN, LOW);
    digitalWrite(LED_BLUE, HIGH);
}

void setLampToYellow()
{
    digitalWrite(LED_RED, HIGH);
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_BLUE, LOW);
}

void setLampToWhite()
{
    digitalWrite(LED_RED, HIGH);
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_BLUE, HIGH);
}

/*
* Init
*/
void setup()
{
    Serial.begin(115200);
    Serial.println(__product__);
    Serial.println(__version__);
    Serial.println(__author__);
    Serial.println((String)__copyright__ + "\n");
    pinMode(LED_RED, OUTPUT);
    pinMode(LED_BLUE, OUTPUT);
    pinMode(LED_GREEN, OUTPUT);
    while (status != WL_CONNECTED)
    {
        Serial.println((String)__product__ + " attempting to connect to Wifi network, WIFI_SSID: " + (String)WIFI_SSID);
        status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
        #ifdef CONNECTION_INDICATOR
        setLampToRed();
        delay(2000);
        setLampToBlue();
        delay(2000);
        setLampToGreen();
        delay(2000);
        #else
        delay(6000);
        #endif
    };
    #ifdef CONNECTION_INDICATOR
    for (int i = 0; i <= 3; i++)
    {
        setLampToGreen();
        delay(250);
        setLampToBlack();
        delay(250);
    };
    #endif
    Serial.println((String)__product__ + " connected to network");
}

void loop()
{
    if ((WiFi.status() == WL_CONNECTED))
    {
        HTTPClient http;
        http.begin(ROCKETBEANS_API);
        int httpCode = http.GET();
        if (httpCode > 0)
        {
            const size_t bufferSize = JSON_ARRAY_SIZE(4) + JSON_ARRAY_SIZE(8) + 2 * JSON_OBJECT_SIZE(2) + 10 * JSON_OBJECT_SIZE(3) + 5 * JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(8) + 1190;
            DynamicJsonBuffer jsonBuffer(bufferSize);
            String payload = http.getString();
            JsonObject &root = jsonBuffer.parseObject(payload);
            JsonObject &data = root["data"];
            JsonObject &data_streamInfo = data["streamInfo"];
            JsonObject &data_streamInfo_info = data_streamInfo["info"];
            String data_streamInfo_info_type = data_streamInfo_info["type"].as<String>();

            if (!root.success())
            {
                Serial.println((String)__product__ + " parsing failed.");
                Serial.println(payload);
                #ifdef ERROR_INDICATOR
                for (int i = 0; i <= 6; i++)
                {
                    for (int i = 0; i <= 3; i++)
                    {
                        setLampToCyan();
                        delay(100);
                        setLampToBlack();
                        delay(100);
                    };
                    delay(500);
                }
                #endif
                setLampToCyan();
            } else
            {
                /* When current show is live */
                if (data_streamInfo_info_type == "live")
                    setLampToRed();
                /* When current show is new */
                else if (data_streamInfo_info_type == "premiere")
                    setLampToBlue();
                /* When current show is playback */
                else if (data_streamInfo_info_type == "rerun")
                    setLampToWhite();
                /* Graceful handling if no type matched */
                else {
                    Serial.println((String)__product__ + " unsupported show type -> "+ data_streamInfo_info_type +".");
                    #ifdef ERROR_INDICATOR
                    for (int i = 0; i <= 3; i++)
                    {
                        for (int i = 0; i <= 3; i++)
                        {
                            setLampToPurple();
                            delay(100);
                            setLampToBlack();
                            delay(100);
                        };
                        delay(500);
                    }
                    #endif
                    setLampToPurple();
                }
            }
        }
        else
        {
            Serial.println((String)__product__ + " error on HTTP request");
            #ifdef ERROR_INDICATOR
            for (int i = 0; i <= 3; i++)
            {
                for (int i = 0; i <= 3; i++)
                {
                    setLampToRed();
                    delay(100);
                    setLampToBlack();
                    delay(100);
                    setLampToYellow();
                    delay(100);
                    setLampToBlack();
                    delay(100);
                };
                delay(500);
            }
            #endif
            setLampToYellow();
        }
        http.end();
    }
    delay(60000);
}
iPXE Boot With Shoelaces

iPXE Boot With Shoelaces

Hey guys,
today I’d like to tell you about Shoelaces from Thousandeyes. Shoelaces may be used to automate Server installation with iPXE Boot.
If you are already familiar with the basics about PXE, iPXE and how to set up the environment you can go ahead to Shoelaces Setup.

Environment

Before I dig into the details I prepared an Image to show you the Software Stack and also the whole server set up.

Software Stack and Server Setup

All commands are executed on a server named pxe-server.local with IP 192.168.0.2 running on CentOS 7.4.

DHCPD (ISC DHCP)

The DHCP Server is the first line of the stack. As soon as the Client boot in PXE Mode, it requests an IP Address and receives an IP address with the command where it can find the Image to boot from.
I prepared an example configuration file below.

[email protected]:[~]: cat /etc/dhcp/dhcpd.conf
subnet 192.168.0.0 netmask 255.255.255.0 {
  range 192.168.0.10 192.168.0.20;
  option subnet-mask 255.255.255.0;
  option routers 192.168.0.1;
  option domain-name-servers 192.168.0.1;
  next-server 192.168.0.2;
  if exists user-class and option user-class = "iPXE" {
    filename "http://192.168.0.2/poll/1/${netX/mac:hexhyp}";
  } else {
    filename "undionly.kpxe";
  }
}

As you can see the DHCP Server manages the IP Range 192.168.0.10-20 and also sends the next-server option which tells the client to boot from 192.168.0.2:69/filename (TFTP Server) option. If the Client supports iPXE it boots from http://192.168.0.2/poll/1/${netX/mac:hexhyp} which is Shoelaces. If the client doesn’t support iPXE it boots a microkernel which supports iPXE.

You can test your DHCP Settings with nmap


[email protected]:[~]: nmap --script broadcast-dhcp-discover
 
Starting Nmap 6.40 ( http://nmap.org ) at 2018-08-06 12:58 UTC
Pre-scan script results:
| broadcast-dhcp-discover:
|   IP Offered: 192.168.0.10
|   DHCP Message Type: DHCPOFFER
|   Server Identifier: 192.168.0.2
|   IP Address Lease Time: 0 days, 0:05:00
|   Subnet Mask: 255.255.255.0
|   Router: 192.168.0.1
|_  Domain Name Server: 192.168.0.1
WARNING: No targets were specified, so 0 hosts scanned.
Nmap done: 0 IP addresses (0 hosts up) scanned in 0.07 seconds

TFTP

The TFTP Server is needed to serve the undionly.kpxe when the client doesn’t support iPXE.

To “install” the microkernel you need to download the file from ipxe.org and save it into /var/lib/tftpboot.

[email protected]:[~]: wget -P /var/lib/tftpboot https://boot.ipxe.org/undionly.kpxe

Xinetd

You need Xinetd to actually serve the microkernel.

Here is the config file we used to glue tftp and Xinetd together:

[email protected]:[~]: cat /etc/xinetd.d/tftp
{
    socket_type     = dgram
    protocol        = udp
    wait            = yes
    user            = root
    server          = /usr/sbin/in.tftpd
    server_args     = -s /var/lib/tftpboot
    disable         = no
    per_source      = 11
    cps             = 100 2
    flags           = IPv4
}

As soon as you created the config file you need to restart xinetd and check if the tftp module is running.

[email protected]:[~]: systemctl status xinetd.service
● xinetd.service - Xinetd A Powerful Replacement For Inetd
   Loaded: loaded (/usr/lib/systemd/system/xinetd.service; enabled; vendor preset: enabled)
   Active: active (running) since Mo 2018-08-11 15:19:41 UTC; 3s ago
  Process: 8060 ExecStart=/usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid $EXTRAOPTIONS (code=exited, status=0/SUCCESS)
 Main PID: 8061 (xinetd)
   CGroup: /system.slice/xinetd.service
           └─8061 /usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid
 
Aug 11 15:19:41 pxe-server xinetd[8061]: removing daytime
Aug 11 15:19:41 pxe-server xinetd[8061]: removing discard
Aug 11 15:19:41 pxe-server xinetd[8061]: removing discard
Aug 11 15:19:41 pxe-server xinetd[8061]: removing echo
Aug 11 15:19:41 pxe-server xinetd[8061]: removing echo
Aug 11 15:19:41 pxe-server xinetd[8061]: removing tcpmux
Aug 11 15:19:41 pxe-server xinetd[8061]: removing time
Aug 11 15:19:41 pxe-server xinetd[8061]: removing time
Aug 11 15:19:41 pxe-server xinetd[8061]: xinetd Version 2.3.15 started with libwrap loadavg labeled-networking options compiled in.
Aug 11 15:19:41 pxe-server xinetd[8061]: Started working: 1 available service
[email protected]:[~]: netstat -tulpen | grep xinetd
udp        0      0 0.0.0.0:69              0.0.0.0:*                           0          235681     8061/xinetd

When you execute the status command at the end of the log should be at least ‘1’ available service printed. The Port 69/UDP should be open as well.

Shoelaces

Now we’re running the basic iPXE Environment. At the moment we are able to boot a client with PXE boot, obtain an IP Address and boot the iPXE microkernel if the client doesn’t support iPXE.

But what happens if the client boot with this line (which we configured in the dhcpd section)?
http://192.168.0.2/poll/1/${netX/mac:hexhyp}
Before answering the question I’ll guide you through the shoelaces installation.

Installation

Shoelaces is a program written in Go Lang. Therefore we need to install go at first.

# Determine your needed go version at https://golang.org/dl/#stable first. I need the amd64 binary which is version 1.10.3 at the moment.
[email protected]:[~]: wget https://dl.google.com/go/go1.10.3.linux-amd64.tar.gz
[email protected]:[~]: tar -xvf go1.10.3.linux-amd64.tar.gz
[email protected]:[~]: mv go /usr/local
[email protected]:[~]: echo "export GOROOT=/usr/local/go" >> /etc/bashrc
[email protected]:[~]: echo "export GOPATH=/opt/go" >> /etc/bashrc
[email protected]:[~]: echo "export PATH=$GOPATH/bin:$GOROOT/bin:$PATH" >> /etc/bashrc
[email protected]:[~]: src /etc/bashrc

To install shoelaces you need to get the repository with go get then you need to build the shoelaces binary.

[email protected]:[~]: go get github.com/thousandeyes/shoelaces
[email protected]:[~]: cd $GOPATH/src/github.com/thousandeyes/shoelaces
[email protected]:[~]: go build

Daemonize

Currently, we’re only able to start the service on demand with a command.

[email protected]:[~]: cd $GOPATH/src/github.com/thousandeyes/shoelaces
[email protected]:[~]: ./shoelaces -config configs/shoelaces.conf

But if you want the process permanently running you need to create a systemd file.
Luckily I already created one which needs a user to be created.

[email protected]:[~]: useradd shoelaces
[email protected]:[~]: cat /lib/systemd/system/shoelaces.service
[Unit]
Description=Shoelaces
Documentation=https://github.com/thousandeyes/shoelaces
ConditionPathExists=/opt/go/src/github.com/thousandeyes/shoelaces
After=network.target

[Service]
User=shoelaces

# Restart Policy
Restart=on-failure
RestartSec=10

# Execution
ExecStartPre=/bin/chown -R shoelaces /opt/go/src/github.com/thousandeyes/shoelaces
ExecStartPre=/bin/chmod u+x /opt/go/src/github.com/thousandeyes/shoelaces/shoelaces
ExecStart=/opt/go/src/github.com/thousandeyes/shoelaces/shoelaces -config configs/shoelaces.conf

# Environment
WorkingDirectory=/opt/go/src/github.com/thousandeyes/shoelaces
PermissionsStartOnly=true
NonBlocking=true

# Logging
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=shoelaces

[Install]
WantedBy=multi-user.target

After you added the content to the file /lib/systemd/system/shoelaces.service execute systemctl daemon-reload; systemctl start shoelaces.service; systemctl status shoelaces.service to start the service and check the status. You can always take a look into logs if you execute journalctl -f -u shoelaces.service.

Configure

I guess you already noticed the only missing part. Configuring Shoelaces.

It’s straight forward and fairly easy. Just go into the shoelaces directory and take a look at the default config file.

[email protected]:[~]: cd $GOPATH/src/github.com/thousandeyes/shoelaces
[email protected]:[/opt/go/src/github.com/thousandeyes/shoelaces]: cat configs/shoelaces.conf
port=8081
domain=localhost
data-dir=configs/example-templates-configs/
template-extension=.slc
mappings-file=mappings.yaml
debug=true

For production I changed the config file to the following one …

[email protected]:[/opt/go/src/github.com/thousandeyes/shoelaces]: cat configs/shoelaces.conf
port=8080
domain=192.168.0.2
data-dir=configs/templates-configs/
template-extension=.slc
mappings-file=mappings.yaml
debug=true

… and moved configs/example-templates-configs to configs/templates-configs.

Starting

Recently we changed the config file. Therefore you need to restart your service.

Now open your shoelaces URL (http://<domain>:<port>) with your favourite browser. If everything went well you should see the following website. If not, take a look into the logs what’s wrong with shoelaces.

Shoelaces Startup Image

Deep Dive

Back to the question before the shoelaces installation.

But what happens if the client boot with this line (which we configured in the dhcpd section)?
http://192.168.0.2/poll/1/${netX/mac:hexhyp}

The client connects to shoelaces and kept in a waiting loop until the user executes a “Boot!” command or shoelaces is able to bootstrap the server with ‘mappings’. This state looks like this:

Server Registered

As soon as you click on ‘Select an iPXE script’ you are able to choose an iPXE script located in $GOPATH/src/github.com/thousandeyes/shoelaces/configs/templates-configs/ipxe.

I clicked on centos.ipxe and filled out both arguments. If you check $GOPATH/src/github.com/thousandeyes/shoelaces/configs/templates-configs/ipxe/centos.ipxe.slc there are two strings which look like {{.<STRING>}}. These strings are the arguments shown in the UI.

[email protected]:[~]: cat $GOPATH/src/github.com/thousandeyes/shoelaces/configs/example-templates-configs/ipxe/centos.ipxe.slc
{{define "centos.ipxe" -}}
#!ipxe
set hostname {{.hostname}}
set release {{.release}}
set base http://mirror.centos.org/centos/${release}/os/x86_64

echo This automatically overwrites data!
echo CentOS ${release}
echo Installing ${hostname}

kernel ${base}/images/pxeboot/vmlinuz initrd=initrd.img repo=${base} ks=http://{{.baseURL}}/configs/centos.ks?hostname=$hostname&release=$release
initrd ${base}/images/pxeboot/initrd.img
boot
{{end}}

As soon as I click on the big red button “Boot!” the client leaves the waiting loop and starts to execute the iPXE script. It depends on your kickstart file how long it takes until your client is back again but normally it should’nt take longer than 10 minutes.

Conclusion

Shoelaces is a very small footprint software written in go which supports bootstrapping a huge amount of server. Even in a small environment, it helps a lot. Take a look at their Github Project Site if you want to discover all further features.

For example you may use the mapping feature if you want to bootstrap server automatically based on IP or Hostname pattern.

Leave a comment or contribute to the project if you like the software.

Our First Anniversary

Our First Anniversary

Hey guys,

We reached our first anniversary. For me, it was an awesome year and I learned a lot.

I prepared some anniversary facts:

Thank you so much for reading our blog.

 

Title Image via LINK

Google CTF 2018 – Web: Translate

Google CTF 2018 – Web: Translate

Hey guys,

I decided to spend some time in solving CTF’s. To be honest, this CTF is the second I solved until yet. Recently I read about the Google CTF which may be a good starting point.

Normally the Google CTF is an event which lasts for two days. The best teams will be rewarded. You can read more about on their Blog.

Anyway, without knowing any of the Challenges I started with the ‘Web’ CTF challenges because this is the environment I know the best. The first challenge is named ‘Translate’.

The task for this CTF is to gather the content of the File named ./flag.txt. There’s also a hint in the task description which says that the app is client-side rendered but not in the browser. This hint told me to watch out for a rendering language. It may be a Python-based rendering-engine like Jinja2 or a Javascript framework like Angular, AngularJS or React.

Information Gathering

I downloaded the file, unzipped it and got an HTML named index.html.

[email protected]:[/ctf]: cat index.html

<script>location=’http://translate.ctfcompetition.com:1337/'</script>

The index.html only redirect the browser to a new location which is http://translate.ctfcompetition.com:1337/. Therefore I open Chrome and open the URL.

As you can see the Webapp is a simple translation utility which is able to translate from english to french or vice versa.

But how it works? I checked the debug translations site and noticed that the app is a simple Key-Value database.

I noticed the {{UserQuery}} string in both dictionaries. The double curly braces are used in Jinja2 or Angular. I started the Chrome observer to check the source.

What a luck! There is an unrendered ng-bind declaration which approves Angular. 🙂

It seems like there’s an object named i18n which have a method named word. I guess it is used to translate a word from one language to the other one.

Let’s check a bit more of the web app if we find some other useful information. I opened the add words link and a wild form appeared.

This may be a security issue in combination with a server-rendered user input. I wanted to make sure I understood how the web app work. So I decided to add a new word and check if it will be translated correctly.

As expected my word was translated correctly.

When I entered a new word I noticed the HTTP GET parameters which are passed into the application.

http://translate.ctfcompetition.com:1337/add?lang=fr&word=moi&translated=my

I wondered if the web app also supports a different language than English or French. So I changed lang=fr to lang=de.

Boom! An unhandled error with stack trace.

Error: ENOENT: no such file or directory, open ‘./i18n/de.json’
   at Object.fs.openSync (fs.js:646:18)
at Object.fs.readFileSync (fs.js:551:33)
at Object.load (/usr/local/chall/srcs/restricted_fs.js:24:20)
at app.get (/usr/local/chall/srcs/server.js:175:57)
at Layer.handle [as handle_request
(/usr/local/chall/node_modules/express/lib/router/layer.js:95:5)
at next (/usr/local/chall/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch     (/usr/local/chall/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request]   (/usr/local/chall/node_modules/express/lib/router/layer.js:95:5)
at /usr/local/chall/node_modules/express/lib/router/index.js:281:22
at Function.process_params   (/usr/local/chall/node_modules/express/lib/router/index.js:335:12)

It seems like the translations are saved on the server side in ./i18n/(fr|en).json.

Exploiting

Now I tried to figure out if I’m able to execute the template on the server side. I already discovered that i18n is the object which I need to exploit.

So I added a new word hackme with the value {{ i18n.word(“moi”) }}.

Checking the word results in a plain string.

Anyway, I remembered the string which rendered into the website. It simply replaced the {{userQuery}} with the translated word I entered before. So I went back to the translation dump copied the key of the string which contains the curly braces (in_lang_query_is_spelled).

I changed the value of in_lang_query_is_spelled to {{ i18n.word(“moi”) }} and checked the output of translate which is already defined.

To my surprise, the template injection did the job.

http://translate.ctfcompetition.com:1337/?query=translate&lang=fr

Since I know the i18n is an object and it’s also in scope I tried to figure out which methods it has.

Let us try if we are able to iterate through the object. I changed the key in_lang_query_is_spelled to <p *ngFor=”let child of this.i18n”>{{ child }}</p>. But this command returns simply nothing. So I read through the AngularJS documentation to figure out how it works there. Accordingly I changed in_lang_query_is_spelled to <p ng-repeat=”child in this.i18n”>{{ child }}</p>. Awesome! I got the available methods.

I noticed there is a seconds method available called forTemplate(t). I guess the function may be template because it’s the same for myI18n.forSingleWord(w) and word.

With the latest knowledge I changed in_lang_query_is_spelled to {{ this.i18n.template(“fr.json”) }} which results in an error.

I noticed the path which is ./fr.json. As I told you at the beginning we’re looking for a file named ./flag.txt. And again I changed in_lang_query_is_spelled to {{ this.i18n.template(“./flag.txt”) }} and opened http://translate.ctfcompetition.com:1337/?query=translate&lang=fr.

Look, the flag smiles at me.

CTF{Televersez_vos_exploits_dans_mon_nuagiciel}

Conclusion

I hope you enjoyed my adventure through this CTF. If you know a better way to find the flag, leave a comment. 🙂

See you soon.

Credits

  • Title Image via LINK
Catching a KeyboardInterrupt Signal

Catching a KeyboardInterrupt Signal

Hey everyone,

as I thought about writing the next post I came across the idea to explain what KeyboardInterrupt actually is and also how to catch it in Python 3.6.

KeyboardInterrupt is a User Signal which is executed by User. As an example could be pressing CTRL+C. In detail, the Signal is called SIGINT (Signal Interrupt). It could be useful catching a KeyboardInterrupt Signal to e.g. cleaning up temporary files which have been created during script execution.

Requirements

At first, we need to create a python script. Let’s name it ‘keyboardinterrupt.py’.

[email protected]: touch keyboardinterrupt.py

Catching SIGINT

Catching the SIGINT Signal may be useful for scripts or application which are multithreaded or when it’s necessary to be caught during the whole script execution.

Prerequisites

To catch the SIGINT it is required to import the signal library.

Importing signal
#!/usr/bin/env python3
import signal
Handler for the caught KeyboardInterrupt SIGINT

Now we need to add a function which is executed as soon as the SIGINT has been caught.

Signal Handler Function
def keyboardInterruptHandler(signal, frame):
    print("KeyboardInterrupt (ID: {}) has been caught. Cleaning up...".format(signal))
    exit(0)

As you can see, the function takes two positional arguments. The signal (integer) and the frame (object). The signal variable is an integer which represents the number of the caught signal. For SIGINT it’s the two (2). The frame variable represents the current Python stack frame which can be useful for debugging purposes.

Registering the signal action

To execute a function before quitting the program you need to register an action.

Action registering
signal.signal(signal.SIGINT, keyboardInterruptHandler)
Executing a long-running task

Now we’re executing a long-running task which will be interrupted by SIGINT.

Long-running task
while True:
    pass

Please note that the script will never end on it’s own.

Putting it all together
Whole script
#!/usr/bin/env python3
import signal

def keyboardInterruptHandler(signal, frame):
    print("KeyboardInterrupt (ID: {}) has been caught. Cleaning up...".format(signal))
    exit(0)

signal.signal(signal.SIGINT, keyboardInterruptHandler)

while True:
    pass

When you run the script from above you will notice that nothing is happening. Just press CTRL+C and you will see the print output from the highlighted line.

Running the script

[email protected]: python3 keyboardinterrupt.py
<PRESS CTRL+C NOW!!!>
^CKeyboardInterrupt (ID: 2) has been caught. Cleaning up…

Catching KeyboardInterrupt exception

The second method reacting to is catching the KeyboardInterrupt exception. It can be useful in a script which is single threaded and also in long-running task situations which can be skipped but not canceling the whole script.

Prerequisites

We use the time library to simulate a running task. Therefore we’re importing it into a new file.

Importing time library
#!/usr/bin/env python3
import time
Adding a long-running task in a “try” block

We’re catching the KeyboardInterrupt exception with a “try-except” block.

try-except block
try:
    while True:
        print("Heavy task!")
        time.sleep(2)
except KeyboardInterrupt:
    print("KeyboardInterrupt has been caught.")
Putting it all together
Catching the exception
#!/usr/bin/env python3
import time

print("Calculating a lot... Can be canceled with CTRL+C")
try:
    while True:
        print("Heavy task!")
        time.sleep(2)
except KeyboardInterrupt:
    print("KeyboardInterrupt has been caught.")

print("Continuing script execution")

As you can see i added some print information to add a hint to a user that the current task in the script can be skipped.

Running the script

[email protected]: python3 keyboardinterrupt.py
Calculating a lot… Can be canceled with CTRL+C
Heavy task!
Heavy task!
Heavy task!
Heavy task!
Heavy task!
^CKeyboardInterrupt has been caught.
Continuing script execution

Conclusion

It’s very easy to implement signal catching in python 3.6. You can easily catch any kind of signal and react to it in a matching manner. As an example taken from dd (this is a unix program to convert and copy files) it is required to send USRSIG1 to the running process to show the current state of the progress.

Do you have any interesting ideas what to do with caught signals?

Further Ressources

Hacking Rocketbeans lamp with ESP32 to display current show type

Hacking Rocketbeans lamp with ESP32 to display current show type

Hey guys,

today I’d like to show you how I modded an ordinary lamp into a smart (IoT) one.

Foreword

I’m a huge Rocketbeans TV Fan. Rocketbeans is a big 24/7 Stream Show focused on gaming. Germans may know the hosts from “Giga” or “Game One”.

As they started selling a USB Lamp with their logo and some RGB-LED I thought about making it smart to display the current type of show running. At the moment there are three types of shows available: “Live”(=Red), “Premiere”(=Blue), “Playback”(=White). The unmodified lamp is able to switch colors on a button press.

via Rocketbeans Forum

Requirements

The Lamp

I bought one of them. As soon as it arrived I started to disassemble the lamp to check the circuit board. To my surprise, it’s straight forward.

The (smart) Microcontroller

Leroy told me about the ESP32 which is a very small Microcontroller with built-in Wifi and Bluetooth. This one seems to be a perfect match for the smart part of the lamp.

Loading

 

You can buy one via eBay

The Schedule of Rocketbeans TV

After viewing and manually scraping their Website I was able to find a Link which returns the current and four further shows as JSON object. (The following image only shows the current show also referred as Array[0]).

Now we gathered all requirements and start tinkering.

Reverse Engineering the Lamp Circuit

We started to measure some circuits and figured out how the embedded controller works. The controller has eight pins: GND, 5v, a Touch Button on the Case and three colors. The remaining ones may be used for infrared.

Next we removed the embedded microcontroller to attach the ESP32 controller.

Writing the Microcontroller Code

Since I never wrote any kind of ESP32 Code I decided to invest some time to read Tutorials/Blogs about “ESP32 Wifi”, “Arduino JSON” and “Arduino HTTP Client”.

After several compile errors and reading through manuals on how to use the above libraries I wrote the following code.

ESP32 Code (Version 1)
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

#define __product__    "Beanduino: Dynamic Color Lamp"
#define __model__      "DyCoLa"
#define __version__    "1000DEV"
#define __author__     "Marvyn Zalewski <[email protected]>"

#define __copyright__ "(c) 2015 The Senso Team"
#define __license__   "All rights reserved."

const char* ssid = "WIFI";
const char* password =  "WIFIPASSWORD";
const char* rbTvUri = "https://www.rocketbeans.tv/?next5Shows=true";
int status = WL_IDLE_STATUS;


void setup() {
  Serial.begin(115200);
  Serial.println(__product__);
  Serial.print(__model__);
  Serial.println(__version__);
  Serial.println(__author__);
  Serial.println((String)__copyright__ + ", " + (String)__license__ + "\n");
  while ( status != WL_CONNECTED) {
    Serial.println("Beanduino - Attempting to connect to Wifi network, SSID: " + String(ssid));
    status = WiFi.begin(ssid, password);
    delay(10000);
  }
  Serial.println("Beanduino - Connected to network");

}

String stateToColor(String state){
  const size_t showStateJsonBufferSize = JSON_OBJECT_SIZE(3);
  DynamicJsonBuffer showStateJsonBuffer(showStateJsonBufferSize);
  JsonObject& showStates = showStateJsonBuffer.createObject();
  showStates["live"] = "red";
  showStates["new"] = "blue";
  showStates["playback"] = "white";
  return showStates[state];
}

void setLampState(String state){
  String showState = state;
  String showColor = stateToColor(state);
  Serial.println(__product__ ";" + showState + ";" + showColor);
}

void loop() {
  if ((WiFi.status() == WL_CONNECTED)) {
      HTTPClient http;
      http.begin(rbTvUri);
      int httpCode = http.GET();
      if (httpCode > 0) {
          const size_t bufferSize = JSON_ARRAY_SIZE(5) + 5*JSON_OBJECT_SIZE(14) + 2970;
          DynamicJsonBuffer jsonBuffer(bufferSize);
          String payload = http.getString();
          JsonArray& root = jsonBuffer.parseArray(payload);
          if (!root.success()){
              Serial.println("PARSING FAILED");
              Serial.println(payload);
          }
          JsonArray& root_ = root;
          JsonObject& root_0 = root_[0];
          int isLive = root_0["isLive"];
          int isNew = root_0["isNew"];
          if (isLive == 1) {
              setLampState("live");
            } else {
            if (isNew == 1) {
                setLampState("new");
            } else {
                setLampState("playback");
             }    
          }
      }
      else {
        Serial.println("Error on HTTP request");
      }
      http.end();
    }
    delay(10000);
}

Running the code

Beanduino: Dynamic Color Lamp
DyCoLa1000DEV
Marvyn Zalewski <[email protected]>
(c) 2015 The Senso Team, All rights reserved.

Beanduino – Attempting to connect to Wifi network, SSID: WIFI
Beanduino – Attempting to connect to Wifi network, SSID: WIFI
Beanduino – Connected to network
Beanduino: Dynamic Color Lamp;live;red
Beanduino: Dynamic Color Lamp;live;red

Putting it all together

Now we know everything to set up the smart lamp. At first, we’re soldering all needed wires and stick them into the battery compartment.

As you can see there are six wires coming from the Lamp Circuit. (VCC which is 5V, GND, directly from the Button and all three colors.)

Due to space issues, we soldered the wires directly onto the ESP32.

Wire Map:

  • ESP32 VIN -> Lamp Circuit VCC
  • ESP32 GND -> Lamp Circuit GND
  • ESP32 D14 -> Lamp Circuit LED Green (the left resistor R5)
  • ESP32 D22 -> Lamp Case Button (without any function at the moment)
  • ESP32 D26 -> Lamp Circuit LED Blue (the middle resistor R6)
  • ESP32 D33 -> Lamp Circuit LED Red (the right resistor R7)

 

After a nearly whole code refactoring, I finished it and moved it to Github.

Beanlamp Code
#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

/*
* Coniguration
** CONNECTION_INDICATOR - blinking led during startup when defined
*/
#define CONNECTION_INDICATOR
#define LED_RED GPIO_NUM_14
#define LED_GREEN GPIO_NUM_27
#define LED_BLUE GPIO_NUM_26

#define WIFI_SSID "WLAN"
#define WIFI_PASSWORD "WLAN_PASSWORD"
#define ROCKETBEANS_API "https://www.rocketbeans.tv/?next5Shows=true"

/*
* Information
*/
#define __product__ "Beanlight"
#define __version__ "1000DEV"
#define __author__ "Marvyn Zalewski <[email protected]>"
#define __copyright__ "(c) 2018 KeyboardInterrupt.org"

/*
* Global Variables
*/
int status = WL_IDLE_STATUS;

/*
* Functions
*/
void setLampToRed()
{
    digitalWrite(LED_RED, HIGH);
    digitalWrite(LED_GREEN, LOW);
    digitalWrite(LED_BLUE, LOW);
}

void setLampToBlue()
{
    digitalWrite(LED_BLUE, HIGH);
    digitalWrite(LED_GREEN, LOW);
    digitalWrite(LED_RED, LOW);
}

void setLampToGreen()
{
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_BLUE, LOW);
    digitalWrite(LED_RED, LOW);
}

void setLampToWhite()
{
    digitalWrite(LED_GREEN, HIGH);
    digitalWrite(LED_BLUE, HIGH);
    digitalWrite(LED_RED, HIGH);
}

void setLampToBlack()
{
    digitalWrite(LED_GREEN, LOW);
    digitalWrite(LED_BLUE, LOW);
    digitalWrite(LED_RED, LOW);
}

/*
* Init
*/
void setup()
{
    Serial.begin(115200);
    Serial.println(__product__);
    Serial.println(__version__);
    Serial.println(__author__);
    Serial.println((String)__copyright__ + "\n");
    pinMode(LED_RED, OUTPUT);
    pinMode(LED_BLUE, OUTPUT);
    pinMode(LED_GREEN, OUTPUT);
    while (status != WL_CONNECTED)
    {
        Serial.println((String)__product__ + " attempting to connect to Wifi network, WIFI_SSID: " + (String)WIFI_SSID);
        status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
        #ifdef CONNECTION_INDICATOR
        setLampToRed();
        delay(2000);
        setLampToBlue();
        delay(2000);
        setLampToGreen();
        delay(2000);
        #else
        delay(6000);
        #endif
    };
    #ifdef CONNECTION_INDICATOR
    for (int i = 0; i <= 3; i++)
    {
        setLampToGreen();
        delay(250);
        setLampToBlack();
        delay(250);
    };
    #endif
    Serial.println((String)__product__ + " connected to network");
}

void loop()
{
    if ((WiFi.status() == WL_CONNECTED))
    {
        HTTPClient http;
        http.begin(ROCKETBEANS_API);
        int httpCode = http.GET();
        if (httpCode > 0)
        {
            const size_t bufferSize = JSON_ARRAY_SIZE(5) + 5 * JSON_OBJECT_SIZE(14) + 2970;
            DynamicJsonBuffer jsonBuffer(bufferSize);
            String payload = http.getString();
            JsonArray &root = jsonBuffer.parseArray(payload);
            if (!root.success())
            {
                Serial.println((String)__product__ + " parsing failed.");
                Serial.println(payload);
            }
            /* When current show is live */
            if (root[0]["isLive"] == 1)
                setLampToRed();
            /* When current show is new */
            else if (root[0]["isNew"] == 1)
                setLampToBlue();
            /* When current show is playback */
            else
                setLampToWhite();
        }
        else
        {
            Serial.println((String)__product__ + " error on HTTP request");
        }
        http.end();
    }
    delay(60000);
}

Result

After working nearly ten hours on the project I would say it was a lot of fun.

If you have any questions or improvements, let me know in the comments. 🙂

Rendering HTML with Jinja2 in Python 3.6

Rendering HTML with Jinja2 in Python 3.6

Hey folks,

today I’d like to show how to render an HTML (or any random non-binary) File with Jinja2 and Python 3.6.

A possible usage for rendering an HTML file is the avoidance of writing duplicate code. I prepared a small HTML file which only consists of a headline and the variable.

Source Template
<h1>{{ greeting }}</h1>

As you can see between the h1 tags the text is padded in double curly braces which is the syntax to render variables with Jinja2.

 

Clearing all conditions

At first, we’re going to install Jinja2 which is needed for Rendering.

Installing Jinja2
[email protected]: pip3 install jinja2

 

Now we’re heading to a new file which will be named renderer.py and start with our imports.

Importing needed modules
#!/usr/bin/env python3
import os
import jinja2
import sys

 

After importing all needed modules we’re going to set up some variables.

setting up Variables
template_filename = "template.html.j2"
rendered_filename = "rendered.html"
render_vars = {
    "greeting": "Oh hi!"
}

script_path = os.path.dirname(os.path.abspath(__file__))
template_file_path = os.path.join(current_path, template_filename)
rendered_file_path = os.path.join(current_path, rendered_filename)
  • template_filename is the variable which contains the name of the reusable source file.
  • rendered_filename is the variable which contains the name of the output file which has been rendered trough Jinja2
  • render_vars contains the dictionary which will be passed to Jinja2 to render the source
  • script_path  contains the absolute path of the renderer.py script. E.g.
    • If the absolute path to renderer.py is /home/dummy/renderer.py then it would contain /home/dummy
  • template_file_path contains the absolute path to the source template file which will be passed to Jinja2
  • rendered_file_path contains the absolute path to the rendered file

 

Render the source template

Now we’re defining an environment which describes where to find the source template.

Defining a render environment
render_environment = jinja2.Environment(loader=jinja2.FileSystemLoader(template_file_path))

We’re using the jinja2.FileSystemLoader() as loader which takes the absolute path to the folder where the template is located.

 

To render the file and save the output into a variable we execute the render() method on render_environment and pass the render_vars dictionary.

Render variables into readable text
output_text = environment.get_template(template_filename).render(render_vars)

 

Putting it all together results in a static render script which prints out the rendered text.

Example - Print rendered Text
#!/usr/bin/env python3
import os
import jinja2

template_filename = "./template.html.j2"
rendered_filename = "rendered.html"
render_vars = {
    "greeting": "Oh hi!"
}

script_path = os.path.dirname(os.path.abspath(__file__))
template_file_path = os.path.join(script_path, template_filename)
rendered_file_path = os.path.join(script_path, rendered_filename)

environment = jinja2.Environment(loader=jinja2.FileSystemLoader(script_path))
output_text = environment.get_template(template_filename).render(render_vars)

print output_text
[email protected]: python3 /home/dummy/renderer.py
<h1>Oh hi!<h1>

 

Writing the output into an HTML file

To write the output into an HTML File you need to replace the last print line with the With-Statement and write the output_text into rendered_file_path.

Replacing print with fileopen
with open(rendered_file_path, "w") as result_file:
    result_file.write(output_text)

As soon as the With-Statement reaches its end, it automatically closes the file and destroys the result_file object. The end of the With-Statement is after the last indented line.

 

Further Ressources

Jinja2 Documentation: http://jinja.pocoo.org/docs/

Multithreading in Python 2.7

Multithreading in Python 2.7

Hey guys,

Today I’d like to tell you how to use multiple threads for heavy computing purposes.

For example, we (at Immowelt) needed Multithreading to speed up our static code analysis. The old process, based on bash, took up to 30 seconds each time the pre-commit Githook triggered. As soon as we used Multithreading the process executed within 5 seconds.

At first, we need to figure which packages we need to import. To use the most simple kind of multithreading we only need the ThreadPool Module which is part of multiprocessing.pool. I also need some other modules for demonstration purposes.

Import Modules; Start execution timer
from multiprocessing.pool import ThreadPool
import random, time, timeit

start_time = timeit.default_timer()

Then we define all needed variables and also the heavy calculating ( 😉 ) function.

Heavy Computing Function
def asyncProcess(threadNumber, threadRuntime):
    time.sleep(threadRuntime)
    return("%i: this Process ran %i seconds" % (threadNumber, threadRuntime))

Everything is ready to go, so we start the main function.

Main Function
if __name__ == '__main__':
    thread_count = 10 # Define the limit of concurrent running threads
    thread_pool = ThreadPool(processes=thread_count) # Define the thread pool to keep track of the sub processes
    known_threads = {}

    # Now we execute 10 parallel threads
    for i in range(0,10):
        known_threads[i] = thread_pool.apply_async(asyncProcess, args=(i,random.randint(0,10),))

    thread_pool.close() # After all threads started we close the pool
    thread_pool.join() # And wait until all threads are done

    # Getting the results of all threads
    for thread in known_threads:
        print known_threads[thread].get()

    print("Parent: this Process ran %s seconds" % str(timeit.default_timer() - start_time))

The Main Function cover the management of the Thread Pool, start all threads, wait until every child is done and gather the information of the child threads.

  • Thread Management (Line 2-4)
    • The limit of concurrent threads must be set. Be careful if you produce a heavy amount of network traffic or IOPS.
    • To spawn any child processes the thread pool must be defined
    • If you don’t have any fire-and-forget processes all children must be known
  • Start all threads (Line 7-8)
    • Here we start 10 Processes which wait 10 seconds and return the needed information
    • All children will be mapped in a dict object to gain their information after the thread pool is done
    • Very important: at line eight (the marked one) the last argument of args= must end with a comma-sign.
  • Wait until every child is done (Line 10-11)
    • The <pool>.close() method tells the pool object to stop accepting new child’s.
    • <pool>.join() blocks the parent until all child’s are done.
  • Gather computed information of the child threads (Line 14-15)
    • We iterate through the dict object and use the <child_thread>.get() method to print the return value generated by the children.

 

Putting it all together:

Whole Code
# Importing all needed modules
from multiprocessing.pool import ThreadPool
import random, time, timeit

# Starting timer for Parent measurement
start_time = timeit.default_timer()

# Define the function which will be executed within the ThreadPool
def asyncProcess(threadNumber, threadRuntime):
    time.sleep(threadRuntime)
    return("%i: this Process ran %i seconds" % (threadNumber, threadRuntime))

if __name__ == '__main__':
    thread_count = 10 # Define the limit of concurrent running threads
    thread_pool = ThreadPool(processes=thread_count) # Define the thread pool to keep track of the sub processes
    known_threads = {}

    # Now we execute 10 parallel threads
    for i in range(0,10):
        known_threads[i] = thread_pool.apply_async(asyncProcess, args=(i,random.randint(0,10),))

    thread_pool.close() # After all threads started we close the pool
    thread_pool.join() # And wait until all threads are done

    # Getting the results of all threads
    for thread in known_threads:
        print known_threads[thread].get()

    print("Parent: this Process ran %s seconds" % str(timeit.default_timer() - start_time))

Example Output

#: python multithreading.py
0: this Process ran 9 seconds
1: this Process ran 3 seconds
2: this Process ran 2 seconds
3: this Process ran 1 seconds
4: this Process ran 5 seconds
5: this Process ran 5 seconds
6: this Process ran 1 seconds
7: this Process ran 10 seconds
8: this Process ran 2 seconds
9: this Process ran 1 seconds
Parent: this Process ran 10.0648648739 seconds

Handcrafted St(r)eam Machine – not as easy as you think

Handcrafted St(r)eam Machine – not as easy as you think

Hey guys,

today I’d like to show you how I build my Steam Machine and why it isn’t as easy as it seems like.

I had the following conditions for the Steam Machine (which are no particularly high requirements):

  • Have to match into the closet
    • width: 50cm
    • height: 30cm
    • depth: 40cm
  • Most bang for bucks – upper limit 1000€

Hardware Parts

I end up choosing the following Parts for the Computing (Server) Part of my Setup:

Nr. Manufacturer Part Price in €
1 Corsair DIMM 16GB DDR4-2133 Kit, RAM 119,9
2 Samsung MZ-75E250B 250 GB, SSD 99,9
3 Fractal Design NODE 304, Case 74,9
4 Sharkoon SilentStorm SFX Gold 500W, PS 79,9
5 Intel Core i5 6500 4x 3.20GHz So.1151 BOX 202,3
6 Asus H110I-PLUS Intel H110 So.1151 Dual Channel DDR4 Mini-ITX Retail 70,34
7 Nvidia 6GB Inno3D GeForce GTX 1060 iChill X3 Aktiv PCIe 3.0 x16 259,00

As Clients, I used two Steam links located in the Bed and also Living Room. They work out of the Box and are able to customize with the Steam link SDK.

Now we miss two parts. Windows 10 disables the Cursor if no Mouse is connected. You are able to install any kind of software to emulate a mouse or plug in a very cheap one.

The second part is a virtual monitor which is very important. Some games try to get the Monitor configuration to set up the resolution. Either you try to install any kind of software to emulate a Monitor or you buy a HDMI Stick which emulates a monitor. You can use e.g. this Model.

Software Part

My Server will be running on Windows 10 because most games in Steam are compatible with Windows. To keep control of the server I use Teamviewer.

Windows 10 need some tweaks to work properly as streaming Machine because e.g. the login keeps appearing after reboot so that Steam isn’t able to start.

Disabling Login Screen on boot up

  • Why?
    • As soon as the Steam machine boot up, steam cannot start because the computer is still locked.
  • How?
    • a. Press [WINDOWS] Button or open start menu
    • b. Enter “netplwiz”
    • c. Remove the checkmark of “Users must enter a username and password to use this computer.”
    • d. Click OK to keep the option

Disabling Lock Screen on Wake on Lan

  • Why?
    • After Waking up the Machine it keeps stuck in a lock screen.
  • How?
    • a. Press [WINDOWS] + [X]
    • b. Select “Command Prompt (admin)”
    • c. Enter the following command
      • powercfg /SETACVALUEINDEX SCHEME_CURRENT SUB_NONE CONSOLELOCK 0

Disabling UAC (User Account Control)

  • Why?
    • The Steam link is not able to work through the UAC when e.g. installing a program.
  • How?
    • First Method:
      • a. Press [WINDOWS] + [X]
      • b. Go to “Control Panel\User Accounts and Family Safety\User Accounts”
      • c. Click on “Change User Account Control settings”
      • d. Move the slider to the bottom to “Never Notify”
      • e. Click ok
    • Second Method:
      • a. Open the Start Menu
      • b. enter ua c and hit [ENTER]
      • c. Go ahead with “First Method” at (d.)
    • Third Method:
      • a. Open Registry Editor (open Start Menu; enter Regedit; hit [ENTER]
      • b. Navigate to “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System”
      • c. Modify the value of the “EnableLUA” DWORD and set it to 0

Putting all together

Does it even perform?

To perform any kind of performance tests I always use Novabench because it’s free with all features included. Here are my results.

Yes. It does! This score is really good in relation to the total costs.