roger 发表于 2019-7-5 12:59:05

2019年7月第一周打卡_压缩包套娃wp

# 压缩包套娃

附件:`first.zip`

## solution

本题是个多层密码加密的题,主要考察`sha256爆破`、`RSA copper smith攻击`、`古典密码`、`CRC32爆破`。

### 0x01 first key

打开`key.py`:

```python
import hashlib
import os
s=os.urandom(8)
print s.encode('hex')
print hashlib.sha256(s).hexdigest()
#3fcb70c42b
#4c6e139bd07cc2c00c8d39995727b697efa4171a9e475d194e654a0f4b0c3285
#password:s.encode('hex')
```

是个sha256的简单爆破,提供的脚本如下:

```python
import hashlib
a='4c6e139bd07cc2c00c8d39995727b697efa4171a9e475d194e654a0f4b0c3285'
b=0x3fcb70c42b
def num2str(num):
    h=hex(num).replace("L","")
    if len(h)%2!=0:
      h='0'+h
    return h.decode('hex')

for i in range(0x100):
    for j in range(0x100):
      for m in range(0x100):
                tmp=num2str(b)+chr(i)+chr(j)+chr(m)
                if hashlib.sha256(tmp).hexdigest()==a:
                  print tmp.encode('hex')
```

爆破得到password:
> 3fcb70c42b95bbdf

### 0x02 second key

根据key进入`second.zip`后,得到的是一个`thrid.zip`与`key.sage`

```python
from Crypto.PublicKey import RSA

key=RSA.generate(1024)
print hex(key.n),hex(key.e)
#0x907c4c35ef8defb9b0a5bf8ba3f1ad5d0d12ba79cb2913e6ef149a7b62ade6b08fad9618650c3508d8357933f83d1984516af4a1f6236ed734095d88a1c987912bf911d0187184c5182344bfab2203feb24b1f4ef7a94dfa86e5dc68caeead4318e3c043c9b19e1726b27c7948b522f89b5f83f37ea66de0e1ea2af36a38406bL 0x10001

m='*************'
c=power_mod(m,key.e,key.n)
print hex(c)
#0x6bad8e8bc4abe06db93504d120eca56ce6637906d5e2ada9c0010ac706490dfe877e913657ac9105b85ee83ea42c5b6989f067dcb2bacd24daad6679d30f304cda8dcf30509ccf67528ce9373bf469353052042bf1d73ee18e61e7b5eae9914b1619a48fcf48e97f483e50719d346af17e60b4bfd2d9f6ba68a44838db707c1dL

print hex((key.p>>200)<<200)
#0xb6b5afc607cdfb5b103b49b3f83a4fdca35ecf74e259a31b2c22898fe55aaea1ae88ad4be1d6b900000000000000000000000000000000000000000000000000L
#密码就在m中:)
```

从题目中我们可以知道:这是个RSA因子高位已知攻击。尝试copper smith攻击,sage脚本如下:

