<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[NotAwful]]></title><description><![CDATA[Thoughts on security, technology, and games. Helpful guides.]]></description><link>https://notawful.org/</link><image><url>https://notawful.org/favicon.png</url><title>NotAwful</title><link>https://notawful.org/</link></image><generator>Ghost 4.28</generator><lastBuildDate>Mon, 13 Apr 2026 04:24:21 GMT</lastBuildDate><atom:link href="https://notawful.org/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Hacking the Blue Team Village Badge (DC27)]]></title><description><![CDATA[<p>Hello! This year at Def Con 27 (2019), a friend of mine reserved me a <a href="https://www.blueteamvillage.org">Blue Team Village</a> badge. Commas go where your heart desires. It is, in my opinion, the coolest of all the badges I saw at Def Con 27 because of all the cool, useful things it</p>]]></description><link>https://notawful.org/hacking-the-blue-team-village-badge-dc27/</link><guid isPermaLink="false">5ec4001ab7157c33df05a511</guid><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Sat, 31 Aug 2019 17:24:57 GMT</pubDate><content:encoded><![CDATA[<p>Hello! This year at Def Con 27 (2019), a friend of mine reserved me a <a href="https://www.blueteamvillage.org">Blue Team Village</a> badge. Commas go where your heart desires. It is, in my opinion, the coolest of all the badges I saw at Def Con 27 because of all the cool, useful things it can do. It is a RasPi Zero W, with an attached face board that features a screen and a bunch of buttons (game boy style), and a built-in rechargeable battery pack.</p><p>It is a WiFi honeypot (HoneyDB), it records ssh sessions of people attached to it (I got a very cute ssh message from another con-goer), and it is readily hackable to do other things as well. It also had an ad-hoc badge-to-badge thing going on, and if you were near BTV the organizers could push updates to it (properly signed and authenticated). It is also set up very well; it&apos;s root password randomizes on start-up and you can find it using the badge&apos;s built-in screen and buttons. You can plug a USB cable into the data port on the RasPi Zero W and enable USB Ethernet so you can ssh into it on a wired connection.</p><p>One of the help files in the badge&apos;s menu said to drop down to a shell. When I did, nothing seemed to happen, it was just a shell and I had no keyboard. After a couple of moments it switched back to the badge menu. So, I decided to do what any gamer with a controller would do: I entered the Konami code using the buttons. Something unlocked!</p><!--kg-card-begin: markdown--><pre><code>this is the konami code unlockable add-on.  now that you know how the unlock process works, know that there are a few other items to unlock.
</code></pre>
<!--kg-card-end: markdown--><p>So, after putting it off for quite a while I decided to get down to it and try to hack the badge like I was told to. So let&apos;s explore what that was like!</p><h1 id="finding-the-challenge">Finding the challenge</h1><p>Following the procedure from the help file, I connected to the USB Ethernet on the device and SSH&apos;d into the badge.</p><!--kg-card-begin: markdown--><pre><code class="language-bash">malachite@xps:~$ ssh -p 22336 root@192.168.7.4
root@192.168.7.4&apos;s password: 
Linux MalachiteOS 4.14.52+ #1123 Wed Jun 27 17:05:32 BST 2018 armv6l
root@MalachiteOS:~# ls -alh
total 52K
drwx------  5 root root 4.0K Aug  1 04:28 .
drwxr-xr-x 23 root root 4.0K Jul 31 08:22 ..
-rw-------  1 root root 1.5K Aug  1 04:28 .bash_history
-rw-r--r--  1 root root  570 Mar 12  2018 .bashrc
drwx------  3 root root 4.0K Jul 22 21:43 .cache
-rw-------  1 root root 1.6K Aug 23  2018 .fbtermrc
drwx------  2 root root 4.0K Jul 23 14:18 .gnupg
-r--------  1 root root   32 Aug  1 04:30 passwd.txt
-rw-r--r--  1 root root  148 Mar 12  2018 .profile
-rw-------  1 root root 1.0K Jul 23 13:41 .rnd
-rw-r--r--  1 root root   75 Jan  5  2019 .selected_editor
dr-x------  2 root root 4.0K Jul 31 23:58 .ssh
-rw-r--r--  1 root root   37 Dec 11  2018 .vimrc
root@MalachiteOS:~# cd .gnupg/
root@MalachiteOS:~/.gnupg# ls -alh
total 12K
drwx------ 2 root root 4.0K Jul 23 14:18 .
drwx------ 5 root root 4.0K Aug  1 04:28 ..
-rw------- 1 root root   32 Jul 23 14:18 pubring.kbx
root@MalachiteOS:~/.gnupg# 
</code></pre>
<!--kg-card-end: markdown--><p>The only thing that stood out to me was the <code>.gnupg</code> folder, which contained a keyring. This might get brought up later.</p><!--kg-card-begin: markdown--><pre><code class="language-bash">root@MalachiteOS:/# ls -alh
total 91K
drwxr-xr-x  23 root root  4.0K Jul 31 08:22 .
drwxr-xr-x  23 root root  4.0K Jul 31 08:22 ..
-rw-r--r--   1 root root     0 Jul 31 10:10 0
# entries cut for space
-rw-r--r--   1 root root     0 Jul 31 08:22 30
drwxr-xr-x   9  501 staff 4.0K Jul 23 08:27 badge
drwxr-xr-x   2 root root  4.0K Aug 17  2018 bin
drwxr-xr-x   3 root root  3.0K Jan  1  1970 boot
drwxr-xr-x   2 root root  4.0K Jun 27  2018 debootstrap
drwxr-xr-x  14 root root  3.4K Aug  1 04:29 dev
lrwxrwxrwx   1 root root    13 May 18 11:12 dialogrc -&gt; /etc/dialogrc
lrwxrwxrwx   1 root root    19 May 18 10:52 dialogrc-green -&gt; /etc/dialogrc-green
lrwxrwxrwx   1 root root    17 May 18 10:59 dialogrc-red -&gt; /etc/dialogrc-red
drwxr-xr-x 100 root root  4.0K Aug  1 04:30 etc
drwxr-xr-x   8 root root  4.0K Jul 14 05:27 home
drwxr-xr-x  16 root root  4.0K May 25 15:12 lib
drwx------   2 root root   16K Jun 27  2018 lost+found
drwxr-xr-x   2 root root  4.0K Jun 27  2018 media
drwxr-xr-x   3 root root  4.0K Jan  5  2019 mnt
drwxr-xr-x   3 root root  4.0K Jun 27  2018 opt
dr-xr-xr-x  72 root root     0 Jan  1  1970 proc
drwx------   5 root root  4.0K Aug  1 04:28 root
drwxr-xr-x  17 root root   520 Aug  1 04:33 run
drwxr-xr-x   2 root root  4.0K Aug 17  2018 sbin
drwxr-xr-x   2 root root  4.0K Jun 27  2018 srv
-rw-r--r--   1 root root     1 Feb 14 12:23 status_tail.txt
dr-xr-xr-x  12 root root     0 Jan  1  1970 sys
drwxrwxrwt   7 root root  4.0K Aug  1 04:38 tmp
drwxr-xr-x  10 root root  4.0K Jun 27  2018 usr
drwxr-xr-x  12 root root  4.0K Jul 23 03:23 var
root@MalachiteOS:/# 
</code></pre>
<!--kg-card-end: markdown--><p>I didn&apos;t know what the 0...30 files were until later: They are the buttons on the face of the BTV badge. Other than that, there is a folder named <code>badge</code>. That looks interesting, we&apos;ll look at that after we check <code>status_tail.txt</code>.</p><!--kg-card-begin: markdown--><pre><code class="language-bash">root@MalachiteOS:/# cat status_tail.txt 

root@MalachiteOS:/# ls -alh badge
total 36K
drwxr-xr-x  9  501 staff 4.0K Jul 23 08:27 .
drwxr-xr-x 23 root root  4.0K Jul 31 08:22 ..
drwxr-xr-x  2  501 staff 4.0K Jul 23 07:03 addons
drwxr-xr-x  3  501 staff 4.0K Jul 30 15:47 admin
drwxr-xr-x  4  501 staff 4.0K Aug  2  2019 art
drwxr-xr-x  3  501 staff 4.0K Jul 31 07:49 bin
drwxr-xr-x  7 root root  4.0K Jul 31 09:07 data
drwxr-xr-x  2  501 staff 4.0K Jul 22 19:25 deploy
drwxr-xr-x  2  501 staff 4.0K Jul 23 09:01 help
root@MalachiteOS:/#
</code></pre>
<!--kg-card-end: markdown--><p><code>status_tail.txt</code> was empty, and there&apos;s a bunch of folders in badge. <code>data</code> is the only one here owned by root, so I want to see what that&apos;s about before I go rooting through other people&apos;s stuff.</p><!--kg-card-begin: markdown--><pre><code class="language-bash">root@MalachiteOS:/badge# ls -alh data
total 76K
drwxr-xr-x 7 root    root    4.0K Jul 31 09:07 .
drwxr-xr-x 9     501 staff   4.0K Jul 23 08:27 ..
-rw-r--r-- 1 root    root       2 Jul 31 07:54 blingrepeat.txt
-rw-r--r-- 1 root    root      42 Jul 31 10:10 friends.txt
-rw-r--r-- 1 root    root      12 Jul 31 08:11 handle.txt
drwxr-xr-x 2 honeydb honeydb 4.0K Jul 31 08:27 honeydb
-rw-r--r-- 1 root    root      14 Jul 31 19:40 honeyssid.txt
-rw-r--r-- 1 root    root    1.1K Aug  1 00:52 log-all-simple.txt
-rw-r--r-- 1 root    root     387 Jul 31 19:41 log-honeydb-simple.txt
-rw-r--r-- 1 root    root       0 Jul 31 07:54 log-honeydb.txt
-rw-r--r-- 1 root    root       0 Jul 31 08:27 log-honeydhcp.txt
-rw-r--r-- 1 root    root      42 Jul 31 09:07 log-honeyssh-simple.txt
-rw-r--r-- 1 root    root       0 Jul 31 07:54 log-honeyssh.txt
-rw-r--r-- 1 root    root     690 Aug  1 00:52 log-honeywap-simple.txt
-rw-r--r-- 1 root    root    1.4K Aug  1 02:01 log-honeywap.txt
drwx------ 2 root    root     16K Jul 31 07:54 lost+found
drwxr-xr-x 2 root    root    4.0K Jul 31 08:11 messages
drwxr-xr-x 2 cowrie  cowrie  4.0K Jul 31 09:08 ssh
drwxr-xr-x 2 root    root    4.0K Aug  1 00:56 unlocks
root@MalachiteOS:/badge# ls -alh data/unlocks/
total 16K
drwxr-xr-x 2 root root 4.0K Aug  1 00:56 .
drwxr-xr-x 7 root root 4.0K Jul 31 09:07 ..
-rwxr-xr-x 1 root root  337 Aug  1 00:57 easy.sh
-rwxr-xr-x 1 root root  258 Jul 31 15:24 konami.sh
root@MalachiteOS:/badge# cat data/unlocks/konami.sh 
#!/bin/bash

# keep this here to read badge variables
source /badge/bin/badge_vars.sh

# put add-on code here
echo &quot;this is the konami code unlockable add-on.  now that you know how the unlock process works, know that there are a few other items to unlock.&quot;
root@MalachiteOS:/badge# cat data/unlocks/easy.sh 
#!/bin/bash

clear

echo &quot;congrats, you&apos;ve unlocked the easy challenge.  there are 3 other unlock challenges that will require skill, luck, hashing or cracking.

prizes await for the first to unlock the hard, medium and expert challenges.

separate from this contest, there are also several games and utilities to unlock on the badge.
&quot;
root@MalachiteOS:/badge# 
</code></pre>
<!--kg-card-end: markdown--><p>Okay. So those files were what I unlocked when I was pushing buttons. <code>easy.sh</code> was just the &apos;A&apos; button, and <code>konami.sh</code> was... well, the konami code (<em>Up Down Up Down Left Right Left Right A B Start</em>). There was a contest but I have to assume someone else managed to unlock them during Def Con 27 and claimed their prize at the venue. Anyways, there are other things that I need to unlock, but at this point I am not sure they are all button inputs. Maybe there are some CTF things I need to do around the badge, so let&apos;s keep looking around.</p><p>Data has all of the user-facing variables, and of course the unlocks. The text tiles contain things like the handle, your HoneyDB scores, friends list, etc.. Basically anything that the badge displays to users. These are all probably handled by other scripts, so messing around with them won&apos;t do anything here.</p><p>So, typically <code>bin</code> is where all the executables and the like are. I reckon that the button scripts are there, and there might be a clue as to what buttons will unlock other challenges.</p><!--kg-card-begin: markdown--><pre><code class="language-bash">root@MalachiteOS:/badge# ls -alh bin
total 308K
drwxr-xr-x 3  501 staff 4.0K Jul 31 07:49 .
drwxr-xr-x 9  501 staff 4.0K Jul 23 08:27 ..          
# many, many entries removed for space
-rwxr-xr-x 1  501 staff 2.5K Jul 31 07:43 button_debug.py
-rwxr-xr-x 1  501 staff 4.1K Jul 31 07:43 button_handler_konami.py
-rwxr-xr-x 1  501 staff 3.3K Jul 31 07:43 button_handler_main_menu.py
-rwxr-xr-x 1  501 staff 3.4K Jul 31 07:43 button_handler_moon-buggy.py
-rwxr-xr-x 1  501 staff 3.4K Jul 31 07:43 button_handler_netris.py
-rwxr-xr-x 1  501 staff 2.9K Jul 31 07:43 button_handler_test.py
# many, many more entries removed for space
root@MalachiteOS:/badge#
</code></pre>
<!--kg-card-end: markdown--><p>There was a load of stuff in the bin folder. More than I expected, honestly. I ended up having to go back and pipe it through <code>less</code> to make it easier to look through them. Most of them were python scripts. I truncated the results above so that I could point out the scripts that caught my eye: <code>button_*.py</code>. I looked at all of them, and <code>button_handler_konami.py</code> is the one that has the booty. I am going to go through that right here.</p><!--kg-card-begin: markdown--><pre><code class="language-python">#!/usr/bin/python
import RPi.GPIO as GPIO
import time
import signal
import sys
import subprocess
import hashlib

&apos;&apos;&apos;
Bunch of stuff truncated here, setting up the GPIO signaling for the face buttons
&apos;&apos;&apos;

global press
press=&quot;&quot;

def interrupt_handler(channel):
    global press

    if channel == 19:
        #print(&quot;19 - A&quot;)
        press+=&quot;A&quot;
        time.sleep(.01)
    elif channel == 26:
        #print(&quot;26 - START&quot;)
        press+=&quot;S&quot;
        subprocess.call([&apos;systemctl&apos;, &apos;restart&apos;, &apos;getty@tty1.service&apos;], shell=False)
        time.sleep(.01)
    elif channel == 20:
        #print(&quot;20 - SELECT&quot;)
        subprocess.call([&apos;/badge/bin/badge_display_pwm.sh&apos;], shell=False)
        time.sleep(.01)
# A bunch more elif commands, setting up the buttons. Followed by the button initialization that reference these inturrupts.

tick = 0
while (tick &lt; 20):
    time.sleep(.25)
    tick = tick + 1

# The rest of the script, continued below.
</code></pre>
<!--kg-card-end: markdown--><p>Alright. So I can tell from opening it that it uses <code>/usr/bin/python</code> and not <code>python3</code>. Then I can see that there is <code>hashlib</code> in there, which tells me there&apos;s going to be some authentication or hashing involved. Looking at <code>press</code> and the <code>interrupt_handler()</code>, I can see that whenever you press a button it appends a character to the string <code>press</code>. It&apos;s <code>global</code>, so it&apos;ll get used everywhere (I don&apos;t know if this gives it any other properties).</p><p>Now, I truncated the <code>interrupt_handler()</code>, but here&apos;s what to take away from it:</p><ul><li>Each button press adds a letter to string <code>press</code></li><li>The list of possible letters that gets added to <code>press</code> are <code>A,S,U,B,L,D,R</code></li><li>Start (<code>S</code>) and Select run subprocesses, that may or may not interrupt the rest of the script</li></ul><p>From what I can tell about the <code>tick</code> part is that this determines how long you get to start pushing buttons, and if it gets interrupted by a button press, the interrupt sleeps for <code>.01</code>s. Without the button interrupts this gives you about five seconds to input buttons, which is a pretty reasonable amount of time to enter any codes.</p><p>What&apos;s next?</p><!--kg-card-begin: markdown--><pre><code class="language-python"># welcome.  this should get you started :)
salt = &apos;XstblibaQNaAWO8dYo:&apos;
codes = {
        &apos;50f9d9597642a0d090d4a613bf81f9d8bc4c7b4ec1db48c9f4abf88225c3cfad&apos; : &apos;konami&apos;,
        &apos;b3a218481d173dcd066a42eb74702714c5bf7f0c77982666c703f0d2b78b4a35&apos; : &apos;admin&apos;,
        &apos;fda91114369fdd643f5ab0c12a808dfbc0d360a1eb2fd3d9a56e66220d000313&apos; : &apos;debug&apos;,
        &apos;df722c019cb312748828b3b547d685448e43092e65d78eae2999d39bd96052b3&apos; : &apos;moonbuggy&apos;,
        &apos;3a868834d0e8d1690020d9fd01793df75496a127d7e82dab8dece423ee0bad5f&apos; : &apos;netris&apos;,
        &apos;90974350053c8ea1fc721da375242d888bde1d5b7c818ab1aa2f5265bcc2eebc&apos; : &apos;da&apos;,
        &apos;1d5420fdff3a2529e89edc307eba788c9b177d8482d49e4f05a3bd71fadf2444&apos; : &apos;easy&apos;, 
        &apos;b62d9205a693b3601ade197944cd39f305c6388dd1b97cff1190b5b7e6801077&apos; : &apos;medium&apos;, 
        &apos;8c30f3cdcf02ec6e0fc8e97f12738be7e2b2f710f5888932da5163a8891f75ea&apos; : &apos;hard&apos;, 
        &apos;f2664042ea3d51d5b94f49c36d5d9892eb9b913fd306b9648e1fd5dcd848932f&apos; : &apos;expert&apos;, 
    }

hashed = hashlib.sha256(salt + press.encode()).hexdigest()

for item in codes:
    if hashed in codes:
        passphrase = salt + press
        addone = &apos;/badge/addons/&apos; + codes[hashed] + &apos;.sh.asc&apos;
        addond = &apos;/badge/data/unlocks/&apos; + codes[hashed] + &apos;.sh&apos;
        
        cmd = &apos;/usr/bin/gpg -q --batch --passphrase &apos; + passphrase + &apos; &apos; + &apos;-d &apos; + addone + &apos; &gt; &apos; + addond
        
        subprocess.Popen([cmd], shell=True)
        
        cmd = &apos;chmod +x &apos; + addond
        subprocess.Popen([cmd], shell=True)


        print codes[hashed] + &apos; unlocked!&apos;
        time.sleep(1)
        break
