Rating:

DEF CON CTF QUALIFIERS 2017 - Crackme 2000

We were given multiple binary files that receive a code from stdin and show a string based on whether the code was correct or not:

$ ./01dd90c3b7d9a36227a5ddc96c7887acbcb973744c1971eaa6da6cccc6c3e261 
enter code:
AAABBBCCCDDD
$ ./01dd90c3b7d9a36227a5ddc96c7887acbcb973744c1971eaa6da6cccc6c3e261 
enter code:
==== The meds helped 
sum is 12

You can download some samples of the provided files, solution here includes:

  • magic
  • sorcery
  • alchemy
  • witchcraft

TL;DR

grep ftw

objdump -M intel -d magic/* | grep -P "cmp\s+rdi" | grep -oP "0x\w{1,2}" | xxd -r -p

objdump -M intel -d sorcery/* | grep -P " 3\w{3}.*cmp\s+[ac]l" | grep -oP "0x\w{1,2}" | xxd -r -p

objdump -M intel -d alchemy/* | grep -P " 4[012]\w{4}:.*cmp\s+r[ac]x,0x\w{2}$" | grep -oP "0x\w{1,2}" | xxd -r -p

objdump -M intel -d witchcraft/* | grep -P "[add|sub|cmp]\s+rdi,0x" | cut -c33-80 | sed 's/ /,/' | python parser.py

Solutions

This may not be an advanced solution but it was one of the fastest way that came to mind. Each one of the problems had a pattern that was easily grepable.

magic

First binary followed this pattern for string comparison:

cmp rdi,0xhh
 93b:   48 83 ff 3d             cmp    rdi,0x3d           // First char
 93f:   74 0e                   je     94f <_init+0x257>
 941:   48 83 ec 08             sub    rsp,0x8
 945:   bf 01 00 00 00          mov    edi,0x1
 94a:   e8 09 fe ff ff          call   758 <_init+0x60>
 94f:   b8 16 00 00 00          mov    eax,0x16
 954:   c3                      ret    
 955:   48 83 ff 3d             cmp    rdi,0x3d           // Second char
 959:   74 0e                   je     969 <_init+0x271>
 95b:   48 83 ec 08             sub    rsp,0x8
 95f:   bf 02 00 00 00          mov    edi,0x2
 964:   e8 ef fd ff ff          call   758 <_init+0x60>
 969:   b8 43 00 00 00          mov    eax,0x43
 96e:   c3                      ret

We used objdump, grep, and xxd to get the solutions for the magic binaries:

objdump -M intel -d $file |
grep -P "cmp\s+rdi" |
grep -oP "0x\w{1,2}" |
xxd -r -p
$ for file in $(ls);do answer=$(objdump -M intel -d $file | grep -P "cmp\s+rdi" | grep -oP "0x\w{1,2}" | xxd -r -p);echo "'$file': '$answer',";done
'01dd90c3b7d9a36227a5ddc96c7887acbcb973744c1971eaa6da6cccc6c3e261': '==== The meds helped ',
'0af4913433ca0adc86ad2befd7ffe465239953364a8e1dcfbddbe05254bb8c25': 'de. On the last night, he presented me with a ',
'0c029d126ab043c1b5137f8ddece16af67857743cc1a8e0d496181f002861c04': 'han anyone -- shir',

We sent the answers to the server using magic.py:

$ python magic.py
File:  5cd47c03c44ab6e407cc48a3ed0244d97e9a0cecc631ee981dad363845e73cfa
Answer:  LCBjb21tb24gbGF3IHJpZ2h0cyBv
...
File:  550d4a3c0add395524dabaa290929a040dadafb3f54740575e3cf43ea8dfddb3
Answer:  IGFuZCB0aGUgaG91c2Ugdw==
FLAG:  The flag is: a color map of the sun sokemsUbif

sorcery

Sorcery binaries followed a similar pattern with a slight modification at the end:

3hhh: cmp [ac]l,0xhh
    36a5:   80 f9 67                cmp    cl,0x67                                 // First char
    36a8:   0f 85 76 03 00 00       jne    3a24 <pthread_mutex_lock@plt+0xe94>
    36ae:   48 83 f8 01             cmp    rax,0x1
    36b2:   0f 84 f8 02 00 00       je     39b0 <pthread_mutex_lock@plt+0xe20>
    36b8:   8a 4f 01                mov    cl,BYTE PTR [rdi+0x1]
    36bb:   80 f9 20                cmp    cl,0x20                                 // Second char
    36be:   0f 85 6c 03 00 00       jne    3a30 <pthread_mutex_lock@plt+0xea0>
    36c4:   48 83 f8 02             cmp    rax,0x2
    36c8:   0f 84 e2 02 00 00       je     39b0 <pthread_mutex_lock@plt+0xe20>
    36ce:   8a 4f 02                mov    cl,BYTE PTR [rdi+0x2]
    ...
    3935:   3c 50                   cmp    al,0x50                                 // Last char
    3937:   0f 85 4f 02 00 00       jne    3b8c <pthread_mutex_lock@plt+0xffc>

The second command used was:

objdump -M intel -d $file |
grep -P " 3\w{3}.*cmp\s+[ac]l" |
grep -oP "0x\w{1,2}" |
xxd -r -p
$ for file in $(ls);do answer=$(objdump -M intel -d $file | grep -P " 3\w{3}.*cmp\s+[ac]l" | grep -oP "0x\w{1,2}" | xxd -r -p);echo "'$file': '$answer',";done
'02a1deee284afc3acd59d1b68cf5c9ad40e4ccf47ba99db55e38df8f1136ef5e': 'g plans, dictating voicemail. P',
'02a72b19e546bc4ef56610c1f5c200ccef462907241a22909fbe341da20d92a2': 'assed it to me',
'0a4def1cae72a724e81042f7565182ca70b7ffb2de9314bd6380b03c42e9bb84': 'elt his ',

Using sorcery.py we could get the flag for the problem:

$ python sorcerer.py
File:  5c28784fd1e7e89499e8180be9325e0b8b10390ff85bb66d7ca5cdbf1c9b66d4
Answer:  IGNhcmVmdWxs
...
File:  eeb4f76a92eda100ea7dbebd2c16136826b1614f3635f93488dafcda597af099
Answer:  eSB2aXNpdCB0aGlzIHBsYWNlIGZvciA=
FLAG:  The flag is: don't forget me when you're famous Klousovnec

alchemy

Following the same reasoning we can find the pattern for this binary which is similar to the sorcery pattern:

4[012]hhhh: cmp r[ca]x,0hh
  40f1b4:   48 83 f9 65             cmp    rcx,0x65                        // First char
  40f1b8:   0f 85 93 09 00 00       jne    40fb51 <_start@@Base+0x24e1>
  40f1be:   41 83 ff 01             cmp    r15d,0x1
  40f1c2:   0f 8e 2f 09 00 00       jle    40faf7 <_start@@Base+0x2487>
  40f1c8:   0f b6 48 01             movzx  ecx,BYTE PTR [rax+0x1]
  40f1cc:   48 83 f9 20             cmp    rcx,0x20                        // Second char
  40f1d0:   0f 85 88 09 00 00       jne    40fb5e <_start@@Base+0x24ee>
  40f1d6:   41 83 ff 02             cmp    r15d,0x2
  40f1da:   0f 8e 17 09 00 00       jle    40faf7 <_start@@Base+0x2487>
  40f1e0:   0f b6 48 02             movzx  ecx,BYTE PTR [rax+0x2]
  ...
  40f5a4:   48 83 f8 65             cmp    rax,0x65                        // Last char
  40f5a8:   0f 85 c5 07 00 00       jne    40fd73 <_start@@Base+0x2703>

The command for this problem was:

objdump -M intel -d $file |
grep -P " 4[012]\w{4}:.*cmp\s+r[ac]x,0x\w{2}$" |
grep -oP "0x\w{1,2}" |
xxd -r -p
# for file in $(ls);do answer=$(objdump -M intel -d $file | grep -P " 4[012]\w{4}:.*cmp\s+r[ac]x,0x\w{2}$" | grep -oP "0x\w{1,2}" | xxd -r -p);echo "'$file': '$answer',";done
'04b7f46deed1405c91d155e535ddd744176611b0b9f0d28962c6025822d34bf8': 'e up the steeplechase and she had the dogle',
'04bff69a80594bfd1b8b4d57f87a2fb80a6cca2eb3582997fe61a97b4f8164ad': ', as far as possible from Dan. He was working h',
'0b96430f0a2d8960ae9116db3a1cb580b56ba9a74bfdd0ba74ad3cfff6778a9a': 'issioned their work. Suneep l',

Using alchemy.py we were able to get the flag:

$ python alchemy.py
File:  762ad3727e8be06b6b10f94291f63028b26328500092c52523f3f13544cce639
Answer:  IGF3YXkgaW4gdGhlIHJhcg==
...
File:  def7fcc4e930d34cb98ab1d7c922a3eaab60d5ed6495d067569dcff5012ca523
Answer:  dmVuIGNhd2VkLCBN
FLAG:  The flag is: end of the world sun clyigujheo

witchcraft

Witchcraft did not follow a so simple pattern as seen before, instead it used additions and substractions to calculate a proper value for each character, however the pattern was:

add rdi,0xhh
sub rdi,0xhh
...
cmp rdi,0xhh
  402120:   55                      push   rbp
  402121:   48 89 e5                mov    rbp,rsp
  402124:   48 85 ff                test   rdi,rdi      // First char
  402127:   0f 84 28 01 00 00       je     402255 <_TTSfq4n_s___TFVs11_StringCore15_encodeSomeUTF8fT4fromSi_TSiVs6UInt64_@plt+0xf75>
  40212d:   48 83 c7 14             add    rdi,0x14
  402131:   0f 80 28 01 00 00       jo     40225f <_TTSfq4n_s___TFVs11_StringCore15_encodeSomeUTF8fT4fromSi_TSiVs6UInt64_@plt+0xf7f>
  402137:   48 83 c7 1f             add    rdi,0x1f
  ...
  402245:   48 81 ff c5 00 00 00    cmp    rdi,0xc5     // Calculated char comparison
  40224c:   75 07                   jne    402255 <_TTSfq4n_s___TFVs11_StringCore15_encodeSomeUTF8fT4fromSi_TSiVs6UInt64_@plt+0xf75>
  40224e:   b8 c5 00 00 00          mov    eax,0xc5
  402253:   5d                      pop    rbp
  402254:   c3                      ret 

parser.py script was used to calculate the initial state of each character:

import sys

values = []
for line in sys.stdin:
    op, dummy, val = line.split(',')

    if val.find('ffffffffffffff') != -1:
        val = (256 - int(val.replace('ffffffffffffff', ''), 16)) * -1
    else:
        val = int(val, 16)
    
    if op == 'cmp':
        result = val
        for value in values:
            result = result + value
        sys.stdout.write(chr(result))
        
        values = []
    else:
        if op == 'add':
            val = val * -1
            
        values = values + [val]

The command used to get the string for each binary was:

objdump -M intel -d $file |
grep -P "[add|sub|cmp]\s+rdi,0x" |
cut -c33-80 |
sed 's/    /,/' |
python parser.py
$ for file in $(ls);do answer=$(objdump -M intel -d $file | grep -P "[add|sub|cmp]\s+rdi,0x" | cut -c33-80 | sed 's/    /,/' | python parser.py);echo "'$file': '$answer',";done
'01b22b4c67a408a08c07b7c3af5a807512cc6dc41380d889f14605dc3bbcda43': 'y Cast. She vanishes, then reappears, forty ',
'02bd830274ed4a6550456eb092a5124701d1d1216bea20ee86e70f7a78ef1729': ' down to work for me, they're all real',
'03b2dcde6fbc4f9cb96ce1de4cbccd73960c196f6345e6dee66da0a9a89bf2d0': ' did the',

Finally, the witchcraft.py was used to get the final flag:

$ python witchcraft.py
File:  6577fdb2474b1a499922c6544b0c1888eedb28e6d48d0003d958dfa24ac16c08
Answer:  VG9uaWdodC4gQWZ0
...
File:  da92359b736fdc3d0d1c7688e9ebd02b67406d4260823514cd849eaed0ec9c13
Answer:  IG9mIHlvdXIgdm9sdW50YXJ5IG5lcnZvdXMgcHJvYw==
FLAG:  The flag is: bustin makes me feel good scengoybEm

Flags

magic

a color map of the sun sokemsUbif

sorcery

don't forget me when you're famous Klousovnec

alchemy

end of the world sun clyigujheo

witchcraft

bustin makes me feel good scengoybEm

Original writeup (https://github.com/sinfocol/ctfs/tree/master/writeups/2017/defcon-qualifiers/crackme-2000).