```python
# -*- coding: utf-8 -*-
import time
from sage import *
# display matrix picture with 0 and X
def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()):
      a = ('%02d ' % ii)
      for jj in range(BB.dimensions()):
            a += '0' if BB == 0 else 'X'
            a += ' '
      if BB >= bound:
            a += '~'
      print a

def coppersmith_howgrave_univariate(pol, modulus, beta, mm, tt, XX):
    """
    Coppersmith revisited by Howgrave-Graham

    finds a solution if:
    * b|modulus, b >= modulus^beta , 0 < beta <= 1
    * |x| < XX
    """
    #
    # init
    #
    dd = pol.degree()
    nn = dd * mm + tt

    #
    # checks
    #
    if not 0 < beta <= 1:
      raise ValueError("beta should belongs in (0, 1]")

    if not pol.is_monic():
      raise ArithmeticError("Polynomial must be monic.")

    #
    # calculate bounds and display them
    #
    # Coppersmith revisited algo for univariate
    #
    # change ring of pol and x
    polZ = pol.change_ring(ZZ)
    x = polZ.parent().gen()

    # compute polynomials
    gg = []
    for ii in range(mm):
      for jj in range(dd):
            gg.append((x * XX)**jj * modulus**(mm - ii) * polZ(x * XX)**ii)
    for ii in range(tt):
      gg.append((x * XX)**ii * polZ(x * XX)**mm)

    # construct lattice B
    BB = Matrix(ZZ, nn)

    for ii in range(nn):
      for jj in range(ii+1):
            BB = gg

    # display basis matrix
    if debug:
      matrix_overview(BB, modulus^mm)

    # LLL
    BB = BB.LLL()

    # transform shortest vector in polynomial
    new_pol = 0
    for ii in range(nn):
      new_pol += x**ii * BB / XX**ii

    # factor polynomial
    potential_roots = new_pol.roots()
    print "potential roots:", potential_roots


    return potential_roots
length_N = 1024
N = 0x907c4c35ef8defb9b0a5bf8ba3f1ad5d0d12ba79cb2913e6ef149a7b62ade6b08fad9618650c3508d8357933f83d1984516af4a1f6236ed734095d88a1c987912bf911d0187184c5182344bfab2203feb24b1f4ef7a94dfa86e5dc68caeead4318e3c043c9b19e1726b27c7948b522f89b5f83f37ea66de0e1ea2af36a38406bL
qbar =0xb6b5afc607cdfb5b103b49b3f83a4fdca35ecf74e259a31b2c22898fe55aaea1ae88ad4be1d6b900000000000000000000000000000000000000000000000000L

F.<x> = PolynomialRing(Zmod(N), implementation='NTL');
pol = x - qbar
dd = pol.degree()

beta = 0.5                           # q >= N^beta
epsilon = beta / 7                     # <= beta/7
mm = ceil(beta**2 / (dd * epsilon))    # optimized
tt = floor(dd * mm * ((1/beta) - 1))   # optimized
XX = ceil(N**((beta**2/dd) - epsilon)) # |diff| < X

# Coppersmith
start_time = time.time()
roots = coppersmith_howgrave_univariate(pol, N, beta, mm, tt, XX)
c=0x6bad8e8bc4abe06db93504d120eca56ce6637906d5e2ada9c0010ac706490dfe877e913657ac9105b85ee83ea42c5b6989f067dcb2bacd24daad6679d30f304cda8dcf30509ccf67528ce9373bf469353052042bf1d73ee18e61e7b5eae9914b1619a48fcf48e97f483e50719d346af17e60b4bfd2d9f6ba68a44838db707c1d
q=qbar-roots
p=N/q
e=0x10001
d=inverse_mod(e,(p-1)*(q-1))
m=power_mod(c,d,N)
print '{:x}'.format(int(m)).decode('hex')
```

得到结果:
> So,the key is h3ll0

### 0x03 thrid key

根据key打开`third.zip`后,发现有个`fourth.zip`和`key.py`。
```python
import sys

alphaL = "abcdefghijklnmopqrstuvwxyz"
alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
num    = "0123456789"
keychars = num+alphaL+alphaU

if len(sys.argv) != 3:
print("Usage: %s SECRET_KEY PLAINTEXT"%(sys.argv))
sys.exit()
key = sys.argv.upper()
if not key.isalnum():
print("Your key is invalid, it may only be alphanumeric characters")
sys.exit()
plaintext = sys.argv
ciphertext=""

for i in range(len(plaintext)):
rotate_amount = keychars.index(key)
if plaintext in alphaL:
    enc_char = ord('a') + (3*(ord(plaintext)-ord('a'))+2*rotate_amount)%26
elif plaintext in alphaU:
    enc_char = ord('A') + (5*(ord(plaintext)-ord('A'))+rotate_amount)%26
elif plaintext in num:
    enc_char = ord('0') + (7*(ord(plaintext)-ord('0'))+3*rotate_amount)%10
else:
    enc_char = ord(plaintext)
ciphertext = ciphertext + chr(enc_char)

print("Encryption complete, ENC(%s,%s) = %s"%(plaintext,key,ciphertext))

# Encryption complete, ENC(ciphertext) = nxos ge kmqb dizh uic:d4k_4y6, cdz nxc jutv rgpv uk: FFB
# key hint:h3ll0长得像哪个单词呢?
```

leet的`hello`可以写成`h3ll0`,而且`h3ll0`很像`hello`。因此密钥为hello。

从中可以看出,key并没有很大程度影响enc_char内容,因此可以当常数看待,变成简单的Affine密码问题,解密脚本如下:

