# GynvaelEN mission 011


# cat mission_11-firmware.txt
# Number of arguments this code object expects
co_argcount 1
# Tuple of constant objects
co_consts (None, '4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89', 'hex', 89, 255, 115, 50)
# Flags
co_flags 67
# Function name
co_name check_password
# Names used
co_names ('decode', 'len', 'False', 'all', 'zip', 'ord')
# Number of local variables
co_nlocals 4
# The depth of the stack
co_stacksize 6
# Argument names
co_varnames ('s', 'good', 'cs', 'cg')

      0 LOAD_CONST          1  stack[0] = '4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89'
      3 LOAD_ATTR           0  names[0] # decode
      6 LOAD_CONST          2  stack[1] = 'hex'
      9 CALL_FUNCTION       1  stack[0] = 'N]N\x92\x86ZNIZ\x86IKZ]IRRa\x86_WXSMJ\x89' # 26
     12 STORE_FAST          1  good = 'N]N\x92\x86ZNIZ\x86IKZ]IRRa\x86_WXSMJ\x89' # 26; stack is empty
     15 LOAD_GLOBAL         1  stack[0] = 'len'
     18 LOAD_FAST           0  stack[1] = 's'
     21 CALL_FUNCTION       1  ?; stack is empty
     24 LOAD_GLOBAL         1  stack[0] = 'len'
     27 LOAD_FAST           1  stack[1] = 'good'
     30 CALL_FUNCTION       1  26; stack is empty
     33 COMPARE_OP          3 (!=) len(s) != len(goog)
     36 POP_JUMP_IF_FALSE  43    if eq goto 43
     39 LOAD_GLOBAL         2    else stack[0] = 'False'
     42 RETURN_VALUE   return 'False'
>>   43 LOAD_GLOBAL         3    stack[0] = 'all'
     46 BUILD_LIST          0    stack[0] = ['all']
     49 LOAD_GLOBAL         4    stack[1] = 'zip'
     52 LOAD_FAST           0    stack[2] = 's'
     55 LOAD_FAST           1    stack[3] = 'good'
     58 CALL_FUNCTION       2    stack[0] = zip(s, good)
     61 GET_ITER      stack[0] = iter(zip(s, good))
>>   62 FOR_ITER           52 (to 117)
     65 UNPACK_SEQUENCE     2    stack[1] = s[i], good[i]
     68 STORE_FAST          2    cs = s[i]
     71 STORE_FAST          3    cg = good[i]
     74 LOAD_GLOBAL         5    stack[0] = 'ord'
     77 LOAD_FAST           2    stack[1] = cs
     80 CALL_FUNCTION       1    stack[0] = ord(cs)
     83 LOAD_CONST          3    stack[1] = 89
     86 BINARY_SUBTRACT     stack[0] = ord(cs) - 89
     87 LOAD_CONST          4    stack[1] = 255
     90 BINARY_AND      stack[0] = (ord(cs) - 89) & 255
     91 LOAD_CONST          5    stack[1] = 115
     94 BINARY_XOR      stack[0] = ((ord(cs) - 89) & 255) ^ 115
     95 LOAD_CONST          6    stack[1] = 50
     98 BINARY_XOR      stack[0] = (((ord(cs) - 89) & 255) ^ 115) ^ 50
     99 LOAD_GLOBAL         5    stack[1] = 'ord'
    102 LOAD_FAST           3    stack[2] = cg
    105 CALL_FUNCTION       1    stack[1] = ord(cg)
    108 COMPARE_OP          2 (==)  computed_cg == cg
    111 LIST_APPEND         2
    114 JUMP_ABSOLUTE      62    goto 62
>>  117 CALL_FUNCTION       1
    120 RETURN_VALUE

# cat mission_11.py
password = ''

for i in '4e5d4e92865a4e495a86494b5a5d49525261865f5758534d4a89'.decode('hex'):
    password += chr(255 & (89 + (ord(i) ^ 50 ^ 115)))

print password

# python mission_11.py
huh, that actually worked!