</code></pre>
<!--kg-card-end: markdown--><p>Aha, here&apos;s the booty. Here is where I get to show some of my college education doing authentication and encryption and the like (and also how long it&apos;s been). Forgive me any incorrect terms. Let&apos;s break down the first half, before the <code>for item in codes</code> bit:</p><ul><li>The <code>salt</code> is <code>XstblibaQNaAWO8dYo:</code>, and this should be added to any hashing we do. This is like a <em>nonce</em>, which helps prevent dictionary attacks by adding additional randomness to the hashes.</li><li><code>codes</code> is a dictionary of salted hashes, next to what appear to be challenge names. Close analogy: usernames and password hashes.</li><li>The hashes are <strong>sha256</strong>, and are generated by adding the <code>salt</code> to <code>press</code> and then running it through <code>hashlib.sha256</code>. The hexdigest moves it from a <code>&lt;byte object&gt;</code> in python to a string, which we can actually use later.</li></ul><p>So, we happen to know what the value of <code>press</code> will be for two entries, <code>konami</code> and <code>easy</code>. If we add <code>XstblibaQNaAWO8dYo:</code> to <code>A</code>, and then run it through the hashing algorithm, we&apos;ll get a hash code that will match <code>easy</code>. For <code>konami</code>, it&apos;s <code>XstblibaQNaAWO8dYo:</code> + <code>UUDDLRLRBAS</code> (<strong>U</strong>p <strong>U</strong>p <strong>D</strong>own <strong>D</strong>own <strong>L</strong>eft <strong>R</strong>ight <strong>L</strong>eft <strong>R</strong>ight <strong>B</strong> <strong>A</strong> <strong>S</strong>tart). I am getting a little ahead of myself though, and should probably explain what we&apos;ll do with this.</p><p>We are going to want to crack the hashes in the table, that is, attempt to find a matching set of <em>words</em> that when put into their hashing function will get us matches. We want to build what&apos;s called a <em>rainbow table</em>. We want to do this in an <em>offline attack</em>, so that our brute-forcing attempts are not being run on the device we&apos;re trying to break into. So what do we have that I have determined is useful to this goal?</p><ul><li>We&apos;ve got a seven character <em>alphabet</em>: <code>A,S,U,B,L,D,R</code></li><li>We have the <em>salt</em>: <code>XstblibaQNaAWO8dYo:</code></li><li>We have the function used to generate the hashes: <code>hashlib.sha256(salt + press.encode()).hexdigest()</code></li><li>We have the list of all correct hashes.</li></ul><h4 id="a-quick-rundown-of-some-cryptography-and-attacker-concepts">A quick rundown of some cryptography and attacker concepts</h4><p>Alright, quick run-down of terminology from above before we continue (how I remember/use them, and not what they may actually mean):</p><ul><li>A <em>salt</em> is like a <em>nonce</em>, which is a <strong>n</strong>umber that is used <strong>once</strong> in encryption. These exist to add randomness.</li><li>A <em>word</em> is well, a word, that you want to encrypt. This stands in for <em>plaintext</em> which is unencrypted information, or <em>pass<strong>word</strong>s</em> in our case.</li><li>A <em>rainbow table</em> is a pre-computed table used for <em>cracking</em> password hashes. It&apos;s a table filled with <em>words</em> and the resulting hashes in different protocols, like sha256. We will be generating this table by <em>brute-forcing</em> all possible button combinations and finding matches. (For those about to be pedantic, brute-forcing and rainbow tables are different things. Actual <a href="https://en.wikipedia.org/wiki/Rainbow_table">rainbow tables</a> are much more complex.)</li><li>An <em>offline attack</em> is an attack that is not run on/against the active system; often cracking passwords/hashes, or decrypting files</li><li>An <em>alphabet</em> in encryption is all the possible characters that could be part of a <em>word</em> or <em>plaintext</em>. If we are cracking actual passwords, the alphabet would be <code>a-zA-Z0-9</code> and maybe a list of special characters.</li></ul><p>I guess now that I have explained an <em>alphabet</em> I should take a moment to explain why a seven character is important for us. If our alphabet only contains seven characters, it means each character in a password can only be one of those seven characters. This decreases the amount of time it will take to compute all reasonable possibilities.</p><p>An important part about encryption (and hashing is a form of encryption, <em>fight me Scott, pedantic teacher of my security class</em>) is that encryption is meant to be <em>timely</em>, which is to say that it cannot be unbreakable. You want an attacker to spend as much time as possible decrypting/discovering your encrypted/hashed data, so much time that by the time the attacker succeeds the data is irrelevant. Encryption protocols/algorithms must advance as computational power advances to ensure attacks against encrypted data are unreasonable for most attackers.</p><p>Below is some math that shows how the size of the alphabet and the width increase computational time by adding an ideally <em>unreasonable</em> amount of possibilities to try.</p><pre><code>4 [alphabet] ^ 4 [width] = 256 possible results
4 [alphabet] ^ 7 [width] = 16384 possible results
7 [alphabet] ^ 4 [width] = 2401 possible results
7 [alphabet] ^ 7 [width] = 823543 possible results
</code></pre><p>Anyways, we need to do the thing. So let&apos;s get back to this script, why don&apos;t we?</p><h4 id="back-to-business">Back to business</h4><p>The script way above includes how when it finds a matching has it locates the file, decrypts the file, moves it to the proper place on the badge, and then change the permissions so it belongs to <code>root</code>. I could take time to really parse it all out and make it into a script, but I want to push the buttons myself (unless one of the combinations is too long). We&apos;re going to be doing a proper attack, but first we should test the functions to make sure our understanding of the inputs is correct.</p><p>Let&apos;s test something first in REPL. (<strong>This fails in python 3 by the way, but succeeds in python 2.7.15+, as noted earlier</strong>)</p><!--kg-card-begin: markdown--><pre><code class="language-python">Python 2.7.15+ (default, Nov 27 2018, 23:36:35) 
[GCC 7.3.0] on linux2
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; import hashlib
&gt;&gt;&gt; 
&gt;&gt;&gt; salt = &apos;XstblibaQNaAWO8dYo:&apos;
&gt;&gt;&gt; press=&quot;&quot;
&gt;&gt;&gt; press+=&quot;A&quot;
&gt;&gt;&gt; codes = {
...         &apos;50f9d9597642a0d090d4a613bf81f9d8bc4c7b4ec1db48c9f4abf88225c3cfad&apos; : &apos;konami&apos;,
...         &apos;b3a218481d173dcd066a42eb74702714c5bf7f0c77982666c703f0d2b78b4a35&apos; : &apos;admin&apos;,
...         &apos;fda91114369fdd643f5ab0c12a808dfbc0d360a1eb2fd3d9a56e66220d000313&apos; : &apos;debug&apos;,
...         &apos;df722c019cb312748828b3b547d685448e43092e65d78eae2999d39bd96052b3&apos; : &apos;moonbuggy&apos;,
...         &apos;3a868834d0e8d1690020d9fd01793df75496a127d7e82dab8dece423ee0bad5f&apos; : &apos;netris&apos;,
...         &apos;90974350053c8ea1fc721da375242d888bde1d5b7c818ab1aa2f5265bcc2eebc&apos; : &apos;da&apos;,
...         &apos;1d5420fdff3a2529e89edc307eba788c9b177d8482d49e4f05a3bd71fadf2444&apos; : &apos;easy&apos;, 
...         &apos;b62d9205a693b3601ade197944cd39f305c6388dd1b97cff1190b5b7e6801077&apos; : &apos;medium&apos;, 
...         &apos;8c30f3cdcf02ec6e0fc8e97f12738be7e2b2f710f5888932da5163a8891f75ea&apos; : &apos;hard&apos;, 
...         &apos;f2664042ea3d51d5b94f49c36d5d9892eb9b913fd306b9648e1fd5dcd848932f&apos; : &apos;expert&apos;, 
...     }
&gt;&gt;&gt; 
&gt;&gt;&gt; hashed = hashlib.sha256(salt + press.encode()).hexdigest()
&gt;&gt;&gt; 
&gt;&gt;&gt; if hashed in codes:
...     print(codes[hashed])
... 
easy
&gt;&gt;&gt; press=&quot;UUDDLRLRBAS&quot;
&gt;&gt;&gt; hashed = hashlib.sha256(salt + press.encode()).hexdigest()
&gt;&gt;&gt; if hashed in codes:
...     print(codes[hashed])
...
&gt;&gt;&gt; press=&quot;UUDDLRLRBA&quot;
&gt;&gt;&gt; hashed = hashlib.sha256(salt + press.encode()).hexdigest()
&gt;&gt;&gt; if hashed in codes:
...     print(codes[hashed])
... 
konami
&gt;&gt;&gt; 
</code></pre>
<!--kg-card-end: markdown--><p>We understand the inputs. We have also learned that the <code>S</code> in my <code>konami</code> entry earlier was incorrect. That&apos;s good to know. This also means we have two known good results that we can use to test against, and one of them is the first possible thing we&apos;ll find. Now we just have to build a brute-forcer in python using these bits. We need to make a script that will check every possible combination of letters from our alphabet with an arbitrary width.</p><p>I had a lot of difficulty getting it to loop past two characters, but luckily I had help from a friend, <a href="https://twitter.com/kardonice">@kardonice</a> who helped me out hugely by introducing itertools (which I had tried but didn&apos;t understand, and I also tried the wrong thing from itertools), and later multiprocessing to speed this up. Let&apos;s see the results of the first pass with the working (single-threaded) version.</p><!--kg-card-begin: markdown--><pre><code class="language-python">Python 2.7.15+ (default, Nov 27 2018, 23:36:35)
[GCC 7.3.0] on linux2
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; import hashlib
&gt;&gt;&gt; import itertools as it
&gt;&gt;&gt;
&gt;&gt;&gt; salt = &apos;XstblibaQNaAWO8dYo:&apos;
&gt;&gt;&gt; codes = {
...         &apos;50f9d9597642a0d090d4a613bf81f9d8bc4c7b4ec1db48c9f4abf88225c3cfad&apos; : &apos;konami&apos;,
...         &apos;b3a218481d173dcd066a42eb74702714c5bf7f0c77982666c703f0d2b78b4a35&apos; : &apos;admin&apos;,
...         &apos;fda91114369fdd643f5ab0c12a808dfbc0d360a1eb2fd3d9a56e66220d000313&apos; : &apos;debug&apos;,
...         &apos;df722c019cb312748828b3b547d685448e43092e65d78eae2999d39bd96052b3&apos; : &apos;moonbuggy&apos;,
...         &apos;3a868834d0e8d1690020d9fd01793df75496a127d7e82dab8dece423ee0bad5f&apos; : &apos;netris&apos;,
...         &apos;90974350053c8ea1fc721da375242d888bde1d5b7c818ab1aa2f5265bcc2eebc&apos; : &apos;da&apos;,
...         &apos;1d5420fdff3a2529e89edc307eba788c9b177d8482d49e4f05a3bd71fadf2444&apos; : &apos;easy&apos;,
...         &apos;b62d9205a693b3601ade197944cd39f305c6388dd1b97cff1190b5b7e6801077&apos; : &apos;medium&apos;,
...         &apos;8c30f3cdcf02ec6e0fc8e97f12738be7e2b2f710f5888932da5163a8891f75ea&apos; : &apos;hard&apos;,
...         &apos;f2664042ea3d51d5b94f49c36d5d9892eb9b913fd306b9648e1fd5dcd848932f&apos; : &apos;expert&apos;,
...     }
&gt;&gt;&gt; alphabet = &quot;ABSURDL&quot;
&gt;&gt;&gt;
&gt;&gt;&gt; def check_match(password):
...     hashed = hashlib.sha256((salt + password).encode()).hexdigest()
...     if hashed in codes:
...         print(password, codes[hashed])
...
&gt;&gt;&gt; for i in range(1, 11):
...    for comb in it.product(alphabet, repeat=i):
...        check_match(&apos;&apos;.join(comb))
...
(&apos;A&apos;, &apos;easy&apos;)
# REDACTED
# REDACTED
# REDACTED
# REDACTED
# REDACTED
(&apos;UUDDLRLRBA&apos;, &apos;konami&apos;)
&gt;&gt;&gt; exit()
</code></pre>
<!--kg-card-end: markdown--><h2 id="conclusion">Conclusion</h2><p>The both the single and multithreaded versions of this script work perfectly. I managed to unlock one additional challenge after setting it to try combinations up to twenty and running it for nine hours on my Dell XPS 13, and only on three cores. I am going to start re-running the script on a more powerful machine, and hopefully I&apos;ll unlock the extreme one sometime soon! Special thanks to Munin, who got me my BTV badge and to @Kardonice, who helped me write the repeater.</p><h1 id="support-the-author">Support the Author</h1><p>Devon Taylor (They/Them) is a Canadian python developer, network architect, and consultant. Their <a href="https://notawful.org/">blog</a> covers technical problems they encounter and design and media analysis. Their <a href="https://twitter.com/awfulyprideful">twitter</a> You can support their independent work via <a href="https://www.patreon.com/notawful">Patreon (USD)</a>, or directly through <a href="https://ko-fi.com/notawful">Ko-Fi (CAD)</a>.</p>]]></content:encoded></item><item><title><![CDATA[DEF CON 27 (2019) Travel Debrief]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Hi there! It has been a while since I made a post on my blog, and I figured what better than to do a wrap-up of my very successful DEFCON 27 trip. It was a blast and I got to meet up with some of my best friends for the</p>]]></description><link>https://notawful.org/def-con-27-2019-travel-debrief/</link><guid isPermaLink="false">5ec4001ab7157c33df05a510</guid><category><![CDATA[Other]]></category><category><![CDATA[Guides]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Wed, 21 Aug 2019 15:24:44 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Hi there! It has been a while since I made a post on my blog, and I figured what better than to do a wrap-up of my very successful DEFCON 27 trip. It was a blast and I got to meet up with some of my best friends for the first time, and it was as successful at every stage of the trip as it possibly could have.</p>
<h1 id="thedefinitionofsuccess">The Definition of Success</h1>
<p>It is really important to start with <strong>why</strong> my trip was successful, because success at a conference is going to be different for everyone attending. Here are the things that I wanted out of my trip:</p>
<ul>
<li>Don&apos;t pack anything I didn&apos;t need (Pack light, pack efficiently)</li>
<li>Spend as much time as possible among friends</li>
<li>Respect my own limits and boundaries</li>
<li>Eat well, stay hydrated, be presentable, and get plenty of rest</li>
<li>Bring home cool swag, but don&apos;t overspend</li>
</ul>
<p>So, what did my DEFCON 27 experience look like at the end of it all? Here is it in list form:</p>
<ul>
<li><strong>I used</strong> (nearly) <strong>everything that I packed.</strong></li>
<li>I did not have any time where I was alone and wondering what to do.</li>
<li>I was among friends at almost all times.</li>
<li>I was comfortable dipping out when I was tired or didn&apos;t feel comfortable, and had ensured I always had a way out if it was needed.</li>
<li>My group and I ate meals often, and ensured each other were staying properly hydrated.</li>
<li>There were not any times when I felt gross or that I wasn&apos;t presentable to new people I was meeting (yay frequent showers).</li>
<li>I actually slept better in Vegas than I have at home all year. I got all my necessary rest and more.</li>
<li>Brought home a Plunder Bug, Packet Squirrel, and a Proxmark 3 RDV4.0. Also brought back the Blue Team Village badge!</li>
<li>I started with $1,000 USD. I bought my friends a lot of meals and presents, covered taxi costs, bought my swag and badges, and had <strong>exactly</strong> $3 USD left over!</li>
</ul>
<h1 id="bigimportantpackinglist">Big Important Packing List</h1>
<ul>
<li><a href="https://www.mec.ca/en/product/5044-007/Duffle-Bag?colour=BK000">Mec Duffle Bag (35L)</a> (can squish to carry-on size)
<ul>
<li>Three books wrapped tight in a bag, and the bag taped in place. Book cube. They were a gift for a friend, and didn&apos;t return with me.</li>
<li><a href="https://www.mec.ca/en/product/5041-233/Cascade-Field-Bag">Mec Cascade Field Bag</a> (Day bag)</li>
<li><a href="https://www.amazon.ca/gp/product/B00F9S85CS">Large Eagle Creek Packing Cube</a>
<ul>
<li>4x 100% Cotton T-shirts (They&apos;re very comfortable)</li>
<li>2x Shorts</li>
<li>1x Swimsuit (in a leak-resistant bag)</li>
</ul>
</li>
<li>Small Eagle Creek Packing Cube
<ul>
<li>4x 100% Cotton Boxer Briefs</li>
<li>2x Quick-drying/antimicrobial polyester boxer briefs (for swimming, and generally useful)</li>
<li>2x Crew-length socks (padded for walking)</li>
<li>4x shorter socks (also padded for walking)</li>
</ul>
</li>
<li>1 Quart Leak-resistant Transparent Bag (wtf tsa, really)
<ul>
<li>Small bottle of Shampoo</li>
<li>Small bottle of body wash</li>
<li>Small aerosol can of shaving gel</li>
<li>Disposable Razor</li>
<li>Travel-size mouthwash</li>
<li>Travel-size toothpaste</li>
<li>Toothbrush</li>
<li>Floss</li>
<li>Anti-Acne stuff</li>
<li>Antiperspirant</li>
</ul>
</li>
<li>Amazon Packing Cube
<ul>
<li>Small cardboard box for stickers and small swag items</li>
<li>Various sizes of anti-static bags</li>
<li>4x Large Ziplock freezer bags</li>
<li>Ziplock bags</li>
<li>Anchored rubber bands</li>
<li>velcro cable ties</li>
<li>small zip ties (for like the inside of PC cases)</li>
<li>Very Small Eagle Creek Packing Cube
<ul>
<li>Power Block for TS100</li>
<li>Solder</li>
<li>Pelican Case
<ul>
<li>Small screwdriver</li>
<li>Side cutters</li>
<li>Disassembled TS100</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.goruck.com/GR1/">GoRuck G1 (21L)</a>
<ul>
<li>Dell XPS 13</li>
<li>Kindle Paperwhite</li>
<li><a href="https://www.amazon.ca/gp/product/B00E4LGVUO">Earbuds</a> coiled around a <a href="https://www.amazon.ca/gp/product/B01979RAWK">Bluelounge spool thing</a></li>
<li><a href="https://www.amazon.ca/gp/product/B01MTB55WH">Portable Bluetooth Speaker</a></li>
<li><a href="https://shop.hak5.org/products/wifi-pineapple?variant=113037961010">Hak5 Wifi Pineapple (Nano Tactical)</a></li>
<li>Bunch of CLIF bars</li>
<li><a href="https://www.amazon.ca/gp/product/B01GKGF08Y">Insulated Water Bottle</a></li>
<li>&quot;Necessities&quot; Kit (<a href="https://www.amazon.ca/gp/product/B01BD0GPKO">LIHIT LAB Compact Pencil Case</a>)
<ul>
<li><a href="https://www.themooltipass.com/">Mooltipass</a> and USB-A to Micro-USB Cable</li>
<li><a href="https://www.amazon.ca/gp/product/B015Z7XE0A">USB-A to USB-C Adapter</a></li>
<li><a href="https://www.amazon.ca/gp/product/B00NLKA0D8">Yubikey FIDO U2F Key</a> (But you should get the updated one <a href="https://www.amazon.ca/dp/B07BYSB7FK">here</a>.)</li>
<li>2x <a href="https://www.amazon.ca/gp/product/B00G9WHMCW">16GB USB 3.0 Flash Drives</a> (They have os installation/recovery media on them)</li>
<li>1x <a href="https://www.amazon.ca/gp/product/B075KQDWGK">64GB USB 3.0 Flash Drive</a> (mass storage)</li>
<li>Mechanical Pencil, Eraser, Ruler, Pen, Sharpie</li>
<li><a href="https://www.amazon.ca/gp/product/B071Y41YY3">Field Notes 48-Page Memo Book</a> (They were much cheaper when I bought a set)</li>
</ul>
</li>
<li>Charging Kit (<a href="https://www.amazon.ca/gp/product/B0058S883K">Skooba Mini Cable Stable</a>)
<ul>
<li><a href="https://www.amazon.ca/gp/product/B077CY4M8P">10,000mAh RAVPower USB-C Battery Bank</a></li>
<li><a href="https://www.amazon.ca/gp/product/B0712252ZQ">Multi-Port USB-C Wall Changer</a></li>
<li><a href="https://www.amazon.ca/gp/product/B01HF0YGCK">USB-C to USB 3.0 Cable</a> (charges battery bank and phone)</li>
<li><a href="https://www.amazon.ca/gp/product/B071XYBPMN">Anker Powerline+ USB-C Cable (3ft)</a> (charges laptop)</li>
<li>USB-A to Micro-USB cable (Charges battery bank and Kindle)</li>
<li><a href="https://www.amazon.ca/gp/product/B00XU6PM68">USB-C USB 3.0 4-port hub and Ethernet Adapter</a></li>
<li>Ethernet Cable (6 ft)</li>
<li><a href="https://www.amazon.ca/gp/product/B015Z7XE0A">USB-A to USB-C Adapter</a></li>
</ul>
</li>
<li><a href="https://www.goruck.com/padded-field-pocket-gr1/">GoRuck Padded Field Pouch (3L)</a>
<ul>
<li>Multi-day travel pill container (holding about 6 days worth of medication)</li>
<li>Official prescription reciepts for all the medication I had</li>
<li>Sheet of Imodium and information packet</li>
<li>Sheet of Reactin and information packet</li>
<li>Deodorant</li>
<li>Travel-sized hand sanitizer</li>
<li>Travel-sized moisturizer</li>
<li>3x pairs of <a href="https://www.amazon.ca/gp/product/B005YUW7A2">disposable earplugs</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="loadoutdebrief">Loadout debrief</h1>
<p>A super important part of traveling light is keeping track of the stuff you used and the stuff you didn&apos;t. Learning from your trips, cutting what you didn&apos;t need, and adding what you did makes future trips more successful! So, let&apos;s do that.</p>
<h2 id="stuffipackedbutdidntuse">Stuff I packed but didn&apos;t use</h2>
<p>The list of stuff I didn&apos;t actually use is way shorter than the list of stuff I packed. In fact the list of things that I packed but did not use consists of seven items:</p>
<ul>
<li>Swimsuit (friend forgot their swimsuit, so pool stuff canceled)</li>
<li>Wifi Pineapple</li>
<li>TS100 soldering iron, and solder</li>
<li>small screwdriver</li>
<li>rubber bands, velcro ties, freezer bags</li>
<li>USB-C USB hub and Ethernet adapter, and ethernet cable</li>
</ul>
<p>Out of those items, I will likely only leave the WiFi Pineapple behind next year.</p>
<p>The TS100 and small screwdriver I will keep in my luggage because they would be invaluable if a badge broke during the conference. The rubber bands, velcro ties, and freezer bags take up such a small ammount of space that I likely won&apos;t dispose of them, and they could come in handy in the right circumstances. I will certainly keep the USB-C hub/ethernet adapter and cable, because it fits in my charging kit and can come in useful at the hotel or at the conference in the future if I take part in a CTF or go to the packet hacking village.</p>
<h3 id="itemsthatipackedanddidntexpecttousebutendedupbeinginvaluable">Items that I packed and didn&apos;t expect to use, but ended up being invaluable</h3>
<p>Earplugs. I did not expect to actually use the earplugs by mid-day friday, but I ended up handing out two sets to my friends and using the third set when the DJ at the bar kept turning up the music slowly. Next year I plan on packing an extra pair to share with another friend, since our walking-around crew tended to be about three to four people. The earplugs were also invaluable at the airport and on the flight home where there were many screaming infants shrieking the whole way. <strong>Lesson: Pack earplugs, you will be happy you have them.</strong></p>
<h2 id="thingsiwishihadpacked">Things I wish I had packed</h2>
<p>Three items: A hairbrush, a USB-C/Thunderbolt to HDMI adapter (and HDMI cable), and a spare (unlocked) phone.</p>
<p>The hairbrush was an item that was actually on my packing list, but I had needed to use it at home the morning of my flight out and I just forgot to toss it in my bag. A friend ended up buying one and bringing it to me at the conference for Saturday. Thanks, by the way!</p>
<p>I wanted to watch a movie on Netflix at the hotel but I had completely forgotten that my laptop does not have a port that any hotel television is going to be able to plug into. I am going to purchase an adapter and cable and maybe put together a &quot;hotel room kit&quot; of stuff I bring that I only need at the hotel. It might just end up being an A/V kit, in which case it&apos;ll be useful if I ever do a talk.</p>
<p>A spare, fresh (unlocked) phone. <em>Oh boy</em>. My friend lost their phone on Friday, and we spent all of Friday night and Saturday night trying to find the phone and then to get them access to their accounts, but were unsuccessful at both. MFA doing what it&apos;s supposed to, I suppose. Anyways, wisdom is learning from the hardship of others, innit? I am going to start bringing DEF CON&apos;s favorite hacker accessory: A spare &quot;burner&quot; phone. I&apos;ll get one when my budget allows.</p>
<p>The spare phone will likely go into my &quot;Essentials&quot; kit, from above, and I will move the pens and notebook elsewhere. It will become an emergency &quot;get running at 100% and lockdown old devices in minutes&quot; kit with one of my yubikeys, my dedicated hardware password manager, and that 64GB flash drive with an encrypted backup of important files from my laptop. This is a kit that I&apos;ve wanted to put in place for a while, so now is as good at time as any.</p>
<h2 id="otherlessonslearned">Other lessons learned</h2>
<p>I learned some things just about traveling in general that I will keep in mind for next year.</p>
<ol>
<li>A 100% disassembled soldering iron will not make it past Canadian TSA</li>
<li>Since my duffel bag is carry-on sized, I can &apos;courtesy check&apos; (aka &apos;gate check&apos;) it for free. No baggage fee, but I still get to bring tools and lockpicks both ways in my small bag.</li>
</ol>
<p>It wasn&apos;t a new lesson, but having the packing cubes and other various kits/bags made sure that I knew where everything was and made forgetting anything next to impossible. When I checked a kit, if there was an open space in it, there was something missing (and I knew what it was). When I was repacking my cubes, if I forgot anything it would be obvious by the size/shape of the cube. Organization good.</p>
<h2 id="whatdidiactuallycarryaroundatthecon">What did I actually carry around at the con?</h2>
<p>My GoRuck GR1 was primarily for getting my laptop and tech to Las Vegas for the conference. Most of the contents stayed back at the hotel room. My main bag was the Mec Cascade Field Bag, which I had packed in my duffel (it squishes up real good). Here&apos;s what I actually had in the Field Bag (all from the lists above):</p>
<ul>
<li>Insulated Water Bottle</li>
<li>Kindle Paperwhite (Gives a little structure to the bag and gives it a bit of weight, so it&apos;s not all floppy)</li>
<li>Battery bank, USB-A to USB-C Cable, and USB-C to USB-C Cable</li>
<li>Some CLIF bars</li>
<li>Pen, small notebook</li>
<li>Earplugs</li>
<li>Hand sanitizer</li>
<li>Anti-static bag filled with stickers to hand out and trade</li>
<li>Passport and wallet</li>
</ul>
<p>The bag actually had loads of space left over for whatever goodies I picked up between trips back to my room. Plenty of space for collecting stickers, badges, and buying stuff at the vendors or more water/snacks. Never wanted for or needed anything that I had left back at the room. For the things that I came to do, I always had what I needed on me at all times without being overburdened, and everything had a place to go back to when I wasn&apos;t using it.</p>
<h1 id="enoughofpackinglistsandloadoutswhatdidyoudo">Enough of packing lists and load-outs. What did you do?</h1>
<p>The last time I went to DEF CON, it was okay, but there were a bunch of issues. The hotel I chose was too far away from the convention, I didn&apos;t bring enough money, I didn&apos;t know enough people, and I spent most of my time wandering around trying to find someone to hang out with. This year was a full shift.</p>
<p>I stayed at the Flamingo, which happened to be where <a href="https://twitter.com/blueteamvillage">Blue Team Village</a> was. This ended up being a 15-30 minute walk from the farthest conference floor at Planet Hollywood, with most of that walk being inside. I take this moment now to recognize that I am an able-bodied person who walks quickly, and who can navigate casinos with ease. There was a back exit from Paris (just stick to the left wall when entering from Bally&apos;s and hitting the Paris casino area) that let you skip the crosswalk that would get you stuck out in the heat, and put Planet Hollywood only a two or three minute walk away. The longest and easily the worst part of the trip was the crosswalk between the Flamingo and Bally&apos;s. <strong>I paid for taxis a total of three times, spending a grand total of $100</strong> (To and from airport, and once to meet friends. I over-tipped quite a bit).</p>
<p>What did I do with full access to all the conference areas without worrying about trips or costs? I met up with a friend on Thursday, and then met up with more friends on Friday, and we stuck together every day throughout the con. We&apos;re all a bunch of introverts and none of us were particularly great at approaching new people or trying to make new friends, so we stuck to our own and enjoyed our time together. I didn&apos;t head out to any parties, or spend too long in areas where the music or sound was <em>too</em> loud.</p>
<p>Our group was very good at checking in on each other and making sure everyone was okay. We ate plenty of meals and made sure each other had plenty of water when we were walking around. At least on my end, I was okay taking off to my room fairly early (typically before or around midnight), and I fell asleep pretty quickly.</p>
<h2 id="adviceandlessonslearned">Advice and Lessons Learned</h2>
<p>I think that I had the best possible experience, for the type of experience I was looking for. Much of my <a href="https://notawful.org/conference-travel-advice/">travel and conference advice</a> still stands true, so I am just going to call out some of the more important ones, and update them with new advice. Mostly, just go read that other post, it was much more information.</p>
<ul>
<li>Never check a bag if you can avoid it. This lowers the chance of your luggage being stolen, lost, or mishandled. If you do check bags, keep all your valuables in your carry-on. If your bag is carry-on size but you have some non-carry-on items, your airline might allow you to courtesy or gate check your bag for free.</li>
<li>Print your boarding passes. The rest of this tip used to read &quot;unless you have a battery pack&quot;, but now I will just say: Just print your boarding passes. See above regarding losing your phone.</li>
<li>If you drink lots of water like I do you will have to dump your water into a garbage bin at every security checkpoint. This goes for all beverages. Dump it out before you&apos;re at the front of the line.</li>
<li><strong>Ask for consent</strong> before taking people&apos;s picture or touching them for any reason. It makes a huge difference, even if you know the person really well.
<ul>
<li>Making eye contact and doing the open-arms-hug? gesture and asking, &quot;Hug?&quot; is easy to execute and isn&apos;t awkward. Respect their decision if they say no.</li>
</ul>
</li>
<li>If you are better known by your online handle than your real name include your handle in your introduction. &quot;Hi, I&apos;m Malachite, also @AwfulyPrideful and @MalachiteOS on Twitter.&quot;
<ul>
<li>Also, try to bring a picture you can pin to your badge or something with your avatar on it. A lot of people do much better remember that than names and handles.</li>
</ul>
</li>
<li>Most important tip of all: <strong>Don&apos;t push yourself.</strong> If you get anxious and can&apos;t be on the conference floor for long periods, just duck out and recharge. Under no circumstance should you force yourself to stay at the conference all day. You will exhaust yourself and ruin the experience if you do.</li>
<li><strong>Stay hydrated, bring snacks.</strong> You will have a better experience this way, I promise you. My personal choices are tap water (yuck, but free) and CLIF bars (trail food, keeps energy up sans meal).
<ul>
<li>Staying hydrated means drink small amounts of water every 15-20 minutes. Drinking lots of water all at once will not hydrate you since your body will only absorb a bit of it at once.</li>
</ul>
</li>
<li>Con Flu is real. Wash your hands often, or carry around hand sanitizer and use it often. The alternative is to not touch anything, but that won&apos;t save you.
<ul>
<li>Good strategy is that if you are about to touch your face, eyes, or nose, stop and use hand sanitizer or wash your hands with soap and water.</li>
<li>Honestly though, last I heard most people get &apos;con flu&apos; on the trip home from the conference.</li>
</ul>
</li>
<li>Wear good walking shoes. You will be on your feet most of the time, either walking around or standing in lines. Get good insoles / inserts for your shoes. Your feet will thank you for it.</li>
<li>Carry lots of cash for tips. Tipping your service people is really important, and people attending conferences are known not to tip well. Also thank them and be patient because it&apos;s really busy and hard for them too.</li>
<li>If you are at a bar or party, always order your own drink and keep your eyes on it. Stick with friends. Look after each other.</li>
</ul>
<h1 id="supporttheauthor">Support the Author</h1>
<p>Devon Taylor (They/Them) is a Canadian python developer, network architect, and consultant. Their <a href="https://notawful.org/">blog</a> covers technical problems they encounter and design and media analysis. Their <a href="https://twitter.com/awfulyprideful">twitter</a> You can support their independent work via <a href="https://www.patreon.com/notawful">Patreon (USD)</a>, or directly through <a href="https://ko-fi.com/notawful">Ko-Fi (CAD)</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Windows Reference Images for Rapid Virtual Machine Deployment]]></title><description><![CDATA[<p><em>This article was last updated <code>2018-12-07</code>.</em></p><p>Hello! When I was learning system and network administration, building test environments and virtual labs was a vital skill. When working with Windows administration having solid, up-to-date reference images sped up testing by enabling us to tear down and reset environments in minutes.</p><p>Creating</p>]]></description><link>https://notawful.org/windows-reference-images-for-rapid-virtual-machine-deployment/</link><guid isPermaLink="false">5ec4001ab7157c33df05a50f</guid><category><![CDATA[Guides]]></category><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Fri, 07 Dec 2018 20:32:51 GMT</pubDate><content:encoded><![CDATA[<p><em>This article was last updated <code>2018-12-07</code>.</em></p><p>Hello! When I was learning system and network administration, building test environments and virtual labs was a vital skill. When working with Windows administration having solid, up-to-date reference images sped up testing by enabling us to tear down and reset environments in minutes.</p><p>Creating reference images for virtual environments allow you to skip installing a fresh operating system every time you need a new machine. Windows reference images need to be set up properly or they will cause issues if multiple copies of the same reference image are connected to the same active directory domain.</p><p>So, let&apos;s learn how to set up proper reference images for <strong>Windows 10 Enterprise</strong> and <strong>Microsoft Server 2016</strong> in <strong>Client Hyper-V</strong>. At the end of this guide, we will have:</p><ul><li>Official ISO files for Windows 10 Enterprise and Microsoft Server 2016</li><li>Virtual hard disk images of fresh installs of Windows 10 Enterprise and Microsoft Server 2016 that can be repeatedly deployed for testing</li></ul><h1 id="gathering-materials">Gathering Materials</h1><p>The first thing that you need is to have the installation files for Windows 10 Enterprise and Windows Server 2016. Windows 10 Enterprise and Windows 10 Enterprise LTSC are available for free <a href="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-10-enterprise">here</a>, and multiple versions of Windows Server 2016 are available <a href="https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016">here</a> for free as well.</p><p><strong>Note</strong>: The downloads from the Microsoft Evaluation Center ask you to fill out information on your company, and provide contact details. However, none of this information is validated or used, so enter dummy data. </p><p>The version of Windows 10 Enterprise that you choose will depend on the environment you want to emulate. You will want to select the <strong>ISO</strong> for Windows Server 2016, because you will be making your reference image yourself. Once you have the images, you can install Client Hyper-V and get working.</p><h1 id="installing-client-hyper-v">Installing Client Hyper-V</h1><p>The official requirements for installing Client Hyper-V on Windows 10 are located on Microsoft Docs <a href="https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/hyper-v-requirements">here</a><a>.</a></p><ol><li>Open the Start Menu and type <code>Turn Windows Features on or off</code>.</li><li>Select <code>Turn Windows Features on or off</code> from the list.</li><li>In the list, mark the checkbox for <code>Hyper-V</code> and press okay.</li><li>You will be prompted to restart to complete the installation. Do so.</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://notawful.org/content/images/2018/12/windowsfeatures-1.PNG" class="kg-image" alt loading="lazy"><figcaption>The Windows Features dialogue box, showing Hyper-V checked.</figcaption></figure><h3 id="installing-client-hyper-v-using-powershell">Installing Client Hyper-V using PowerShell</h3><p>Open PowerShell as Administrator and enter:</p><pre><code>Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
Restart-Computer</code></pre><h1 id="setting-up-your-reference-vm-in-hyper-v">Setting up your reference VM in Hyper-V</h1><ol><li>Open <strong>Hyper-V Manager</strong>.</li><li>On the right side, under <strong>Actions</strong>, select <strong>New &gt; Virtual Machine</strong>. The New Virtual Machine Wizard will open.</li><li>On the Specify Name and Location page, name your virtual machine. Click Next.</li><li>On the Specify Generation page, select <strong>Generation 1 or 2</strong>. Selecting Generation 2 will change some of the menus later on when working with this virtual machine, but otherwise has no impact on our function. Click Next.</li><li>On the Assign Memory page, give your virtual machine a decent amount of memory. I give my virtual machines <strong>2-4GB</strong> and leave <strong>Dynamic Memory</strong> <strong>enabled</strong>. Dynamic Memory will let your Windows host reclaim memory not in use by your virtual machines. Click Next.</li><li>On the Configure Networking page, select <strong>Default Switch</strong> from the drop-down menu. Click next.</li><li>On the Connect Virtual Hard Disk page, select <strong>Create a virtual hard disk</strong>, and name the disk so it is easily identifiable later. Examples, <code>win10ent-reference.vhdx</code> and <code>server2016-reference.vhdx</code>.</li><li>On the Installation Options page, select <strong>Install an operating system from a bootable CD/DVD-ROM</strong> and select one of the ISO files you downloaded earlier. Click Finish.</li></ol><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/vm1.PNG" width="704" height="533" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/vm1.PNG 600w, https://notawful.org/content/images/2018/12/vm1.PNG 704w"></div><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/vm3.PNG" width="704" height="533" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/vm3.PNG 600w, https://notawful.org/content/images/2018/12/vm3.PNG 704w"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/vm4.PNG" width="704" height="533" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/vm4.PNG 600w, https://notawful.org/content/images/2018/12/vm4.PNG 704w"></div><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/vm6.PNG" width="704" height="533" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/vm6.PNG 600w, https://notawful.org/content/images/2018/12/vm6.PNG 704w"></div></div></div><figcaption>The New Virtual Machine Wizard, showing several steps.</figcaption></figure><h3 id="quick-script-for-creating-a-virtual-machine-for-a-fresh-installation">Quick script for creating a virtual machine for a fresh installation</h3><pre><code>$VMName = ClientGold
$ISOPath = &quot;C:\Hyper-V\ISOs\1534.RS3.Win10Ent.x64.iso