```python
import gmpy2

alphaL = "abcdefghijklnmopqrstuvwxyz"
alphaU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
num    = "0123456789"
keychars = num+alphaL+alphaU

key="hello"

enc_text="nxos ge kmqb dizh uic:d4k_4y6, cdz nxc jutv rgpv uk: FFB"

plaintext=""
i1=gmpy2.invert(3,26)
i2=gmpy2.invert(5,26)
i3=gmpy2.invert(7,10)
for i in range(len(enc_text)):
    rotate_amount = keychars.index(key)
    if enc_text in alphaL:
      char = ord('a') + (i1*((ord(enc_text)-ord('a'))-2*rotate_amount))%26
    elif enc_text in alphaU:
      char = ord('A') + (i2*((ord(enc_text)-ord('A'))-rotate_amount))%26
    elif enc_text in num:
      char = ord('0') + (i3*((ord(enc_text)-ord('0'))-3*rotate_amount))%10
    else:
      char = ord(enc_text)
    plaintext = plaintext + chr(char)
print(plaintext)
```

得到结果:
> this is your next key:n3w_6u9, and the next hint is: CRC


### 0x04 fourth key

根据key进入`fourth.zip`,发现有个`flag.zip`,打得开`flag.zip`,但是解压要密码。又有提示:CRC,因此采取CRC32爆破。但是爆破一个是5bytes,一个是6bytes。因此我们分别用两个脚本爆破。

```python
# -*- coding: utf-8 -*-
# 5bytes CRC32
import itertools
import binascii
import string


class crc32_reverse_class(object):
    # the code is modified from https://github.com/theonlypwner/crc32/blob/master/crc32.py
    def __init__(self, crc32, length, tbl=string.printable,
               poly=0xEDB88320, accum=0):
      self.char_set = set(map(ord, tbl))
      self.crc32 = crc32
      self.length = length
      self.poly = poly
      self.accum = accum
      self.table = []
      self.table_reverse = []

    def init_tables(self, poly, reverse=True):
      # build CRC32 table
      for i in range(256):
            for j in range(8):
                if i & 1:
                  i >>= 1
                  i ^= poly
                else:
                  i >>= 1
            self.table.append(i)
      assert len(self.table) == 256, "table is wrong size"
      # build reverse table
      if reverse:
            found_none = set()
            found_multiple = set()
            for i in range(256):
                found = []
                for j in range(256):
                  if self.table >> 24 == i:
                        found.append(j)
                self.table_reverse.append(tuple(found))
                if not found:
                  found_none.add(i)
                elif len(found) > 1:
                  found_multiple.add(i)
            assert len(self.table_reverse) == 256, "reverse table is wrong size"

    def rangess(self, i):
      return ', '.join(map(lambda x: '[{0},{1}]'.format(*x), self.ranges(i)))

    def ranges(self, i):
      for kg in itertools.groupby(enumerate(i), lambda x: x - x):
            g = list(kg)
            yield g, g[-1]

    def calc(self, data, accum=0):
      accum = ~accum
      for b in data:
            accum = self.table[(accum ^ b) & 0xFF] ^ ((accum >> 8) & 0x00FFFFFF)
      accum = ~accum
      return accum & 0xFFFFFFFF

    def findReverse(self, desired, accum):
      solutions = set()
      accum = ~accum
      stack = [(~desired,)]
      while stack:
            node = stack.pop()
            for j in self.table_reverse[(node >> 24) & 0xFF]:
                if len(node) == 4:
                  a = accum
                  data = []
                  node = node + (j,)
                  for i in range(3, -1, -1):
                        data.append((a ^ node) & 0xFF)
                        a >>= 8
                        a ^= self.table
                  solutions.add(tuple(data))
                else:
                  stack.append(((node ^ self.table) << 8,) + node + (j,))
      return solutions

    def dfs(self, length, outlist=['']):
      tmp_list = []
      if length == 0:
            return outlist
      for list_item in outlist:
            tmp_list.extend()
      return self.dfs(length - 1, tmp_list)

    def run_reverse(self):
      # initialize tables
      self.init_tables(self.poly)
      # find reverse bytes
      desired = self.crc32
      accum = self.accum
      # 4-byte patch
      if self.length >= 4:
            patches = self.findReverse(desired, accum)
            for patch in patches:
                checksum = self.calc(patch, accum)
                print 'verification checksum: 0x{0:08x} ({1})'.format(
                  checksum, 'OK' if checksum == desired else 'ERROR')
            for item in self.dfs(self.length - 4):
                patch = map(ord, item)
                patches = self.findReverse(desired, self.calc(patch, accum))
                for last_4_bytes in patches:
                  if all(p in self.char_set for p in last_4_bytes):
                        patch.extend(last_4_bytes)
                        print ': {1} ({0})'.format(
                            'OK' if self.calc(patch, accum) == desired else 'ERROR', ''.join(map(chr, patch)))
      else:
            for item in self.dfs(self.length):
                if crc32(item) == desired:
                  print ': {0} (OK)'.format(item)


def crc32_reverse(crc32, length, char_set=string.printable,
                  poly=0xEDB88320, accum=0):
    '''

    :param crc32: the crc32 you wnat to reverse
    :param length: the plaintext length
    :param char_set: char_set
    :param poly: poly , default 0xEDB88320
    :param accum: accum , default 0
    :return: none
    '''
    obj = crc32_reverse_class(crc32, length, char_set, poly, accum)
    obj.run_reverse()


def crc32(s):
    '''

    :param s: the string to calculate the crc32
    :return: the crc32
    '''
    return binascii.crc32(s) & 0xffffffff

l=

for k in l:
      crc32_reverse(k,5)
      print '======='
```