Source

https://www.youtube.com/watch?v=s5gOW-N9AAo (1:46:20)

# GynvaelEN mission 010


# cat mission_10.py
from pwn import *

host = '31.133.0.131' #'127.0.0.1'
port = 9393

def get_mask_len():
 i = 64
 while True:
  j = i * 8
  r = remote(host, port)
  r.sendlineafter('\n', 'A' * j)
  re = r.recvuntil('\n')
  r.close()
  if 'Meh' in re:
   break
  i += 1
 return j

def get_secret2():
 b1 = '00000001'
 mask = b1 * (ml / 8)
 r = remote(host, port)
 r.sendlineafter('\n', mask)
 re = r.recvuntil('\n')

 b0 = '0'
 bits = b0 * (ml / 8)
 r.sendline(bits)

 re = r.recvuntil('\n')
 if 'Access Granted' in re:
  for _ in range(4):
   print r.recvuntil('\n')
 r.close()

def get_bits():
 b = ''
 for i in range(len(mask)):
  if mask[i] == '1':
   b += secret1[i]
 return b

def check_result(m, b):
 r = remote(host, port)
 r.sendlineafter('\n', m)
 re = r.recvuntil('\n')
 r.sendline(b)
 re = r.recvuntil('\n')
 r.close()
 return re

def binary_to_str(binary):
 s = ''
 for i in range(0, len(binary), 8):
  b = ''
  for j in range(8):
   b = binary[i + j] + b
  s += chr(int(b, 2))
 return s

def get_secret1():
 for i in range(len(mask)):
  if mask[i] == '0':
   mask[i] = '1'
   tmask = ''.join(mask)
   for j in '01':
    secret1[i] = j
    bits = get_bits()
    if 'Access Granted' in check_result(tmask, bits):
     break


ml = get_mask_len()
get_secret2()

secret1 = ['0'] * ml
mask = [i for i in '00000001'] * (ml / 8)
get_secret1()

print binary_to_str(get_bits())

# python mission_10.py
You have received one secret message:

---

Just Another Secret Message

---

This Crypto Is Absolutely Secure And There Will Be No Problem With It.

Source

https://www.youtube.com/watch?v=Vs8PLpHCoNY (1:45:30)

# GynvaelEN mission 009


# cat mission_09.py
import datetime

# Microsoft rand
def rand():
 global seed
 seed = (seed * 214013 + 2531011) % 2147483648
 return seed / 65536


key = [0] * 31
secret = [0] * 31
xor = [0x9a, 0x60, 0x76, 0x14, 0x8b, 0x36, 0x5a, 0x10, 0x2b, 0x91, 0xc4, 0x6c, 0xab, 0x27, 0x92, 0x99, 0xf8, 0x6a, 0xec, 0x5d, 0x32, 0x20, 0x3d, 0x61, 0x8f, 0xc7, 0xfb, 0xdd, 0x02, 0x72, 0xbf]


for s in range(1500336000, 1500508800):
 seed = s
 for i in range(31):
  eax = rand()
  rdx = (2139127681 * eax) >> 39
  ecx = eax >> 31
  edx = (rdx & 0xffffffff) - ecx
  ecx = edx << 8
  edx += ecx
  eax -= edx
  key[i] = eax & 0xff

 for i in range(31):
  secret[i] = (xor[i] ^ key[i]) & 0xff

 if all(c < 128 for c in secret):
  print 'seed =', s
  print 'time = ', datetime.datetime.fromtimestamp(s)
  print ''.join([chr(c) for c in secret])
  break

# python mission_09.py
seed = 1500483661
time =  2017-07-19 18:01:01
Who needs to store keys anyway.

Source

https://www.youtube.com/watch?v=7RotbY17tKk (1:47:55)

Reference

https://en.wikipedia.org/wiki/COFF

# Decrypt Wildfly/Jboss vault passwords


