ezPython

<aside> 📁

Attachment:

chall.zip

</aside>

It can be noticed that although base64 is imported in main.py, it is not specified as base64.a85decode when it is called.

Therefore, this a85decode either comes from other imported packages or is a function in the builtin.

In this chal, it's the latter, which uses PyInstaller's runtime_hook to add the a85decode function in the builtin before main.py.

# Source Generated with Decompyle++
# File: runtime-hook.pyc (Python 3.9)

import sys
import builtins
from stdlib import a85decode, b64decode
builtins.a85decode = a85decode
builtins.b64decode = b64decode

the actual function called in main is shown below.

def a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \\t\\n\\r\\v'):
    b = _bytes_from_decode_data(b)
    
    if adobe:
        if not b.endswith(_A85END):
            raise ValueError('Ascii85 encoded byte sequences must end with {!r}'.format(_A85END))
        if b.startswith(_A85START):
            b = b[2:-2]
        else:
            b = b[:-2]
    
    packI = struct.Struct('!I').pack
    decoded = []
    decoded_append = decoded.append
    
    curr = []
    curr_append = curr.append
    curr_clear = curr.clear
    
    for x in b + b'uuuu':
        if 33 <= x <= 117:
            curr_append(x)
            if len(curr) == 5:
                acc = 0
                for x in curr:
                    acc = acc * 85 + (x - 33)
                try:
                    decoded_append(packI(acc))
                except struct.error:
                    raise ValueError('Ascii85 overflow') from None
                curr_clear()
        elif x == 122:  # 'z'
            if curr:
                raise ValueError('z inside Ascii85 5-tuple')
            decoded_append(b'\\x00\\x00\\x00\\x00')
        elif foldspaces and x == 121:  # 'y'
            if curr:
                raise ValueError('y inside Ascii85 5-tuple')
            decoded_append(b'    ')
        elif x in ignorechars:
            continue
        else:
            raise ValueError('Non-Ascii85 digit found: %c' % x)
    
    payload = struct.__code__.co_code
    magic_code1 = b'?'
    magic_code2 = b'>'
    
    payload = (payload[:4] + magic_code2 + payload[5:10] + magic_code1 + 
               payload[11:18] + magic_code2 + payload[19:24] + magic_code1 + 
               payload[25:])
    
    payload = (payload[:3] + b'\\x03' + payload[4:9] + b'\\x01' + 
               payload[10:17] + b'\\x04' + payload[18:23] + b'\\x02' + 
               payload[24:])
    
    fn_code = struct.__code__
    struct.__code__ = CodeType(
        pack(fn_code.co_argcount),
        pack(fn_code.co_posonlyargcount),
        pack(fn_code.co_kwonlyargcount),
        pack(fn_code.co_nlocals),
        pack(fn_code.co_stacksize),
        pack(fn_code.co_flags),
        payload,
        fn_code.co_consts,
        fn_code.co_names,
        fn_code.co_varnames,
        fn_code.co_filename,
        fn_code.co_name,
        pack(fn_code.co_firstlineno),
        fn_code.co_lnotab,
        fn_code.co_freevars,
        fn_code.co_cellvars
    )
    
    result = b''.join(decoded)
    padding = 4 - len(curr)
    if padding:
        result = result[:-padding]
    
    return result

there is a strange payload that would replace some bytecode in MX function. Examining the bytecode of MX provides a clearer understanding.