New-VM -Name $VMName -Generation 2 -MemoryStartupBytes 4GB -NewVHDPath &quot;$VMName.vhdx&quot; -NewVHDSize 127GB -SwitchName (Get-VMSwitch).Name
Add-VMDvdDrive -VMName $VMName -Path $ISOPath
Set-VMFirmware $VMName -FirstBootDevice (Get-VMDvdDrive $VMName)</code></pre><h1 id="creating-the-reference-images">Creating the reference images</h1><p>To create the reference images, we will simply go through the installation process normally once you start up the virtual machine.</p><p>In Windows 10 Enterprise, you will be asked to login with a Microsoft account. Below and to the left there is a button that says <strong>Domain Join</strong>. Press that button to create a local account. We will be deleting this account before we finish up.</p><p>Once you have installed Windows 10 Enterprise or Windows Server 2016, open the &#xA0;<strong>Settings</strong> app (<code>Win + i</code>) and navigate to <strong>Update &amp; Security</strong>. Click <strong>Check for updates</strong> and let everything download and install. When it&apos;s finished, you will have to reboot, so do that. If it does not prompt you to reboot, do it manually.</p><p>After the reboot, and you are logged in fully, open File Explorer (<code>Win + e</code>) and navigate to <code>C:\Windows\System32\Sysprep</code>. Run sysprep.exe, and the Sysprep dialogue will appear.</p><p>Optionally, in Windows 10 Enterprise we can remove the account created on the VM. Select <strong>Enter Audit Mode</strong>, optionally select <strong>Generalize</strong>, <strong>Reboot</strong> and click okay.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://notawful.org/content/images/2018/12/sysprepaudit.PNG" class="kg-image" alt loading="lazy"><figcaption>Sysprep tool, entering audit mode and rebooting.</figcaption></figure><p>When the device reboots and enters audit mode, open Windows Search (<code>Win</code>) and type <code>Add, edit, or remove other users</code>. The Settings app will open to the users page. Under <strong>Other Users</strong>, you should see the local administrator account that you made when you initially installed. <strong>Remove </strong>it.</p><p>Once that&apos;s done, open the Sysprep tool, select <strong>Enter Out of Box Experience (OOBE)</strong>, <strong>Generalize</strong>, and <strong>Shutdown</strong>, and click <strong>OK</strong>.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/sysprepaudit2-1.PNG" width="1026" height="872" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/sysprepaudit2-1.PNG 600w, https://notawful.org/content/images/size/w1000/2018/12/sysprepaudit2-1.PNG 1000w, https://notawful.org/content/images/2018/12/sysprepaudit2-1.PNG 1026w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/sysprepaudit3-1.PNG" width="1026" height="872" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/sysprepaudit3-1.PNG 600w, https://notawful.org/content/images/size/w1000/2018/12/sysprepaudit3-1.PNG 1000w, https://notawful.org/content/images/2018/12/sysprepaudit3-1.PNG 1026w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Removing the previous user (Left) and finishing Sysprep (Right)</figcaption></figure><p>If you don&apos;t care about leaving behind a user from the previous install, once Windows 10 Enterprise is finished updating you can just run Sysprep with <strong>Enter Out Of Box Experience (OOBE)</strong>, <strong>Generalize</strong>, and <strong>Shutdown</strong> selected, and move on.</p><h2 id="windows-server-2016">Windows Server 2016</h2><p>Windows Server 2016 does not create a new user when you install it. Instead it just assigns the local Administrator account a password. This will be removed when it Sysprep is run. After you finish updating Windows Server run Sysprep with <strong>Enter Out Of Box Experience (OOBE)</strong>, <strong>Generalize</strong>, and <strong>Shutdown</strong> selected, and click <strong>OK</strong>.</p><h2 id="last-steps">Last Steps</h2><p>The final step is moving our reference disks to a permanent home. Before we can move the files, we need to remove any checkpoints that Hyper-V has made. Open the Hyper-V Manager window, select your reference machine, right-click the top-level Automated Checkpoint, and select <strong>Delete Checkpoint</strong>.</p><figure class="kg-card kg-image-card"><img src="https://notawful.org/content/images/2018/12/checkpointdelete.png" class="kg-image" alt loading="lazy"></figure><p>Deleting the checkpoint rolls the virtual machine&apos;s current state into it&apos;s previous state, creating a single hard drive file. Once it is all rolled up, we can move it to a dedicated folder for reference images so can find them later. On my machine, I use <code>C:\HyperV\reference</code> to store my reference images.</p><h1 id="using-the-reference-images-in-hyper-v">Using the reference images in Hyper-V</h1><p>To use the reference images, we need to create a differencing disk. This is a VHDX file that only stores the differences between the current state and the state of the parent image. After the first time we boot it up, it will take up about 4-5GB, but that is significantly smaller than multiple different installations.</p><p><strong>Note</strong>: Once you create a differencing disk and select the target reference disk, you cannot move the reference disk without breaking the child images.</p><ol><li>In the Hyper-V Manager window, click <strong>New &gt; Hard Disk...</strong>. The New Virtual Hard Disk Wizard will open.</li><li>In the wizard, select <strong>Differencing Disk</strong>, then click Next.</li><li>Choose the name of your virtual hard disk, and where it is stored. Click Next.</li><li>On the Configure Disk page, click <strong>Browse</strong> and select the reference image you want this disk to be based on. Then click Next.</li></ol><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/diff-1.PNG" width="1028" height="780" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/diff-1.PNG 600w, https://notawful.org/content/images/size/w1000/2018/12/diff-1.PNG 1000w, https://notawful.org/content/images/2018/12/diff-1.PNG 1028w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/diff-2.PNG" width="704" height="533" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/diff-2.PNG 600w, https://notawful.org/content/images/2018/12/diff-2.PNG 704w"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/diff-3.PNG" width="704" height="533" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/diff-3.PNG 600w, https://notawful.org/content/images/2018/12/diff-3.PNG 704w"></div><div class="kg-gallery-image"><img src="https://notawful.org/content/images/2018/12/diff-5.PNG" width="704" height="533" loading="lazy" alt srcset="https://notawful.org/content/images/size/w600/2018/12/diff-5.PNG 600w, https://notawful.org/content/images/2018/12/diff-5.PNG 704w"></div></div></div><figcaption>Steps 1 through 4</figcaption></figure><p>Now that we have the differencing disk, all that&apos;s left is to create a new virtual machine to attach it to. When you are creating your virtual machine, at the hard drive step, select your hard drive. After you create the virtual machine, you will have to go into its settings and edit the boot order to ensure it boots from the drive first.</p><p>It may take a little while the first time one of your child disks start up. This is due to the <strong>Generalize</strong> that we preformed earlier that removed machine-specific information that now needs to be regenerated for this new machine.</p><p>Once a child VM has started, the bottom corner of the desktop shows the current status of the VM&apos;s license. The evaluation license should be between 90 and 180 days. Because we ran Sysprep.exe, every time we create a new child disk off our reference disk it will have a fresh license of 90 or 180 days.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://notawful.org/content/images/2018/12/sysprepaudit4.PNG" class="kg-image" alt loading="lazy"><figcaption>The evaluation license is valid for 90 days.</figcaption></figure><h1 id="what-next">What next?</h1><p>I don&apos;t know what you needed these for. Create a lab environment. Deploy active directory. Configure group policy. <a href="https://notawful.org/windows-pki/">Create a PKI environment</a>. Infect them with malware (<a href="https://leanpub.com/avatar">you&apos;ll need a more robust lab for this</a>). Or do whatever else you would want short-term Windows virtual machines for.</p><h1 id="support-the-author">Support the Author</h1><p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>]]></content:encoded></item><item><title><![CDATA[Python Image Crawler, Part 2]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Alright, so I wanted to work on my last <a href="https://notawful.org/basic-python-web-crawler/">crawler</a> that did a couple new things. Here are the goals for this one.</p>
<ul>
<li>Use <code>lxml</code>&apos;s <code>etree</code> html parser over <code>bs4</code></li>
<li>Separate into functions / clean up code a bit</li>
<li>Cut out duplicate entries</li>
</ul>
<p>I do not understand xpaths, but</p>]]></description><link>https://notawful.org/python-image-crawler-part-2/</link><guid isPermaLink="false">5ec4001ab7157c33df05a50e</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Tue, 21 Aug 2018 20:46:55 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Alright, so I wanted to work on my last <a href="https://notawful.org/basic-python-web-crawler/">crawler</a> that did a couple new things. Here are the goals for this one.</p>
<ul>
<li>Use <code>lxml</code>&apos;s <code>etree</code> html parser over <code>bs4</code></li>
<li>Separate into functions / clean up code a bit</li>
<li>Cut out duplicate entries</li>
</ul>
<p>I do not understand xpaths, but that&apos;s the idea with this change is to learn about them. So, let&apos;s start with some raw guesses and some googling. I initially had some trouble, but thankfully <a href="https://stackoverflow.com/questions/21455349/xpath-query-get-attribute-href-from-a-tag">stackoverflow</a> came to the rescue.</p>
<pre><code>import requests
import lxml
from lxml import etree

htmlparser = etree.HTMLParser()