# cat standalone.xml
...
  <vault>  
      <vault-option name="KEYSTORE_URL" value="${user.home}/vault.store"/>  
      <vault-option name="KEYSTORE_PASSWORD" value="MASK-3y28rCZlcKR"/>  
      <vault-option name="KEYSTORE_ALIAS" value="vault"/>  
      <vault-option name="SALT" value="12438567"/>  
      <vault-option name="ITERATION_COUNT" value="50"/>  
      <vault-option name="ENC_FILE_DIR" value="${user.home}/vault.dat"/>  
    </vault> 
...

# cat vaultbreaker.py
import hashlib
import javaobj # pip install javaobj-py3
import jks # pip install pyjks
import string
import sys
from Crypto.Cipher import AES, DES

def clean(s):
 return filter(lambda x: x in string.printable, s).strip()

def get_derived_key(password, salt, count):
 key = password + salt
 for i in range(count):
  m = hashlib.md5(key)
  key = m.digest()
 return (key[:8], key[8:])

def customb64decode(msg):
 alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./_'
 result = ''
 for i in range(0, len(msg), 4):
  p0 = alphabet.index(msg[i])
  p1 = alphabet.index(msg[i + 1])
  p2 = alphabet.index(msg[i + 2])
  p3 = alphabet.index(msg[i + 3])
  if p0 != 64:
   result += chr(((p1 & 0x30) >> 4) | (p0 << 2))
  if p1 != 64:
   result += chr(((p2 & 0x3c) >> 2) | ((p1 & 0xf) << 4))
  result += chr(((p2 & 3) << 6) | p3)
 return result

def decrypt_keystore_password(enc_keystore_password, password, salt, iteration_count):
 num = 4 - (len(enc_keystore_password) % 4)
 if num != 4:
  enc_keystore_password = ('_' * num) + enc_keystore_password
 enc_text = customb64decode(enc_keystore_password)
 (dk, iv) = get_derived_key(password, salt, iteration_count)
 crypter = DES.new(dk, DES.MODE_CBC, iv)
 text = crypter.decrypt(enc_text)
 return clean(text)

def get_secret_key(keystore_filename, alias, keystore_password):
 ks = jks.KeyStore.load(keystore_filename, keystore_password)
 for a, sk in ks.secret_keys.items():
  if a == alias:
   return sk.key
 return null

def decrypt_vault_passwords(vault_filename, secret_key):
 decryption_suite = AES.new(secret_key, AES.MODE_ECB)
 print '[+] Vault passwords ='
 jobj = open(vault_filename).read()
 pobj = javaobj.loads(jobj)
 for i in range(0, len(pobj.annotations[1].annotations), 2):
  key = pobj.annotations[1].annotations[i]
  value = pobj.annotations[1].annotations[i + 1]
  if key:
   plain_text = decryption_suite.decrypt(str(value))
   print '\t -', key, '=', clean(plain_text)


passwd = "somearbitrarycrazystringthatdoesnotmatter"
KEYSTORE_PASSWORD = sys.argv[1]
KEYSTORE_ALIAS = sys.argv[2]
SALT = sys.argv[3]
ITERATION_COUNT = int(sys.argv[4])
keystore_filename = sys.argv[5]
vault_filename = sys.argv[6]

keystore_password = decrypt_keystore_password(KEYSTORE_PASSWORD, passwd, SALT, ITERATION_COUNT)
print '[+] Keystore password = ' + keystore_password

secret_key = get_secret_key(keystore_filename, KEYSTORE_ALIAS, keystore_password)
print '[+] Secretkey password = ' + secret_key.encode('hex')

decrypt_vault_passwords(vault_filename, secret_key)

# python vaultbreaker.py 3y28rCZlcKR vault 12438567 50 vault.store vault.dat
[+] Keystore password = vault22
[+] Secretkey password = 0e8f11aae5222d8280533a93bfaff4c3
[+] Vault passwords =
  - ssl::SSLUSER = ssl_user
  - datasource::HOST = 192.1.2.3
  - ssl::SSLPASS = ssl_pass
  - ssl::SSLALIAS = test
  - datasource::PORT = 1521
  - datasource::PASS = db_pass
  - datasource::SERVICENAME = db
  - datasource::USER = db_user