得到合适的结果:
> 6u9ku

```python
#!/usr/bin/env python
# CRC32 tools by Victor 6bytes

import argparse
import os
import sys

permitted_characters = set(
    map(ord, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_'))# \w

testing = False

args = None


def get_poly():
    poly = parse_dword(args.poly)
    if args.msb:
      poly = reverseBits(poly)
    check32(poly)
    return poly


def get_input():
    if args.instr:
      return tuple(map(ord, args.instr))
    with args.infile as f:# pragma: no cover
      return tuple(map(ord, f.read()))


def out(msg):
    if not testing:# pragma: no cover
      args.outfile.write(msg)
      args.outfile.write(os.linesep)

table = []
table_reverse = []


def init_tables(poly, reverse=True):
    global table, table_reverse
    table = []
    # build CRC32 table
    for i in range(256):
      for j in range(8):
            if i & 1:
                i >>= 1
                i ^= poly
            else:
                i >>= 1
      table.append(i)
    assert len(table) == 256, "table is wrong size"
    # build reverse table
    if reverse:
      table_reverse = []
      found_none = set()
      found_multiple = set()
      for i in range(256):
            found = []
            for j in range(256):
                if table >> 24 == i:
                  found.append(j)
            table_reverse.append(tuple(found))
            if not found:
                found_none.add(i)
            elif len(found) > 1:
                found_multiple.add(i)
      assert len(table_reverse) == 256, "reverse table is wrong size"
      if found_multiple:
            out('WARNING: Multiple table entries have an MSB in {0}'.format(
                rangess(found_multiple)))
      if found_none:
            out('ERROR: no MSB in the table equals bytes in {0}'.format(
                rangess(found_none)))


def calc(data, accum=0):
    accum = ~accum
    for b in data:
      accum = table[(accum ^ b) & 0xFF] ^ ((accum >> 8) & 0x00FFFFFF)
    accum = ~accum
    return accum & 0xFFFFFFFF


def rewind(accum, data):
    if not data:
      return (accum,)
    stack = [(len(data), ~accum)]
    solutions = set()
    while stack:
      node = stack.pop()
      prev_offset = node - 1
      for i in table_reverse[(node >> 24) & 0xFF]:
            prevCRC = (((node ^ table) << 8) |
                     (i ^ data)) & 0xFFFFFFFF
            if prev_offset:
                stack.append((prev_offset, prevCRC))
            else:
                solutions.add((~prevCRC) & 0xFFFFFFFF)
    return solutions


def findReverse(desired, accum):
    solutions = set()
    accum = ~accum
    stack = [(~desired,)]
    while stack:
      node = stack.pop()
      for j in table_reverse[(node >> 24) & 0xFF]:
            if len(node) == 4:
                a = accum
                data = []
                node = node + (j,)
                for i in range(3, -1, -1):
                  data.append((a ^ node) & 0xFF)
                  a >>= 8
                  a ^= table
                solutions.add(tuple(data))
            else:
                stack.append(((node ^ table) << 8,) + node + (j,))
    return solutions

# Tools


def parse_dword(x):
    return int(x, 0) & 0xFFFFFFFF


def reverseBits(x):
    # http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
    # http://stackoverflow.com/a/20918545
    x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1)
    x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2)
    x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4)
    x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8)
    x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16)
    return x & 0xFFFFFFFF

# Compatibility with Python 2.6 and earlier.
if hasattr(int, "bit_length"):
    def bit_length(num):
      return num.bit_length()
else:
    def bit_length(n):
      if n == 0:
            return 0
      bits = -32
      m = 0
      while n:
            m = n
            n >>= 32
            bits += 32
      while m:
            m >>= 1
            bits += 1
      return bits


def check32(poly):
    if poly & 0x80000000 == 0:
      out('WARNING: polynomial degree ({0}) != 32'.format(bit_length(poly)))
      out('         instead, try')
      out('         0x{0:08x} (reversed/lsbit-first)'.format(poly | 0x80000000))
      out('         0x{0:08x} (normal/msbit-first)'.format(reverseBits(poly | 0x80000000)))


def reciprocal(poly):
    ''' Return the reversed reciprocal (Koopman notatation) polynomial of a
      reversed (lsbit-first) polynomial '''
    return reverseBits((poly << 1) | 1)


def print_num(num):
    ''' Write a numeric result in various forms '''
    out('hex: 0x{0:08x}'.format(num))
    out('dec:   {0:d}'.format(num))
    out('oct: 0o{0:011o}'.format(num))
    out('bin: 0b{0:032b}'.format(num))

import itertools


def ranges(i):
    for kg in itertools.groupby(enumerate(i), lambda x: x - x):
      g = list(kg)
      yield g, g[-1]


def rangess(i):
    return ', '.join(map(lambda x: '[{0},{1}]'.format(*x), ranges(i)))

# Parsers


def get_parser():
    ''' Return the command-line parser '''
    parser = argparse.ArgumentParser(
      description="Reverse, undo, and calculate CRC32 checksums")
    subparsers = parser.add_subparsers(metavar='action')

    poly_flip_parser = argparse.ArgumentParser(add_help=False)
    subparser_group = poly_flip_parser.add_mutually_exclusive_group()
    subparser_group.add_argument(
      '-m', '--msbit', dest="msb", action='store_true',
      help='treat the polynomial as normal (msbit-first)')
    subparser_group.add_argument('-l', '--lsbit', action='store_false',
                                 help='treat the polynomial as reversed (lsbit-first) ')

    desired_poly_parser = argparse.ArgumentParser(add_help=False)
    desired_poly_parser.add_argument(
      'desired', type=str, help=' desired checksum')

    default_poly_parser = argparse.ArgumentParser(add_help=False)
    default_poly_parser.add_argument(
      'poly', default='0xEDB88320', type=str, nargs='?',
      help=' polynomial ')

    accum_parser = argparse.ArgumentParser(add_help=False)
    accum_parser.add_argument(
      'accum', type=str, help=' accumulator (final checksum)')

    default_accum_parser = argparse.ArgumentParser(add_help=False)
    default_accum_parser.add_argument(
      'accum', default='0', type=str, nargs='?',
      help=' starting accumulator ')

    outfile_parser = argparse.ArgumentParser(add_help=False)
    outfile_parser.add_argument('-o', '--outfile',
                              metavar="f",
                              type=argparse.FileType('w'),
                              default=sys.stdout,
                              help="Output to a file instead of stdout")

    infile_parser = argparse.ArgumentParser(add_help=False)
    subparser_group = infile_parser.add_mutually_exclusive_group()
    subparser_group.add_argument('-i', '--infile',
                                 metavar="f",
                                 type=argparse.FileType('rb'),
                                 default=sys.stdin,
                                 help="Input from a file instead of stdin")
    subparser_group.add_argument('-s', '--str',
                                 metavar="s",
                                 type=str,
                                 default='',
                                 dest='instr',
                                 help="Use a string as input")

    subparser = subparsers.add_parser('flip', parents=,
                                    help="flip the bits to convert normal(msbit-first) polynomials to reversed (lsbit-first) and vice versa")
    subparser.add_argument('poly', type=str, help=' polynomial')
    subparser.set_defaults(
      func=lambda: print_num(reverseBits(parse_dword(args.poly))))

    subparser = subparsers.add_parser('reciprocal', parents=,
                                    help="find the reciprocal (Koopman notation) of a reversed (lsbit-first) polynomial and vice versa")
    subparser.add_argument('poly', type=str, help=' polynomial')
    subparser.set_defaults(func=reciprocal_callback)

    subparser = subparsers.add_parser('table', parents=[outfile_parser,
                                                      poly_flip_parser,
                                                      default_poly_parser],
                                    help="generate a lookup table for a polynomial")
    subparser.set_defaults(func=table_callback)

    subparser = subparsers.add_parser('reverse', parents=[
      outfile_parser,
      poly_flip_parser,
      desired_poly_parser,
      default_accum_parser,
      default_poly_parser],
      help="find a patch that causes the CRC32 checksum to become a desired value")
    subparser.set_defaults(func=reverse_callback)

    subparser = subparsers.add_parser('undo', parents=[
      outfile_parser,
      poly_flip_parser,
      accum_parser,
      default_poly_parser,
      infile_parser],
      help="rewind a CRC32 checksum")
    subparser.add_argument('-n', '--len', metavar='l', type=str,
                           default='0', help=' number of bytes to rewind ')
    subparser.set_defaults(func=undo_callback)

    subparser = subparsers.add_parser('calc', parents=[
      outfile_parser,
      poly_flip_parser,
      default_accum_parser,
      default_poly_parser,
      infile_parser],
      help="calculate the CRC32 checksum")
    subparser.set_defaults(func=calc_callback)

    return parser


def reciprocal_callback():
    poly = parse_dword(args.poly)
    check32(poly)
    print_num(reciprocal(poly))


def table_callback():
    # initialize tables
    init_tables(get_poly(), False)
    # print table
    out('[{0}]'.format(', '.join(map('0x{0:08x}'.format, table))))


def reverse_callback():
    # initialize tables
    init_tables(get_poly())
    # find reverse bytes
    desired = parse_dword(args.desired)
    accum = parse_dword(args.accum)
    # 4-byte patch
    patches = findReverse(desired, accum)
    for patch in patches:
      out('4 bytes: {{0x{0:02x}, 0x{1:02x}, 0x{2:02x}, 0x{3:02x}}}'.format(*patch))
      checksum = calc(patch, accum)
      out('verification checksum: 0x{0:08x} ({1})'.format(
            checksum, 'OK' if checksum == desired else 'ERROR'))
    # 6-byte alphanumeric patches
    for i in permitted_characters:
      for j in permitted_characters:
            patch =
            patches = findReverse(desired, calc(patch, accum))
            for last_4_bytes in patches:
                if all(p in permitted_characters for p in last_4_bytes):
                  patch.extend(last_4_bytes)
                  out('alternative: {1}{2}{3}{4}{5}{6} ({0})'.format(
                        'OK' if calc(patch, accum) == desired else 'ERROR', *map(chr, patch)))


def undo_callback():
    # initialize tables
    init_tables(get_poly())
    # calculate checksum
    accum = parse_dword(args.accum)
    maxlen = int(args.len, 0)
    data = get_input()
    if not 0 < maxlen <= len(data):
      maxlen = len(data)
    out('rewinded {0}/{1} ({2:.2f}%)'.format(maxlen, len(data),
      maxlen * 100.0 / len(data) if len(data) else 100))
    for solution in rewind(accum, data[-maxlen:]):
      out('')
      print_num(solution)


def calc_callback():
    # initialize tables
    init_tables(get_poly(), False)
    # calculate checksum
    accum = parse_dword(args.accum)
    data = get_input()
    out('data len: {0}'.format(len(data)))
    out('')
    print_num(calc(data, accum))


def main(argv=None):
    ''' Runs the program and handles command line options '''
    parser = get_parser()

    # Parse arguments and run the function
    global args
    args = parser.parse_args(argv)
    args.func()

if __name__ == '__main__':
    main()# pragma: no cover
```

得到合适的结果:
> _w0rld

因此组合一起就出来key:
> 6u9ku_w0rld

### 0x05 decode

解压得到`flag.txt`文件内容:

```
flag? 就在这里哟
4d5a5747435a33334f35535443595a514e555a56363542514c35525445364c514f525858323d3d3d
```

先hex解码,得到:
> MZWGCZ33O5STCYZQNUZV65BQL5RTE6LQORXX2===

显然这是base64的编码,因此尝试各种base家族解码,最终发现为base32。

得到flag:

> flag{we1c0m3_t0_c2ypto}
题目:



题目所用的所有解题脚本:



33911628 发表于 2019-11-22 14:30:55

{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}{:6_151:}

如风过境。 发表于 2020-10-16 10:52:41

谢谢大佬,学习了

evi1ox 发表于 2020-10-19 14:30:07

谢谢大佬,这个脚本写的很赞
页: [1]
查看完整版本: 2019年7月第一周打卡_压缩包套娃wp