def web(page,WebUrl):
    if(page&gt;0):
        url = WebUrl
        code = requests.get(url)
        plain = code.text
        # Using a new html parser
        tree = etree.fromstring(plain, htmlparser)
        for link in tree.xpath(&apos;//a/@href&apos;):
        	print(link)
if __name__ == &apos;__main__&apos;:
	web(1,&apos;https://notawful.org&apos;)
</code></pre>
<p>The result:</p>
<pre><code>malachite@localhost:~/python/scraper3$ python3 scraper3-test.py
https://notawful.org/
https://notawful.org/about/
https://notawful.org/content/images/2018/07/devon_taylor_resume.pdf
https://notawful.org/reading-list/
https://www.patreon.com/notawful
https://ko-fi.com/notawful
https://twitter.com/awfulyprideful
https://feedly.com/i/subscription/feed/https://notawful.org/rss/
/hotkeys-shortcuts-commands/
/author/notawful/
/data-security-while-traveling/
/author/notawful/
/installing-and-troubleshooting-netbox/
</code></pre>
<p>Alright, so from here what I want to do is cut out duplicates and keep only the links to pages under my domain. To cut out the duplicates we are going to create a <code>set()</code> which we will dump our list of links into. This will automatically remove any duplicate entries. I don&apos;t want my web crawler to wander from my web page, so I am going to need to filter out anything that doesn&apos;t start with <code>/</code>.</p>
<pre><code>def web(page,WebUrl):
    if(page&gt;0):
        url = WebUrl
        code = requests.get(url)
        plain = code.text
        tree = etree.fromstring(plain, htmlparser)
        pages = set()
        na_tasks = []
        for link in tree.xpath(&quot;//a/@href&quot;):
            if link.startswith(&apos;/&apos;):
                na_tasks.append(link)
        pages.update(na_tasks)
        for entry in pages:
            print(WebUrl + entry)
</code></pre>
<p>And the results:</p>
<pre><code>malachite@localhost:~/python/scraper3$ python3 scraper3-test.py
https://notawful.org/password-managers/
https://notawful.org/fixing-ghost-on-digitalocean/
https://notawful.org/sr-part2/
https://notawful.org/data-security-while-traveling/
https://notawful.org/lessons-learned-october-2017/
https://notawful.org/gogs-install/
https://notawful.org/college-university-guide/
https://notawful.org/installing-and-troubleshooting-netbox/
https://notawful.org/online-presence-on-a-budget/
...
</code></pre>
<p>That is exactly the list that I was looking to get. Now, I might want to structure this script a little differently so that I can reuse the same function again. Let&apos;s try that next. Also I am going to reduce another line down a little.</p>
<pre><code>def make_request(url):
    resp = etree.fromstring(requests.get(url).text, htmlparser)
    return resp

def get_images(page,WebUrl):
    if(page&gt;0):
        pages = set()
        na_tasks = []
        tree = make_request(WebUrl)
        for link in tree.xpath(&quot;//a/@href&quot;):
            if link.startswith(&apos;/&apos;):
                na_tasks.append(link)
        pages.update(na_tasks)
        for entry in pages:
            print(WebUrl + entry)
</code></pre>
<p>That looks cleaner. It produces the same result as above. So now I need to start digging for images on other pages. We&apos;re going to basically duplicate the xpath search, except instead of <code>&quot;//a/@href&quot;</code> we&apos;ll be looking for <code>&quot;//img/@src&quot;</code> instead. To make things a little cleaner in case I wanted to pipe the list of images to a downloader, I am going to separate remote and local images and append the root URL to the local images. Here&apos;s what the function ends up looking like:</p>
<pre><code>def get_images(page,WebUrl):
    if(page&gt;0):
        pages = set()
        na_tasks = []
        tree = make_request(WebUrl)
        for link in tree.xpath(&quot;//a/@href&quot;):
            if link.startswith(&apos;/&apos;):
                na_tasks.append(link)
        pages.update(na_tasks)
        image_local = []
        image_remote = []
        image_set = set()
        for entry in pages:
            req = WebUrl+entry
            resp = make_request(req)
            for img in resp.xpath(&apos;//img/@src&apos;):
                if img.startswith(&apos;/&apos;):
                    image_local.append(WebUrl+img)
                else:
                    image_remote.append(img)
        image_set.update(image_local)
        image_set.update(image_remote)
        for entry in image_set:
            print(entry)
</code></pre>
<p>And the results are exactly what I wanted to see, as well. Here are some of those:</p>
<pre><code>malachite@localhost:~/python/scraper3$ python3 scraper3-test2.py
https://assets.digitalocean.com/articles/pdocs/site/control-panel/networking/domains/no-domains-yet.png
https://notawful.org/content/images/2018/07/role-services-window.png
https://notawful.org/content/images/2018/07/rootCDP.png
https://assets.digitalocean.com/articles/putty_do_keys/new_ssh_key_prompt.png
https://notawful.org/content/images/2018/07/certtemplatecomputer.png
https://notawful.org/content/images/2018/08/webcrawl1-chromeconsole.png
https://pbs.twimg.com/media/DUp9AABW0AA82cI.jpg:large
https://notawful.org/content/images/2018/07/submitarequest.png
https://notawful.org/content/images/2018/07/rootCAProperties.png
https://notawful.org/content/images/2018/07/rootCAName.png
https://notawful.org/content/images/2018/07/revoked.png
https://notawful.org/content/images/2018/07/adcsconfigurationwindow.png
https://imgs.xkcd.com/comics/security.png
...
</code></pre>
<p>This script took a little while to run because it had to make all the requests one by one and wait for each result before it could move onto the next step. If I were to continue iterating on this I would have to make this script make <em>asynchronous</em> requests. *wink*.</p>
<p>All in all, changing over the HTML parser and cleaning up the script took about two and a half hours. List of things that I used that I was not aware of when I started making this post:</p>
<ul>
<li>Anything related to <a href="https://en.wikipedia.org/wiki/XPath">xpaths</a></li>
<li><a href="https://docs.python.org/3.7/library/stdtypes.html?highlight=set#set">set()</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html?highlight=startswith#str.startswith">.startswith()</a></li>
</ul>
<p>Here&apos;s the final code for this post:</p>
<pre><code>import requests
import lxml
from lxml import etree

htmlparser = etree.HTMLParser()

def make_request(url):
    resp = etree.fromstring(requests.get(url).text, htmlparser)
    return resp

def get_images(page,WebUrl):
    if(page&gt;0):
        pages = set()
        na_tasks = []
        tree = make_request(WebUrl)
        for link in tree.xpath(&quot;//a/@href&quot;):
            if link.startswith(&apos;/&apos;):
                na_tasks.append(link)
        pages.update(na_tasks)
        image_local = []
        image_remote = []
        image_set = set()
        for entry in pages:
            req = WebUrl+entry
            resp = make_request(req)
            for img in resp.xpath(&apos;//img/@src&apos;):
                if img.startswith(&apos;/&apos;):
                    image_local.append(WebUrl+img)
                else:
                    image_remote.append(img)
        image_set.update(image_local)
        image_set.update(image_remote)
        for entry in image_set:
            print(entry)

if __name__ == &apos;__main__&apos;:
    get_images(1,&apos;https://notawful.org&apos;)
</code></pre>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Hotkeys/Shortcuts/Commands]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Just collecting the hotkeys, shortcuts, and commands that I use frequently (and some I use infrequently but often have to go looking for).</p>
<h1 id="windows">Windows</h1>
<h2 id="generaluse">General Use</h2>
<ul>
<li><code>Win + L</code>: Lock computer (very useful)</li>
<li><code>Win + X</code>: Quick link window (links to a bunch of administrative tools)</li>
<li><code>Win + X, U</code>: Shutdown menu, follow</li></ul>]]></description><link>https://notawful.org/hotkeys-shortcuts-commands/</link><guid isPermaLink="false">5ec4001ab7157c33df05a50d</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Sun, 12 Aug 2018 18:01:02 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Just collecting the hotkeys, shortcuts, and commands that I use frequently (and some I use infrequently but often have to go looking for).</p>
<h1 id="windows">Windows</h1>
<h2 id="generaluse">General Use</h2>
<ul>
<li><code>Win + L</code>: Lock computer (very useful)</li>
<li><code>Win + X</code>: Quick link window (links to a bunch of administrative tools)</li>
<li><code>Win + X, U</code>: Shutdown menu, follow with a <code>U</code> for shutdown, <code>R</code> for restart, and <code>I</code> for sign out</li>
<li><code>Win + Shift + S</code>: Snipping tool (not the program, select an area of the screen to copy to clipboard as an image)</li>
<li><code>Win + Shift + [Right|Left Arrow]</code>: Move focused window to next monitor</li>
<li><code>Win + [1-9]</code>: Select window on taskbar, open pinned application if closed</li>
<li><code>Win + E</code>: Open Windows Explorer<br>
<strong>Bonus:</strong> <code>Win + Spacebar</code>: Switches through keyboard layout, lets you escape the French Canadian keyboard mode.</li>
</ul>
<h2 id="administrativestuff">Administrative Stuff</h2>
<ul>
<li><code>Win + X, I</code>: Powershell</li>
<li><code>Win + X, A</code>: Powershell (Admin)</li>
<li><code>Win + X, G</code>: Computer Management (Task scheduler, event viewer, local users, performance, device manager, disk management)</li>
<li><code>Win + I</code>: Settings menu</li>
</ul>
<h1 id="chromebook">Chromebook</h1>
<h2 id="croutonshell">Crouton/Shell</h2>
<p><code>Ctl + Alt + T</code>: Open Chrome developer terminal<br>
<code>Crtl + Alt + Shift + [Forward|Backward]</code>: Shift back and forth between ChromeOS and graphical chroot</p>
<h1 id="nix">*nix</h1>
<h2 id="vi">VI</h2>
<h3 id="modes">Modes</h3>
<ul>
<li><code>esc</code>: Exit insert/append mode and enter command mode</li>
<li><code>i</code>: enter insert mode; insert text before cursor</li>
<li><code>shift + i</code>: enter insert mode; insert text at start of line</li>
<li><code>a</code>: enter append mode; append text after cursor</li>
<li><code>shift + a</code>: enter append mode; append text to end of line</li>
</ul>
<h3 id="commandmode">Command Mode</h3>
<ul>
<li><code>dd</code>: delete this line</li>
<li><code>d10000d</code>: fuck this entire file (as long as file is under 10,000 lines)</li>
<li><code>yy</code>: cut (yank) this line</li>
<li><code>pp</code>: put buffer here (paste copied line)</li>
<li><code>:read ~/secretkey</code>: copy the contents of ~/secretkey to the current line</li>
<li><code>^</code>: place cursor at start of line (start from the top)</li>
<li><code>$</code>: place cursor at end of line (collect money at end)</li>
<li><code>/word</code>: find next word; can find <em>broadsword</em> or <em>password</em></li>
<li><code>?word</code>: find previous word; can find <em>broadsword</em> or <em>password</em></li>
<li><code>/\&lt;word\&gt;</code> find next complete instance of <em>word</em>; does not find swords or password</li>
<li><code>?\&lt;word\&gt;</code> find previous complete instance of <em>word</em>; does not find swords or password</li>
<li><code>:q</code>: quit</li>
<li><code>:q!</code>: I SAID GOOD DAY, SIR. (Quit, but shouting and bypasses save prompts)</li>
<li><code>:w</code>: write to file (save)</li>
<li><code>:wq</code>: Write and quit</li>
</ul>
<h2 id="git">Git</h2>
<h3 id="setup">Set-up</h3>
<p>Getting git set up initially:</p>
<pre><code>sudo apt install git
git config --global user.name &quot;Name&quot;
git config --global user.email email@example.com
git config --global core.editor vim
git config --global credential.helper cache
git config --global credential.helper &apos;cache --timeout=3600&apos;
</code></pre>
<p>The <code>credential.helper</code> configurations are so that you don&apos;t have to re-enter your password repeatedly when cloning over HTTPS.</p>
<ul>
<li><code>git config --list</code>: this will list the current configuration settings git has.</li>
</ul>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Data Security While Traveling]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Ohai. I was thinking about how I would secure sensitive data while traveling without inconveniencing myself, of course. I figured it would be an interesting thought to share, to see if anyone wanted to comment on it.</p>
<p>Goals:</p>
<ul>
<li>Data is secure in transit</li>
<li>Cannot be feasibly coerced into providing access</li></ul>]]></description><link>https://notawful.org/data-security-while-traveling/</link><guid isPermaLink="false">5ec4001ab7157c33df05a50c</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Sat, 11 Aug 2018 01:58:54 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Ohai. I was thinking about how I would secure sensitive data while traveling without inconveniencing myself, of course. I figured it would be an interesting thought to share, to see if anyone wanted to comment on it.</p>
<p>Goals:</p>
<ul>
<li>Data is secure in transit</li>
<li>Cannot be feasibly coerced into providing access to data</li>
<li>Able to get up and running quickly after border crossings</li>
</ul>
<p>Tools:</p>
<ul>
<li>Chromebook (Intel x86_64, with TPM)</li>
<li>USB Flash Drive or Portable SSD</li>
<li><a href="https://www.themooltipass.com/">Mooltipass</a></li>
<li>VeraCrypt (Or your choice of disk/volume encryption software.)</li>
</ul>
<p>So, this is how it would work, before you travel:</p>
<ol>
<li>Create an encrypted volume/partition on your mass storage with VeraCrypt and use the Mooltipass to create a long, secure password.</li>
<li>Create a copy of your Mooltipass&apos;s card and back up the mooltipass&apos;s database (binary). Store these safely before you travel. Write the incorrect PIN on the back of both cards.
<ul>
<li>The Mooltipass allows three attempts at entering a pin (each digit is 1-F), and if you can&apos;t get in by then it erases the card which makes that card unable to decrypt your database.</li>
<li>When you back up your mooltipass&apos;s binary, the database is still fully encrypted and needs to be loaded onto a mooltipass and access with the correct card in order to access them again.</li>
</ul>
</li>
<li>Back up your <a href="https://github.com/dnschneid/crouton">crouton chroot</a> to your VeraCrypt protected portable storage. Put in anything else you&apos;ll need with you on there at the same time.</li>
<li><a href="https://support.google.com/chromebook/answer/183084?hl=en">Powerwash</a> your Chromebook. In the help article, do it through the settings menu.
<ul>
<li>ChromeOS encrypts its drive by default and stores the encryption keys in the Trusted Platform Module (TPM). When you Powerwash, it deletes the keys in the TPM, making it impossible (within reason) to decrypt the drive and recover files from the previous installation.</li>
</ul>
</li>
<li>Log into your Chromebook with a management account. It shouldn&apos;t be a completely clean account, but an account with no access to any important files or tied to any of your other devices.</li>
</ol>
<p>So, at this point you have a clean device, a USB drive, and a Mooltipass and its card. If you are stopped at the border, the only way for anyone including you to access the encrypted data you are traveling with is to use the Mooltipass. You can give them the wrong pin or enter it incorrectly, and now nobody can access the encrypted data.</p>
<p>In order to get back up and running once you&apos;ve crossed the border:</p>
<ol>
<li>Powerwash the chromebook again, just in case.</li>
<li>Log into your chromebook with whatever account you want to use.</li>
<li>Install crouton and create a temporary chroot.</li>
<li>Download/install veracrypt into your chroot, decrypt your portable storage.</li>
<li>Restore your previous chroot from your backup.</li>
</ol>
<p>Now you&apos;re back up and running and you can continue on without any hassle. All you have to do now is backup your chroot again before you cross another border, powerwash your chromebook again, and you&apos;re ready to go.</p>
<p>All in all, this basically just saves you time when you are setting up your environment at your destination. You can also just backup your chroot to storage online and pull it back down at the destination, but this is definitely faster than that (and can be done offline).</p>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Installing and Troubleshooting Netbox]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>There&apos;s a story to this. Short version is a friend posted about <a href="https://github.com/digitalocean/netbox">netbox</a>, an IP address management and data center infrastructure tool. Later I wanted to redo my virutal lab environment from scratch but with better documentation. I figured I&apos;d set up an instance of netbox</p>]]></description><link>https://notawful.org/installing-and-troubleshooting-netbox/</link><guid isPermaLink="false">5ec4001ab7157c33df05a50b</guid><category><![CDATA[Technology]]></category><category><![CDATA[Guides]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Thu, 09 Aug 2018 21:04:55 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>There&apos;s a story to this. Short version is a friend posted about <a href="https://github.com/digitalocean/netbox">netbox</a>, an IP address management and data center infrastructure tool. Later I wanted to redo my virutal lab environment from scratch but with better documentation. I figured I&apos;d set up an instance of netbox to document how my lab <strong>should</strong> be. So here goes.</p>
<h1 id="secondattemptandtroubleshooting">SECOND ATTEMPT AND TROUBLESHOOTING</h1>
<p>After following the documentation once and making notes, this is what I had going into my second attempt at installing netbox. I am not going to break this up, this is just raw text file that I have been working out of. We&apos;ll get into a bunch of troubleshooting as we go, and I&apos;ll point out my changes along the way. This is pretty close to the documentation, with a few changes to respond to issues I ran into on my first attempt.</p>
<pre><code>sudo apt update ; sudo apt upgrade
sudo apt autoremove

sudo apt-get install -y postgresql libpq-dev
sudo -u postgres psql
CREATE DATABASE netbox;
CREATE USER netbox WITH PASSWORD &apos;XY,gTr6GP-Xy&apos;;
GRANT ALL PRIVILEGES ON DATABASE netbox TO netbox;
\q

sudo apt-get install -y python3 python3-dev python3-setuptools build-essential libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev zlib1g-dev
sudo apt install -y python3-pip

wget https://github.com/digitalocean/netbox/archive/v2.4.2.tar.gz
sudo tar -xzf v2.4.2.tar.gz -C /opt
cd /opt/
sudo ln -s netbox-2.4.2/ netbox
cd /opt/netbox/

pip3 install -r requirements.txt

cd /opt/netbox/netbox/netbox
sudo cp configuration.example.py configuration.py
python3 /opt/netbox/netbox/generate_secret_key.py &gt; ~/secretkey

sudo vi configuration.py
ALLOWED_HOSTS = [&apos;192.168.0.11&apos;]
DATABASE = {
    &apos;NAME&apos;: &apos;netbox&apos;,
    &apos;USER&apos;: &apos;netbox&apos;,
    &apos;PASSWORD&apos;: &apos;XY,gTr6GP-Xy&apos;,
    &apos;HOST&apos;: &apos;localhost&apos;,
    &apos;PORT&apos;: &apos;&apos;,
}

SECRET_KEY = &apos;J8g4+*aFfpV#2tGEPOSA%NriQx=Yvb_uWzKLy-U)0^w$Tl&amp;5os&apos;

cd /opt/netbox/netbox/
python3 manage.py migrate
python3 manage.py createsuperuser
sudo python3 manage.py collectstatic --no-input

sudo apt install -y nginx
sudo vi /etc/nginx/sites-available/netbox
server {
    listen 80;

    server_name 192.168.0.11;

    client_max_body_size 25m;

    location /static/ {
        alias /opt/netbox/netbox/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        add_header P3P &apos;CP=&quot;ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV&quot;&apos;;
    }
}

cd /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/netbox

cd ~
sudo pip3 install gunicorn --install-option=&quot;--install-scripts=/usr/bin&quot; -t python_modules/
which gunicorn

sudo vi /opt/netbox/gunicorn_config.py

command = &apos;/usr/bin/gunicorn&apos;
pythonpath = &apos;/opt/netbox/netbox&apos;
bind = &apos;127.0.0.1:8001&apos;
workers = 3
user = &apos;www-data&apos;

sudo apt install -y supervisor

sudo vi /etc/supervisor/conf.d/netbox.conf

[program:netbox]
command = gunicorn -c /opt/netbox/gunicorn_config.py netbox.wsgi
directory = /opt/netbox/netbox/
user = www-data