Reference

https://developer.jboss.org/wiki/JBossAS7SecuringPasswords

Done in collaboration

https://atorralba.github.io/

# GynvaelEN mission 008


# cat mission_08.py
number = 1087943696176439095600323762148055792209594928798662843208446383247024

i = 1

while True:
 h = hex(number / i)[2:-1]
 if len(h) % 2 == 0:
  s = h.decode('hex')
  if all(ord(c) < 128 for c in s):
   print s, i
   break
 i += 1

# python mission_08.py
Text is just a long number. 31336

Source

https://www.youtube.com/watch?v=OAk23u9b-88 (1:50:10)

# GynvaelEN mission 007


# cat parser.py
#        00000000  50 4b 03 04 14 00 00 00  00 00 fc 96 dc 4a 73 03  |PK...........Js.|
#        00000010  1a 7b 5e 00 00 00 7a 00  00 00 0a 00 00 00 72 65  |.{^...z.......re|
#        00000020  70 6f 72 74 2e 74 78 74  f8 07 54 16 3b 8b 63 cc  |port.txt..T.;.c.|
#        00000030  78 b7 03 42 4c 35 b8 0f  72 43 87 8f ab a8 55 3b  |x..BL5..rC....U;|
#        00000040  7f ee 9b 48 88 1a 2b cb  f3 52 73 bf 6f e2 11 37  |...H..+..Rs.o..7|
#        00000050  f2 06 a9 d3 53 12 2b d1  fe ff 47 34 58 be be 03  |....S.+...G4X...|
#        00000060  7f cb 15 08 b2 5a 58 2e  3a 51 61 1c b2 db 63 b6  |.....ZX.:Qa...c.|
#        00000070  5e 3a 76 98 0b 9a 32 12  88 cb b2 8c d3 d6 d4 fa  |^:v...2.........|
#        00000080  b7 37 b5 27 00 00 50 4b  01 02 14 00 14 00 00 00  |.7.'..PK........|
#        00000090  00 00 fc 96 dc 4a 73 03  1a 7b 5e 00 00 00 7a 00  |.....Js..{^...z.|
#        000000a0  00 00 0a 00 24 00 00 00  00 00 01 00 20 00 00 00  |....$....... ...|
#        000000b0  00 00 00 00 72 65 70 6f  72 74 2e 74 78 74 0a 00  |....report.txt..|
#        000000c0  20 00 00 00 00 00 01 00  18 00 00 6e 5c 69 2f f0  | ..........n\i/.|
#        000000d0  d2 01 00 6e 5c 69 2f f0  d2 01 80 d0 e0 52 2f f0  |...n\i/......R/.|
#        000000e0  d2 01 50 4b 05 06 00 00  00 00 01 00 01 00 5c 00  |..PK..........\.|
#        000000f0  00 00 86 00 00 00 00 00                           |........|
#        000000f8

import struct
import sys

def extract_bytes(offset, num, ctype = None):
    j = offset
    k = j + num
    if ctype:
        return struct.unpack(ctype, data[j:k])[0]
    else:
        return repr(data[j:k])

with open(sys.argv[1]) as f:
    data = f.read()