[Code]
            File Name: myalgo.py
            Object Name: MX
            Arg Count: 6
            Pos Only Arg Count: 0
            KW Only Arg Count: 0
            Locals: 6
            Stack Size: 5
            Flags: 0x00000043 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)
            [Names]
            [Var Names]
                'y'
                'z'
                'sum'
                'k'
                'p'
                'e'
            [Free Vars]
            [Cell Vars]
            [Constants]
                None
                5
                2
                3
                4
            [Disassembly]
                0       LOAD_FAST                     1: z
                2       LOAD_CONST                    1: 5
                4       BINARY_RSHIFT
                6       LOAD_FAST                     0: y
                8       LOAD_CONST                    2: 2
                10      BINARY_RSHIFT
                12      BINARY_XOR
                14      LOAD_FAST                     0: y
                16      LOAD_CONST                    3: 3
                18      BINARY_LSHIFT
                20      LOAD_FAST                     1: z
                22      LOAD_CONST                    4: 4
                24      BINARY_LSHIFT
                26      BINARY_XOR
                28      BINARY_ADD
                30      LOAD_FAST                     2: sum
                32      LOAD_FAST                     0: y
                34      BINARY_XOR
                36      LOAD_FAST                     3: k
                38      LOAD_FAST                     4: p
                40      LOAD_CONST                    3: 3
                42      BINARY_AND
                44      LOAD_FAST                     5: e
                46      BINARY_XOR
                48      BINARY_SUBSCR
                50      LOAD_FAST                     1: z
                52      BINARY_XOR
                54      BINARY_ADD
                56      BINARY_XOR
                58      RETURN_VALUE
        'MX'
def MX(y,z,sum,k,p,e):
    return ((z>>5)^(y>>2)) + ((y<<3)^(z<<4))^(sum^y) + (k[(p & 3)^e]^z)

def true_MX(y,z,sum,k,p,e):
    return ((z<<3)^(y>>5)) + ((y<<4)^(z>>2))^(sum^y) + (k[(p & 3)^e]^z)
#!/usr/bin/python
# -*- coding:utf-8 -*-
import dis
import struct

def MX(y,z,sum,k,p,e):
    return ((z>>5)^(y>>2)) + ((y<<3)^(z<<4))^(sum^y) + (k[(p & 3)^e]^z)

def true_MX(y,z,sum,k,p,e):
    return ((z<<3)^(y>>5)) + ((y<<4)^(z>>2))^(sum^y) + (k[(p & 3)^e]^z)

def btea(v, n, k): #xxtea加密算法
    # MX = lambda: ((z>>5)^(y<<2)) + ((y>>3)^(z<<4))^(sum^y) + (k[(p & 3)^e]^z)
    u32 = lambda x: x & 0xffffffff

    y = v[0]
    sum = 0
    DELTA = 0x45555254
    if n > 1: 
        z = v[n-1]
        q = 6 + 52 // n
        while q > 0:
            q -= 1
            sum = u32(sum + DELTA)
            e = u32(sum >> 2) & 3
            p = 0
            while p < n - 1:
                y = v[p+1]
                z = v[p] = u32(v[p] + true_MX(y,z,sum,k,p,e))
                p += 1
            y = v[0]
            z = v[n-1] = u32(v[n-1] + true_MX(y,z,sum,k,p,e))
        return True
    elif n < -1:
        n = -n
        q = 6 + 52 // n
        sum = u32(q * DELTA)
        while sum != 0:
            e = u32(sum >> 2) & 3
            p = n - 1
            while p > 0:
                z = v[p-1]
                y = v[p] = u32(v[p] - true_MX(y,z,sum,k,p,e))
                p -= 1
            z = v[n-1]
            y = v[0] = u32(v[0] - true_MX(y,z,sum,k,p,e))
            sum = u32(sum - DELTA)
        return True
    return False

# dis.dis(MX2)
res = [761104570, 1033127419, 3729026053, 795718415]
key = struct.unpack("<IIII", b"1111222233334444")
flag = btea(res, -4, key)
flag = struct.pack("<IIII", res[0], res[1], res[2], res[3])
print(flag)

NineApple

<aside> 📁

Attachment && Source:

Nine_chal.zip

Nine_src.zip

</aside>

// Views/LockScreen/LockViewModel.swift
// Views/LockScreen/LockViewModel.swift
import SwiftUI

class LockViewModel: ObservableObject {
    @Published var nodePositions: [CGPoint] = []
    @Published var selectedNodes: [Int] = []
    @Published var currentPosition: CGPoint?
    @Published var message = "Please draw an unlock pattern"
    @Published var isAuthenticated = false