[program:netbox-rqworker]
command = python3 /opt/netbox/netbox/manage.py rqworker
directory = /opt/netbox/netbox/
user = www-data
</code></pre>
<p>Ends in 502 Bad Gateway. This is an expected error. From <a href="https://netbox.readthedocs.io/en/latest/installation/3-http-daemon/">the documents</a>, &apos;If you receive a 502 (bad gateway) error, this indicates that gunicorn is misconfigured or not running.&apos;</p>
<p>Checking the status of supervisor confirms that gunicorn is not running.</p>
<pre><code>malachite@netbox:~$ service supervisor status
&#x25CF; supervisor.service - Supervisor process control system for UNIX
   Loaded: loaded (/lib/systemd/system/supervisor.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2018-08-09 18:10:01 UTC; 9min ago
     Docs: http://supervisord.org
  Process: 51436 ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown (code=exited, status=0/SUCCESS)
 Main PID: 51437 (supervisord)
    Tasks: 1 (limit: 972)
   CGroup: /system.slice/supervisor.service
           &#x2514;&#x2500;51437 /usr/bin/python /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf

Aug 09 18:10:06 netbox supervisord[51437]: 2018-08-09 18:10:06,610 INFO spawned: &apos;netbox&apos; with pid 51468
Aug 09 18:10:06 netbox supervisord[51437]: 2018-08-09 18:10:06,614 INFO spawned: &apos;netbox-rqworker&apos; with pid 51469
Aug 09 18:10:06 netbox supervisord[51437]: 2018-08-09 18:10:06,881 INFO exited: netbox-rqworker (exit status 1; not expected)
Aug 09 18:10:06 netbox supervisord[51437]: 2018-08-09 18:10:06,989 INFO exited: netbox (exit status 1; not expected)
Aug 09 18:10:09 netbox supervisord[51437]: 2018-08-09 18:10:09,995 INFO spawned: &apos;netbox&apos; with pid 51475
Aug 09 18:10:09 netbox supervisord[51437]: 2018-08-09 18:10:09,998 INFO spawned: &apos;netbox-rqworker&apos; with pid 51476
Aug 09 18:10:10 netbox supervisord[51437]: 2018-08-09 18:10:10,259 INFO exited: netbox-rqworker (exit status 1; not expected)
Aug 09 18:10:10 netbox supervisord[51437]: 2018-08-09 18:10:10,351 INFO gave up: netbox-rqworker entered FATAL state, too many start retries too quickly
Aug 09 18:10:10 netbox supervisord[51437]: 2018-08-09 18:10:10,368 INFO exited: netbox (exit status 1; not expected)
Aug 09 18:10:11 netbox supervisord[51437]: 2018-08-09 18:10:11,370 INFO gave up: netbox entered FATAL state, too many start retries too quickly
</code></pre>
<p>These workers exist because of the python scripts <code>/etc/supervisor/conf.d/netbox.conf</code> and <code>/opt/netbox/gunicorn_config.py</code>. We know that <code>/etc/supervisor/conf.d/netbox.conf</code> is working because supervisor is attempting to spawn the workers defined in the script.</p>
<p><code>/etc/supervisor/conf.d/netbox.conf</code> is run first and reads:</p>
<pre><code>[program:netbox]
command = gunicorn -c /opt/netbox/gunicorn_config.py netbox.wsgi
directory = /opt/netbox/netbox/
user = www-data

[program:netbox-rqworker]
command = python3 /opt/netbox/netbox/manage.py rqworker
directory = /opt/netbox/netbox/
user = www-data
</code></pre>
<p><code>/opt/netbox/gunicorn_config.py</code> is run by the <strong>netbox</strong> worker defined in the first script.</p>
<pre><code>command = &apos;/usr/bin/gunicorn&apos;
pythonpath = &apos;/opt/netbox/netbox&apos;
bind = &apos;127.0.0.1:8001&apos;
workers = 3
user = &apos;www-data&apos;
</code></pre>
<p>So, looking back at the status, it runs <strong>netbox</strong> first, and then starts <strong>netbox-rqworker</strong>. When <strong>netboxrqworker</strong> fails, it closes and takes <strong>netbox</strong> with it. Either that or it is starting them both and they are both failing for the same reason. Although it has to do with php, <a href="https://stackoverflow.com/questions/28937722/supervisord-exit-status-1-not-expected-running-php-script">this question</a> at StackOverflow brings up what I&apos;ve been thinking.</p>
<blockquote>
<p>&quot;I more assume that the file is located under your home directory and the root user is not able access and run it. So I would try to change the location of the script.&quot;</p>
</blockquote>
<p>The first time I had to sudo to put something into <code>/opt/</code> I figured this was going to become an issue, especially since both <code>/etc/supervisor/conf.d/netbox.conf</code> and <code>/opt/netbox/gunicorn_config.py</code> both specify <code>user = www-data</code>. So there are two potential problems. Either gunicorn isn&apos;t installed correctly, as the <strong>502 Bad Gateway</strong> implies, or <strong>www-data</strong> is the user attempting to run the scripts and cannot access anything in <code>/opt/</code>.</p>
<p>Running <code>which gunicorn</code> shows that gunicorn exists at <code>/usr/bin/gunicorn</code> which is what we intended when we ran <code>sudo pip3 install gunicorn --install-option=&quot;--install-scripts=/usr/bin&quot; -t python_modules/</code>. So let&apos;s make sure that the agent is able to use gunicorn.</p>
<pre><code>malachite@netbox:~$ ls -alh /usr/bin | tail -5
-rwxr-xr-x  1 root   root    1.8K Jun 28  2017 xzless
-rwxr-xr-x  1 root   root    2.2K Jun 28  2017 xzmore
-rwxr-xr-x  1 root   root     31K Jan 18  2018 yes
-rwxr-xr-x  1 root   root     19K Apr 16 20:14 zdump
-rwxr-xr-x  1 root   root     48K Jul 18 14:21 zipdetails
malachite@netbox:~$ ls -alh /usr/bin/gunicorn
-rwxr-xr-x 1 root root 390 Aug  9 17:37 /usr/bin/gunicorn
</code></pre>
<p>The permissions are identical to the typical binaries in <code>/usr/bin</code>, so we can check that off. This means gunicorn itself isn&apos;t the issue, which makes the official documentation&apos;s implication that Gunicorn is &apos;misconfigured&apos; a little frustrating. This is clearly a permissions issue.</p>
<p>Let&apos;s test something. I cannot <code>su - www-data</code> to see if it can see inside /opt/, but I made another user <code>netbox</code> with <code>--logon-disabled</code> to see if I can even see inside of <code>/opt/</code>.</p>
<pre><code>malachite@netbox:~$ sudo su - netbox
[sudo] password for malachite:
netbox@netbox:~$ ls -alh /opt/netbox
lrwxrwxrwx 1 root www-data 13 Aug  9 17:23 /opt/netbox -&gt; netbox-2.4.2/
netbox@netbox:~$ ls -alh /opt/netbox/
total 84K
drwxrwsr-x  6 root www-data 4.0K Aug  9 17:39 .
drwxr-xr-x  3 root root     4.0K Aug  9 17:23 ..
-rw-rwSr--  1 root www-data  492 Aug  8 13:16 base_requirements.txt
-rw-rwSr--  1 root www-data 5.5K Aug  8 13:16 CONTRIBUTING.md
drwxrwsr-x 10 root www-data 4.0K Aug  8 13:16 docs
-rw-rwSr--  1 root www-data   17 Aug  8 13:16 .gitattributes
drwxrwsr-x  3 root www-data 4.0K Aug  8 13:16 .github
-rw-rwSr--  1 root www-data  190 Aug  8 13:16 .gitignore
-rw-r-Sr--  1 root www-data  118 Aug  9 17:39 gunicorn_config.py
-rw-rwSr--  1 root www-data  10K Aug  8 13:16 LICENSE.txt
-rw-rwSr--  1 root www-data 2.3K Aug  8 13:16 mkdocs.yml
drwxrwsr-x 17 root www-data 4.0K Aug  9 17:32 netbox
-rw-rwSr--  1 root www-data   38 Aug  8 13:16 old_requirements.txt
-rw-rwSr--  1 root www-data 2.3K Aug  8 13:16 README.md
-rw-rwSr--  1 root www-data  458 Aug  8 13:16 requirements.txt
drwxrwsr-x  2 root www-data 4.0K Aug  8 13:16 scripts
-rw-rwSr--  1 root www-data  297 Aug  8 13:16 .travis.yml
-rwxrwsr-x  1 root www-data 1.8K Aug  8 13:16 upgrade.sh
</code></pre>
<p>The fact that <code>www-data</code> has group permissions was the result of me attempting to fix this permissions issue previously by using <a href="https://stackoverflow.com/questions/9133024/www-data-permissions">this method</a> from StackOverflow.</p>
<pre><code>sudo chown -R root:www-data /opt/netbox
sudo chmod -R g+s /opt/netbox
sudo chown -R root:www-data /opt/netbox-2.4.2/
sudo chmod -R g+s /opt/netbox-2.4.2/
</code></pre>
<p>Either way, a non-sudo user can peak inside. Can it read execute anything?</p>
<pre><code>netbox@netbox:~$ ls -alh /opt/netbox/netbox
total 76K
drwxrwsr-x 17 root www-data 4.0K Aug  9 17:32 .
drwxrwsr-x  6 root www-data 4.0K Aug  9 17:39 ..
drwxrwsr-x  7 root www-data 4.0K Aug  9 17:32 circuits
drwxrwsr-x  7 root www-data 4.0K Aug  9 17:32 dcim
drwxrwsr-x  8 root www-data 4.0K Aug  9 17:32 extras
-rwxrwsr-x  1 root www-data  305 Aug  8 13:16 generate_secret_key.py
drwxrwsr-x  7 root www-data 4.0K Aug  9 17:32 ipam
-rwxrwsr-x  1 root www-data  249 Aug  8 13:16 manage.py
drwxrwsr-x  3 root www-data 4.0K Aug  8 13:16 media
drwxrwsr-x  3 root www-data 4.0K Aug  9 17:32 netbox
drwxrwsr-x  8 root www-data 4.0K Aug  8 13:16 project-static
drwxrwsr-x  2 root www-data 4.0K Aug  8 13:16 reports
drwxrwsr-x  9 root www-data 4.0K Aug  9 17:32 secrets
drwxr-sr-x 14 root www-data 4.0K Aug  9 17:32 static
drwxrwsr-x 14 root www-data 4.0K Aug  8 13:16 templates
drwxrwsr-x  6 root www-data 4.0K Aug  9 17:32 tenancy
drwxrwsr-x  5 root www-data 4.0K Aug  9 17:32 users
drwxrwsr-x  6 root www-data 4.0K Aug  9 17:32 utilities
drwxrwsr-x  7 root www-data 4.0K Aug  9 17:32 virtualization
netbox@netbox:~$ python3 /opt/netbox/netbox/manage.py
Traceback (most recent call last):
  File &quot;/opt/netbox/netbox/manage.py&quot;, line 8, in &lt;module&gt;
    from django.core.management import execute_from_command_line
ModuleNotFoundError: No module named &apos;django&apos;
</code></pre>
<p>Oh, that&apos;s an issue isn&apos;t it? Another issue I suspected, but this confirmed it. <code>ModuleNotFoundError: No module named &apos;django&apos;</code> means that it wasn&apos;t just gunicorn that needed to be installed specially somewhere. Using <code>pip3 install -r requirements.txt</code> only installed the requirements for <strong>malachite@netbox</strong> rather than system-wide. I had to use <code>sudo pip3 install gunicorn --install-option=&quot;--install-scripts=/usr/bin&quot; -t python_modules/</code> specifically because gunicorn wasn&apos;t being installed to <code>/usr/bin/</code>.</p>
<p>This has actually been an issue I have run into generally. Pip and Pip3 do not function the way developers are expecting it to on <strong>Ubuntu 18.04.1 LTS</strong>. They do not seem to be installed with <code>python3-setuptools</code> as implied by other documentation, and <code>python3-pip</code> has been a pain when it comes to installing modules.</p>
<p><a href="https://docs.python.org/3/installing/index.html#installing-into-the-system-python-on-linux">This page</a> from the official documentation says that there is a possibility that pip isn&apos;t installed by default in some cases.</p>
<p>Before I blow away my installation and reset this, let&apos;s see if running <code>pip3 install -r requirements.txt</code> with sudo will change where the packages are installed...</p>
<pre><code>malachite@netbox:/opt/netbox$ sudo pip3 install -r requirements.txt
The directory &apos;/home/malachite/.cache/pip/http&apos; or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo&apos;s -H flag.
The directory &apos;/home/malachite/.cache/pip&apos; or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo&apos;s -H flag.
Requirement already satisfied: Django&lt;2.1,&gt;=1.11 in /home/malachite/.local/lib/python3.6/site-packages (from -r requirements.txt (line 1))
Requirement already satisfied: django-cors-headers==2.4.0 in /home/malachite/.local/lib/python3.6/site-packages (from -r requirements.txt (line 2))
Requirement already satisfied: django-debug-toolbar==1.9.1 in /home/malachite/.local/lib/python3.6/site-packages (from -r requirements.txt (line 3))
...
</code></pre>
<p>Alright, so pip3 is installing everything to <code>/home/malachite/.local/lib/python3.6/site-packages</code>. This is why when <code>www-data</code> attempts to execute the scripts it fails. Also, I know that <code>www-data</code> in particular is attempting to execute these scripts because in the <a href="http://supervisord.org/configuration.html#program-x-section-values">supervisord documentation</a> it states:</p>
<blockquote>
<p><code>user</code><br>
Instruct supervisord to use this UNIX user account as the account which runs the program. The user can only be switched if supervisord is run as the root user. If supervisord can&#x2019;t switch to the specified user, the program will not be started.</p>
</blockquote>
<p>Continuing to read the python documention, I have come across something rather important <a href="https://packaging.python.org/guides/installing-using-linux-tools/">here</a>.</p>
<blockquote>
<p>Warning Recent Debian/Ubuntu versions have modified pip to use the <a href="https://pip.pypa.io/en/stable/user_guide/#user-installs">&#x201C;User Scheme&#x201D;</a> by default, which is a significant behavior change that can be surprising to some users.</p>
</blockquote>
<p>Doing further digging I decided to look at some comparisons. Checking installed python3 modules on <strong>malachite@netbox</strong>:</p>
<pre><code>malachite@netbox:~$ python3 -m pip list
DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning.
asn1crypto (0.24.0)
attrs (17.4.0)
Automat (0.6.0)
bcrypt (3.1.4)
blinker (1.4)
certifi (2018.4.16)
cffi (1.11.5)
chardet (3.0.4)
click (6.7)
cloud-init (18.3)
colorama (0.3.7)
command-not-found (0.3)
configobj (5.0.6)
constantly (15.1.0)
coreapi (2.3.3)
coreschema (0.0.4)
cryptography (2.3)
distro-info (0.18)
Django (2.0.8)
...
</code></pre>
<p>Versus installed python3 modules on <strong>netbox@netbox</strong></p>
<pre><code>netbox@netbox:~$ python3 -m pip list
DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning.
asn1crypto (0.24.0)
attrs (17.4.0)
Automat (0.6.0)
blinker (1.4)
certifi (2018.1.18)
chardet (3.0.4)
click (6.7)
cloud-init (18.3)
colorama (0.3.7)
command-not-found (0.3)
configobj (5.0.6)
constantly (15.1.0)
cryptography (2.1.4)
distro-info (0.18)
httplib2 (0.9.2)
hyperlink (17.3.1)
idna (2.6)
incremental (16.10.1)
Jinja2 (2.10)
jsonpatch (1.16)
...
</code></pre>
<p>I decided to go to this specific level because we see some differences. User <strong>malachite@netbox</strong> in just this top section of modules has the followind additional packages:</p>
<pre><code>bcrypt (3.1.4)
cffi (1.11.5)
coreapi (2.3.3)
coreschema (0.0.4)
Django (2.0.8)
...
</code></pre>
<p>And more. This means that python packages are being installed per user, and since <code>www-data</code> doesn&apos;t have a shell, let a lone a home directory, it cannot use any non-system packages for python3.</p>
<p>So I am going to try to cheat, here.</p>
<pre><code>malachite@netbox:~$ sudo su - root
root@netbox:~# pip3 install -r /opt/netbox/requirements.txt
</code></pre>
<p>Now let&apos;s restart supervisor and see what we get...</p>
<pre><code>malachite@netbox:~$ service supervisor status
&#x25CF; supervisor.service - Supervisor process control system for UNIX
   Loaded: loaded (/lib/systemd/system/supervisor.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2018-08-09 19:53:04 UTC; 6s ago
     Docs: http://supervisord.org
  Process: 52035 ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown (code=exited, status=0/SUCCESS)
 Main PID: 52036 (supervisord)
    Tasks: 2 (limit: 972)
   CGroup: /system.slice/supervisor.service
           &#x251C;&#x2500;52036 /usr/bin/python /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf
           &#x2514;&#x2500;52080 python3 /opt/netbox/netbox/manage.py rqworker

Aug 09 19:53:07 netbox supervisord[52036]: 2018-08-09 19:53:07,220 INFO spawned: &apos;netbox&apos; with pid 52071
Aug 09 19:53:07 netbox supervisord[52036]: 2018-08-09 19:53:07,221 INFO success: netbox-rqworker entered RUNNING state, process has stayed up for &gt; than 1 seconds (startsecs)
Aug 09 19:53:07 netbox supervisord[52036]: 2018-08-09 19:53:07,756 INFO exited: netbox (exit status 1; not expected)
Aug 09 19:53:07 netbox supervisord[52036]: 2018-08-09 19:53:07,772 INFO exited: netbox-rqworker (exit status 1; not expected)
Aug 09 19:53:08 netbox supervisord[52036]: 2018-08-09 19:53:08,775 INFO spawned: &apos;netbox-rqworker&apos; with pid 52073
Aug 09 19:53:09 netbox supervisord[52036]: 2018-08-09 19:53:09,779 INFO spawned: &apos;netbox&apos; with pid 52078
Aug 09 19:53:09 netbox supervisord[52036]: 2018-08-09 19:53:09,779 INFO success: netbox-rqworker entered RUNNING state, process has stayed up for &gt; than 1 seconds (startsecs)
Aug 09 19:53:10 netbox supervisord[52036]: 2018-08-09 19:53:10,239 INFO exited: netbox-rqworker (exit status 1; not expected)
Aug 09 19:53:10 netbox supervisord[52036]: 2018-08-09 19:53:10,255 INFO spawned: &apos;netbox-rqworker&apos; with pid 52080
Aug 09 19:53:10 netbox supervisord[52036]: 2018-08-09 19:53:10,292 INFO exited: netbox (exit status 1; not expected)
</code></pre>
<p>That is a new error. <strong>netbox-rqworker</strong> entered running state and stayed up for more than 1 second. Now, on the incredibly small off chance that it works. The log shows that it started to work, but crashed immediately after. The web page wasn&apos;t able to load, but we&apos;re closer. It&apos;s now very unstable unstable rather than dead. This is suprisingly a very good sign. Back under <strong>root@netbox</strong>, let&apos;s run <code>pip3 install gunicorn</code> just in case.</p>
<pre><code>Aug 09 20:02:16 netbox systemd[1]: Stopped Supervisor process control system for UNIX.
Aug 09 20:02:16 netbox systemd[1]: Started Supervisor process control system for UNIX.
Aug 09 20:02:16 netbox supervisord[53810]: 2018-08-09 20:02:16,993 CRIT Supervisor running as root (no user in config file)
Aug 09 20:02:16 netbox supervisord[53810]: 2018-08-09 20:02:16,995 INFO Included extra file &quot;/etc/supervisor/conf.d/netbox.conf&quot; during parsing
Aug 09 20:02:17 netbox supervisord[53810]: 2018-08-09 20:02:17,003 INFO RPC interface &apos;supervisor&apos; initialized
Aug 09 20:02:17 netbox supervisord[53810]: 2018-08-09 20:02:17,003 CRIT Server &apos;unix_http_server&apos; running without any HTTP authentication checking
Aug 09 20:02:17 netbox supervisord[53810]: 2018-08-09 20:02:17,003 INFO supervisord started with pid 53810
Aug 09 20:02:18 netbox supervisord[53810]: 2018-08-09 20:02:18,006 INFO spawned: &apos;netbox&apos; with pid 53835
Aug 09 20:02:18 netbox supervisord[53810]: 2018-08-09 20:02:18,009 INFO spawned: &apos;netbox-rqworker&apos; with pid 53836
Aug 09 20:02:19 netbox supervisord[53810]: 2018-08-09 20:02:19,559 INFO success: netbox entered RUNNING state, process has stayed up for &gt; than 1 seconds (startsecs)
Aug 09 20:02:19 netbox supervisord[53810]: 2018-08-09 20:02:19,559 INFO success: netbox-rqworker entered RUNNING state, process has stayed up for &gt; than 1 seconds (startsecs)
Aug 09 20:02:22 netbox supervisord[53810]: 2018-08-09 20:02:22,674 INFO exited: netbox-rqworker (exit status 1; not expected)
Aug 09 20:02:23 netbox supervisord[53810]: 2018-08-09 20:02:23,680 INFO spawned: &apos;netbox-rqworker&apos; with pid 53869
</code></pre>
<p>Checking the web site, it was still down. Refreshed again a little after and it&apos;s back up. Let&apos;s check the status one last time.</p>
<pre><code>Aug 09 20:03:55 netbox supervisord[53810]: 2018-08-09 20:03:55,914 INFO success: netbox-rqworker entered RUNNING state, process has stayed up for &gt; than 1 seconds (startsecs)
Aug 09 20:03:56 netbox supervisord[53810]: 2018-08-09 20:03:56,167 INFO exited: netbox-rqworker (exit status 1; not expected)
Aug 09 20:03:57 netbox supervisord[53810]: 2018-08-09 20:03:57,170 INFO spawned: &apos;netbox-rqworker&apos; with pid 54135
Aug 09 20:03:58 netbox supervisord[53810]: 2018-08-09 20:03:58,171 INFO success: netbox-rqworker entered RUNNING state, process has stayed up for &gt; than 1 seconds (startsecs)
</code></pre>
<p>So installing the requirements from pip3 from <code>root@netbox</code> actually works. Which means we can cut out a bunch of the things we tried but didn&apos;t succeed with and then we can blow it all away and check again.</p>
<h1 id="workingsteps">WORKING STEPS</h1>
<p>Here&apos;s the deployment, starting from a fresh install of <strong>Ubuntu 18.04.1 LTS</strong> as user <strong>malachite@netbox</strong> (we will have to <code>su - root</code>, but I try to avoid being there as much as possible).</p>
<pre><code>sudo apt update ; sudo apt upgrade
sudo apt autoremove

sudo apt-get install -y postgresql libpq-dev
sudo -u postgres psql
CREATE DATABASE netbox;
CREATE USER netbox WITH PASSWORD &apos;XY,gTr6GP-Xy&apos;;
GRANT ALL PRIVILEGES ON DATABASE netbox TO netbox;
\q

sudo apt-get install -y python3 python3-dev python3-setuptools build-essential libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev zlib1g-dev python3-pip

wget https://github.com/digitalocean/netbox/archive/v2.4.2.tar.gz
sudo tar -xzf v2.4.2.tar.gz -C /opt/
cd /opt/
sudo ln -s netbox-2.4.2/ netbox
</code></pre>
<p>Use <code>sudo su - root</code> to switch to root in order to install the python modules we are going to need.</p>
<pre><code>pip3 install -r /opt/netbox/requirements.txt
pip3 install gunicorn
exit
</code></pre>
<p>Back as <strong>malachite@netbox</strong>, continue:</p>
<pre><code>cd /opt/netbox/netbox/netbox
sudo cp configuration.example.py configuration.py
python3 /opt/netbox/netbox/generate_secret_key.py &gt; ~/secretkey

sudo vi configuration.py
</code></pre>
<p><code>configuration.py</code> changes, using <code>:read ~/secretkey</code> to pull the secret key into the file:</p>
<pre><code>ALLOWED_HOSTS = [&apos;192.168.0.11&apos;]
DATABASE = {
    &apos;NAME&apos;: &apos;netbox&apos;,
    &apos;USER&apos;: &apos;netbox&apos;,
    &apos;PASSWORD&apos;: &apos;XY,gTr6GP-Xy&apos;,
    &apos;HOST&apos;: &apos;localhost&apos;,
    &apos;PORT&apos;: &apos;&apos;,
}

SECRET_KEY = &apos;J8g4+*aFfpV#2tGEPOSA%NriQx=Yvb_uWzKLy-U)0^w$Tl&amp;5os&apos;
</code></pre>
<pre><code>rm ~/secretkey

cd /opt/netbox/netbox/
python3 manage.py migrate
python3 manage.py createsuperuser
sudo python3 manage.py collectstatic --no-input

sudo apt install -y nginx
sudo vi /etc/nginx/sites-available/netbox
</code></pre>
<p><code>/etc/nginx/sites-available/netbox</code> contents:</p>
<pre><code>server {
    listen 80;

    server_name 192.168.0.11;

    client_max_body_size 25m;

    location /static/ {
        alias /opt/netbox/netbox/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        add_header P3P &apos;CP=&quot;ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV&quot;&apos;;
    }
}
</code></pre>
<p>As <strong>malachite@netbox</strong>:</p>
<pre><code>cd /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/netbox

sudo vi /opt/netbox/gunicorn_config.py
</code></pre>
<p><code>/opt/netbox/gunicorn_config.py</code> contents:</p>
<pre><code>command = &apos;/usr/bin/gunicorn&apos;
pythonpath = &apos;/opt/netbox/netbox&apos;
bind = &apos;127.0.0.1:8001&apos;
workers = 3
user = &apos;www-data&apos;
</code></pre>
<p>Install Supervisor, to keep things running, I guess.</p>
<pre><code>sudo apt install -y supervisor

sudo vi /etc/supervisor/conf.d/netbox.conf
</code></pre>
<p><code>/etc/supervisor/conf.d/netbox.conf</code> contents:</p>
<pre><code>[program:netbox]
command = gunicorn -c /opt/netbox/gunicorn_config.py netbox.wsgi
directory = /opt/netbox/netbox/
user = www-data

[program:netbox-rqworker]
command = python3 /opt/netbox/netbox/manage.py rqworker
directory = /opt/netbox/netbox/
user = www-data
</code></pre>
<p>Finally restart the service to get everything up and running.</p>
<pre><code>service nginx restart
service supervisor restart
</code></pre>
<p>And now netbox should be running and available on the network!</p>
<h2 id="thoughts">Thoughts</h2>
<p>The installation process was frustrating because I never log onto <code>root@localhost</code> unless I absolutely have to. This was the first instance I have encountered where I require things to be installed by <code>root@localhost</code>. So, things I&apos;ve learned in summary:</p>
<ul>
<li>Most documentation seems to assume that you just exist as <code>root@localhost</code> all the time, which is an awful assumption and an awful practice.</li>
<li>Python modules are installed into a user&apos;s home directory.</li>
<li><code>Supervisor</code> as a service is run by <code>root@localhost</code>, and the user set in the conf file is only changed to using <code>setuid</code>, meaning that the program still uses all of <code>root@localhost</code>&apos;s environment variables (including where python modules are).</li>
<li>I misattributed the python module error to a permission error in accessing the folders. This was due to me assuming that the python modules were installed for everyone and not per user, and that <code>Supervisor</code> specifically ran programs as <code>root@localhost</code>. I found these things in the documentation.</li>
</ul>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Basic Python Web Crawler (Image Search)]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I wanted to build a web crawler in python to dive into pages and look for images. So of course the first thing I did was google it. After looking through several pages, I stumbled across <a href="https://dev.to/pranay749254/build-a-simple-python-web-crawler">this</a> simple article. Well, that seems easy enough let&apos;s see if we</p>]]></description><link>https://notawful.org/basic-python-web-crawler/</link><guid isPermaLink="false">5ec4001ab7157c33df05a50a</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Mon, 06 Aug 2018 00:32:30 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I wanted to build a web crawler in python to dive into pages and look for images. So of course the first thing I did was google it. After looking through several pages, I stumbled across <a href="https://dev.to/pranay749254/build-a-simple-python-web-crawler">this</a> simple article. Well, that seems easy enough let&apos;s see if we can&apos;t build from it.</p>
<p>Here&apos;s the original code, for reference:</p>
<pre><code>import requests
from bs4 import BeautifulSoup
def web(page,WebUrl):
    if(page&gt;0):
        url = WebUrl
        code = requests.get(url)
        plain = code.text
        s = BeautifulSoup(plain, &quot;html.parser&quot;)
        for link in s.findAll(&apos;a&apos;, {&apos;class&apos;:&apos;s-access-detail-page&apos;}):
            tet = link.get(&apos;title&apos;)
            print(tet)
            tet_2 = link.get(&apos;href&apos;)
            print(tet_2)