ep = 0
print 'Local file header signature', extract_bytes(ep + 0, 4)
print 'Version needed to extract', extract_bytes(ep + 4, 2)
print 'General purpose bit flag', extract_bytes(ep + 6, 2)
print 'Compression method', extract_bytes(ep + 8, 2)
print 'File last modification time', extract_bytes(ep + 10, 2)
print 'File last modification date', extract_bytes(ep + 12, 2)
print 'CRC-32', extract_bytes(ep + 14, 4)
cs = extract_bytes(ep + 18, 4, '<I')
print 'Compressed size', cs
print 'Uncompressed size', extract_bytes(ep + 22, 4)
n = extract_bytes(ep + 26, 2, '<H')
print 'File name length (n)', n
m = extract_bytes(ep + 28, 2, '<H')
print 'Extra field length (m)', m
print 'File name', extract_bytes(ep + 30, n)
print 'Extra field', extract_bytes(ep + 30 + n, m)
print '--------'
epcdfhs = data.index('PK\x01\x02')
i = ep + 30 + n
j = epcdfhs - cs - i
print 'Extra field', extract_bytes(i, j)
print 'Compressed data', extract_bytes(i + j, cs)
print '--------'
ep = epcdfhs
print 'Central directory file header signature', extract_bytes(ep + 0, 4)
print 'Version made by', extract_bytes(ep + 4, 2)
print 'Version needed to extract', extract_bytes(ep + 6, 2)
print 'General purpose bit flag', extract_bytes(ep + 8, 2)
print 'Compression method', extract_bytes(ep + 10, 2)
print 'File last modification time', extract_bytes(ep + 12, 2)
print 'File last modification date', extract_bytes(ep + 14, 2)
print 'CRC-32', extract_bytes(ep + 16, 4)
print 'Compressed size', extract_bytes(ep + 20, 4)
print 'Uncompressed size', extract_bytes(ep + 24, 4)
n = extract_bytes(ep + 28, 2, '<H')
print 'File name length (n)', n
m = extract_bytes(ep + 30, 2, '<H')
print 'Extra field length (m)', m
k = extract_bytes(ep + 32, 2, '<H')
print 'File comment length (k)', k
print 'Disk number where file starts', extract_bytes(ep + 34, 2)
print 'Internal file attributes', extract_bytes(ep + 36, 2)
print 'External file attributes', extract_bytes(ep + 38, 4)
print 'Relative offset of local file header', extract_bytes(ep + 42, 4)
print 'File name', extract_bytes(ep + 46, n)
print 'Extra field', extract_bytes(ep + 46 + n, m)
print 'File comment', extract_bytes(ep + 46 + n + m, k)

# python parser.py report.zip
Local file header signature 'PK\x03\x04'
Version needed to extract '\x14\x00'
General purpose bit flag '\x00\x00'
Compression method '\x00\x00'
File last modification time '\xfc\x96'
File last modification date '\xdcJ'
CRC-32 's\x03\x1a{'
Compressed size 94
Uncompressed size 'z\x00\x00\x00'
File name length (n) 10
Extra field length (m) 0
File name 'report.txt'
Extra field ''
--------
Extra field ''
Compressed data "\xf8\x07T\x16;\x8bc\xccx\xb7\x03BL5\xb8\x0frC\x87\x8f\xab\xa8U;\x7f\xee\x9bH\x88\x1a+\xcb\xf3Rs\xbfo\xe2\x117\xf2\x06\xa9\xd3S\x12+\xd1\xfe\xffG4X\xbe\xbe\x03\x7f\xcb\x15\x08\xb2ZX.:Qa\x1c\xb2\xdbc\xb6^:v\x98\x0b\x9a2\x12\x88\xcb\xb2\x8c\xd3\xd6\xd4\xfa\xb77\xb5'\x00\x00"
--------
Central directory file header signature 'PK\x01\x02'
Version made by '\x14\x00'
Version needed to extract '\x14\x00'
General purpose bit flag '\x00\x00'
Compression method '\x00\x00'
File last modification time '\xfc\x96'
File last modification date '\xdcJ'
CRC-32 's\x03\x1a{'
Compressed size '^\x00\x00\x00'
Uncompressed size 'z\x00\x00\x00'
File name length (n) 10
Extra field length (m) 36
File comment length (k) 0
Disk number where file starts '\x00\x00'
Internal file attributes '\x01\x00'
External file attributes ' \x00\x00\x00'
Relative offset of local file header '\x00\x00\x00\x00'
File name 'report.txt'
Extra field '\n\x00 \x00\x00\x00\x00\x00\x01\x00\x18\x00\x00n\\i/\xf0\xd2\x01\x00n\\i/\xf0\xd2\x01\x80\xd0\xe0R/\xf0\xd2\x01'
File comment ''

