KCTF 2019 Finals

作者: Kirin_say | 来源:发表于2019-12-30 20:25 被阅读0次

    一鼓作气,再而衰,三而竭orz
    开始准备AK,后面因为参加D3CTF Finals断层,陆陆续续就没有怎么做了

    Emmmm

    0x01 无限流

    #kanxue1
    key1="cuk!ogl"
    key2="goluck!"
    ans=""
    for i in key2:
        ans+=str(key1.index(i))
    print ans
    

    0x02 南充茶坊

    首先看到南充茶坊.exe本身没有什么操作,并且可以看到新进程CM.exe
    直接binwalk即可提取出CM.exe,同时可以看到压缩包,提取后是一些pyc,同时可以看到CM.exe中只是解压了一段数据(python的bootloader)并运行新的进程,可以看到是pyinstaller
    直接使用pyinstxtractor.py提取失败,打开winhex,发现CM.exe的py数据最后四字节有问题:

    DEADCODE
    去掉后重新提取,即可在提取的资源中发现主程序的pyc数据(\CM.exe_extracted\CM),添加pyc文件头,再使用uncompyle6.exe反编译,其他几个引用库,修复pyc文件头再反编译即可:
    Header
    得到三个主要文件:
    CM.py:
    from CMpub import pub_n_list, pub_e_list
    from general import valid_serial, valid_username, get_enc_seq, enc0
    from Crypto.Util.number import bytes_to_long
    from os import system
    
    def check(serial, seq):
        now = serial
        for j in range(len(seq)):
            i = seq[j]
            n = pub_n_list[i]
            e = pub_e_list[i]
            if now > pub_n_list[i]:
                return False
            if i > 0:
                now = pow(now, e, n)
            else:
                now = enc0(now, e, n)
                if 0 == now:
                    return False
    
        return now
    
    
    def main():
        username0 = input('Please input Username:\t')
        serial0 = input('Please input Serial:\t')
        try:
            valid_serial(serial0)
            valid_username(username0)
        except:
            print('\nInvalid Input\n')
            return
        else:
            serial = int(serial0, 16)
            username = username0.encode('utf-8')
            seq = get_enc_seq(username)
            username = bytes_to_long(username)
            check_value = check(serial, seq)
            if check_value == username:
                print('\nCorrect!\n')
            else:
                print('\nOops! The encrypted Serial is not "' + username0 + '".\n')
            system('pause')
    
    
    if __name__ == '__main__':
        main()
    

    CMpub.py:

    pub_n_list = [
     1230179032923966355216193664456989083993912178939747632284136330115404600706909248395341278324517175820853286404743710145952644302282044037365125019184623573863075946389644423629304167773956447181872440665027369039751736977631813405,
     1230197346433601576871359147146318345794660644587556813317361121112259736906064757424819981626600536503633922734399282271582600808051530362336101942259823441839299355603967188517947938968081105138847731565260537550727639211683197879,
     1230175103148238074642625667635930176506433011031372989302833308622794392947506662903737049730958209740476679577696669046125817578949235076594181707520951908118478466754559490593457625240572006099913042624607335165510041897534435209,
     1230195419889872144876656964938566328205499234259210834822623032535100815525647028091967713768075507813160118981470260555950945392043292242406398016007295386079663975352275199434079009613722639414793342879897781273997830701207001839,
     1230174827420484335136670743465041112031290639257519417607166302989196920558878232997045073571925815302061150702941267642751824310540753494973942944709182841639107639574372312103384805313053886702823094137071927771880443876497834223,
     1230185988420782350445684618408731075592594374149632568516959143947765502111474165779039038375448460747737995123102271976521803891348006321134167925739699715318653372703575221067707929872597815216923462345784043188962130180912008349,
     1230182067416272799574586563163545941454653137377618375202713428977357995541722463977983142958255155175313052685136009571171283284447072364958073918526788611457558644331357136872625495927467161960356807765452292242153097362794537297,
     1230175667673121302771605367336364266247327534138626737357715728447532125946735363769285915448580252805827932111511713836998047583507066247536863749820566853793525683889378074234787827092004094825431802398656705570322640700888234457,
     1230196215847264556140155304337951587143228841840811940425256504477922246092127337047231112435502675187836322205816125340569108923142351137471846665258392302281836567949024626839375922984390501852966755563575130612920367863203222919,
     1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413]
    pub_e_list = [
     516522834974778788822737622050071002228140433403308439492366176194856535110473049678585760137133115927927751389873437178270126066640141239601219481836725664034890696476089482771855782560150644578106217444113872365843767702019835165,
     763019398230639020385095590111745749325105506404934257293148841704076883985487416392986296273940405033341623636375465706381388882972769884442281412068657916633528090146878551371406807944205178239005601520958614234641039656871416589,
     758988267473789691400810521950661631157749425185051374867090671651749645717430551141709267885532654476900928589359256799354189000242807872593092025777838259738748452497031844208902096380110211365512236965688511597330444132995706089,
     1160624249765899561934599801255171381291816170997589163041066264243942985094428544791715385847964978883555709070528078257993944356365754193784103706700609561770247555832359683008614876192584738534198649251915489100192910235497240729,
     969584864618350548573879514405798110739020997255786147754353418263140741786718203172013471748696400657712445258494301947449205093259143099235572291402265831321039275001497928579686310419303799571460516007736804962510266314260484877,
     977635869919691501956476862485989771461480520535754082824505449124021204549179419508424215127475063873127952064971338143136691515421466871627243381628988777601672460858098249144471775030169367579800220246847826510445608881346258241,
     78547544255936347746865903259860321812178052069796003154571672431430356709336862994676369608681727972743046653319757142976388460246409300441314120923213061705641045744824531268088986356203751782848608006264879618324460588039470689,
     917852167599983949701460374746950168138446186550074700858636881546192542929330983597633909534315139846805451671216776221152853388795036676315895948093016056124384137235869660229554239964598066970045657946753074761366723500887474273,
     512767915836808239407587704603826045022417741229198687031456060058182528147222248648359114235863620155721720617193551963387219761946115199232261616095015003006138489919312964841684835736418589367746436759162173805823822810793395319,
     1528664914065178933673821962753205462549978186396819666317790993639228285768444490202530533489915241541000489357324333513953018274712260336841857865516134868397620862505815431605074038377372062941322744934135683316080403207986149067]
    

    general.py:

    from hashlib import sha256
    
    def valid_serial(serial):
        assert type(serial) == str
        assert len(serial) > 0
        if not '0' != serial[0]:
            assert 1 == len(serial)
            for c in serial:
                if not ord('0') <= ord(c) <= ord('9'):
                    if not ord('A') <= ord(c) <= ord('F'):
                        raise AssertionError
    
            return serial
    
    
    def valid_username(username):
        assert type(username) == str
        assert len(username) > 0
        for c in username:
            assert ord(c) > 0
    
        return username
    
    
    def get_enc_seq(username):
        h = sha256()
        h.update(username)
        hash_value = int(h.hexdigest(), 16)
        T = 9
        S = 10
        stat = [0] * (T + 1)
        seq = [0]
        n = hash_value
        while n > 0:
            if S * T > len(seq):
                now = n % T + 1
                if stat[now] < S:
                    seq.append(now)
                    stat[now] += 1
                n //= T
    
        return seq
    
    
    def enc0(m, e, n):
        nbit = 192
        T = (n.bit_length() - 1) // nbit + 1
        ans = 0
        for i in range(T - 1, -1, -1):
    
            def xxx(x, t):
                return x >> t * nbit & (1 << nbit) - 1
    
            now_n = xxx(n, i)
            now_e = xxx(e, i)
            now_m = xxx(m, i)
            if now_m >= now_n:
                return 0
            ans = (ans << nbit) + pow(now_m, now_e, now_n)
    
        return ans
    

    可以看到是多重RSA
    解九个n需要
    其中大部分可以靠factordb/yafu/RSACTFTools解出
    注意其中:
    pub_n_list[0]需要分块求解
    pub_n_list[3]需要msieve
    pub_n_list[7]和pub_n_list[8]共模求解
    pub_n_list[5]利用循环:

    n = pub_n_list[5]
    e = pub_e_list[5]
    c = pow(2,e,n)
    print("start")
    for i in range(1,2000):
        c = pow(c,e,n)
        if c == 2:
            print(i)
    

    最终求解即可:

    from Crypto.Util.number import long_to_bytes
    from gmpy2 import invert
    e = [False,True,False,True,True,False,False,True,True,True]
    pub_n_list = [
     1230179032923966355216193664456989083993912178939747632284136330115404600706909248395341278324517175820853286404743710145952644302282044037365125019184623573863075946389644423629304167773956447181872440665027369039751736977631813405,
     1230197346433601576871359147146318345794660644587556813317361121112259736906064757424819981626600536503633922734399282271582600808051530362336101942259823441839299355603967188517947938968081105138847731565260537550727639211683197879,
     1230175103148238074642625667635930176506433011031372989302833308622794392947506662903737049730958209740476679577696669046125817578949235076594181707520951908118478466754559490593457625240572006099913042624607335165510041897534435209,
     1230195419889872144876656964938566328205499234259210834822623032535100815525647028091967713768075507813160118981470260555950945392043292242406398016007295386079663975352275199434079009613722639414793342879897781273997830701207001839,
     1230174827420484335136670743465041112031290639257519417607166302989196920558878232997045073571925815302061150702941267642751824310540753494973942944709182841639107639574372312103384805313053886702823094137071927771880443876497834223,
     1230185988420782350445684618408731075592594374149632568516959143947765502111474165779039038375448460747737995123102271976521803891348006321134167925739699715318653372703575221067707929872597815216923462345784043188962130180912008349,
     1230182067416272799574586563163545941454653137377618375202713428977357995541722463977983142958255155175313052685136009571171283284447072364958073918526788611457558644331357136872625495927467161960356807765452292242153097362794537297,
     1230175667673121302771605367336364266247327534138626737357715728447532125946735363769285915448580252805827932111511713836998047583507066247536863749820566853793525683889378074234787827092004094825431802398656705570322640700888234457,
     1230196215847264556140155304337951587143228841840811940425256504477922246092127337047231112435502675187836322205816125340569108923142351137471846665258392302281836567949024626839375922984390501852966755563575130612920367863203222919,
     1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413]
    pub_e_list = [
     516522834974778788822737622050071002228140433403308439492366176194856535110473049678585760137133115927927751389873437178270126066640141239601219481836725664034890696476089482771855782560150644578106217444113872365843767702019835165,
     763019398230639020385095590111745749325105506404934257293148841704076883985487416392986296273940405033341623636375465706381388882972769884442281412068657916633528090146878551371406807944205178239005601520958614234641039656871416589,
     758988267473789691400810521950661631157749425185051374867090671651749645717430551141709267885532654476900928589359256799354189000242807872593092025777838259738748452497031844208902096380110211365512236965688511597330444132995706089,
     1160624249765899561934599801255171381291816170997589163041066264243942985094428544791715385847964978883555709070528078257993944356365754193784103706700609561770247555832359683008614876192584738534198649251915489100192910235497240729,
     969584864618350548573879514405798110739020997255786147754353418263140741786718203172013471748696400657712445258494301947449205093259143099235572291402265831321039275001497928579686310419303799571460516007736804962510266314260484877,
     977635869919691501956476862485989771461480520535754082824505449124021204549179419508424215127475063873127952064971338143136691515421466871627243381628988777601672460858098249144471775030169367579800220246847826510445608881346258241,
     78547544255936347746865903259860321812178052069796003154571672431430356709336862994676369608681727972743046653319757142976388460246409300441314120923213061705641045744824531268088986356203751782848608006264879618324460588039470689,
     917852167599983949701460374746950168138446186550074700858636881546192542929330983597633909534315139846805451671216776221152853388795036676315895948093016056124384137235869660229554239964598066970045657946753074761366723500887474273,
     512767915836808239407587704603826045022417741229198687031456060058182528147222248648359114235863620155721720617193551963387219761946115199232261616095015003006138489919312964841684835736418589367746436759162173805823822810793395319,
     1528664914065178933673821962753205462549978186396819666317790993639228285768444490202530533489915241541000489357324333513953018274712260336841857865516134868397620862505815431605074038377372062941322744934135683316080403207986149067]
    n0 = [3 , 5 , 7185931 , 158808094450561 , 71865654341558562052460445666991255437082533473043601769875791167176804170025423671296979603455163675313185119614191555444054006826175580211227699148166547437793831583944982630043047905318718417096260894067097]
    phi = 1
    for i in n0:
        phi*=i-1
    d0 = invert(pub_e_list[0]//5,phi)
    e0 = 5 * 269 * 100479347467620083 * 3821997825858167173910810789124613195528533611172697535969177289820154015727992167281727873897740742740733210910948103694567722741818071770351352777527509949808410647489867809691490251021126067129368597541181679
    
    n1 = [3012613441, 408348887278831461892610983390456945715612189153595698014201723503582993177671598068402822885301113712860820610804438839912419021850712894809826330539771179969086301239790288951959814006579380835620367785931463468740342519]
    e1 = 763019398230639020385095590111745749325105506404934257293148841704076883985487416392986296273940405033341623636375465706381388882972769884442281412068657916633528090146878551371406807944205178239005601520958614234641039656871416589
    phi = 1
    for i in n1:
        phi*=i-1
    d1 = invert(pub_e_list[1],phi)
    p2 = 36796059015915981995743432427927902530578158826788662547913509800095517347118062478922356654842254755916234587213077
    q2 = pub_n_list[2]//p2
    phi2 = (p2-1)*(q2-1)
    e2 = 7537 * 9255264980309 * 10880470858007751390324652265533378235468341001689138251269957159180985184462655657188218085336892458554427149527946369954194849737545474019636015673163686687177842336952960193593542624177105956374331546420180666133
    d2 = invert(e2,phi2)
    print(d2)
    n7 = pub_n_list[7]
    n8 = pub_n_list[8]
    p4 = 35073848198058968264749790689745197012385911613318747771531468291963621262770461817935405298684967674641247961569207
    q4 = 35073848198058968264749790689745197012385911613318747864444430571453140176920442860760331340059929469925220710756489
    d4 = invert(pub_e_list[4],(p4-1)*(q4-1))
    e5 = 11 * 13 * 191 * 733657391 * 4026960893339 * 12115379258185536246641740189808027591877771839170387403121796053010686218208201487072073319275818512067594227149411425111528971256589583807806340652926403236922622238967009523050297874688181255609871957293
    p = gcd(n7,n8)
    p6 = 26153324138194530872309557862464074525306735281010877795840958405693838446634362256045094682633541743845611502925341
    q6 = 47037312003475104493787837223043052957997925611039410248119470723357078273788416725476266555402464778755414193496517
    phi6 = (p6-1)*(q6-1)
    d5 = 1
    d6 = invert(pub_e_list[6],phi6)
    d7 = invert(pub_e_list[7],(p-1)*(n7//p-1))
    d8 = invert(pub_e_list[8],(p-1)*(n8//p-1))
    p9 = 33478071698956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489
    q9 = pub_n_list[9]//p9
    phi9 = (p9-1)*(q9-1)
    d9 = invert(pub_e_list[9],phi9)
    print(0,d1,d2,0,d4,0,d6,d7,d8,d9)
    c = pow(2,pub_e_list[9],pub_n_list[9])
    assert 2 == pow(c,d9,pub_n_list[9])
    def solve_n5(n,e,c,iteration=972):
        for i in range(iteration):
            c = pow(c,e,n) 
        return c
    # solve_n5(pub_n_list[5],pub_e_list[5],c,972)
    from gmpy2 import gcd,is_prime,next_prime
    pub_n_list = [
     1230179032923966355216193664456989083993912178939747632284136330115404600706909248395341278324517175820853286404743710145952644302282044037365125019184623573863075946389644423629304167773956447181872440665027369039751736977631813405,
     1230197346433601576871359147146318345794660644587556813317361121112259736906064757424819981626600536503633922734399282271582600808051530362336101942259823441839299355603967188517947938968081105138847731565260537550727639211683197879,
     1230175103148238074642625667635930176506433011031372989302833308622794392947506662903737049730958209740476679577696669046125817578949235076594181707520951908118478466754559490593457625240572006099913042624607335165510041897534435209,
     1230195419889872144876656964938566328205499234259210834822623032535100815525647028091967713768075507813160118981470260555950945392043292242406398016007295386079663975352275199434079009613722639414793342879897781273997830701207001839,
     1230174827420484335136670743465041112031290639257519417607166302989196920558878232997045073571925815302061150702941267642751824310540753494973942944709182841639107639574372312103384805313053886702823094137071927771880443876497834223,
     1230185988420782350445684618408731075592594374149632568516959143947765502111474165779039038375448460747737995123102271976521803891348006321134167925739699715318653372703575221067707929872597815216923462345784043188962130180912008349,
     1230182067416272799574586563163545941454653137377618375202713428977357995541722463977983142958255155175313052685136009571171283284447072364958073918526788611457558644331357136872625495927467161960356807765452292242153097362794537297,
     1230175667673121302771605367336364266247327534138626737357715728447532125946735363769285915448580252805827932111511713836998047583507066247536863749820566853793525683889378074234787827092004094825431802398656705570322640700888234457,
     1230196215847264556140155304337951587143228841840811940425256504477922246092127337047231112435502675187836322205816125340569108923142351137471846665258392302281836567949024626839375922984390501852966755563575130612920367863203222919,
     1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413]
    def enc0(m, e, n):
        nbit = 192
        T = (n.bit_length() - 1) // nbit + 1
        ans = 0
        for i in range(T - 1, -1, -1):
    
            def xxx(x, t):
                return x >> t * nbit & (1 << nbit) - 1
    
            now_n = xxx(n, i)
            print(now_n)
            now_e = xxx(e, i)
            print(now_e)
            now_m = xxx(m, i)
            if now_m >= now_n:
                return 0
            ans = (ans << nbit) + pow(now_m, now_e, now_n)
    
        return ans
    enc0(1,pub_e_list[0],pub_n_list[0])
    n0 = [4973828634074084070222279013150769733596223651485114564273,6277087998938792861042405062580257456598525084583043892593,6277031389885428580574088212602983156224270664268963309877,6277052177355867862708971469565390229110991711310599757597]
    e0 = [2088392012863598836930805392926351714650188437587735488361,3784482471495320575589048728247975381653380680141800804337,5452515267665389664275951067811576469767027838421869719869,2513827307676496129699780837261567723809491022362599687453]
    p00,q00 = 66428487199875098638682243347, 74874934591065563984140844459
    p11,q11 = 62221071216704155848060102353, 100883637587609016545825234081
    p22,q22 = 46189403453019724968918893779, 135897650123798146894617474263
    p33,q33 = 50652156588134490618159573691, 123924677647906847227542543367
    phi00 = (p00-1)*(q00-1)
    phi11 = (p11-1)*(q11-1)
    phi22 = (p22-1)*(q22-1)
    phi33 = (p33-1)*(q33-1)
    p3 = 34508357350889186927005771176009938286558270433215942821530479444470024241863567490529149148811494601012847563892119
    q3 = pub_n_list[3]//p3
    phi3 = (p3-1)*(q3-1)
    d3 = invert(pub_e_list[3],phi3)
    phi = [phi00,phi11,phi22,phi33]
    d0 = [invert(e0[i],phi[i]) for i in range(4)]
    pub_d_list = [0,1229061379053482744823152709690434888605435342198642793057361495153090878523561729157374272032362247153476832905266577597936263875922635915871109425948662301015976854125739243408750974498511602088546684816889116185016023460221241669,777757116197429014363243483536399809327489478672765799132008796613164652485176625704586327714092043960999487185568710834915265226900792279774759093092549483626423808502139519027474793822444064402879491357824424416255246538629615833,d3,201419093613711761798540120588879041236840801068737686325195769604622639638393093247553615834885873886104026065263583851481439063609372020322699902282368593094548575688163522138303256348801329176798576662806247524508598547117027909,0,922636550562204599680939922372659456090989853033213781402035071733018496656291847983487357218691366381484789513851952285401356211108779700672241308549478980097499945532484886375918716118514969665502334756552044884230174523807259329,627567627354326605515830278366021138159090868181225778581524414114680017539982571880336309158282300554534438008003322761037932916796881232969829716072515736525821134512405811777096389132413642826921797756179543381108933388795804769,87763698610259057934268676955169084425464093902524300758830437447338976666426976224860384022223934601405128988601223084334832120115000652985202766336425151725676233383146800117094745887838499800523801384784235875984207816521259103,476133969086427941368462698171675198835788633664060460402567958612344234273279077792246479357026462298849835811290909092037603283477920353500979549470973082158764663458247740861805650589884280458476962517272738918503674157481390819]
    from Crypto.Util.number import long_to_bytes
    def dec0(pub_n,n0,d0,m):
        nbit = 192
        T = (pub_n.bit_length() - 1) // nbit + 1
        ans = 0
        for i in range(T - 1, -1, -1):
            idx = 3-i
            def xxx(x, t):
                return x >> t * nbit & (1 << nbit) - 1
            now_m = xxx(m, i)
            ans = (ans << nbit) + pow(now_m, d0[idx], n0[idx])
        return hex(ans)[2:].upper()
    def reverse(serial, seq):
        now = serial
        for j in range(len(seq)):
            i = seq[j]
            n = pub_n_list[i]
            e = pub_e_list[i]
            d = pub_d_list[i]
            if i > 0 and i!=5:
                now = pow(now, d, n)
            elif i==5:
                now = solve_n5(n,e,now,iteration=972)
            else:
                now = dec0(pub_n_list[0],n0,d0, now)
                if 0 == now:
                    return False
        return now
    username = "KCTF"
    sec = get_enc_seq(username.encode('utf-8'))
    from Crypto.Util.number import long_to_bytes,bytes_to_long
    name = bytes_to_long(username.encode('utf-8'))
    #"89F3A5FD45E2A383"
    #A7E477BED7F21984189BF0E42F92252C5B8EAC3D816F0A354A60FA97ACCA5E45EF94D1BD557C0BFD4AE6AF65236BDC205B084D3B9D7771C630611D5B2C3D4F10F14CE7C206DCDFDBD3578335BE68AD23018D2FC95D2F1F16ABD7FC78D22E2DB9
    #CA1F4C8740C8E620C4AC306F2C3BCB3FD7ABCC1378D94779DD78481EDC412ACE40A2092B4726A259367C43F3AA3DDDDF54C2513318A11C61867BC43882498F32EBD694C77786A9091FC1333E810888C01049E97F15E4B64123A913E5794B0614
    t = hex(bytes_to_long(b'\xa7\xe4w\xbe\xd7\xf2\x19\x84\x18\x9b\xf0\xe4/\x92%,[\x8e\xac=\x81o\n5J`\xfa\x97\xac\xca^E\xef\x94\xd1\xbdU|\x0b\xfdJ\xe6\xafe#k\xdc [\x08M;\x9dwq\xc60a\x1d[,=O\x10\xf1L\xe7\xc2\x06\xdc\xdf\xdb\xd3W\x835\xbeh\xad#\x01\x8d/\xc9]/\x1f\x16\xab\xd7\xfcx\xd2.-\xb9'))
    print("a7e477bed7f21984189bf0e42f92252c5b8eac3d816f0a354a60fa97acca5e45ef94d1bd557c0bfd4ae6af65236bdc205b084d3b9d7771c630611d5b2c3d4f10f14ce7c206dcdfdbd3578335be68ad23018d2fc95d2f1f16abd7fc78d22e2db9".upper())
    print(reverse(name,sec[::-1]))
    

    0x03 街机少年

    XYLearn tqllllll ! 7feilee tqllllll !
    应该是非预期了
    IDA载入程序,看到主程序:

      if ( get_input() )
      {
        SetUnhandledExceptionFilter(TopLevelExceptionFilter);
        if ( GAME((unsigned __int8 *)name, name_len) )
          output("You Win! You are very clever!");
        else
          output("Game Over");
        system("pause");
        result = 0;
      }
      else
      {
        output("Game Over");
        system("pause");
        result = 0;
      }
    

    程序读入name和serial,而后走过一个判断,通过GAME函数返回值来判断是否正确
    首先读入name和serial:

      for ( i = 0; i < 32; ++i )
      {
        v4 = read_input();
        if ( (v4 < 97 || v4 > 122) && (v4 < 65 || v4 > 90) && (v4 < 48 || v4 > 57) )
        {
          if ( v4 != 13 && v4 != 10 )
            return 0;
          name_len = i;
          break;
        }
        name[i] = v4;
      }
      if ( name_len < 4 || name_len > 16 )
        return 0;
      output("Please input your KEY: ( : to z )");
      for ( j = 0; j < 128; ++j )
      {
        v3 = read_input();
        if ( v3 < ':' || v3 > 'z' )
        {
          if ( v3 != 13 && v3 != 10 )
            return 0;
          serial_len = j;
          break;
        }
        serial[j] = v3;
      }
      if ( !serial_len || serial_len > 128 )
        return 0;
      dec_len = dec1((int)decode_serial, (int)serial, serial_len);
    

    简单判断name和serial合法后decode并存入全局变量数组
    decode类似一个base64,只是对应处理位不同,直接对照求逆即可:

    def dec1(str1):
        ans=[]
        for i in range(len(str1)/3):
            ans.append(ord(str1[i*3])>>2)
            ans.append(((ord(str1[i*3])&0b11)<<4)+((ord(str1[i*3+1])&0b11110000)>>4))
            ans.append((ord(str1[i*3+1])&0b1111)+((ord(str1[i*3+2])&0b11000000)>>2))
            ans.append(ord(str1[i*3+2])&0b00111111)
        s=""
        for i in ans:
            s+=chr(i+ord(":"))
        return s
    #后面不足对应相同位置并补'z'即可
    

    在GAME中,首先使用大量数组查表隐藏了算法和计算过程
    首先将第一轮table扣下来:

    def func1(x, m):
        s = x
        none_primitive_root = 0
        for i in range(1, m):
            y = min(x, s)
            z = max(x, s)
            s = y * y % 17 ^ triangle_at(xor_table, y, z)
    ......
    

    根据返回值发现是一个乘方运算(取模意义下),计算pow(x,m)
    故而在第一层:

       for ( i = 1; i < 16; ++i )
        {
          v35 = v38;
          v27 = v51;
          if ( v38 > (signed int)v51 )
          {
            v35 = v51;                              // 乘方
            v27 = v38;
          }
          v38 = v35 * v35 % 17 ^ table[(unsigned __int8)(v27 + (v35 - 1) * (34 - v35) / 2 - v35)];
          if ( v38 == 1 && i != 15 )  // (p-1)/2不满足 
          {
            v51 = (v51 + 1) % 16 + 1;  
            v38 = 0;
            break;
          }
        }
    

    其实际计算的是相对v51步长2的模17下最近的非二次剩余
    而后其根据输入name初始化了一个256长度数组(前16参与运算)
    而后大致流程可以取下:

    from base64 import b64decode,b64encode
    import os
    from functools import reduce
    
    xor_table = [0,3,2,5,4,7,6,9,8,11,10,13,12,15,14,17,0,2,12,14,8,10,20,5,7,1,3,13,15,9,11,0,5,6,8,13,14,3,4,25,11,12,1,2,7,0,19,23,27,31,18,22,26,30,17,21,25,29,0,5,9,14,3,24,12,1,6,10,15,4,0,10,12,1,11,13,6,8,18,7,9,0,10,3,13,6,31,9,2,12,5,0,9,1,14,6,15,7,12,4,0,8,3,11,2,10,29,5,0,7,14,4,11,1,8,0,15,5,3,14,4,0,11,7,2,13,0,28,24,20,0,15,10,0,6,0]
    
    
    with open('E:/Totoro.exe', 'rb') as f:
        f.seek(0x16088, os.SEEK_SET)
        bytes_397288 = f.read(65536)
        bytes_397288 = [i for i in bytes_397288]
        f.seek(0x26088, os.SEEK_SET)
        bytes_3A7288 = f.read(256)
        bytes_3A7288 = [i for i in bytes_3A7288]
        f.seek(0x14E88, os.SEEK_SET)
        bytes_396088 = f.read(256)
        bytes_396088 = [i for i in bytes_396088]
        f.seek(0x14F88, os.SEEK_SET)
        bytes_396188 = f.read(4096)
        bytes_396188 = [i for i in bytes_396188]
        f.seek(0x15F88, os.SEEK_SET)
        bytes_417188 = f.read(256)
        bytes_417188 = [i for i in bytes_417188]
    
    def quadratic_residues(x, m):
        while True:
            s = x
            for i in range(1, m):
                y = s
                z = x
                if s > x:
                    y = x
                    z = s
                idx = (z + (y - 1) * (34 - y) // 2 - y)%256
                s = (y * y % 17) ^ xor_table[idx]
                if s == 1 and i != 15:
                    x = (x + 1) % 16 + 1
                    s = 0
                    break
            if s == 1:
                break
        return x
    
    def _pow(x, m):
        s = x
        global flag
        for i in range(1, m):
            y = s
            z = x
            if s > x:
                y = x
                z = s
            idx = (z + (y - 1) * (34 - y) // 2 - y)%256
            s = (y * y % 17) ^ xor_table[idx]
            if s == 1 and i != 15:
                flag = 1
        return s
    
    def triangle_index(y, z):
        return z + (y - 1) * (34 - y) // 2 - y
    
    def triangle_at(mat, y, z):
        return mat[triangle_index(y, z)]
    
    def get_inv(x):
        while True:
            s = x
            for i in range(1, 16):
                y = min(x, s)
                z = max(x, s)
                s = y * y % 17 ^ triangle_at(xor_table, y, z)
                if s == 1 and i != 16 - 1:
                    x = (x + 1) % 16 + 1
                    s = 0
                    break
            if s == 1:
                break
        return x
    
    def pows(x, m):
        s = x
        none_primitive_root = 0
        for i in range(1, m):
            y = min(x, s)
            z = max(x, s)
            s = y * y % 17 ^ triangle_at(xor_table, y, z)
            if s == 1 and i != m-1:
                none_primitive_root = 1
        return s,none_primitive_root
    
    def map_username(username,bytes_417188):
        for i in range(len(username) - 1):
            ch = username[i + 1]
            if i >= 7:
                if i >= 10:
                    if i >= 13:
                        bytes_417188[i] = ch % 8 + 8
                    else:
                        bytes_417188[i] = 7 - ch % 7
                else:
                    bytes_417188[i] = ch % 8 + 8
            else:
                bytes_417188[i] = 7 - ch % 7
        return bytes_417188
    
    
    
    def get_idx(a, b, c):
        lo = ((b >> 2) ^ ((c << 4) & 0xC0)) ^ ((a << 2) & 0x3C) ^ 0xC7
        hi = (c & 3) ^ ((b << 2) & 0xf) ^ 2
        x = lo | (hi << 8)
        return x
    
    
    def unknown(a, b, c):
        '''a^b^c'''
        return bytes_397288[((a + b + c) % 16) | (a << 4) | (b << 8) | (c << 12)]
    
    
    def check(username, key):
       #key=  [61, 136, 200, 67, 168, 249, 66, 246, 42, 33, 212, 253, 243, 246, 246, 244, 246, 249, 244, 255, 237]
        key_len = len(key)
        ch = username[0] % 16 + 1
        username = map_username(username,bytes_417188)
        primitive_root = quadratic_residues(ch,16)
        kidx = 0
        x = 5
        while kidx < key_len:
            key_low = key[kidx] % 16 + 1
            key_high = (key[kidx]>>4) % 16 + 1
            flag = 1
            if primitive_root and key_low <= 16 and key_high <=16:
                flag = 0
                key_low = _pow(primitive_root, key_low)
                r2 = quadratic_residues(key_low,16)
                key_high = _pow(r2, key_high) 
                if flag:
                    break
            primitive_root = quadratic_residues(key_high,16)
            primitive_flag = (key_high * bytes_396088[username[key_low - 1] - key_high])&0xff
            for i in range(key_low, 128):
                v4 = int(username[i] == 0)
                v5 = (((v4 + bytes_396188[get_idx(username[key_low - 1], username[i], key_high)])%256) * primitive_flag)&0xff
                primitive_flag = (((v5 + 1) & int(v5 != 0)) + v5)&0xff
            username[key_low - 1] = (username[key_low - 1] + ((((2 * ((primitive_flag + 1) & 1) - 1)&0xff) * key_high)&0xff))&0xff
            #print("test xor is {}".format(reduce(lambda x, y: x ^ y, username)))
            if not primitive_flag:
                break
            if not any(username):
                return kidx == key_len - 1
            key_low_ = (key[kidx] >> 4) % 16 + 1
            key_high_ = key[kidx + 1] % 16 + 1
            bytes_396088[key_low_ // 2] = (bytes_396088[key_low_ // 2] + key_high_) % 127 + 1
            kidx += 1
            tmp = unknown(username[2], username[1], username[0])
            tmp = unknown(username[4], username[3], tmp)
            tmp = unknown(username[6], username[5], tmp)
            tmp = unknown(username[8], username[7], tmp)
            tmp = unknown(username[10], username[9], tmp)
            tmp = unknown(username[12], username[11], tmp)
            tmp = unknown(username[14], username[13], tmp)
            if tmp:
                for i in range(15):
                    val = bytes_3A7288[tmp | (username[i] << 4)]
                    if val & 1:
                        username[i] = val >> 1
                        break
            else:
                print(kidx)
                fflag = 0
                while True:
                    if username[x] == 1:
                        username[x] = (username[x] - 1)%256
                        x = (x + 4) % 15
                        fflag = 1
                        break
                    if username[x] > 1:
                        break
                    x = (x + 4) % 15
                if fflag == 0:
                    username[x] = username[x] - ((x * x + 1) % username[x] + 1)
                    x = (x + 4) % 15
            if not reduce(lambda x, y: x | y, username):
                break
        return False
    

    在循环过程中,每轮12bit key参与运算,首先根据第一步结果进行乘方,再进行一次求非平方剩余,再进行一次乘方(不出现单位元),根据结果更新一位username数组,而后进行一轮变换(unknown),根据变换结果更新一位username
    通过观察数组及结果发现unknown操作是进行一组异或
    若异或后不为0则依赖第二个数组将其每位异或结果调整为0
    通过观察正确序列号发现:

    正确退出应该是循环过每一位序列号经由最后: 循环次数==序列号长度-1来return
    在正确序列号变换下tmp每轮是0
    

    其实到这里也没猜到具体的数学背景是什么,最开始肖神通过unknown中变换的数组搜索到AES的add field,不过后来发现只是一组异或,遂失败
    不过从上面分析可以看到,每轮参与计算的只有12bit,而且通过正确序列号的程序流可以猜测到每一轮tmp=0即为控制条件,以此进行逐位爆破即可得到可能解
    最后肖神用unicorn成功解出(tqlll !!!)(python爆破我写得有点问题没搞定(飞神那边应该快改好了,已经出了几位)):

    import sys
    import os
    import struct
    from base64 import b64decode, b64encode
    from capstone import *
    from capstone.x86_const import *
    from unicorn import *
    from unicorn.x86_const import *
    
    def p32(n):
      return struct.pack("<I", n)
    to = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    base = b':;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxy'
    def base64_enc(data):
        data = b64encode(data)
        res = []
        for i in data:
            res.append(base[to.index(i)])
        return ("".join([chr(i) for i in res])).encode()
    def base64_dec(data):
        res = []
        for i in data:
            res.append(to[base.index(i)])
        return b64decode("".join([chr(i) for i in res]).encode())
    
    cs = Cs(CS_ARCH_X86, CS_MODE_32)
    
    last_addr = 0
    def hook_code(mu, addr, size, user_data):
      global last_addr
      last_addr = addr
      code = mu.mem_read(addr, 10)
      ins = next(cs.disasm(code, addr, 1))
      disasm = "{:08x}: {} {}".format(addr, ins.mnemonic, ins.op_str)
      print(disasm)
    
    mu = None
    with open("./Totoro.exe", 'rb') as f:
        f.seek(0x400, os.SEEK_SET)
        text = f.read(0xf13b - 0x400)
        f.seek(0xf320, os.SEEK_SET)
        rdata = f.read(0x14c68 - 0xf320)
        f.seek(0x14E00, os.SEEK_SET)
        data = f.read(0x26C00-0x14E00)
    def check(username, key, addr_points=None):
      global mu
      mu = Uc(UC_ARCH_X86, UC_MODE_32)
      text_start , text_end  = 0x401000, 0x410000
      idata_start, idata_end = 0x410000, 0x410120
      rdata_start, rdata_end = 0x410120, 0x416000
      data_start , data_end  = 0x416000, 0x429000
      stack_start, stack_end = 0x7ffa0000, 0x80000000
      cache_start, cache_end = 0x100000, 0x110000
      stack_curr = 0x7fff0000
      mu.mem_map(text_start, data_end - text_start)
      mu.mem_write(text_start, text)
      mu.mem_write(rdata_start, rdata)
      mu.mem_write(data_start, data)
      mu.mem_map(stack_start, stack_end - stack_start)
      mu.mem_map(cache_start, cache_end - cache_start)
      mu.reg_write(UC_X86_REG_ESP, stack_curr)
      # key
      key_addr = 0x427E38
      key_len_addr = 0x427F38
      mu.mem_write(key_addr, key)
      mu.mem_write(key_addr + len(key), b'\0' * (256 - len(key)))
      mu.mem_write(key_len_addr, p32(len(key)))
      # username
      mu.mem_write(stack_curr + 4, p32(cache_start))
      mu.mem_write(cache_start, username)
      mu.mem_write(stack_curr + 8, p32(len(username)))
      # hook
      # mu.hook_add(UC_HOOK_CODE, hook_code)
      # execute
      check_addr = 0x401480
      check_ret_addr = 0x402088
      try:
        if not addr_points:
          mu.emu_start(check_addr, check_ret_addr)
        else:
          start = check_addr
          for addr in addr_points:
            mu.emu_start(start, addr)
            mu.emu_start(addr, 0, count=1)
            start = mu.reg_read(UC_X86_REG_EIP)
      except UcError as e:
        return False
      return True
    
    username = b'KCTF'
    key = [0 for i in range(0x20)]
    
    # key = [61, 136, 200, 67, 168, 249, 66, 246, 42, 33, 212, 253, 243, 246, 246, 244, 246, 249, 244, 255, 237, 249]
    end = False
    for i in range(21):
      for ch in range(256):
        key[i] = ch
        if not check(username, bytes(key), [0x401F00] * (i+1)):
          continue
        if mu.reg_read(UC_X86_REG_EAX) == 0:
          print(i, ch)
          break
        key[i] = 0x100
      if key[i] == 0x100:
        break
    i += 1
    for ch in range(256):
      key[i] = ch
      if not check(username, bytes(key)):
        continue
      if mu.reg_read(UC_X86_REG_EAX) == 0:
        print(i, ch)
        break
    print(key)
    #Output:[61, 136, 200, 67, 168, 249, 66, 246, 42, 33, 212, 253, 243, 246, 246, 244, 246, 249, 244, 255, 237, 249]
    -------------------------------------------------------------------------------
    def dec1(str1):
        ans=[]
        for i in range(len(str1)/3):
            ans.append(ord(str1[i*3])>>2)
            ans.append(((ord(str1[i*3])&0b11)<<4)+((ord(str1[i*3+1])&0b11110000)>>4))
            ans.append((ord(str1[i*3+1])&0b1111)+((ord(str1[i*3+2])&0b11000000)>>2))
            ans.append(ord(str1[i*3+2])&0b00111111)
        ans.append(ord(str1[21])>>2)
        ans.append((ord(str1[21])&0b11)<<4)
        s=""
        for i in ans:
            s+=chr(i+ord(":"))
        return s+"zz"
    s=""
    final_key=[61, 136, 200, 67, 168, 249, 66, 246, 42, 33, 212, 253, 243, 246, 246, 244, 246, 249, 244, 255, 237, 249]
    for i in final_key:
        s+=chr(i)
    print dec1(s)
    #Output:IRrBJtrsJi@dBWnwvyppwIpswIygxJzz
    

    0x04 小虎还乡

    Windows 10 64位驱动程序
    由于我用的是驱动测试签名,因此请打开测试驱动签名,请做如下操作:
    管理员运行cmd,然后输入:
    bcdedit /set testsigning on
    回车,重启即可。
    

    可以看到关键判断:

     scanf(&input_format, &v8);                    // %d
      if ( (signed __int64)v8 <= 96000 && (signed __int64)v8 >= 90000 )
      {
        v15 = 66i64;
        v16 = 169i64;
        v12 = 231i64;
    

    其他地方没有输入的程序流
    所以猜测答案在90000-96000之间,由此确定了应该使用爆破,接下来patch掉输入在90000-96000时的弹窗即可
    经过几次patch可以发现在:

    __int64 sub_140002050()
    {
      __int64 v0; // rax
    
      MessageBoxW(0i64, L"wrong!", L"err", 0);
      v0 = sub_1400014D0(aKerneloadriven);
      return sub_140001E70(v0);
    }
    

    将这里的MessageBoxW patch掉,而后爆破直到程序本身弹出"ok"即可:

    import time
    from subprocess import Popen, PIPE
    
    for i in range(90000, 96000):
      p = Popen("./ConsoleApplication1.exe", stdin=PIPE, stdout=PIPE, stderr=PIPE)
      # time.sleep(0.01)
      p.stdin.write(str(i)+"\n")
      res = p.communicate()
      # if len(res[0])!=len("Enter pass word: "):
      #     print str(i),res
      # if i %100==0:
      print i
    #输出ok时为:91024
    

    相关文章

      网友评论

        本文标题:KCTF 2019 Finals

        本文链接:https://www.haomeiwen.com/subject/awphgctx.html