web(1,&apos;http://www.amazon.in/s/ref=s9_acss_bw_cts_VodooFS_T4_w?rh=i%3Aelectronics%2Cn%3A976419031%2Cn%3A%21976420031%2Cn%3A1389401031%2Cn%3A1389432031%2Cn%3A1805560031%2Cp_98%3A10440597031%2Cp_36%3A1500000-99999999&amp;bbn=1805560031&amp;rw_html_to_wsrp=1&amp;pf_rd_m=A1K21FY43GMZF8&amp;pf_rd_s=merchandised-search-3&amp;pf_rd_r=2EKZMFFDEXJ5HE8RVV6E&amp;pf_rd_t=101&amp;pf_rd_p=c92c2f88-469b-4b56-936e-0e65f92eebac&amp;pf_rd_i=1389432031&apos;)
</code></pre>
<p>Naturally, the first thing I did was pop open a file in Vi, pase the code in, and run it with <code>python3</code>. Naturally, it failed, which was the point. Specifically the error it returned cited a failure on the second line, meaning that I already had the module <code>requests</code>. All that I need is to get BeautifulSoup4, and it should work.</p>
<pre><code>$ sudo apt-get install python3-pip
$ pip3 install beautifulsoup4
</code></pre>
<p>I changed the website to my own before I ran it this time. Mostly because it&apos;s okay if <em>I</em> break my website: <code>web(1,&apos;https://notawful.org&apos;)</code>. And when I ran it...</p>
<pre><code>malachite@localhost:~$ vi crawler_fail.py
malachite@localhost:~$ python3 crawler_fail.py
malachite@localhost:~$
</code></pre>
<p>Pros: No errors. Cons: No successes. Now is where I start breaking it down and figuring out how this little script works. The best place to start is probably the <a href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/#">documentation</a>, but I found it wholly unhelpful because I have not had the programming experience or patience with tiny words to read through and understand it. My strategy here was to <em>fail fast</em>, and itterate quickly. But first to read the code and approximate what it does.</p>
<p>Well, the entirety of the program is a function, which is cool because I understand those. We pass through <code>page</code>, which was set to <code>1</code>, and <code>WebUrl</code> which was set to <code>https://notawful.org</code>.</p>
<pre><code>def web(page,WebUrl):
    if(page&gt;0):
        url = WebUrl
        code = requests.get(url)
        plain = code.text
        s = BeautifulSoup(plain, &quot;html.parser&quot;)
        for link in s.findAll(&apos;a&apos;, {&apos;class&apos;:&apos;s-access-detail-page&apos;}):
            tet = link.get(&apos;title&apos;)
            print(tet)
            tet_2 = link.get(&apos;href&apos;)
            print(tet_2)
</code></pre>
<p>Stepping through:</p>
<ol>
<li>if <em>page</em> is greater than zero, do the rest. There is no itterator on <em>page</em>, so it will remain as what we set it as when we called web()</li>
<li><em>url</em> contains the value of WebUrl. So this should be a string value of <code>https://notawful.org</code></li>
<li><em>code</em> contains the object resulting from requests.get(url). I do not know what kind of object this is, but my guess is that it did a <em>get</em> request to <em>url</em> and stored it in <em>code</em></li>
<li><em>plain</em> contains the result of <em>code</em> after running the object function/method <em>text</em> on it. I assume this converted whatever <em>requests.get(url)</em> to plaintext, probably the plaintext html.</li>
<li><em>s</em> contains BeautifulSoup(plain, &quot;html.parser&quot;). I do know that BeautifulSoup turns HTML into objects that can be searched and operated on. I also know this statement is creating a BeautifulSoup object and storing it in <em>s</em> This however, I don&apos;t understand.</li>
<li>for each <em>link</em>, something we&apos;re just creating right now, in <em>s.findAll(&apos;a&apos;,{&apos;class&apos;:&apos;s-access-detail-page&apos;})</em> we are going to do a thing.</li>
</ol>
<p>Alright, I can assume that steps one through five worked as intended, since it is just shifting around object values and if any of those operations were to fail I would get some kind of invalid type error. The next couple of steps operate on <em>link</em>, which will be a new object for each itteration of this link stepping through the values of <em>s.findAll(&apos;a&apos;,{&apos;class&apos;:&apos;s-access-detail-page&apos;})</em>.</p>
<p>The smart thing to do at this point would have been to look at the documentation for what arguments <em>findAll()</em> took, and surely I am a smart person. Well, galaxy brain: I did not.</p>
<p>So, from <em>link</em> we are looking to <em>get(&apos;title&apos;)</em> and <em>get(&apos;href&apos;)</em>. These are going to be strings, because we use <code>print()</code> on them. <em>link</em> is an itterated object containing the results of <em>findAll()</em>, which is presumably limiting its search to objects containing both properties &apos;a&apos; and {&apos;class&apos;:&apos;s-access-detail-page&apos;}. Or, perhaps its looking for objects that have property &apos;a&apos; that contains {&apos;class&apos;:&apos;s-access-detail-page&apos;}. Either way, I opened the Chrome Developer console to see if I can spot anything.</p>
<p><img src="https://notawful.org/content/images/2018/08/webcrawl1-chromeconsole.png" alt="A picture of the chrome developer tab. Relevant contents below." loading="lazy"></p>
<pre><code>&lt;a class=&quot;post-card-content-link&quot; href=&quot;/conference-travel-advice/&quot;&gt;...&lt;/a&gt;
</code></pre>
<p>Well then, that clears that up. <code>s.findAll(&apos;a&apos;,{&apos;class&apos;:&apos;s-access-detail-page&apos;})</code> is looking through <em>s</em> for something that is similar to this, except that instead of <code>post-card-content-link</code> it is looking for one that contains <code>s-access-detail-page</code>. I know this is the one that I am looking for because we printed `link.get(&apos;href&apos;), which would be the value of href. It doesn&apos;t appear that this one contains a title field, so I&apos;ll remove that. So let&apos;s make some changes to that script.</p>
<p>Now we are running:</p>
<pre><code>import requests
from bs4 import BeautifulSoup
def web(page,WebUrl):
    if(page&gt;0):
        url = WebUrl
        code = requests.get(url)
        plain = code.text
        s = BeautifulSoup(plain, &quot;html.parser&quot;)
        for link in s.findAll(&apos;a&apos;, {&apos;class&apos;:&apos;post-card-content-link&apos;}):
            tet = link.get(&apos;href&apos;)
            print(tet)
web(1,&apos;https://notawful.org&apos;)
</code></pre>
<p>And this is what we got back:</p>
<pre><code>malachite@localhost:~$ python3 crawler_small.py
/college-university-guide/
/fixing-ghost-on-digitalocean/
/conference-travel-advice/
/gogs-install/
/online-presence-on-a-budget/
/sr-part2/
/sr-part1/
/strava/
/defcon-travel-advice/
/windows-pki/
/engineering-journals/
/why-i-hate-mathematicians/
/lessons-learned-october-2017/
/password-managers/
malachite@localhost:~$
</code></pre>
<p>So I hit the nail on the head with my initial instinct. We also know that we are pulling exactly the right tag in all of the html document because we see <code>/conference-travel-advice/</code> in that list, which was the one I used as a reference.</p>
<p>Something else I noticed is that those hrefs contain a leading <code>/</code>, which means that we can concatenate the href and our WebUrl to get the full url of a child page. This gives us the ability to iterate and run a second loop to pull things from child pages.</p>
<p>To start opening up child pages, I copied the object creation so we could run <code>findAll()</code> on it and just added <code>_low</code> to the end of all the variables. I made sure that I printed the href value so that I could see what page the images I pulled were from. Here&apos;s a snippet from that:</p>
<pre><code>for link in s.findAll(&apos;a&apos;,{&apos;class&apos;:&apos;post-card-content-link&apos;}):
    tet = link.get(&apos;href&apos;)
    print(&quot;href &quot; + tet)
    url_low = WebUrl + tet
    code_low = requests.get(url_low)
    plain_low = code_low.text
    s_low = BeautifulSoup(plain_low, &quot;html.parser&quot;)
</code></pre>
<p>Now, before I ran this I needed to know what the tags for images looked like. The only page that I know has lots of images is /windows-pki/.<br>
<img src="https://notawful.org/content/images/2018/08/webcrawl2-imgtag.png" alt="Screencap of https://notawful.org/windows-pki/, highlighting an image and it&apos;s associated tag (tag content below)." loading="lazy"></p>
<pre><code>&lt;img src=&quot;/content/images/2018/07/PKI-DomainMap.jpg&quot; alt=&quot;PKI-DomainMap&quot;&gt;
</code></pre>
<p>This makes it very easy. My <code>s_low.findAll()</code> is only going to have to look for <code>img</code>, and then I will have to print the value of <code>src</code>. So here&apos;s what the final code looks like:</p>
<pre><code>import requests
from bs4 import BeautifulSoup
def web(page,WebUrl):
    if(page&gt;0):
        url = WebUrl
        code = requests.get(url)
        plain = code.text
        s = BeautifulSoup(plain, &quot;html.parser&quot;)
        for link in s.findAll(&apos;a&apos;,{&apos;class&apos;:&apos;post-card-content-link&apos;}):
            tet = link.get(&apos;href&apos;)
            print(&quot;href &quot; + tet)
            url_low = WebUrl + tet
            code_low = requests.get(url_low)
            plain_low = code_low.text
            s_low = BeautifulSoup(plain_low, &quot;html.parser&quot;)
            for img in s_low.findAll(&apos;img&apos;):
                tet_low = img.get(&apos;src&apos;)
                print(tet_low)

web(1,&apos;https://notawful.org&apos;)
</code></pre>
<p>I ran it, and it worked, but there was a lot of the same image so I piped the results through <code>uniq</code>. Here&apos;s what those results looked like:</p>
<pre><code>malachite@localhost:~$ python3 crawler.py | uniq
href /college-university-guide/
/content/images/2018/07/headshot.jpg
href /fixing-ghost-on-digitalocean/
https://assets.digitalocean.com/articles/pdocs/site/control-panel/networking/domains/no-domains-yet.png
https://assets.digitalocean.com/articles/putty_do_keys/new_ssh_key_prompt.png
https://assets.digitalocean.com/articles/pdocs/site/control-panel/droplets/n-choose-image.png
https://pbs.twimg.com/media/DaMLUoGXUAI21V6.jpg:large
/content/images/2018/07/headshot.jpg
href /conference-travel-advice/
/content/images/2018/07/headshot.jpg
...
</code></pre>
<p>From no crawler to a one-level image crawler in next to no time. I spent more time on iteration than I showed here. Most of that extra time was spent on getting python to work since I had never had to install pip before, and I installed the wrong pip about three times. I also tried a couple different ways to make web crawlers, but this one I decided was the easiest since I had to build all the moving pieces myself. Building all of tiny pieces myself made me learn how all the pieces fit together, which is how I learn best.</p>
<p>This is what <em>failing fast</em> means to me, particularly. I made one or two changes here and there, test it, watch where it fails, and look into what those failures mean. Repeat until it works as intended. All in all, even with the issues with pip and watnot this took me about an hour and a half to figure out without knowing anything about web page design or having seen any of these packages before.</p>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[College/University Guide]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Since I know several young people heading to college or university soon, I thought that I&apos;d write a quick guide for things to do to manage things. This is based on my own experiences.</p>
<h1 id="personalbrand">Personal Brand</h1>
<p>I hate the phrase &quot;personal brand&quot;, and I am only</p>]]></description><link>https://notawful.org/college-university-guide/</link><guid isPermaLink="false">5ec4001ab7157c33df05a508</guid><category><![CDATA[Other]]></category><category><![CDATA[Guides]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Wed, 01 Aug 2018 20:36:23 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Since I know several young people heading to college or university soon, I thought that I&apos;d write a quick guide for things to do to manage things. This is based on my own experiences.</p>
<h1 id="personalbrand">Personal Brand</h1>
<p>I hate the phrase &quot;personal brand&quot;, and I am only using it ironically here. You are at the point where you will want to start caring about how you present yourself online and what kind of online footprint you have. You are also going to want to start collecting stuff and curating a CV/Resume. So, here are some tips:</p>
<ul>
<li>Buy a professional-enough domain name, a cheap blog or web page, and email hosting under your domain name. This combo costs about $15-20/year for the domain name, and about $12-15 a month.
<ul>
<li>Do not use your professional email address to sign up to websites or games. Use it to communicate with your professors, and potential employers.</li>
</ul>
</li>
<li>Align your public social media handles with your domain name, or create new social media accounts that share your &quot;brand name&quot;. This helps push all the things you want people to see about you to the top of the pile, and helps bury connections you&apos;d rather people not make.</li>
<li>Use your blog to practice your writing and communication and to collect your ideas and experiences. Take your time to make clear blog posts that you can come back to in order to re-learn things.</li>
<li>Keep track of every major project that you do while you are in college and university. When it happened, how many people were involved, what did you do on the project, and what did you deliver. All of this can be added to your Resume/CV, so keep your records of this stuff up-to-date.</li>
<li>Learn to check-in with people. Get people&apos;s contact information and check in on them once or twice a month if you don&apos;t see each other. Maintain the relationships you make at college and university and they will serve you well.</li>
</ul>
<h1 id="security">Security</h1>
<p>This is a pretty important transition. You&apos;re an adult now, and as an adult you have things you want to protect.</p>
<ol>
<li>You need to have a couple email addresses.
<ul>
<li>Your professional one is for emailing professors and doing business-related things. This is primarily because as your main account you want to avoid it being filled up by spam or notifications.</li>
<li>You will also need a <em>management</em> address, which you will use for managing your devices and your domain name, web, and mail hosting services. It is a good collection point for reciepts and such.</li>
<li>Your final address (one you likely already have) is for everything else. Signing up to games, websites, and the like.</li>
</ul>
</li>
<li>Get a password manager, a security key, and a good two-factor-authentication application. Change all of your current passwords so that none of them are the same or versions of another (your password manager will help with this). This is the biggest thing you can do for personal account security.
<ul>
<li>Last I checked, <a href="https://keepass.info/">keepass</a> is still the go-to for free software-based password managers. But there are many good ones available, so do some research and figure out what ones meet your needs. I personally use a paid license for <a href="https://www.dashlane.com/">Dashlane</a> as a backup for my hardware-based <a href="https://www.themooltipass.com/">Mooltipass</a>. My Mooltipass was invaluable for college since I was constantly moving from public computer to public computer, but your mileage may vary.</li>
<li>A security key is a hardware-based Two-Factor Authentication (2FA) token that will drastically reduce the likelihood of losing control of your accounts. There are a number of good security keys available. The updated <a href="https://www.amazon.ca/Yubico-Security-Key-USB-Authentication/dp/B07BYSB7FK">Yubico Security Key</a> is cheap and reliable. I suggest buying two of them, and keeping one in a safe space for backup.</li>
<li>Google Authenticator, Authy, and Duo are all solid 2FA applications. I use Authy because it keeps a backup of all my 2FA tokens because I have changed or reflashed my phone multiple times.</li>
</ul>
</li>
<li>Enable 2FA on all accounts possible. Even if someone gets a password for one of your accounts, 2FA will go a long ways to prevent people from getting access.
<ul>
<li>SMS (text message) based 2FA can be compromised relatively easily, or phished fairly easily. If you can enable other forms of 2FA then do so and try to avoid letting SMS be a &quot;backup&quot; 2FA method.</li>
<li>If you use an app authenticator (Google Authenticaor, Authy, etc.) you will often recieve a list of back-up codes. Add these to your password manager&apos;s entry for those accounts in case you lose access to your authenticator.</li>
</ul>
</li>
<li>If you use Chrome, run the uBlock Origin, Privacy Badger, and HTTPS Everywhere extensions. Or run Brave. There are similar extensions in Firefox. Ads and tracking are a security problem. Just do it and don&apos;t feel bad about lost ad revenue, try to support the people who make content you enjoy directly whenever possible.</li>
</ol>
<h1 id="collegeuniversitylife">College/University Life</h1>
<p>This is where I am going to give some probably shady advice, but I understand the struggle so listen closely. Most of academia is an awful money-grab and it&apos;s all bullshit. Here&apos;s some advice that I give as someone who has been through it.</p>
<h2 id="textbooks">Textbooks</h2>
<p>College textbooks are absolute bullshit. They are not expensive because they are difficult to produce or because they cost a lot to put together, they are expensive so that they can get a lot of money out of students. What do you do about it?</p>
<ol>
<li>Find out what your book list is as soon as possible. Your college/university bookstore will have this information when it is available.</li>
<li>Separate that list into two categories:
<ul>
<li>Plain old textbooks (the more the better)</li>
<li>Textbooks with special access codes for online material</li>
</ul>
</li>
<li>Check with the bookstore if they can or do order just the codes for those textbooks that come with online codes. The codes are often cheaper alone than with the book.</li>
<li>Get together with a bunch of your classmates and pool your money for one of each plain text book. Search them out and talk to them if you can. If you can&apos;t, don&apos;t buy your books until after the first week. You can take the text book to a print shop, have them shear off the binding and scan the entire book into PDF form for a fee. This will save the whole class hundreds of dollars if they are up for using a PDF.</li>
<li>If not enough people want to do it, look for PDFs of your textbooks. Just be a bit wary. Not paranoid, but exercise caution.</li>
</ol>
<h2 id="keepingnotes">Keeping Notes</h2>
<p>In my personal experience, making notes during lectures helps me far less than listening and engaging in class discussion (if that&apos;s a thing your professors enjoy). If making notes during class does help you, then go for it. But whatever you do, <em>write all your notes in your own words</em>. Do not copy explanations from wikipedia or from your professors&apos; slides. Writing things in your own words makes them stick better, and makes it easier for reference later on.</p>
<p>Keep an <a href="https://notawful.org/engineering-journals/">engineering journal</a>, preferably in a nice paper notebook, but an online copy is good too. What you do is whenever you encounter a difficult problem you track the symptoms/circumstances, what your troubleshooting steps are, and how you solved things. Then you record the symptoms and solutions in your engineering journal. Also when you have a large concept that you know is important, do some experiments and exploration of those concepts and explain <em>in your own words</em> that concept in your engineering journal. Write all of these so that if you handed your engineering journal to someone with very little background knowledge can use it to solve a problem or understand a concept because <em>you will forget those important details</em>.</p>
<p>Between your blog and engineering journal you will amass a significant knowledge base that you can tap into. Your goal with both of these is to ensure that you never have to come up with a solution to the same problem twice. Reference your previous solution first, and see if that works.</p>
<h2 id="takingtests">Taking Tests</h2>
<p>I have a good history with tests. I have a bad record with history tests. There&apos;s a reason for that. For the most part, taking tests is more about knowing how to take a test than it is about learning the material. Here&apos;s my advice on studying for tests and how to work through tests.</p>
<p>Try not to study or cram the night before or just before the test. Study a couple days before the test and get a good restful sleep before the test. This pushes all the stuff you&apos;ve studied and researched into long-term memory which is less likely to bail on you when you get handed the exam.</p>
<p>I have friends who have some other tricks for tests. They do memory association stuff like eating an apple while they study for a test and then eating an apple right before the test. I am not sure how much it helps, but you might find those tricks work for you.</p>
<p>The test is in front of you. You have limited time and the test is long. Remember what the goal of an exam is: To accumulate as many points as you can in the time you have. There is a process here that you will want to learn and follow on the tests you know you&apos;ll struggle on. On all of my college tests, the questions and tests were all marked with how much they were worth.</p>
<ol>
<li>Look at how much the test is worth, and quickly figure out what a passing grade is. Exam out of 75, a 55% is a passing grade, you need 38 marks to pass. You want to get to 38 marks as fast as possible</li>
<li>Read through the test and look at each category (multiple choice, true/false, short answers, long answer, essay, case study). You are looking for the <em>easiest</em> questions first.</li>
<li>Do your first pass on the test. If you hesitate on a question move on immediately. If you read the question and are immediately certain of an answer then write it down, it&apos;s probably correct. You shouldn&apos;t be spending any time thinking on your first pass. Make a small note of how many questions you answered/marks you got, we will assume you got them all correct.</li>
<li>Check how many more marks you need in order to pass and how long you have left. You should still have most of your exam period left, and you will see there isn&apos;t much left before you pass the test or that <em>you&apos;ve already passed</em>. If you feel it&apos;s still going to struggle, don&apos;t worry.</li>
<li>If you have thought of the answer to a question you didn&apos;t do on your first pass then go back and do it now. This was the point of taking the moment to assess how much farther you needed to go. It gave you a moment to relax and think.</li>
<li>Add the new marks to your total and see how close you are to passing. You have done all the easy questions or problems at this point. Take another read through the test and assess how much <em>work</em> answering each question you know you can answer is going to be.</li>
<li>Work your way through the easiest/fastest questions first and the harder/slower ones last. If you get stuck, move onto another question. It&apos;s better to have half an answer for a question than no answer since you can get partial marks most of the time. Add finished questions to your running total. By the time you have reached this stage, chances are you will have passed the test with lots of time to spare, so you can take your time and relax.</li>
</ol>
<h2 id="generallifeadvice">General life advice</h2>
<p>I recognize that many people have different circumstances, and some of this advice will be more difficult for some people than others.</p>
<ul>
<li>Get a solid rest each night. Eat three meals a day. Drink lots of water throughout the day. Try to get some exercise in. It&apos;s simple shit but it will help you more than anything else will.</li>
<li>Budget your time. You will have to make compromises, but you need to balance downtime with getting your work done, and if something has to give <em>never</em> let it be your rest.</li>
<li><strong>Associate with good people, not smart people.</strong> Often those two groups will overlap, but don&apos;t associate with people who are toxic or talk down about their peers. Those people are more trouble than they&apos;re worth. College and university get easier when you have good friends.</li>
<li>Be a good person. Not helpful, right? What I mean by &quot;be a good person&quot; is that if you see someone struggling, help them out. If you have privilege, use it to help others by telling your fellow privileged people when their behavior is unacceptable. Don&apos;t let people speak over voices that don&apos;t get heard enough (read: tell white people to be quiet).
<ul>
<li>When someone tells you that you&apos;ve made a mistake, listen to them. You don&apos;t get to decide that you didn&apos;t hurt someone. Most minority groups bear the weight of being discriminated against and having to teach others how not to, so don&apos;t make them take that. Do your own research on it and learn to be better. It&apos;s work, but also worth it.</li>
</ul>
</li>
</ul>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Fixing Ghost on a DigitalOcean Droplet]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Hello! I recently moved my blog (you&apos;re reading it!) from the official Ghost hosting to DigitalOcean. I can spend a moment talking about why I made the transition: It&apos;s hella fucking cheaper and way easier now. That was quick, so I guess now is where I</p>]]></description><link>https://notawful.org/fixing-ghost-on-digitalocean/</link><guid isPermaLink="false">5ec4001ab7157c33df05a507</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Wed, 01 Aug 2018 02:53:09 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Hello! I recently moved my blog (you&apos;re reading it!) from the official Ghost hosting to DigitalOcean. I can spend a moment talking about why I made the transition: It&apos;s hella fucking cheaper and way easier now. That was quick, so I guess now is where I talk about how I actually did it.</p>
<p>Strangely, it went quite according to the documentation. I signed up for a DigitalOcean account, and <a href="https://www.digitalocean.com/docs/networking/dns/how-to/add-domains/">added my domain name</a> first before I did anything. There was an option to simply add all the gmail mail servers, so I did that as well.</p>
<p><a href="https://www.digitalocean.com/docs/networking/dns/how-to/add-domains/"><img src="https://assets.digitalocean.com/articles/pdocs/site/control-panel/networking/domains/no-domains-yet.png" alt="DNS QuickStart" loading="lazy"></a></p>
<p>I had to boot up my chromebook&apos;s <a href="https://github.com/dnschneid/crouton">chroot</a> and install the <a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">Windows Subsystem for Linux (WSL)</a> on my desktop because I needed to make SSH keys to add to DigitalOcean.</p>
<p><a href="https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/to-account/"><img src="https://assets.digitalocean.com/articles/putty_do_keys/new_ssh_key_prompt.png" alt="Adding a new key to your DigitalOcean account" loading="lazy"></a></p>
<p>Making the keys was easy, done and done. I added a password to the keys as well because it was important to do, right? After I made them I <a href="https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/to-account/">uploaded them</a> through the dashboard.</p>
<pre><code>ssh-keygen -t rsa -b 4096 ~/.ssh/id_rsa
</code></pre>
<p>Now I have some keys I can attach droplets at creation so that the <code>root</code> account never has to have a password. Now I just need the droplets. So that goes by pretty quick too. The <a href="https://www.digitalocean.com/docs/droplets/how-to/create/">creation-process</a> is very quick. In the image below, I selected &apos;One-click apps&apos; and selected Ghost. I also selected the cheapest droplet ($5/mo) because I know that my blog doesn&apos;t take much to run.</p>
<p><img src="https://assets.digitalocean.com/articles/pdocs/site/control-panel/droplets/n-choose-image.png" alt loading="lazy"></p>
<p>I had to wait for my droplet to spin up and get assigned an IP address. At this point is where this blog post becomes a work of fiction because I ran into a host of tiny issues (ones partially of my creation and partially the universe&apos;s). I&apos;ll go over them because they are all really interesting little things.</p>
<p>I wanted to make <a href="https://twitter.com/highmeh/status/1021785249859203077">SSH config files</a> so I would be able to just jump straight into my droplet. You can do it with whatever editor you want, but this is what mine ended up being.</p>
<pre><code>Host ghost
    Hostname &lt;ip&gt;
    User root
    Port 22
    Identityfile ~/.ssh/id_rsa
</code></pre>
<p>I ran into issues with my config file on my Windows computer. This is what happened when I tried to <code>ssh ghost</code>:</p>
<pre><code>malachite@localhost:~$ ssh ghost
Bad owner or permissions on /home/malachite/.ssh/config
malachite@localhost:~$ ls -alh .ssh
total 8.0K
drwx------ 1 malachite malachite  512 Jul 31 18:40 .
drwxr-xr-x 1 malachite malachite  512 Jul 31 18:40 ..
-rw-rw-rw- 1 malachite malachite   99 Jul 31 18:40 config
-rw------- 1 malachite malachite  222 Jul 31 16:42 known_hosts
...
</code></pre>
<p>Do you see the problem? Because I sure as hell didn&apos;t until I googled &quot;bad owner or permissions&quot; and came across this <a href="https://gist.github.com/grenade/6318301">gist</a>. As it turns out, ssh wants your config file to have very specific permissions. As a quick primer for how permissions in unix work, here&apos;s a comic from <a href="https://twitter.com/b0rk?lang=en">Julia Evans</a> that does a much better job that I can in the space I have.</p>
<p><a href="https://twitter.com/b0rk/status/982641594305273856?lang=en"><img src="https://pbs.twimg.com/media/DaMLUoGXUAI21V6.jpg:large" alt="Julia Evans&apos; comic" loading="lazy"></a></p>
<p>According to the gist, linked previously, the proper permissions for config should be <code>644</code>, which translates to <code>-rw-r--r--</code>. The owner can read/write, and everyone else can just read it. This is quickly solved with a <code>chmod 644 .ssh/config</code>. With the permission fixed I can just pop into my droplet with a quick <code>ssh ghost</code>.</p>
<p>I followed the <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-the-digitalocean-ghost-one-click-application-for-ubuntu-16-04">configuration steps</a> from the community tutorials. It did not work. I ran into issues that no matter what I did I could not access the site with my domain name, but could on either http or https with the plain IP address. Specifically I was getting a 504 Bad Gateway error when I attempted to access with my domain name, which told me that it was something to do with the nginx configuration and not DNS. Clearly I am reaching my server, but my server can&apos;t handle the request.</p>
<p>When I initially loaded my droplet and signed into it, there was a message on screen that said that it was setting things up and that it would update Ghost if updates were available. I happen to recall that the DigitalOcean droplet comes installed with <strong>Ghost 1.24.9</strong>, but it updated to  <strong>Ghost 1.25.3</strong>.</p>
<p><a href="https://semver.org/">Semantic Versioning</a> is a version number system that contains three positions. The first is MAJOR version, the second is MINOR version, and the third is PATCh version.</p>
<ol>
<li>MAJOR version when you make incompatible API changes,</li>
<li>MINOR version when you add functionality in a backwards-compatible manner, and</li>
<li>PATCH version when you make backwards-compatible bug fixes.</li>
</ol>
<p>This had <em>nothing</em> to do with the issue, but made me consider the possibility that the update broke a dependency. So I updated stuff and it worked. Here&apos;s the steps to avoid encountering this.</p>
<pre><code>sudo apt-get update
sudo apt-get upgrade
sudo apt-get autoremove
sudo -i -u ghost-mgr
cd /var/www/ghost
ghost update
ghost restart
</code></pre>
<p><code>ghost update</code> shouldn&apos;t do anything, but the <code>ghost restart</code> is really important because it reinitializes things. The upgrade and restart did the trick and my droplet was back online.</p>
<h2 id="okayitsrunninganythingelse">Okay, it&apos;s running, anything else?</h2>
<p>Well part of the reason I wanted to move to self-hosting wasn&apos;t just because it was cheaper but because I could also use it to host files. I wanted to host my resume on my server so people could just click a link and get it right there in front of them.</p>
<p>From my Windows PC:</p>
<pre><code>scp /mnt/c/Users/malachite/Desktop/resume.pdf ghost:/var/www/ghost/content/images/2018/07/devon_taylor_resume.pdf
ssh ghost
sudo chown ghost:ghost /var/www/ghost/content/images/2018/07/devon_taylor_resume.pdf
sudo chmod 644 /var/www/ghost/content/images/2018/07/devon_taylor_resume.pdf
</code></pre>
<p>I knew that since I was going to be copying it into the content folder as root, I needed to change it to the owner. All the files in /var/www/ghost/content is owned by ghost:ghost, so I logged into the server to give them permission of the file. I also had to change the permissions since when the file was copied in it came with some wonky permissions that made the pdf an executable.</p>
<h2 id="followup">Follow-up</h2>
<p>While I was writing this post Ghost crashed causing it to return 404 errors, right around the time I was saying that everything was working beautifully and I solved all the problems. I was <s>mostly</s> right.</p>
<p><strong>Edit:</strong> The 404 errors were caused by Chrome sporadically choosing to use an old DNS record that targeted my website at a previous IP address where it is no longer located. After struggling with the developer tools in Chrome a little I managed to force it to ignore its cache and do a fresh lookup. So I configured things correctly the first time, Chrome was just gaslighting me.</p>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Conference Advice]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>This is an update to my <a href="https://notawful.org/defcon-travel-advice/">DEF CON travel advice</a> from 2017. I have made some major changes and separated DEF CON and InfoSec specific advice from general conference advice.</p>
<h3 id="alsocheckout">Also check out:</h3>
<p>If you want advice from someone who is a more seasoned traveler about traveling as an introvert</p>]]></description><link>https://notawful.org/conference-travel-advice/</link><guid isPermaLink="false">5ec4001ab7157c33df05a506</guid><category><![CDATA[Other]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Sat, 28 Jul 2018 19:00:28 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>This is an update to my <a href="https://notawful.org/defcon-travel-advice/">DEF CON travel advice</a> from 2017. I have made some major changes and separated DEF CON and InfoSec specific advice from general conference advice.</p>
<h3 id="alsocheckout">Also check out:</h3>
<p>If you want advice from someone who is a more seasoned traveler about traveling as an introvert then check out <a href="https://twitter.com/hacks4pancakes">Lesley Carhart</a>&apos;s blog post &quot;<a href="https://tisiphone.net/2017/11/27/the-infosec-introvert-travel-blog/">The Infosec Introvert Travel Blog</a>.&quot;</p>
<h1 id="generaltraveladvice">General Travel Advice</h1>
<h2 id="packing">Packing</h2>
<ul>
<li>Never check bags if you can avoid it. This prevents loss, theft, or mishandling. If you check bags then keep all your valuables in your carry-on.</li>
<li>Pack light; leave room for treasure. If you plan on collecting lots of treasure then pack an ultralight duffel in your carry-on. They pack small and you can check it on the trip home.</li>
<li>Your personal item should be a cross-body bag or backpack. The &apos;personal item&apos; is the one that goes under the seat in front of you on a plane. Keep your valuables and electronics there.</li>
<li>Your carry-on item should be a frameless soft-bodied item. Pack your clothes and toiletries in this bag. Since you will be the only one handling it, you do not have to worry about it getting roughed up.</li>
<li>Smaller airlines have tighter carry-on requirements and hard-shell and rolling luggage can be refused. You can squish soft-bodied luggage to fit.</li>
<li>Check out other people&apos;s conference packing lists. They will give you an idea of what you should bring with you.
<ul>
<li>It is a good idea to pack a battery pack. I believe the largest the TSA allows is a ~<a href="https://www.amazon.ca/Anker-20100mAh-Portable-Charger-PowerCore/dp/B00X5RV14Y/">20,000mAh pack</a>. Between long distances between outlets and fake/additional cell towers in the area, your phone battery will drain faster than you expect.</li>
</ul>
</li>
</ul>
<h2 id="laundry">Laundry</h2>
<p>You can get away with packing less if you know how to do laundry in the sink of your hotel room. You just need a large enough sink and your shampoo. Someone I met at DEF CON was nice enough to teach me how, and I am passing that on to you.</p>
<ol>
<li>Half-fill sink with water. Put a couple drops of shampoo in the water.</li>
<li>Swish clothes in soapy sink-water.</li>
<li>Rinse clothes.</li>
<li>Repeat steps 2 and 3.</li>
<li>Wring clothes out as best you can.</li>
<li>Roll up the clothes in a hotel towel then stomp the towel. This will soak up additional moisture.</li>
<li>Hang to dry.</li>
</ol>
<p>Cotton is comfortable because it is soft and wicks moisture, but it holds moisture and becomes odorous. Cotton clothes also take forever to dry even in well-ventilated areas. Synthetic fibers don&apos;t absorb moisture as well but dry quickly and are less prone to stinking. If you intend on doing your laundry in your sink then pack a clothesline made of medical rubber that ends with hooks or carabiners. Braided rubber grips clothes without pins, and suction cups will fail you. Once you get the routine down doing your laundry takes a couple of minutes, meaning you can pack lighter.</p>
<p><strong>Often overlooked fact</strong>: Odor is caused by bacteria, which thrives in moist environments. Even if you shower and scrub every day, if people are complaining about B.O. it is your clothes. Doing your laundry is important.</p>
<h2 id="airtravel">Air Travel</h2>
<ul>
<li>Print your boarding passes. Unless you travel with a battery pack and can ensure your phone is always available do not rely solely on electronic boarding passes.</li>
<li>In my experience, airport security has shifted focus towards getting people through as quickly as possible. Shoes on, belt on. Sometimes stuff goes through x-ray sans bin. This is not always the case.</li>
<li>RFID-blocking wallets <strong>will</strong> set off the metal detector. Even if the agents say it won&apos;t. Put it in the bin.</li>
<li>Customs is all electronic now. Stop at a terminal, follow the prompts, give your receipt to the person at the exit. Quick, quick.</li>
<li>If you drink lots of water like I do you will have to dump your water into a garbage bin at every security checkpoint. This goes for all beverages. Dump it out before you&apos;re at the front of the line.</li>
<li>If you&apos;re traveling from Canada you will likely go through TSA pre-clearance which is just US customs/border but before you take off rather than land. No special rules/precautions.</li>
</ul>
<h3 id="defconinfosecspecific">DEF CON / InfoSec Specific</h3>
<ul>
<li>If asked about your lockpicks: refer to lockpicking as lock-sport and always have practice locks in the same bag as the lockpicks. If you bring intrusion tools pack them in checked baggage if possible.</li>
</ul>
<h1 id="conferences">Conferences</h1>
<h2 id="planning">Planning</h2>
<ul>
<li>Book everything as far ahead in advance as possible. Hotels charge for the first night when you make your reservation but everything else gets charged when you check-in. Booking ahead is always cheaper.</li>
<li>Do not do mobile check-in if any member of your party is under the age of 21. It will cause headaches. Just check-in at the desk.</li>
<li>Your trip is going to be more expensive than you expect. Bring a bunch of cash but don&apos;t be seen with it, only keep enough in your wallet.</li>
</ul>
<h3 id="defconinfosecspecific">DEF CON / InfoSec Specific</h3>
<ul>
<li><strong>Never, ever, do BlackHatUSA on your own dime.</strong> This advice comes from someone who can&apos;t afford it.</li>
<li>If you have mobility or anxiety issues, book at Ceasers for DEF CON, and if you can manage it try to get in on the floors that have a direct elevator to the conference areas. This will make your life substantially easier.
<ul>
<li>If you have mobility issues this lets you skip the casino floor and the mess everyone else goes through to get to the conference area. Also take some time on Thursday to find the elevators in the conference area so you don&apos;t have to find the signs while the halls are crowded with people.</li>
<li>If you have anxiety issues, booking at the Ceasers in any tower gives you a quick and free way to get away from the conference area to your own quiet space so you can recharge.</li>
</ul>
</li>
<li>Most people&apos;s first year at DEF CON is spent learning how DEF CON works and where to spend their time. Don&apos;t get discouraged because you feel you didn&apos;t do enough your first year.</li>
<li>The villages at DEF CON are closed on Thursday for set-up and close early on Sunday to pack up. As such the crowds are thin on these days and the lines are shorter. Take Thursday to wander around with a DEF CON map in hand and find where things will be so you don&apos;t have to do it with the crowds.</li>
</ul>
<h2 id="execution">Execution</h2>
<h3 id="interactingwithpeople">Interacting with People</h3>
<ul>
<li>Ask for consent before taking people&apos;s picture or touching them for any reason. It makes a huge difference, even if you know the person really well.
<ul>
<li>Making eye contact and doing the open-arms-hug? gesture and asking, &quot;Hug?&quot; is easy to execute and isn&apos;t awkward. Respect their decision if they say no.</li>
</ul>
</li>
<li>Bring business cards with basic contact information: name/alias, social media, business email. Asking for someone else&apos;s contact information can make them uncomfortable. By offering your contact information instead you give them control of the next interaction.</li>
<li>If you are better known by your online handle than your real name include your handle in your introduction. &quot;Hi, I&apos;m Amanda, also @AwfulyPrideful on Twitter.&quot;</li>
</ul>
<h3 id="generalconferenceadvice">General Conference Advice</h3>
<ul>
<li>Most important tip of all: <strong>Don&apos;t push yourself</strong>. If you get anxious and can&apos;t be on the conference floor for long periods, just duck out and recharge. Under no circumstance should you force yourself to stay at the conference all day. You will exhaust yourself and ruin the experience if you do.</li>
<li><strong>Stay hydrated, bring snacks</strong>. You will have a better experience this way, I promise you. My personal choices are tap water (yuck, but free) and CLIF bars (trail food, keeps energy up sans meal).
<ul>
<li>Staying hydrated means drink small amounts of water every 15-20 minutes. Drinking lots of water all at once will not hydrate you since your body will only absorb a bit of it at once.</li>
</ul>
</li>
<li>Con Flu is real. Wash your hands often, or carry around hand sanitizer and use it often. The alternative is to not touch anything, but that won&apos;t save you.
<ul>
<li><a href="https://twitter.com/da_667/status/1023276945696542722">Good strategy</a> is that if you are about to touch your face, eyes, or nose, stop and use hand sanitizer or wash your hands with soap and water.</li>
</ul>
</li>
<li>Wear good walking shoes. You will be on your feet most of the time, either walking around or standing in lines. Get good insoles / inserts for your shoes. Your feet will thank you for it.</li>
<li>Carry lots of cash for tips. Tipping your service people is really important, and people attending conferences are known not to tip well. Also thank them and be patient because it&apos;s really busy and hard for them too.
<ul>
<li>If you like to drink, <a href="https://twitter.com/_r00k_/status/1023221280923770883">drop a twenty</a> as a tip for your first drink. That bartender will be your friend all night and you will get served when you go to them.</li>
</ul>
</li>
<li>Easy advice for working out how much to tip:  <blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/hashtag/GoodDefconAdvice?src=hash&amp;ref_src=twsrc%5Etfw">#GoodDefconAdvice</a> Good rule of thumb for tips: Move the decimal one place over to the left, then double that number. that is 20% tip. Add half of that number to reach 15% (e.g. 60.00 = 6.00 * 2 = 12.00 tip. 6.00 * 1.5 = 9.00)</p>&#x2014; SummerOfSYN (@da_667) <a href="https://twitter.com/da_667/status/1023268806834245633?ref_src=twsrc%5Etfw">July 28, 2018</a></blockquote>
</li>
</ul>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<ul>
<li>If you are at a bar or party, always order your own drink and keep your eyes on it. Stick with friends. Look after each other.</li>
</ul>
<h3 id="defconinfosecspecific">DEF CON / InfoSec Specific</h3>
<ul>
<li>Never leave a good conversation to see a talk. That is, unless your employer sent you and you need to report on some talks to justify the expenses. Face-to-face networking is a huge deal, so don&apos;t pass up those opportunities.</li>
<li>Hotels partnered with DEF CON stream the talks to hotel TVs. Some people host small viewing parties for talks. If you are invited to one by people you know, you should go. It is good for networking and you can ask people about content you don&apos;t understand.</li>
<li>Taking pictures of the crowd is against DEF CON&apos;s privacy policy. People are there to make memories, not evidence. Don&apos;t be the asshole with the GoPro strapped to your stupid forehead.</li>
<li>Don&apos;t interrogate people. If they&apos;re dodging where they work or what they do, leave them be. Remember where you are, and remember that they don&apos;t know you.</li>
<li>If you&apos;re gossiping or telling stories, don&apos;t name drop unnecessarily. People just don&apos;t appreciate it. You also seem more mysterious if you have secrets or &quot;know someone.&quot;</li>
<li>LINECON is real. Start lining up at least half an hour before a talk is scheduled to start. People in line in front and behind you are there for the same reason you are and you don&apos;t have to lose networking time. This is one of the easiest ways to meet new people since you already have something to talk about.</li>
<li>HALLCON is where it&apos;s at. Go to a chill area and ask if a seat&apos;s open at a table or unofficial-circle-of-people-on-the-floor. Listen to people&apos;s conversations and ask some questions. Don&apos;t intrude/eavesdrop on clearly personal conversations. You can enter a lot of conversations just by proximity.</li>
<li>If you plan on getting swag from the Swag Shop at DEF CON, do it on the first day. In 2017, when I attended, the line on the first day snaked through the hallway, and through the entirety of a large conference room. Swag sells out fast, so get in line right after you get your badge.
<ul>
<li>A good way to <a href="https://twitter.com/da_667/status/1023272295626878976">make friends</a> is to get in line, and then tell your friends that you&apos;ll pick up their swag for them. Have them meet you in line (or before you get in line) and give you cash and a list of stuff they want.</li>
</ul>
</li>
</ul>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Local Git using Gogs]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Hey there. I have been working on getting an installation of <a href="gogs.io">Gogs</a> running. I want to get a git instance up that I control so that I can dig into how it works without that showing up on <a href="https://github.com/notawful">github</a>. Controlling it also means that I can make as many private</p>]]></description><link>https://notawful.org/gogs-install/</link><guid isPermaLink="false">5ec4001ab7157c33df05a505</guid><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Fri, 27 Jul 2018 17:40:33 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Hey there. I have been working on getting an installation of <a href="gogs.io">Gogs</a> running. I want to get a git instance up that I control so that I can dig into how it works without that showing up on <a href="https://github.com/notawful">github</a>. Controlling it also means that I can make as many private repositories as I want without worry. So, with all that said here was my adventure in getting this done.</p>
<p>Hyper-V Virtual Machine (Gen 2):</p>
<ul>
<li>1 vProcessor</li>
<li>1024 MB RAM (Dynamic Memory)</li>
<li>25 GB VHDX (on an SSD)</li>
<li>Ubuntu 18.04 Server</li>
</ul>
<h1 id="firstpassinstallhttps">First Pass (Install + HTTPS)</h1>
<p>I followed and spliced together a number of guides. My first attempts were to install Gogs from source, but I ran into issues with Go. Mostly that I could not for the life of me get it to recognize where Go was installed. Apparently I am <a href="https://twitter.com/errorinn/status/1021870236507811843">not the only one</a> who has had this problem. After a couple hours of trying to get that to work I moved on to installing Gogs from the <a href="https://gogs.io/docs/installation/install_from_binary">binary</a>.</p>
<p>Due to a confluence of <a href>r00k&apos;s</a> Hack the Box walkthrough on <a href="https://youtu.be/5S5yVSbJknw?t=30m15s">Aragog</a>, and several guides I was reading, I learned that git services are typically installed on a user named <code>git</code> and also that they typically do not have a shell or login privileges. So that&apos;s where I started on this.</p>
<p>After I installed Ubuntu and got to my sudo/root user <code>notawful</code>, I ran <code>sudo apt-get update</code> and <code>sudo apt-get upgrade</code>, followed by <code>sudo apt-get autoremove</code>.  Once all that was done I added <code>git</code> as a user, then entered it to download and extract the Gogs binary.</p>
<pre><code>sudo adduser --disabled-login -gecos &apos;Gogs&apos; git
sudo su - git
wget https://dl.gogs.io/0.11.53/gogs_0.11.53_linux_amd64.tar.gz
tar -xzf gogs_0.11.53_linux_amd64.tar.gz
</code></pre>
<p>This created a new directory in <code>git</code>&apos;s home directory, <code>/home/git/gogs</code>, which contains all the files for Gogs to run. That is easy enough to do. From <code>/home/git/gogs</code> run <code>./gogs web</code>. It runs correctly, and now I can finish configuration from the web interface. At this point my only worry is about an internet-based deployment since it is HTTP only at this point.</p>
<p>From a web browser on the Gogs installation screen I do the following:</p>
<ul>
<li>Choose <strong>SQLLite3</strong> since the binary includes this by default and there is no additional configuration required.</li>
<li>Add admin account <code>gogsadmin</code> with password <code>r_3=zBd_aQH{</code>. After enabling HTTPS I plan on adding a new admin user account and removing this one.</li>
</ul>
<p>Now I exit/stop Gogs and exit back to <code>notawful</code>. Now I need to create some certificates. If I was running this in production I would create actual web certificates using Let&apos;s Encrypt, probably. Instead I will make a couple certificates using openssl. I use <code>192.168.229.99</code> as the domain when making the certificate because I don&apos;t plan on setting up a DNS for my test environment.</p>
<pre><code>sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/gogs-self.key -out /etc/ssl/private/gogs-self.crt
sudo chown git:git /etc/ssl/private/gogs-self.key
sudo chown git:git /etc/ssl/private/gogs-self.crt
</code></pre>
<p>I thought I knew what I was doing when I changed the owner of the key and certificate. My linux was rusty and when I made changes to the Gogs configuration file, it couldn&apos;t fetch them. I&apos;ll get to that in a second.</p>
<p>I made the following changes under the <code>[server]</code> heading in <code>~/gogs/custom/conf/app.ini</code> from user <code>git</code>. There are a bunch of other entries in this section, but these are the only ones that I played with.</p>
<pre><code>[server]
PROTOCOL = https
DOMAIN = 192.168.229.99
ROOT_URL = https://192.168.229.99:3000
CERTFILE = /etc/ssl/private/gogs-self.crt
KEYFILE = /etc/ssl/private/gogs-self.key
LANDING_PAGE = explore
</code></pre>
<p>I imagine that some people already know what error I was about to run into. When I ran <code>/home/git/gogs/gogs web</code> it came back with an error that did not show on the on-screen output. I had to go to <code>/home/git/gogs/log/gogs.log</code> and check.<br>
<img src="https://notawful.org/content/images/2018/07/gogs-denied.png" alt="grepped output showing PERMISSION DENIED for /etc/ssl/private/gogs-self.crt" loading="lazy"></p>
<p>Yeah. So I know the files are owned by the right account, because I did that earlier. So I just needed to move them over to somewhere that <code>git</code> could actually see without sudo privileges. So I went back to user <code>notawful</code>.</p>
<pre><code>sudo mv /etc/ssl/private/gogs-self.key /home/git/gogs-self.key
sudo mv /etc/ssl/private/gogs-self.crt /home/git/gogs-self.crt
</code></pre>
<p>Now, from <code>git</code> I reflect those changes in <code>/home/git/gogs/custom/conf/app.ini</code> and run <code>/home/git/gogs/gogs web</code> again.</p>
<pre><code>[server]
CERTFILE = /etc/ssl/private/gogs-self.crt
KEYFILE = /etc/ssl/private/gogs-self.key
</code></pre>
<p>It works! Except I get an error that does not appear in <code>gogs.log</code>, but in the on-screen stuff. I did not read the whole error because I saw the magical phrase <strong>tls handshake</strong> and knew exactly what the error was. I confirmed in my browser that I was trying to navigate to <code>192.168.229.99:3000</code>, without specifying http:// or https://. Since I set the server to only operate with https, it was denying the request because it did not look the start of a TLS handshake. I instead navigated to <code>https://192.168.229.99:3000</code> and was greeted by...</p>
<p><img src="https://notawful.org/content/images/2018/07/gogs-denied2.png" alt="Google Chrome screen stating certificate invalid, cannot proceed." loading="lazy"></p>
<p>Okay, that&apos;s fair and something I should have expected by making my own certificate. I was able to click pas it and get into an HTTPS instance of Gogs. From there I created a new admin user and deleted the previous one that I created over HTTP. I haven&apos;t analyzed the traffic in the HTTP session but I don&apos;t trust it, so this is the mitigation I went with.</p>
<p>So, it works over HTTPS, and does not work over HTTP. If I had a DNS server I would have it redircet any HTTP requests to HTTPS to this instance. I will get to play around with git now that this is set up, but I still need to get this set up so I don&apos;t have to manually start the service every time.</p>
<h2 id="performance">Performance</h2>
<p>Now, because I plan on running this in a DigitalOcean Droplet that will also contain an instance of by blog, I am a little worried about memory usage. Checking with Hyper-V I saw that the VM had 694MB assigned to the VM, but only <strong>582MB</strong> was in use with Gogs running and being logged into the web service.</p>
<p>So then, how much was Gogs actually using? I stopped Gogs and exited <code>git</code>&apos;s shell. Logged only into <code>notawful</code>, the VM&apos;s memory usage was a <strong>485MB</strong>, which means that Gogs was using about <strong>97MB</strong> while running with one user. On a second test, I ran <code>/home/git/gogs/gogs web &amp;</code> and then logged out of both <code>git</code> and <code>notawful</code>. Memory usage in this state was at about <strong>563MB</strong>.</p>
<p>I have been told that Gogs memory footprint does not increase significantly with more users or more repositories. So, I think under reasonable circumstances I am likely to be able to run Ghost and Gogs on the same droplet with only 1GB of memory.</p>
<h1 id="secondpassthesameasthelastbutwithlessfuckups">Second Pass (the same as the last, but with less fuck-ups)</h1>
<p>As <code>notawful</code>:</p>
<pre><code>sudo apt-get update
sudo apt-get upgrade
sudo apt-get autoremove
sudo adduser --disabled-login git
sudo su - git
</code></pre>
<p>As <code>git</code>:</p>
<pre><code>wget https://dl.gogs.io/0.11.53/gogs_0.11.53_linux_amd64.tar.gz
tar -xzf gogs_0.11.53_linux_amd64.tar.gz
cd gogs
./ gogs web
</code></pre>
<p>Connect to web site, select <strong>SQLLite3</strong>, create an admin user <code>gogsadmin</code>. Email doesn&apos;t matter, since <code>Mailer</code> service is disabled (and won&apos;t be enabled).</p>
<p>As <code>notawful</code>:</p>
<pre><code>sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /home/git/gogs-self.key -out /home/git/gogs-self.crt
sudo chown git:git /home/git/gogs-self.key
sudo chown git:git /home/git/gogs-self.crt
</code></pre>
<p>As <code>git</code>, editing <code>/home/git/gogs/custom/conf/app.ini</code>:</p>
<pre><code>[server]
PROTOCOL = https
CERTFILE = /home/git/gogs-self.crt
KEYFILE = /home/git/gogs-self.key
LANDING_PAGE = explore
</code></pre>
<p>As <code>git</code> from <code>/home/git/gogs</code>, run: <code>./gogs web &amp;</code><br>
From browser, log in to <code>gogsadmin</code>, create a new user and give them admin privileges. Then log into the new admin account and delete the <code>gogsadmin</code>.</p>
<h1 id="nextsteps">Next Steps?</h1>
<p>Figure out how to make this into an auto-run when the server starts. This is not something that I have done in a long time, and I have certainly never done so for a user that shouldn&apos;t be logging in. I will have to take some time to figure this side of things out.</p>
<h2 id="supporttheauthor">Support the Author</h2>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Online Presence on a Budget]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I have been thinking about this for a bit now, so I thought I would share. A year ago when I set up my blog and professional email address, tools were a bit limited. There was a lot of it was do-it-yourself, which can be rather annoying and intimidating when</p>]]></description><link>https://notawful.org/online-presence-on-a-budget/</link><guid isPermaLink="false">5ec4001ab7157c33df05a503</guid><category><![CDATA[Technology]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Mon, 02 Jul 2018 22:05:22 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I have been thinking about this for a bit now, so I thought I would share. A year ago when I set up my blog and professional email address, tools were a bit limited. There was a lot of it was do-it-yourself, which can be rather annoying and intimidating when you do not have years of experience under your belt. Some tools have been streamlined since I started, so without further ado:</p>
<p>Here&apos;s a quick guide for getting a professional-enough online presence for less than your monthly Netflix subscription ($15/mo).</p>
<h1 id="whatdoineed">What do I need?</h1>
<p>This is an easy list, actually.</p>
<ol>
<li>A domain name</li>
<li>A web page</li>
<li>An email address under your domain name</li>
</ol>
<p>That&apos;s three whole things. The goal here is really to get the email address attached to a custom domain name, but in my experience with <a href="https://gsuite.google.com/">G Suite</a> you need to put some code in your web page&apos;s header briefly to prove ownership over the domain name.</p>
<h1 id="whatarewegoingtodo">What are we going to do?</h1>
<p>This is another easy list!</p>
<ol>
<li>Create an email address for management of your online presence</li>
<li>Purchase a domain name</li>
<li>Create a blog on a virtual private server (VPS)</li>
<li>Create a professional email address</li>
<li>Host our resume/CV on our VPS</li>
</ol>
<p>First step is creating an email address for management. You can use Gmail or Outlook for this. Since our domain name and our blog is important, because it represents us, we are not going to use this email address for anything outside of paying for our domain name, our VPS, and our email hosting service.</p>
<h1 id="purchaseadomainname">Purchase a domain name</h1>
<p>Take some time to think about what you want your domain name to be. It&apos;s important because you are going to be pretty locked into it once you start using it professionally.</p>
<p>Everyone has a different favorite domain name provider. I won&apos;t suggest any, since they all have different prices for different Top Level Domains (TLDs, examples: .com, .ca,. org). Shop around for the domain name you want and see where you can get it cheapest. Most places will offer WHOIS protection nowadays, and I know <a href="https://www.namecheap.com/">namecheap</a> offers that for free since GDPR.</p>
<p>Domain names are purchased/renewed yearly, but can be cheap &#x2014; from $10-$25/year.</p>
<p>Create an account, buy the domain name you want, and then we&apos;re ready to get our blog up and running.</p>
<h1 id="createawebpageblog">Create a web page/blog</h1>
<p>I was going to shy away from suggesting specific products, but <a href="https://www.digitalocean.com/">DigitalOcean</a> has really kicked it out of the park since I last checked. They now have a number of one-click installation templates ready, and two are of particular use for us.</p>
<p>You can get a cheap VPS, pre-installed with <a href="https://www.digitalocean.com/products/one-click-apps/ghost/">Ghost</a> or <a href="https://www.digitalocean.com/products/one-click-apps/wordpress/">Wordpress</a>, for $5/month. You will have to do some configuration, but Digitalocean has some good guides associated with their different one-click installs to help you get set up. The updated <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-the-digitalocean-ghost-one-click-application-for-ubuntu-16-04">Ghost One-Click Application</a> guide is particularly good as well.</p>
<p>When you sign up to DigitalOcean you should use the email address you created for managing this stuff. Find the installation guide for whichever one you choose and follow the instructions. DigitalOcean&apos;s linux guides are all top-notch for simple administration, and their guides will get you set up with secured SSH access to your VPS and getting an SSL/TLS certificate for your web page.</p>
<p>My personal advice is to use DigitalOcean&apos;s <a href="https://ghost.org/">Ghost</a> installation. Ghost&apos;s interface is pleasant to work with and the installation is easy to <a href="https://docs.ghost.org/v1.0.0/docs/cli-update">update</a>. I wish any of the products mentioned were paying me to promote them.</p>
<h1 id="getemailhosting">Get email hosting</h1>
<p>So, there are a bunch of email hosting options and they are all quite cheap. I use <a href="https://gsuite.google.com/">G Suite</a>, but Microsoft&apos;s <a href="https://products.office.com/en-ca/exchange/exchange-online">Exchange Online</a> is also quite good.</p>
<p>If you don&apos;t want to be tethered to a large corporation you can also use <a href="https://protonmail.com/signup">ProtonMail</a>. ProtonMail has suffered from <a href="https://www.bleepingcomputer.com/news/security/protonmail-ddos-attacks-are-a-case-study-of-what-happens-when-you-mock-attackers/">DDOS</a> attacks, costs a little more (4&#x20AC; vs $5 USD) a month, and gives you less storage space. They do encrypt your mail and give you the ability to send encrypted emails to other people. If you are okay trading availability for privacy, this would be a good option.</p>
<p>In each case you will have to prove that you are the owner of your domain. G Suite does this by giving you something to put in your website header temporarily, and I imagine other providers do the same. Once you validate your domain name they will give you their mail servers which you will have to add to your DNS records so mail can reach you.</p>
<h1 id="hostyourresumecv">Host your resume/CV</h1>
<p>First, read <a href="https://www.amazon.ca/Women-Tech-Practical-Inspiring-Stories/dp/1632170663">Women in Tech</a> by <a href="https://tarah.org/">Tarah Wheeler</a>. Then, read it a couple more times just to be sure. The chapter on making a resume suited for applying to tech jobs is particularly good.</p>
<p>Now all you need to do after making a PDF of your resume is to put it where your web server hosts content (like images and so on). Get the web path to your PDF and put it in a post/page on your blog next to your contact information. Keep your resume updated and in that same spot.</p>
<p>Something else you will probably want to do is set up IP address logging for your resume or other files you are hosting on your web server. You can often associate IP addresses directly with businesses or organizations who are looking at your files. In case you wanted to know when government agencies are reading your draft guide to <a href="https://www.amazon.ca/Building-Virtual-Machine-Labs-Hands/dp/1546932631">building virtual machine labs</a> or something.</p>
<h1 id="nowwhat">Now what?</h1>
<p>To be perfectly honest, I am still working on the &quot;what next&quot; part of this whole process. Do cool projects, write posts about them, and post them to your web page. Make some business cards and put your cool new website on it. Panic realizing you are probably paying more to watch anime online than to be professional on the internet. Post some cool recipes since cooking is the new cool thing for people working in technology to talk about doing now.</p>
<h2 id="supporttheauthor">Support the Author</h2>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Shadowrun System Analysis: Part Two]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In the <a href="https://notawful.org/sr-part1/">last post</a> I talked about the core resolution mechanics in Shadowrun Fifth Edition. At the end of that post I talked about three questions that are useful for designing games.</p>
<ol>
<li>What is your game about?</li>
<li>How is your game about that?</li>
<li>How does your game reward players for</li></ol>]]></description><link>https://notawful.org/sr-part2/</link><guid isPermaLink="false">5ec4001ab7157c33df05a502</guid><category><![CDATA[Games]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Fri, 09 Mar 2018 18:46:32 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In the <a href="https://notawful.org/sr-part1/">last post</a> I talked about the core resolution mechanics in Shadowrun Fifth Edition. At the end of that post I talked about three questions that are useful for designing games.</p>
<ol>
<li>What is your game about?</li>
<li>How is your game about that?</li>
<li>How does your game reward players for engaging the things your game is about?</li>
</ol>
<p>When you are designing a game it helps to answer those questions in order. Each question leads into the next and together they focus your game&apos;s mechanisms to deliver on the specific experience that you want players to have. Those questions can be generalized and applied to the design of almost any system.</p>
<ol>
<li>What is this system intended to do?</li>
<li>How does this system work to deliver that?</li>
<li>How does the system incentivize users to engage in the system as intended?</li>
</ol>
<p>Any system that a person can interact with is made up of intent, mechanisms, and incentives. A system&apos;s intent can also be interpreted as the goals or results of the system. Designers sometimes misrepresent a system&apos;s intent, or more often have a one-sided view of the results of that system.</p>
<p>We are going to figure out what Shadowrun is about by working backwards. We are going to examine Shadowrun&apos;s rewards and the mechanisms that players have to pursue them, and we are going to see what Shadowrun is about.</p>
<h1 id="returnrewards">RETURN REWARDS;</h1>
<p>Rewards and incentives are perhaps the most interesting element of system design. The rewards that you offer players contextualize the mechanisms that they have been presented with. This interaction occurs in real world systems as well as game systems. Before we dig into Shadowrun I am going to talk about a couple of other game systems.</p>
<h2 id="enterdungeon">&gt;ENTER DUNGEON</h2>
<p><a href="http://www.dungeon-world.com/">Adam Koebel&apos;s multi-award winning tabletop roleplaying game <strong>Dungeon World</strong> featuring Sage LaTorra on drums</a><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> is a game about going on adventuring with people you are at least loosely associated with, exploring the world, killing monsters, and collecting treasure. All of the <a href="https://roll20.net/compendium/dw/CategoryIndex:Moves#content">moves</a> Dungeon World provides are focused on enabling player characters to do those things. Dungeon World rewards players for this at the <a href="https://roll20.net/compendium/dw/End%20of%20Session?fromList=End%20of%20Session&amp;#content">end of a session</a>.</p>
<blockquote>
<p>When you reach the end of a session, choose one your bonds that you feel is resolved (completely explored, no longer relevant, or otherwise). Ask the player of the character you have the bond with if they agree. If they do, mark XP and write a new bond with whomever you wish.</p>
<p>Once bonds have been updated look at your alignment. If you fulfilled that alignment at least once this session, mark XP. Then answer these three questions as a group:</p>
<ul>
<li>Did we learn something new and important about the world?</li>
<li>Did we overcome a notable monster or enemy?</li>
<li>Did we loot a memorable treasure?</li>
</ul>
<p>For each &#x201C;yes&#x201D; answer everyone marks XP.</p>
</blockquote>
<p>At the end of every session of play, players have the opportunity to earn up to five experience. Each point of experience lines up with something that one of the game&apos;s designers has stated the game is about. A player also gets to mark experience when they <a href="https://roll20.net/compendium/dw/Playing%20the%20Game#toc_3">fail a roll</a>.</p>
<blockquote>
<p>Each move will tell you what happens on a 10+ and a 7&#x2013;9. Most moves won&#x2019;t say what happens on a 6-, that&#x2019;s up to The GM but you also always mark XP.</p>
</blockquote>
<p>This point of experience lessens the blow of failure. When you make a move you either succeed, partially succeed, or fail and get experience. This encourages players take narratively exciting risks because there is always an incentive to act. When you engage in the Dungeon World&apos;s themes and mechanics your character gets better.</p>
<h2 id="usebladeondarkness">&gt;USE BLADE ON DARKNESS</h2>
<p><a href="https://bladesinthedark.com/">Blades in the Dark</a> is a tabletop role-playing game about a crew of daring scoundrels seeking their fortunes on the haunted streets of an industrial-fantasy city. That line come directly the cover of the book and on the game&apos;s web page. Like Dungeon World, Blades in the Dark rewards players for engaging the game&apos;s themes.</p>
<p>Players are <a href="https://bladesinthedark.com/advancement">rewarded</a> for making desperate (read: exciting) actions, for expressing their beliefs, heritage or background, and for struggling with their vices or traumas. The player&apos;s crew advances for contending with challenges above their station, bolstering their reputation, and expressing inner conflict or the essential nature of the crew. Each playbook and crew type has a personalized experience trigger to incentivize players towards specific things. Cutters, who are fighters and leaders, get experience for addressing problems with violence or coercion. Hawkers, crews of drug dealers and vice purveyors, get experience for procuring better product and for pulling in richer clients.</p>
<p>There are only twelve <a href="https://bladesinthedark.com/actions-attributes">actions</a> that a player can make such as Attune, Consort, Prowl, Skirmish, and Wreck. These are deliberate choices that restrict the ways that characters can approach problems. The name of each action was chosen to invoke a specific tone to shape how players think. Every problem looks like a corpse-to-be when you have four dice in Skirmish. Cutters will lean towards Skirmishing with foes over attempting to Consort with them because picking a fight will earn them experience.</p>
<h2 id="returnkarma">RETURN KARMA;</h2>
<p>Dungeon World and Blades in the Dark align the tools players have with rewards for engaging in the themes and mechanisms of the game.</p>
<p>Shadowrun rewards player characters for completing shadowruns. This is the entirety of Shadowrun Fifth Edition&apos;s reward structure: Do the job. Characters are awarded currency and Karma.</p>
<p>Characters are paid a base amount plus some extra for each net hit on an opposed Negotiation test. The base is then multiplied based on a number of factors involved in the run. Examples of these factors include the largest dice pool rolled against a player and how outnumbered the players were in combat. On a list of about eight possible modifiers only one is affected by decisions that the players make: Runners accomplished the task with impressive speed and/or subtlety.</p>
<p>Karma is Shadowrun&apos;s experience analog and is used for character progression. Karma is spent on things such as learning new spells and advancing skill ratings. Characters receive Karma at the end of a run for surviving the run, for completing some or all of the objectives, and some karma based off a calculation involving the largest dice pool rolled against a character.</p>
<p>Characters only receive rewards when they complete a run. In order to complete the run they must have completed some or all of the objectives of the run. Characters can only receive rewards if they are alive to collect them. The first two triggers are little more than an attendance award for the player, and players have no ability to affect the third trigger.</p>
<p>Since none of Shadowrun&apos;s rewards are based on choices that the players make, Shadowrun offers no incentives to the players to <strong>do</strong> anything. The reward structure is based almost entirely off of decisions that the gamemaster makes before anyone sits at the table to play. The way that players advance their characters is by showing up to play Shadowrun.</p>
<h3 id="returnethicsinshadowrunning">RETURN &apos;ETHICS IN SHADOWRUNNING&apos;;</h3>
<p>There are a set of monetary and karmic rewards based on whether the run is about killing and oppressing people, or about helping out the little guy. This is Shadowrun&apos;s threadbare attempt at expressing ethics or alignment mechanically. They fail at this dramatically due to the lack of player agency in the reward system.</p>
<p>The gamemaster comes to play with everything planned out including whether or not the mission makes the runners bad people or good people. The only agency the characters have is to not take runs that oppose their own ethics. At the table this leaves players without choice due to the inherent social pressure. Nobody gets to play Shadowrun if you decline the run that the gamemaster prepared.</p>
<h1 id="returnresults">RETURN RESULTS;</h1>
<p>Blades in the Dark and Dungeon World&apos;s reward structures are connected directly to choices that the players make. These games reward players for engaging in the type of fiction that those games want to create. Blades in the Dark and Dungeon World do not explicitly require that you to engage the mechanisms in order to earn rewards, but the mechanisms are such that they become a natural part of working towards those rewards.</p>
<p>Shadowrun&apos;s core mechanics and intricate web of systems are not incentivized. The complete disconnect between player actions and incentives leave Shadowrun feeling over-designed. Why do I have a entirely separate, well designed, vehicular combat system? Without a clear reason to engage mechanics, those mechanics end up detracting from the game. A system with no clear use is dead weight.</p>
<p>And that was the whole point of this post. Shadowrun tells us what Shadowrun is about by way of its reward system. Shadowrun is about showing up to play Shadowrun. There is a monetary reward for engaging notable elements of Shadowrun lore, but it is another thing that the gamemaster decides will or won&apos;t happen before a session.</p>
<h2 id="nextinput">NEXT INPUT</h2>
<p>The next post will explore the gamemasting chapter of Shadowrun&apos;s core rulebook. We will see how the game intends for gamemasters to prepare for and run a game. I will continue to compare Shadowrun to other game systems as we explore the gamemaster-facing systems.</p>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>The explanation for this joke is here: <a href="https://clips.twitch.tv/BetterFrailMallardDoritosChip">https://clips.twitch.tv/BetterFrailMallardDoritosChip</a> <a href="#fnref1" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
</ol>
</section>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Shadowrun System Analysis: Part One]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Shadowrun has a long history in the information security community. I cannot count the number of people with fond memories of Shadowrun and who love to share stories of their previous adventures. The game has a special place for me. I have spent a lot of time learning about tabletop</p>]]></description><link>https://notawful.org/sr-part1/</link><guid isPermaLink="false">5ec4001ab7157c33df05a501</guid><category><![CDATA[Games]]></category><dc:creator><![CDATA[Devon Taylor]]></dc:creator><pubDate>Wed, 07 Mar 2018 21:40:14 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Shadowrun has a long history in the information security community. I cannot count the number of people with fond memories of Shadowrun and who love to share stories of their previous adventures. The game has a special place for me. I have spent a lot of time learning about tabletop roleplaying games and how they are designed, and I would like to share some of my insights.</p>
<p>In this series of posts I am going to analyze the systems of Shadowrun Fifth Edition. I have a lot of opinions about Shadowrun&apos;s design, and in particular the design of Fifth Edition Shadowrun. I am planning on talking about the following elements:</p>
<ul>
<li>Core system</li>
<li>Reward System</li>
<li>Gamemastering</li>
<li>Other Systems</li>
</ul>
<p>This post will be focused on Shadowrun&apos;s core system mechanics and then briefly touch on how I do system analysis. Core mechanics are the mechanisms that resolve conflict, resolve action, and determine the general flow of the game. Following posts will delve deeper into the system interactions in Shadowrun.</p>
<h1 id="coreresolutionmechanics">CORE RESOLUTION MECHANICS</h1>
<p>Every roleplaying game has a <em>core resolution mechanic</em>. A core resolution mechanic is the means by which actions in the narrative are resolved.</p>
<p>In Dungeons &amp; Dragons the core resolution mechanic is <code>1d20+[Modifier] vs Target Number</code>. The <em>Modifier</em> is determined by a combination of skill bonuses and character statistics, and the Target Number might be someone else&apos;s <code>1d20+[Modifier]</code> or the Difficulty Class assigned to the task. In Dungeons &amp; Dragons, non-player characters (NPCs) use the same resolution mechanic as player characters (PCs). If you meet or exceed the Target Number then you succeed at your action, and if your result is lower then you fail. Dungeons &amp; Dragons is a binary success-fail system.</p>
<p>In <a href="http://apocalypse-world.com/">Apocalypse World</a> the core resolution mechanic follows <code>2d6+[Modifier]. On a 10+ you succeed. On a 7-9, you succeed partially or must make a hard choice. On a 6 or lower, you miss.</code>. If you ask a probability expert you will learn that most results of two six-sided die will fall between 7-9. In Apocalypse World most of your actions in the narrative will partially succeed and complications will arise.</p>
<p>Shadowrun&apos;s core resolution mechanic is a <a href="https://en.wikipedia.org/wiki/Dice_pool">dice pool</a> system. In Shadowrun Fifth Edition your dice pool for an action is based on a relevant skill and attribute, then you roll that many six-sided dice and for every five and six that show up on those dice you count a hit. This is called a test. The three types of tests in Shadowrun are Success tests, Opposed tests, and Extended tests. Some key values are <em>skill</em>, <em>attribute</em>, <em>limit</em>, <em>threshold</em>, and for extended tests the test <em>interval</em>.</p>
<p>A Success test is represented by <code>Skill + Attribute [Limit](Threshold)</code>. An opposed test is similar in that the success threshold for the test is replaced by a test made by your target. An extended test is represented as <code>Skill + Attribute [Limit](Threshold, Interval)</code>.</p>
<p><em>Skill</em> represents your character&apos;s training in a specific task. A character&apos;s <em>attribute</em> represents your character&apos;s strength, agility, reaction, willpower, and so on. Most tests have a <em>limit</em> which represents a limitation that prevents a player from rolling too many hits on a test. You can only have hits on a test up to your limit. The <em>threshold</em> is the number of hits that are required to succeed on a test. Extended tests have an <em>interval</em>, which represents how long each test takes and you carry over your hits between tests until you meet the threshold.</p>
<p>Everything in Shadowrun is handled by this system from shooting to not getting shot, and anything in between. The Shadowrun core rulebook spends fifty pages explaining the lore of the world before presenting this information. This vital information is then overshadowed by character creation which spans another fifty pages.</p>
<h3 id="theruleoffour">THE RULE OF FOUR</h3>
<p>For every group of four dice you roll, on average, at least one of those dice should result in a five or six. Unless the gamemaster says otherwise, you can &apos;buy&apos; one hit for every four dice in your test&apos;s dice pool instead of rolling the test. Shadowrun never connects buying hits on tests with the narrative importance of skill rating 4. Each skill rating from 1 to 12 are described in narrative context.</p>
<p>A skill rating of 4 is considered &apos;Professional.&apos; Therefore, a professional locksmith, with a skill rating of 4, who is suited for the task, an agility rating of 4, will always succeed at any <em>Average</em> (Threshold 2) locksmithing tasks. Tools add to the user&apos;s dice pool and limit for a specific test, and so Professional tools would be rating 4. Given professional tools, a professional locksmith can be reasonably assured that with a bit of effort they can complete <em>Hard</em> (Threshold 4) tasks often enough to make an honest enough living.</p>
<p>I have called this out because this is indicative of both how thoughtfully designed the mechanics of the game are and how poorly they are presented. This interaction contextualizes Shadowrun&apos;s balance choices, and enables players to make more meaningful choices during character creation.</p>
<p><strong>Edit (Clarity):</strong> Buying hits is worse on average than rolling dice on a test. The purpose of this mechanic is to bypass low-stakes tests that you are likely to succeed at.</p>
<h1 id="initiatecombatprotocols">INITIATE COMBAT PROTOCOLS</h1>
<p>The core resolution mechanic in Shadowrun is flexible. It effectively resolves any situation that one can encounter in play. This creates a smooth game flow when the players or the gamemaster know what pools are required and how the results affect the narrative. The core resolution mechanics take up six pages in the core rulebook.</p>
<p>Every conflict is resolved in exactly the same way, regardless of the situation. The combat rules include an action economy comprised of complex actions, simple actions, and free actions. You can always take a free action on your turn, and you may take one complex action or two simple actions on your turn.</p>
<p>All combat involves similar steps.</p>
<ol>
<li>Attacker rolls against Defender. Example, in meat-space: <code>Pistols+Agility [Accuracy] vs Reaction+Intuition</code></li>
<li>If the attacker wins, then add their net hits to their attack&apos;s Damage Value. Example, attacker 2 net hits: <code>Ares Predator DV 8P + 2 (net hits) = DV 10</code></li>
<li>Defender resists the damage. Example, meat-space: <code>Defender rolls Body+Armor. Defender rolled 4 hits. DV10-4 = DV6. Defender takes 6 damage.</code></li>
</ol>
<p>This same process occurs whether you are hitting someone with a taser, throwing data spikes in cyberspace, or hurling conjured acid. The only difference is what skills and attributes are used at each step.</p>
<p>At the start of a combat turn, players roll initiative and when a character takes a turn they reduce its initiative by ten, and any character who has an initiative above 0 may make another turn on the next initiative pass. This continues until everyone has an initiative of 0 or lower and then everyone rolls fresh initiatives. This initiative system is difficult to describe but plays smoothly. It ends up as a simple core system that plays the same in meat space, the matrix, or in astral space.</p>
<p>The core combat mechanics are simple enough that I summarized everything you need to know in a few paragraphs. The combat section of the Shadowrun core rulebook spends fifty pages explaining every possible modifier one may encounter. Experienced players who have memorized all the exceptions and situational modifiers like cover, wind, visibility, and distance can flow from one action to the next as fast they can roll the dice. The problem is that combat, like many sections of the game, has a simple core system and pages upon pages of modifiers. For any possible situation in combat that might make it easier or harder to shoot there is a modifier to consider.</p>
<h1 id="analyzing">ANALYZING...</h1>
<p>Shadowrun comes from an old school of game design: <em>simulationism</em>. I am certain that it is alive and well and that it has a group of loyal followers. Every gun has unique stats or features, and every runner has a personal favorite. There are dozens of tools and at least one of them is exactly what you need to open that door. The more careful runners will spend hours planning every detail of a run to command as many positive modifiers as possible and to minimize negative ones.</p>
<p>Fortunately we have learned a lot about design since the days of Dungeons &amp; Dragons Third Edition (Revised) and Shadowrun First Edition. There is a new trend in game design that demands more than a series of generic resolution mechanics. Games can deliver specific experiences and guide players to specific types of narratives through clever use of mechanics.</p>
<h2 id="inputquery">INPUT QUERY</h2>
<p>Smarter people than I have created a set of questions that guide and focus the design of games.</p>
<ol>
<li>What is your game about?</li>
<li>How is your game about that?</li>
<li>How does your game reward players for engaging the things your game is about?</li>
</ol>
<p>These questions work very well for designing games. I have found a lot of use in them for analyzing systems outside of games. If we look at those questions we are examining a couple things in particular.</p>
<ol>
<li>What is this system intended to do?</li>
<li>How does this system work to deliver that?</li>
<li>How does the system incentivize users to engage in the system as intended?</li>
</ol>
<p>With this set of questions we can examine practically anything and come to an understanding of how the mechanisms involved function and what the results of different interactions will be. It is not important to answer these questions in order when you examine an existing sytem. All that matters is that you work your way through each mechanic you can interact with until they all have context and you have sufficient answers to each question.</p>
<h1 id="nextinput">NEXT INPUT</h1>
<p>There is a good core system in Shadowrun Fifth Edition. There are problems with the tacked-on systems that I am going to explore in future posts. I will examine different mechanisms and interactions through the lens of these three elements: intentions, mechanisms, and incentives. My goal is to show how to analyze systems using Shadowrun as an example.</p>
<p>The first system that I am going to explore in my next post will be the <a href="https://notawful.org/sr-part2/">reward system in Shadowrun.</a></p>
<h1 id="supporttheauthor">Support the Author</h1>
<p><a href="https://notawful.org/about">Devon Taylor</a> (They/Them) is a Canadian network architect, security consultant, and blogger. They have experience developing secure network and active directory implementations in low-budget and low-personnel environments. Their <a href="https://notawful.org/">blog</a> offers a unique and detailed perspective on security and game design, and they <a href="https://twitter.com/AwfulyPrideful">tweet</a> about technology, security, games, and social issues. You can support their work via <a href="https://patreon.com/notawful">Patreon (USD)</a>, or directly via <a href="http://ko-fi.com/notawful">ko-fi</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>