# cat generate_zipfiles.py
from subprocess import Popen, PIPE
from sys import argv

cm = {  0: 'The file is stored (no compression)',
        1: 'The file is Shrunk',
        2: 'The file is Reduced with compression factor 1',
        3: 'The file is Reduced with compression factor 2',
        4: 'The file is Reduced with compression factor 3',
        5: 'The file is Reduced with compression factor 4',
        6: 'The file is Imploded',
        7: 'Reserved for Tokenizing compression algorithm',
        8: 'The file is Deflated',
        9: 'Enhanced Deflating using Deflate64(tm)',
        10: 'PKWARE Data Compression Library Imploding (old IBM TERSE)',
        11: 'Reserved by PKWARE',
        12: 'File is compressed using BZIP2 algorithm',
        13: 'Reserved by PKWARE',
        14: 'LZMA (EFS)',
        15: 'Reserved by PKWARE',
        16: 'Reserved by PKWARE',
        17: 'Reserved by PKWARE',
        18: 'File is compressed using IBM TERSE (new)',
        19: 'IBM LZ77 z Architecture (PFS)',
        97: 'WavPack compressed data',
        98: 'PPMd version I, Rev 1'
}

ofn = argv[1]
nfn = 'new'
rfn = 'report.txt'

with open(ofn) as f:
    data = f.read()

for i in range(256):
    data = data[:8] + chr(i) + data[9:144] + chr(i) + data[145:]
    with open(nfn, 'wb') as f:
        f.write(data)
    command = '7z x ' + nfn
    p = Popen(command.split(), stdout = PIPE, stderr = PIPE)
    o, e = p.communicate()
    if 'Everything is Ok' in o:
        print i, cm[i]
        command = 'cat ' + rfn
        p = Popen(command.split(), stdout = PIPE, stderr = PIPE)
        o, e  = p.communicate()
        print o
    command = 'rm ' + nfn + ' ' + rfn
    p = Popen(command.split(), stdout = PIPE, stderr = PIPE)
    p.communicate()

# python generate_zipfiles.py report.zip
98 PPMd version I, Rev 1
The secret password is:
  My name is Bond, James Bond.
Seriously, you could have guessed this based on the mission ID.

Source

https://www.youtube.com/watch?v=z9hfkajoAvc (1:25:15)

References

https://en.wikipedia.org/wiki/Zip_(file_format)
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT (4.4.5)
https://github.com/corkami/pics/blob/master/binary/zip101/zip101.pdf

# An XOR alternative


# cat xor_alternative.py
import sys

# data contains hex chars (a-f + 0-9) and random chars
# Chars are not repeated
data = '2ubfLkR0vsJ#)=SQtXNcO6AYPT1U+ja7W*h9I-y4GeHzn5&BK;_@$U3dm8^%'

def obfuscate(s, step):
 out = ''
 ldata = len(data)
 ls = len(s)
 for i in s:
  h = hex(ord(i))[2:]
  for j in h:
   p = data.index(j)
   p2 = (p + step) % ldata
   out += data[p2]
 return out

def deobfuscate(s, step):
 out = ''
 ldata = len(data)
 ls = len(s)
 for i in range(0, ls, 2):
  h = ''
  for j in range(2):
   p = data.index(s[i + j])
   p2 = (p - step) % ldata
   h += data[p2]
  out += chr(int(h, 16))
 return out

action = sys.argv[1]
s = sys.argv[2]
step = int(sys.argv[3])

print 'String =', s
print 'Step =', step

if action == 'o':
 print 'Action = Obfuscate'
 print 'Result =', obfuscate(s, step)
elif action == 'd':
 print 'Action = Deobfuscate'
 print 'Result =', deobfuscate(s, step)

# python xor_alternative.py o SECRET 1234
String = SECRET
Step = 1234
Action = Obfuscate
Result = c+=c=+ch=cc=

