Objective 10

The Elfscrow Crypto tool is a vital asset used at Elf University for encrypting SUPER SECRET documents. We can't send you the source, but we do have debug symbols that you can use.

Recover the plaintext content for this encrypted document. We know that it was encrypted on December 6, 2019, between 7pm and 9pm UTC.

What is the middle line on the cover page? (Hint: it's five words)

For hints on achieving this objective, please visit the NetWars room and talk with Holly Evergreen.

The hints point to Ron Bowes' presentation.

I managed to follow Ron's presentation and took a look at his decrypting example in detail. I managed to get IDA installed and to load up the executable and debug symbols. I then started to browse the dis-assembled code. It is awhile since I have look at assemble code but I was surprise by how much I remembered and the presentation reminded me about.

I started looking for the decryption routine and found do_decrypt. This line stood out:

.text:00402AA4                 push    offset aCryptimportkey_0 ; "CryptImportKey failed for DES-CBC key"
This meant that I was dealing with DES-CBC and an 8 byte key.

There are also super_secure_rand, super_secure_random and generate_key functions

funciotns

In the generate_key function at .text:00401E0E there is a call to time that would infer that the seed is time based. The key is 8 bytes based on the comparison at .text:00401E31.

.text:00401DF0 generate_key    proc near               ; CODE XREF: do_encrypt+67↓p
.text:00401DF0
.text:00401DF0 var_4           = dword ptr -4
.text:00401DF0 arg_0           = dword ptr  8
.text:00401DF0
.text:00401DF0                 push    ebp
.text:00401DF1                 mov     ebp, esp
.text:00401DF3                 push    ecx
.text:00401DF4                 push    offset aOurMiniatureEl ; "Our miniature elves are putting togethe"...
.text:00401DF9                 call    ds:__imp____iob_func
.text:00401DFF                 add     eax, 40h
.text:00401E02                 push    eax             ; File
.text:00401E03                 call    ds:__imp__fprintf
.text:00401E09                 add     esp, 8
.text:00401E0C                 push    0               ; Time
.text:00401E0E                 call    time
.text:00401E13                 add     esp, 4
.text:00401E16                 push    eax
.text:00401E17                 call    super_secure_srand
.text:00401E1C                 add     esp, 4
.text:00401E1F                 mov     [ebp+var_4], 0
.text:00401E26                 jmp     short loc_401E31
.text:00401E28 ; ---------------------------------------------------------------------------
.text:00401E28
.text:00401E28 loc_401E28:                             ; CODE XREF: generate_key+5D↓j
.text:00401E28                 mov     eax, [ebp+var_4]
.text:00401E2B                 add     eax, 1
.text:00401E2E                 mov     [ebp+var_4], eax
.text:00401E31
.text:00401E31 loc_401E31:                             ; CODE XREF: generate_key+36↑j
.text:00401E31                 cmp     [ebp+var_4], 8
.text:00401E35                 jnb     short loc_401E4F
.text:00401E37                 call    super_secure_random
.text:00401E3C                 movzx   ecx, al
.text:00401E3F                 and     ecx, 0FFh
.text:00401E45                 mov     edx, [ebp+arg_0]
.text:00401E48                 add     edx, [ebp+var_4]
.text:00401E4B                 mov     [edx], cl
.text:00401E4D                 jmp     short loc_401E28
.text:00401E4F ; ---------------------------------------------------------------------------
.text:00401E4F
.text:00401E4F loc_401E4F:                             ; CODE XREF: generate_key+45↑j
.text:00401E4F                 mov     esp, ebp
.text:00401E51                 pop     ebp
.text:00401E52                 retn
.text:00401E52 generate_key    endp
.text:00401E52
.text:00401E52 ; ---------------------------------------------------------------------------
.text:00401E53                 align 10h
.text:00401E60
Now what I wanted to do was convert the function super_secure_random into a format that I could drop into Ron's decryptor.rb template.
.text:00401DC0 super_secure_random proc near           ; CODE XREF: generate_key+47↓p
.text:00401DC0                 push    ebp
.text:00401DC1                 mov     ebp, esp
.text:00401DC3                 mov     eax, state
.text:00401DC8                 imul    eax, 343FDh
.text:00401DCE                 add     eax, 269EC3h
.text:00401DD3                 mov     state, eax
.text:00401DD8                 mov     eax, state
.text:00401DDD                 sar     eax, 10h
.text:00401DE0                 and     eax, 7FFFh
.text:00401DE5                 pop     ebp
.text:00401DE6                 retn
.text:00401DE6 super_secure_random endp
hex numbers converted to decimal
.text:00401DC0 super_secure_random proc near           ; CODE XREF: generate_key+47↓p
.text:00401DC0                 push    ebp
.text:00401DC1                 mov     ebp, esp
.text:00401DC3                 mov     eax, state   <- get the current value seed
.text:00401DC8                 imul    eax, 214013  <- multiply by 214013  
.text:00401DCE                 add     eax, 2531011 <- add 2531011
.text:00401DD3                 mov     state, eax   <- store the value of seed
.text:00401DD8                 mov     eax, state   
.text:00401DDD                 sar     eax, 10h     <- shift right 16
.text:00401DE0                 and     eax, 7FFFh   <- AND with 7FFF hex
.text:00401DE5                 pop     ebp
.text:00401DE6                 retn
.text:00401DE6 super_secure_random endp
to convert this to ruby:

key += ((((seed  = (214013 * seed + 2531011) ) >> 16 ) & 0x7fff) & 0x0FF).chr

So my version of the decryptor script was:

require 'openssl'
require 'date'

KEY_LENGTH = 8 # TODO

def generate_key(seed)
  key = ""
  1.upto(KEY_LENGTH) do
    key += ((((seed  = (214013 * seed + 2531011) ) >> 16 ) & 0x7fff) & 0x0FF).chr
  end

  return key
end

def decrypt(data, key)
  begin
    c = OpenSSL::Cipher.new('DES-CBC') # TODO
    c.decrypt
    c.key = key

    return (c.update(data) + c.final())
  # added this so that script does not crash when decryption fails 
  rescue OpenSSL::Cipher::CipherError => e
  end
end

# use this to get list of supported ciphers
#puts OpenSSL::Cipher.ciphers
#exit

# convert time range to epoch seconds
start_sec = DateTime.parse('2019-12-06T19:00:00Z').to_time.to_i
end_sec   = DateTime.parse('2019-12-06T21:00:00Z').to_time.to_i

puts "Start time: #{start_sec}"
puts "End time:   #{end_sec}"

enc_file  = "ElfUResearchLabsSuperSledOMaticQuickStartGuideV1.2.pdf.enc"
dec_file  = "tmp2/ElfUResearchLabsSuperSledOMaticQuickStartGuideV1.2.pdf"

# foreach seed which is epoch seconds try to decrypt
(start_sec..end_sec).each { |seed|
  # read contents of file in binary format
  content = File.binread("#{enc_file}")
  # generate a new key
  key = generate_key(seed)
  # attempt to decrypt
  new_content = decrypt(content, key)
  # check if there is some contents and if it looks like a pdf file
  if new_content && new_content.match(/^%PDF-/)
    puts("#{seed}  - Generated key: #{key.unpack('H*')}")
    File.binwrite("#{dec_file}.#{seed}", new_content)
  end
}
When I ran the script:
$ ruby decryptor.rb 
Start time: 1575658800
End time:   1575666000
1575663650  - Generated key: ["b5ad6a321240fbec"]
This gave me the unencrypted document, which was titled “Super Sled-O-Matic - Machine Learning Sleigh Route Finder - QUICK-START GUIDE”

The original file must have been encrypted at Friday, December 6, 2019 8:20:50 PM UTC

This opened the door to the Bell Tower

Answer

The Answer is “Machine Learning Sleigh Route Finder”