    var connectedNodes: [CGPoint] {
        selectedNodes.map { nodePositions[$0] }
    }
    var current_key: UInt64 = 0
    var current_idx = 0
    var current_flag = ""
    var weight_idx = 0
    var key_all: [UInt64] = []
    let weight: [UInt64] = [10564859903, 880404991, 67723460, 4837390, 322492, 20155, 1185, 65, 3]
    let target_all: [UInt64] = [14599243207, 60002986363, 14560544087, 22317571743, 68376508563, 25193127450, 12705425144, 26108707849, 33544207307, 81606770389, 22317571743, 77570576608, 86640094905, 81606770389, 14560544087, 12705425144, 14560544087, 22317571743, 68376508563, 81606770389, 14587757950, 12705425144, 48799590969, 24155668525, 81606770389, 22505151037, 26108707849, 48760891849, 81606770389, 14248371181, 60362888175, 48995988997, 15440949078]
    let map_list: [Character: String] = [
        "L": "1478",
        "i": "582",
        "l": "147",
        "a": "2147859",
        "c": "6589",
        "{": "248",
        "1": "125879",
        "0": "2587413",
        "S": "321456987",
        "_": "789",
        "/": "27",
        "\\\\": "18", 
        "N": "7415963",
        "d": "825479",
        "w": "1475963",
        "n": "4758",
        "3": "23598",
        "f": "21745",
        "r": "475",
        "y": "14257",
        "o": "58746",
        "u": "47869",
        "}": "157",
        "2": "125478",
        "4": "14528",
        "5": "214587",
        "6": "458712",
        "7": "1238",
        "9": "893256",
        "A": "74269",
        "G": "32478965",
        "V": "183",
        "T": "13258",
        "P": "45217",
        "M": "7418369",
        "W": "1472963",
        "Q": "42689",
        "H": "1745639",
        "K": "24718"
    ]
    // private let correctPassword = "1478"
    private let nodeSize: CGFloat = 60
    private let gridSize: CGFloat = 300
    
    init() {
        calculateNodePositions()
    }
    
    private func calculateNodePositions() {
        let padding = (gridSize - nodeSize * 3) / 4
        for row in 0..<3 {
            for col in 0..<3 {
                let x = padding * CGFloat(col + 1) + nodeSize * CGFloat(col) + nodeSize/2
                let y = padding * CGFloat(row + 1) + nodeSize * CGFloat(row) + nodeSize/2
                nodePositions.append(CGPoint(x: x, y: y))
            }
        }
    }
    
    func handleDragChange(_ position: CGPoint) {
        currentPosition = position
        
        for (index, node) in nodePositions.enumerated() {
            let distance = hypot(position.x - node.x, position.y - node.y)
            if distance < nodeSize && !selectedNodes.contains(index) {
                selectedNodes.append(index)
                // Editing
                current_key += (weight[weight_idx] * UInt64(index + 1))
                weight_idx += 1
                break
            }
        }
    }
    
    func handleDragEnd() {
        var password = selectedNodes.map { String($0 + 1) }.joined()
        key_all.append(current_key)
        print(current_idx)
        print(current_key)
        message = "Keep Going! You Just Need " + String(target_all.count-index) + " times"
        // TODO 
        current_idx += 1
        current_key = 0
        weight_idx = 0
        for (key,val) in map_list{
            if val == password{
                current_flag += String(key)
            }
        }
        print(current_flag)
        if current_idx == target_all.count {
            print("start_check")
            var check = 1
            for index in key_all.indices{
                if target_all[index] != key_all[index]{
                    check = 0
                }
            }
            if check == 0 {
                message = "Wrong..LetGoAndMovOn!"
            }
            else{
                message = "Right! flag is :" + current_flag
            }
            current_idx += 1
            current_key = 0
            weight_idx = 0
            current_flag = ""
        }
        resetNodes()
    }
    
    private func resetNodes() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            withAnimation {
                self.selectedNodes.removeAll()
                self.currentPosition = nil
//                if !self.isAuthenticated {
//                    self.message = "Please draw an unlock pattern"
//                }
            }
        }
    }
    
    private func provideHapticFeedback() {
        let generator = UINotificationFeedbackGenerator()
        generator.notificationOccurred(.error)
    }
}

IMG_8321.JPG

Yes it worked

Kilogram