# python xor_alternative.py d c+=c=+ch=cc= 1234
String = c+=c=+ch=cc=
Step = 1234
Action = Deobfuscate
Result = SECRET

Reference

https://isc.sans.edu/forums/diary/Obfuscating+without+XOR/22544/

# GynvaelEN mission 006


# cat mission_06.py
from PIL import Image
import qrtools

n = 25
m = 3
fn = 'mission_06.png'

matrix = [255 for i in range(n * n)]

with open('mission_06.data') as f:
 data = f.read().splitlines()

for line in data:
 line = line[1:-1].split(', ')
 i = int(line[0])
 j = int(line[1])
 matrix[(i*n) + j] = 0

image = Image.new('L', (n, n))
image.putdata(matrix)
image.save(fn)

img = Image.open(fn)
img = img.rotate(-90)
img = img.resize((n * m, n * m), Image.ANTIALIAS)
img.save(fn)

qr = qrtools.QR(filename = fn)
if qr.decode():
 print qr.data

# python mission_06.py
Mirrored QR? Seriously?!

Source

https://www.youtube.com/watch?v=KvyBn4Btv8E (1:32:02)

# GynvaelEN mission 005


# curl -v http://gynvael.vexillium.org/ext/thepicture/picture.image
Content-Encoding: rle
Content-Type: image/raw; w=640,h=212,bpp=8
# cat mission_05.py
from struct import unpack
from PIL import Image

with open('picture.image') as f:
 cdata = f.read()

ddata = []

for i in range(0, len(cdata), 2):
 v = unpack('B', cdata[i + 1])[0] ^ 1
 for _ in range(unpack('B', cdata[i])[0]):
  ddata.append(v * 255)

image = Image.new('L', (640, 212))
image.putdata(ddata)
image.save('output.png')
# python mission_05.py


Source

https://www.youtube.com/watch?v=W7s5CWaw6I4 (1:20:57)

Reference

https://en.wikipedia.org/wiki/Run-length_encoding

# Confidence CTF 2k17: Starbyte - misc - 200 pts


# cat starbyte.py
from PIL import Image, ImageDraw
import scipy.io.wavfile
import sys
import wave

inputfile = sys.argv[1]

wave_read_object = wave.open(inputfile, 'rb')
print 'Number of audio channels = ',  wave_read_object.getnchannels()
print 'Sample width = ', wave_read_object.getsampwidth(), '(bytes)'
print 'Sampling frequency = ', wave_read_object.getframerate(), '(Hz)'
frames = wave_read_object.getnframes()
print 'Number of audio frames = ', frames
wave_read_object.close()
rate, data = scipy.io.wavfile.read(inputfile)

last_frame = -1
c = ''
r = ''
i = 0

for frame in data:
 if frame > 90:
  if last_frame != 1:
   c += '1'
   i += 1
  last_frame = 1
 elif frame > 23:
  if  last_frame != 0:
   c += '0'
   i += 1
  last_frame = 0
 else:
  last_frame = -1
 if i == 10:
  nc = ''
  for j in c:
   nc = j + nc
  r += chr(int(nc, 2))
  c = ''
  i = 0
r = r.split('\n')

image = Image.new('RGB', (1000, 1000), 'black')
draw = ImageDraw.Draw(image)

for line in r:
 line = line.split()
 if 'LINE' in line:
  x1, y1, x2, y2 = map(int, line[1:])
  draw.line([(x1, y1), (x2, y2)], 'green')
 #elif 'REKT' in line:
 # x1, y1, x2, y2 = map(int, line[1:])
 # draw.rectangle([(x1, y1), (x2, y2)], None, 'green')
 elif 'CRCL' in line:
  x1, y1, rad = map(int, line[1:])
  draw.arc([(x1 - rad, y1 - rad), (x1 + rad, y1 + rad)], 0, 360, 'green')

image.save('image.png')
# python starbyte.py starbyte.wav
Number of audio channels =  1
Sample width =  1 (bytes)
Sampling frequency =  44100 (Hz)
Number of audio frames =  3885808
# eog image.png